通过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;
}