fmod:1.通过SoundManager在OGRE中使用fmod

来源:互联网 发布:勇芳软件 编辑:程序博客网 时间:2024/04/24 12:59

通过SoundManager在OGRE中使用fomd

 
使用SoundManager来在OGRE中播放音乐:
【前期工作】

   1.确保你的OGRE程序能正确运行,即使是只有一个地面ground或则一个ogre.mesh的示例代码,使用了ExampleFramelistener和ExampleApplication那种。有关如何使用VC2005建立OGRE的project这里就不再多说了,可以参考我介绍pagedGeometry时构建的步骤。
【文章末尾附录1:有最简单的缓冲输入实现,运行结果就是一个怪物的头在平坦的地面之上,WSAD实现空间移动,摁住鼠标右键实现镜头的转动】

 

   2. 在官网下载一个SoundManager的simple例子,顺便下载FMOD的声音引擎(为什么呢,因为那个例子没有附带FMOD的一些运行库和我们所需要的一些头文件:像 fmod.h,fmod.hpp,fmod_codec.h ,fmod_dsp.h ,fmod_output.h ,fmod_errors.h,fmod_memoryinfo.h)

网站链接:

fmod:http://www.fmod.org/index.php/download

SoundManager:http://www.ogre3d.org/tikiwiki/FMOD+SoundManager

 

   3. 给程序添加(include)相应的文件  fmod.h,fmod.hpp,fmod_codec.h ,fmod_dsp.h ,fmod_output.h fmod_memoryinfo.h,fmod_errors.hSoundManager.h和SoundManager.cpp,你可以把他们放到一个指定的文件夹,比如:include里,然后包含该文件夹路径。
【否则报错:无法打开包括文件:“SoundManager.h”: No such file or directory之类的错误】

 

   4.再在lib文件夹里添加fmodex_vc.lib(至于这些个文件从何得来,你可以在你下载的例子中找到,或者你以前有使用过FMOD的声音引擎,这个可以直接拷贝进去,fmodex_vc.lib表示编译器为VC++,其他编译器按情况来定),添加完了之后,在项目属性--配置--连接器--输入一栏,记得添加依赖库文件fmodex_vc.lib,多个库文件用 ; 隔开(有些编译器用空格隔开,这点要留心,最保险就是点输入栏右边的浏览选项,进入lib列表的文本框,用回车换行添加)。
【否则出错:出现N多:
   无法解析的外部符号 ……
   无法解析的外部符号 ……
   无法解析的外部符号 ……
 

 

   5.添加相应的链接库fmodex.dll和fmodexL.dll(如果有)到Ogre的目标文件生成文件夹release(也就是生成.exe的文件夹)
【否则后面运行程序会报错:无法启动程序,因为计算机中丢失fmodex.dll】

 

   6.在目标生成文件夹release或者debug文件夹中找到resources.cfg文件,以记事本的形式打开,检查资源文件,务必要把你所要播放的文件夹包含进来,否则使用ExampleApplication的话,加载资源时没有加载到你所要播放的声音文件。
比如声音文件1.mp3存放在 media文件夹下的sound文件夹中,resources.cfg中就必须有这么一句话:
FileSystem=../../media/sound
…(其他包含路径)
【很多时候检查使用发现播放无声音,使用方法又没什么错误,基本就是这个原因了。】

 

   7.可能有些旧的版本或者新的版本函数有更新,比如函数:

 result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback, &fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048);

旧的版本是接受5个参数,可能编译会提示“该函数不接受5个参数”,你可以吧鼠标移动到函数名上,看到当前版本函数的参数列表比如:


红色位置是鼠标光标位置,会弹出函数参数列表框,照着补齐就可以了。
再就是不同版本的OGRE函数写法有点不一样,比如

下载下来的文件中使用获取Node节点的位置函数为:

Node->getWorldPosition();

我的1.6.5 OGRE中,对应函数为:

Node->getPosition();

编译报错的话要耐心去排除,上论坛求助或者google搜索解决方案,或者和楼主一起讨论,楼主很乐意一起学习。

 

   之所以我会把操作细节讲得这么大篇幅,是因为要是不能扔程序跑起来运行观看效果的话,一切学习都是空谈,真正运用到的时候还是会被这些小问题卡住的。


 

 

【开始使用】


    1.为了简洁起见,方便学习,我们定义一些全局变量,省去参数的传递

 SoundManager  *soundMgr;
 int qplSoundIndex;
 int qplSoundChannel;
 SceneNode *soundNode; //声源位置
 SceneNode *camNode;
//以相机位置作为听众位置

 

   2.在Application类的createscene函数中,在你初始化完了场景环境光、相机等等之后加下面这一段代码:

  soundMgr=new SoundManager; //新建SoundMananger类
  soundMgr->Initialize();  //初始化类,无非就是建立fmodsystem,channel,Fmod的Init等
  qplSoundIndex=soundMgr->CreateLoopedSound(Ogre::String("qpl.wma"));//加载声音
  qplSoundChannel=INVALID_SOUND_CHANNEL; //播放频道,初始值为空
  soundMgr->PlaySound(qplSoundIndex,soundNode,&qplSoundChannel); //播放声音,将声音绑定在soundNode
  //的位置上,并给刚才的频道赋值
  soundMgr->Set3DMinMaxDistance(qplSoundChannel,50.0f,2000.0f);
//设置远近声音增大变小,与声源的
         //距离小于50,则声音不再变大;与声源距离大于2000,则声音不再减少;50~2000之间线性减小。

 

   3.在Framelistener类中,的frameRenderingQueued中,增加一行代码:

 soundMgr->FrameStarted(mCamNode,evt.timeSinceLastFrame);

 

运行下你的程序,如果能成功运行了,那么恭喜你,你已经会用soundManager了,但是奇怪的现象的发生了,当我们试图向地图中的怪物头(声源)移动的时候,听到了曲子变调了,靠近声源会变得尖锐,远离会变得低沉。这是因为这些代码的作者精心给我们封装的声音效果,这种效果叫多普勒效应。因为移动速度太快而引起声音变调,我们可以适当修改下:


SoundManager.h中,加入这一句

#define SOUNDSCALE 5

表示移动距离的比例,表示程序的速度 5米/s = 实际多普勒效应中的 1米/s


SoundManager.cpp中,找到:

void SoundManager::FrameStarted(Ogre::SceneNode *listenerNode, Ogre::Real timeElapsed)函数,
找到这段代码:  
for (channelIndex = 0; channelIndex < MAX_SOUND_CHANNELS; channelIndex++)
{
 if (channelArray[channelIndex].sceneNode != NULL)
 {
  system->getChannel(channelIndex, &nextChannel);
  if (timeElapsed > 0)
   vectorVelocity = (channelArray[channelIndex].sceneNode->getPosition() - channelArray[channelIndex].prevPosition) / timeElapsed;
  else
   vectorVelocity = Vector3(0, 0, 0)
 }
在这段代码的else后加一句:listenerVelocity/=SOUNDSCALE;并用{}把他和他前面的那一句括号起来,变成if的执行动作
for (channelIndex = 0; channelIndex < MAX_SOUND_CHANNELS; channelIndex++)
{
 if (channelArray[channelIndex].sceneNode != NULL)
 {
  system->getChannel(channelIndex, &nextChannel);
  if (timeElapsed > 0)
  
   vectorVelocity = (channelArray[channelIndex].sceneNode->getPosition() - channelArray[channelIndex].prevPosition) / timeElapsed;
   vectorVelocity/=SOUNDSCALE;
   }
else
vectorVelocity = Vector3(0, 0, 0);

再运行程序看看是不是变得真实多了?

文件构架:



我把最终的例子代码贴出来,附录2:完整实例代码

 

在这里你可能会很疑惑?

你怎么知道要去哪里改什么东西?

SoundManager里到底又写了些什么东西?

封装了那些特效,实现原理是什么?


要是觉得SoundManager好用,可以熟悉下他的用法然后用到自己的程序中,因为这是开源免费的,不过要饮水思源哦。如果想更深入了解fmod,想封装自己的类类实现某种特殊需要,那么请看下一篇文章。

 

 

附录:

【附录1:最简单的缓冲输入的实现】

以下代码在 OgreTemplate.cpp 中


#include "ExampleApplication.h"

 

 SceneNode *mCamNode;
 SceneNode *soundNode;

 

 class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
{
public:
 TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager*sceneMgr)
  : ExampleFrameListener(win, cam, true, true)
 {
   mMouse->setEventCallback(this);
   mKeyboard->setEventCallback(this);
   mDirection = Vector3::ZERO;
   mSceneMgr = sceneMgr; 
   mRotate = 0.13;
   mMove = 250;
   mContinue = true;
 }


protected:
  Real mRotate;
  Real mMove;
  SceneManager *mSceneMgr;
  bool mContinue;
  Vector3 mDirection;

 

 bool frameStarted(const FrameEvent &evt)
  {
  if(mMouse)
    mMouse->capture();
  if(mKeyboard)
    mKeyboard->capture();
  mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
  return mContinue;
  }

 

 bool mouseMoved(const OIS::MouseEvent &e)
 {
   if (e.state.buttonDown(OIS::MB_Right)) 
 
  mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD); 
     mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL); 
   
  return true;
 }

 

bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }


bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
 
bool keyPressed(const OIS::KeyEvent &e)
 
  switch (e.key)
  {
  case OIS::KC_ESCAPE:  mContinue = false; break;
  case OIS::KC_UP:
  case OIS::KC_W:  mDirection.z -= mMove; break;
  case OIS::KC_DOWN:
     case OIS::KC_S:  mDirection.z += mMove; break;
  case OIS::KC_LEFT:
  case OIS::KC_A:  mDirection.x -= mMove;  break;
  case OIS::KC_RIGHT: 
  case OIS::KC_D:  mDirection.x += mMove; break;
  default: break;
  }
 return true;
}

 

bool keyReleased(const OIS::KeyEvent &e)
switch (e.key)
  {
 case OIS::KC_UP:
 case OIS::KC_W:  mDirection.z += mMove;  break;
 case OIS::KC_DOWN:
 case OIS::KC_S:  mDirection.z -= mMove;    break;
 case OIS::KC_LEFT:
 case OIS::KC_A:  mDirection.x += mMove;   break;
 case OIS::KC_RIGHT:
 case OIS::KC_D: mDirection.x -= mMove;  break;
 default: break;
  }
 return true;
}
};


class TutorialApplication : public ExampleApplication
{
public:
  void createCamera(void)
  {
   mCamera = mSceneMgr->createCamera("PlayerCam");
   mCamera->setNearClipDistance(5);
   }
 void createScene(void)
 {
  mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));
  // add a MESH
  Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
  soundNode = mSceneMgr->getRootSceneNode()->createChildSceneNode ("headNode");
  soundNode->translate(Vector3(0,10,0));
  soundNode->attachObject(ent);
  // create the light
  Light *light = mSceneMgr->createLight("Light1");
  light->setType(Light::LT_POINT);
  light->setPosition(Vector3(250, 150, 250));
  // Create the scene node
  mCamNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode");
  mCamNode->translate(Vector3(-400, 200, 400));
  mCamNode->yaw(Degree(-45));
  mCamNode->attachObject(mCamera);
  //add ground plane
     Plane plane;
  plane.normal=Vector3::UNIT_Y;
  plane.d=0;
  MeshManager::getSingleton().createPlane("floor",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,plane,4000,\
  4000,10,10,true,1,50,50,Vector3::UNIT_Z);
  Entity *pPlaneEnt=mSceneMgr->createEntity("grassfloor","floor");
  pPlaneEnt->setMaterialName("Examples/GrassFloor");
  mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(pPlaneEnt);
 }

 

 void createFrameListener(void)
 {
  mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
  mRoot->addFrameListener(mFrameListener);
  mFrameListener->showDebugOverlay(true);
 }
};

//where main() runned

 #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
  TutorialApplication app;
 try {
 app.go();
 } catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
 MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
  fprintf(stderr, "An exception has occurred: %s\n",
  e.getFullDescription().c_str());
 #endif
 }
 return 0;
}


【附录2:完整示例代码】


#include "ExampleApplication.h"
#include "SoundManager.h"


 SceneNode *mCamNode;
 SceneNode *soundNode;
 SoundManager  *soundMgr;
 int qplSoundIndex;
 int qplSoundChannel;

 

 class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
{
public:
 TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager*sceneMgr)
  : ExampleFrameListener(win, cam, true, true)
 {
   mMouse->setEventCallback(this);
      mKeyboard->setEventCallback(this);
   mDirection = Vector3::ZERO;
   mSceneMgr = sceneMgr; 
   mRotate = 0.13;
   mMove = 250;
      mContinue = true;
 }


protected:
  Real mRotate;
  Real mMove;
  SceneManager *mSceneMgr;
  bool mContinue;
  Vector3 mDirection;

 

 bool frameStarted(const FrameEvent &evt)
  {
  if(mMouse)
    mMouse->capture();
  if(mKeyboard)
    mKeyboard->capture();
  mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
  soundMgr->FrameStarted(mCamNode,evt.timeSinceLastFrame);
  return mContinue;
  }

 

 bool mouseMoved(const OIS::MouseEvent &e)
 {
   if (e.state.buttonDown(OIS::MB_Right)) 
 
  mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD); 
     mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL); 
   
  return true;
 }

 

bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }


bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
 
bool keyPressed(const OIS::KeyEvent &e)
 
  switch (e.key)
  {
  case OIS::KC_ESCAPE:  mContinue = false; break;
  case OIS::KC_UP:
  case OIS::KC_W:  mDirection.z -= mMove; break;
  case OIS::KC_DOWN:
     case OIS::KC_S:  mDirection.z += mMove; break;
  case OIS::KC_LEFT:
  case OIS::KC_A:  mDirection.x -= mMove;  break;
  case OIS::KC_RIGHT: 
  case OIS::KC_D:  mDirection.x += mMove; break;
  default: break;
  }
 return true;
}

 

bool keyReleased(const OIS::KeyEvent &e)
switch (e.key)
  {
 case OIS::KC_UP:
 case OIS::KC_W:  mDirection.z += mMove;  break;
 case OIS::KC_DOWN:
 case OIS::KC_S:  mDirection.z -= mMove;    break;
 case OIS::KC_LEFT:
 case OIS::KC_A:  mDirection.x += mMove;   break;
 case OIS::KC_RIGHT:
 case OIS::KC_D: mDirection.x -= mMove;  break;
 default: break;
  }
 return true;
}
};


class TutorialApplication : public ExampleApplication
{
public:
  void createCamera(void)
  {
   mCamera = mSceneMgr->createCamera("PlayerCam");
   mCamera->setNearClipDistance(5);
   }
 void createScene(void)
 {
  mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));
  // add a MESH
  Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
  soundNode = mSceneMgr->getRootSceneNode()->createChildSceneNode ("headNode");
  soundNode->translate(Vector3(0,10,0));
  soundNode->attachObject(ent);
  // create the light
  Light *light = mSceneMgr->createLight("Light1");
  light->setType(Light::LT_POINT);
  light->setPosition(Vector3(250, 150, 250));
  // Create the scene node
  mCamNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode");
  mCamNode->translate(Vector3(-400, 200, 400));
  mCamNode->yaw(Degree(-45));
  mCamNode->attachObject(mCamera);
  //add ground plane
     Plane plane;
  plane.normal=Vector3::UNIT_Y;
  plane.d=0;
  MeshManager::getSingleton().createPlane("floor",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,plane,4000,\
  4000,10,10,true,1,50,50,Vector3::UNIT_Z);
  Entity *pPlaneEnt=mSceneMgr->createEntity("grassfloor","floor");
  pPlaneEnt->setMaterialName("Examples/GrassFloor");
  mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(pPlaneEnt);
     //add sound
  soundMgr=new SoundManager; //新建SoundMananger类
  soundMgr->Initialize();  //初始化类,无非就是建立fmodsystem,channel,Fmod的Init等
  qplSoundIndex=soundMgr->CreateLoopedSound(Ogre::String("qpl.wma"));//加载声音
  qplSoundChannel=INVALID_SOUND_CHANNEL; //播放频道,初始值为空
  soundMgr->PlaySound(qplSoundIndex,soundNode,&qplSoundChannel); //播放声音,将声音绑定在soundNode
  //的位置上,并给刚才的频道赋值
  soundMgr->Set3DMinMaxDistance(qplSoundChannel,50.0f,2000.0f); //设置远近声音增大变小,与声源的
     //距离小于50,则声音不再变大;与声源距离大于2000,则声音不再减少;50~2000之间线性减小。

 }

 

 void createFrameListener(void)
 {
  mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
  mRoot->addFrameListener(mFrameListener);
  mFrameListener->showDebugOverlay(true);
 }
};

//where main() runned

#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
  TutorialApplication app;
 try {
 app.go();
 } catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
 MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
  fprintf(stderr, "An exception has occurred: %s\n",
  e.getFullDescription().c_str());
 #endif
 }
 return 0;
}

0 0