fmod:2.SoundManager的解析和在OGRE中使用fmod

SoundManager的解析和在OGRE中使用fmod

 

【SoundManager的解析】

 

注意到了程序中使用的語句:
=================================
第一句:soundMgr=new SoundManager;
=================================
在SoundManager.h中定義了SoundManager類,他的構造函數

SoundManager::SoundManager()
   {
   system = NULL;
   prevListenerPosition = Vector3(0, 0, 0);
   soundInstanceVector = new SoundInstanceVector;
//typedef std::vector<SoundInstance *> SoundInstanceVector;
//這裏定義了一個容器vector,裝載指向我們的SoundInstance類的指針

   nextSoundInstanceIndex = 0;//下標爲0,指向容器的第一個元素

//通過遞增下標簡單初始化100個元素,用clear函數設置他們的各項屬性爲空
soundInstanceVector->resize(INITIAL_VECTOR_SIZE);//INITIAL_VECTOR_SIZE爲100
 for (int vectorIndex = 0; vectorIndex < INITIAL_VECTOR_SIZE; vectorIndex++)
      {
      soundInstanceVector->at(vectorIndex) = new SoundInstance;
      soundInstanceVector->at(vectorIndex)->Clear();
      }
//通過遞增下標簡單初始化200個元素,用clear函數設置他們的各項屬性爲空
   for (int channelIndex = 0; channelIndex < MAX_SOUND_CHANNELS; channelIndex++)
      channelArray[channelIndex].Clear();
   }

這裏利用了STL的容器,這樣相對於一般的數組或者鏈表,string , vector等的at()成員函數相較下標運算符[]而言,增加了下標越界檢查、異常處理等。增加程序的安全性和穩定性。這一句主要實現了構架封裝,與fmod本身關聯不大。

 

==============================
第二句:soundMgr->Initialize()
==============================
void SoundManager::Initialize(void)
   {
   FMOD_RESULT result; //fmod函數執行結果的判斷依據,執行結果的查詢
//建立一個FMOD::System ,然後檢查建立的結果
  result = FMOD::System_Create(&system);
   if (result != FMOD_OK)
        OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");

//系統初始化,參數1:代表能播放音樂的最大channel數量
//參數2:初始化類型設定,可以使用|符號來制定多種規格的合併
//參數3:額外數據,可以傳遞給外部輸出插件,比如寫出文件名,這裏我們不用
  result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0); // Initialize FMOD.
      if (result != FMOD_OK)
       OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");

//設置3D效果,參數1:多普勒效應的變換因素。想象下汽車快速從我們身邊開過,靠近我們的時候喇叭變得尖銳
//遠離的時候變得低沉的效果。這個因素就是這種效果的劇烈程度,越大越明顯
//參數2:距離比例,假設爲X,那麼程序中的X米=現實中的1米.
//參數3:高頻音效衰減?跟聲音類型有關?不太理解,默認爲1
  system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);

//有些舊版本的>setFileSystem只有5個參數,新版本是7個參數,那兩個爲0的參數是
//  FMOD_FILE_ASYNCREADCALLBACK  userasyncread, 異步讀,
//  FMOD_FILE_ASYNCCANCELCALLBACK  userasynccancel, 異步取消
//  不太瞭解具體的作用,看函數名來理解吧
  result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback, &fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048);
   if (result != FMOD_OK)
        OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "FMOD error! (" + StringConverter::toString(result) + "): " + FMOD_ErrorString(result), "SoundManager::Initialize");
     
  
   Ogre::LogManager::getSingleton().logMessage("SoundManager Initialized");
   }

 

抽出來呢,Initial就下面幾句話:
FMOD::System system;
result = FMOD::System_Create(&system);
result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0);
system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);
result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback,
&fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048)//這個函數到現在還不是特別理解,就知道是設置了4個函數,供打開、關閉、閱讀、搜索文件調用

 

======================================================================
第三句:

qplSoundIndex=soundMgr->CreateLoopedSound(Ogre::String("qpl.wma"));
======================================================================
int SoundManager::CreateLoopedSound(String &fileName)
   {
   return CreateSound(fileName, SOUND_TYPE_3D_SOUND_LOOPED);
   }

 

CreateLoopedSound調用了CreateSound(fileName, SOUND_TYPE_3D_SOUND),那麼轉去看CreateSound的代碼

 

int SoundManager::CreateSound(String &fileName, SOUND_TYPE soundType)
   {
   Archive *      fileArchive;
   FMOD_RESULT    result;
   FMOD::Sound *  sound;
   String         fullPathName;
   SoundInstance *newSoundInstance;

   int soundIndex;
   soundIndex = FindSound(fileName, soundType); //查看容器中是否已經有這個音樂文件
   if (soundIndex != INVALID_SOUND_INDEX)
    return soundIndex; //如果已經有了,返回他的下標

   fullPathName = fileName;
   FileLocator * fileLocator = (FileLocator * )ResourceGroupManager::getSingletonPtr();
   fileArchive = fileLocator->Find(fullPathName); //在資源路徑FileSystem中查找這個文件明
   if (!fileArchive)  //找不到
      {
      Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not find sound '" + fileName + "'");
       return INVALID_SOUND_INDEX;
      }

   IncrementNextSoundInstanceIndex(); //聲音實例容器下標+1
   newSoundInstance = soundInstanceVector->at(nextSoundInstanceIndex);//容器新增的元素
   newSoundInstance->fileName = fileName;
   newSoundInstance->fileArchive = fileArchive;
   newSoundInstance->soundType = soundType;

   switch (soundType) //根據指定的soundType聲音類型來建立聲音,剛纔我們默認使用SOUND_TYPE_3D_SOUND_LOOPED類型
      {
      case SOUND_TYPE_3D_SOUND:
         {
        result = system->createSound((const char *)newSoundInstance, FMOD_3D, 0, &sound);
         break;
         }

      case SOUND_TYPE_3D_SOUND_LOOPED:
         {
        //這裏纔是真正的創建聲音 system->createSound()
         result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
         break;
         }

      case SOUND_TYPE_2D_SOUND:
         {
        result = system->createStream((const char *)newSoundInstance, FMOD_DEFAULT, 0, &sound);
         break;
         }

      case SOUND_TYPE_2D_SOUND_LOOPED:
         {
         result = system->createStream((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_2D | FMOD_HARDWARE, 0, &sound);
         break;
         }

      default:
         {
         Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not load sound '" + fileName + "' (invalid soundType)");
         return INVALID_SOUND_INDEX;
         }
      }

   if (result != FMOD_OK)
      {
      Ogre::LogManager::getSingleton().logMessage("SoundManager::CreateSound could not load sound '" + fileName + "'  FMOD Error:" + FMOD_ErrorString(result));
      return INVALID_SOUND_INDEX;
      }

   newSoundInstance->fmodSound = sound;
   return nextSoundInstanceIndex; //返回該聲音實例的下標
   }

抽出來就這一句:


result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
其他大部分時間都在操作聲音實例的容器,添加,查找,賦值等等。
FMOD::system->createSound();函數原型爲:
FMOD_RESULT System::createSound(
  const char *  name_or_data,
  FMOD_MODE  mode,
  FMOD_CREATESOUNDEXINFO *  exinfo,
  FMOD::Sound **  sound
)
參數1表示要創建的聲音文件的名字,可以帶路徑,比如:“../media/qpl.wma”
參數2表示設置聲音效果,支持用 | 來實現多種效果合併
參數3額外信息,比如用戶播放音樂時音樂的長度,這裏不用設爲0
參數4將新創建的音樂地址賦值給sound,可以使用sound來操作該音樂的播放了


=================================================================
第四、五句: 
qplSoundChannel=INVALID_SOUND_CHANNEL; //播放頻道下標,初始值爲-1
soundMgr->PlaySound(qplSoundIndex,soundNode,&qplSoundChannel);
=================================================================
第四句不管他,主要是看第五句PlaySound()函數
void SoundManager::PlaySound(int soundIndex, SceneNode *soundNode, int *channelIndex)
   {
   int            channelIndexTemp;
   FMOD_RESULT    result;
   FMOD_VECTOR    initialPosition;
   FMOD::Channel *channel;
   SoundInstance *soundInstance; //各種初始化,看看就好了

   if (soundIndex == INVALID_SOUND_INDEX) //無效音樂實例下標
      return;

   if (channelIndex)
      channelIndexTemp = *channelIndex;
   else
      channelIndexTemp = INVALID_SOUND_CHANNEL;

   assert((soundIndex > 0) && (soundIndex < (int)soundInstanceVector->capacity()));//斷言,基本是供調試用

 // 如果channel下標所指向的channel正在播放聲音,檢查與PlaySound函數給定的ScenNode是否一致
//如果檢測到該Node已經在播放指定聲音了,那麼返回,不再重複播放
   if ((channelIndexTemp != INVALID_SOUND_CHANNEL) && (channelArray[channelIndexTemp].sceneNode != NULL))
      {
     result = system->getChannel(channelIndexTemp, &channel);
      if (result == FMOD_OK)
         {
         bool isPlaying;
        result = channel->isPlaying(&isPlaying);
         if ((result == FMOD_OK) && (isPlaying == true) && (channelArray[channelIndexTemp].sceneNode == soundNode))
            return;  // 已經在該Node播放了該聲音
         }
      }
//否則準備播放
   soundInstance = soundInstanceVector->at(soundIndex);//根據指定下標取實例
//播放FMOD::System::playSound
//參數1表示使用哪個channel播放,FMOD_CHANNEL_FREE,表示使用當前空閒通道來播放
//參數2表示要播放的聲音
//參數3表示 paused,true表示在發出聲音之前允許用戶修改聲音屬性,如set3DAttributes;False的話直接在這裏就發出聲音了
//參數4讓如果成功播放聲音,channel將指向播放這個聲音的頻道
   result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
   if (result != FMOD_OK)
      {
      Ogre::LogManager::getSingleton().logMessage(String("SoundManager::PlaySound could not play sound  FMOD Error:") + FMOD_ErrorString(result));
      if (channelIndex)
         *channelIndex = INVALID_SOUND_CHANNEL;//播放完了之後channel又賦值-1,空閒準備下一個音樂的播放
      return
;
      }
//保存該播放聲音的channel所綁定到的SceneNode
   channel->getIndex(&channelIndexTemp);
   channelArray[channelIndexTemp].sceneNode = soundNode;

   if (soundNode)
      {
      channelArray[channelIndexTemp].prevPosition = soundNode->getPosition();
      initialPosition.x = soundNode->getPosition().x;
      initialPosition.y = soundNode->getPosition().y;
      initialPosition.z = soundNode->getPosition().z;
     channel->set3DAttributes(&initialPosition, NULL);//設置3D屬性,播放位置,運動速度
      }

  result = channel->setVolume(1.0);//音量大小
  // paused=false,表示播放,也就是開始發出聲音
  result = channel->setPaused(false);

   if (channelIndex)
      *channelIndex = channelIndexTemp;
   }


主要是檢測是否正在播放,再就是保存Node,一些繁雜的數組和容器操作。
同樣,爲了學習,把Fmod函數部分抽出來:


result = system->getChannel(channelIndexTemp, &channel);
bool isPlaying;
result = channel->isPlaying(&isPlaying);
result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
initialPosition.x = soundNode->getPosition().x;
initialPosition.y = soundNode->getPosition().y;
initialPosition.z = soundNode->getPosition().z;  //取Node座標
channel->set3DAttributes(&initialPosition, NULL); //設置3D屬性,播放位置,運動速度
result = channel->setVolume(1.0); //音量大小
result = channel->setPaused(false);

 

 

========================================================
第六句:

soundMgr->FrameStarted(mCamNode,evt.timeSinceLastFrame);
========================================================
void SoundManager::FrameStarted(Ogre::SceneNode *listenerNode, Ogre::Real timeElapsed)
   {
   int            channelIndex;
   FMOD::Channel *nextChannel;
   FMOD_VECTOR    listenerPosition;
   FMOD_VECTOR    listenerForward;
   FMOD_VECTOR    listenerUp;
   FMOD_VECTOR    listenerVelocity;
   Ogre::Vector3  vectorVelocity;
   Ogre::Vector3  vectorForward;
   Ogre::Vector3  vectorUp; //各種定義,看看就好

//下面的代碼是檢測【聽衆】的移動方位、速度來設置3D音效,如多普勒效應和立體聲音
//  速度=路程 / 時間
   if (timeElapsed > 0)
       vectorVelocity = (listenerNode->getPosition() - prevListenerPosition) / timeElapsed;
   else
      vectorVelocity = Vector3(0, 0, 0);
//Forward 和 up
   vectorForward = listenerNode->getOrientation().zAxis();
   vectorForward.normalise();
   vectorUp = listenerNode->getOrientation().yAxis();
   vectorUp.normalise();
//取得聽衆位置
   listenerPosition.x = listenerNode->getPosition() .x;
   listenerPosition.y = listenerNode->getPosition() .y;
   listenerPosition.z = listenerNode->getPosition() .z;
//設置聽衆的Forward 、up屬性,下面的函數需要提供這些參數
   listenerForward.x = vectorForward.x;
   listenerForward.y = vectorForward.y;
   listenerForward.z = vectorForward.z;
   listenerUp.x = vectorUp.x;
   listenerUp.y = vectorUp.y;
   listenerUp.z = vectorUp.z;
//設置聽衆速度
   listenerVelocity.x = vectorVelocity.x;
   listenerVelocity.y = vectorVelocity.y;
   listenerVelocity.z = vectorVelocity.z;
// 設置聽衆的3D屬性,保存當前位置,一邊下一幀進行Pre的計算
   system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
   system->update();
   prevListenerPosition = listenerNode->getPosition() ;

//下面的代碼是檢測【聲源】的移動來設置3D音效,如多普勒效應
//用for循環取得所有綁定到節點Node的聲音,計算他們的移動速度
 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);
//取得聽衆位置
         listenerPosition.x = channelArray[channelIndex].sceneNode->getPosition().x;
         listenerPosition.y = channelArray[channelIndex].sceneNode->getPosition().y;
         listenerPosition.z = channelArray[channelIndex].sceneNode->getPosition().z;
//聽衆移動速度(即使是聽衆不動,但是Node移動,這個速度是指相對速度)
         listenerVelocity.x = vectorVelocity.x;
         listenerVelocity.y = vectorVelocity.y;
         listenerVelocity.z = vectorVelocity.z;
//設置播放聲音的channel的3D屬性,然後同樣是保存Node位置方便下一幀查詢
         nextChannel->set3DAttributes(&listenerPosition, &listenerVelocity);
         channelArray[channelIndex].prevPosition = channelArray[channelIndex].sceneNode->getPosition();
         }
      }
   }

    FrameStarted主要是實現了3D場景的聲音屬性,比如立體聲音,假設是聲源在你右邊,右耳朵聽起來會比較大聲,還有就是多普勒效應了,當你面向聲源移動(或者聲源面向你移動)聲音會變得比較尖銳;反之低沉。注意的是,fmod聲音引擎有自己的座標系,FMOD_VECTOR  posFmod;Ogre也有自己的座標系 Ogre::Vector3  posOgre,我們用Ogre的距離表現聲音距離的時候應該注意轉換:
posFmod.x = posOgre.x;
posFmod.y = posOgre.y;
posFmod.z = posOgre.z; 

慣例,抽取Fmod的函數代碼:
//set3DListenerAttributes函數,system->update每幀執行更新
//參數1聽衆編號,只有一個的話設爲0
//參數2聽衆位置,影響音量大小
//參數3聽衆速度,影響多普勒效應
//參數4聽衆面向,影響左右耳聲音大小,3D場景模擬現實判斷聲源位置的依據
//參數5聽衆的向上位置?必須與面向垂直,樓主也不知道做什麼用
   system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
   system->update();
//set3DAttributes
//參數1聽衆位置,影響音量大小
//參數2和聽衆的相對速度,影響多普勒效應
   Channel->set3DAttributes(&listenerPosition, &listenerVelocity);

 

=============================================================
第七句:
soundMgr->Set3DMinMaxDistance(qplSoundChannel,50.0f,2000.0f);
=============================================================
//設置距離對聲音的影響
//0~minDistance, 聲音爲最大且不變;minDistance~maxDistance,聲音線性減小到預設的最小值
//maxDistance~∞ ,聲音爲預設的最小值,且不變 
void SoundManager::Set3DMinMaxDistance(int channelIndex, float minDistance, float maxDistance)
   {
   FMOD_RESULT    result;
   FMOD::Channel *channel;

   if (channelIndex == INVALID_SOUND_CHANNEL)
      return;

   result = system->getChannel(channelIndex, &channel);
   if (result == FMOD_OK)
     channel->set3DMinMaxDistance(minDistance, maxDistance);
   }

    抽取fmod函數:
channel->set3DMinMaxDistance(minDistance, maxDistance);

 

================================================
好了現在已經把SoundManager的主心骨都給抽出來了
================================================


//最基本的播放實現
FMOD::System system;
result = FMOD::System_Create(&system);
result = system->init(MAX_SOUND_CHANNELS, FMOD_INIT_NORMAL, 0);
system->set3DSettings(DOPPLER_SCALE, DISTANCE_FACTOR, ROLLOFF_SCALE);
result = system->setFileSystem(&fmodFileOpenCallback, &fmodFileCloseCallback, &fmodFileReadCallback, &fmodFileSeekCallback, 0,0,2048)
result = system->createSound((const char *)newSoundInstance, FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
result = system->playSound(FMOD_CHANNEL_FREE, soundInstance->fmodSound, true, &channel);
channel->set3DMinMaxDistance(100.0f, 2000.0f);
result = channel->setPaused(false);

 

//爲了實現3D音效,而增加的代碼
initialPosition.x = soundNode->getPosition().x;
initialPosition.y = soundNode->getPosition().y;
initialPosition.z = soundNode->getPosition().z;  //取Node座標
channel->set3DAttributes(&initialPosition, NULL); //設置3D屬性,播放位置,運動速度
result = channel->setVolume(1.0); //音量大小
result = channel->setPaused(false);
//下面的代碼最好放在幀循環中隨時更新
system->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
system->update();
Channel->set3DAttributes(&listenerPosition, &listenerVelocity);

 

 

【在OGRE中使用fmod】

=================================================
現在試試不用SoundManager,試試我們的Ogre播放聲音。
=================================================


  1.在昨天使用了SoundManager的基礎上,重新Load【最簡單的緩衝輸入實現】的代碼,這樣沒有包含SoundManager,確保昨天的能正確運行並播放聲音哦,否則這一章還是要用了一些庫文件,和資源配置,不會的話參考上一篇文章,只是不要在 OgreTemplate.cpp中包含SoundManager.的文件頭和cpp)
 然後在OgreTemplate.cpp中最前面加上句 :#include "fmod.hpp" 和 #include "fmod_errors.h”,因爲這兩個包含原本是在SoundManager裏的。

 

   2.定義一些全局變量(爲了便於學習理解,省去參數傳遞的麻煩,但編寫要發行的遊戲該視具體情況而定)
 SceneNode *mCamNode;
 SceneNode *soundNode;
 FMOD::System soundsystem; //最好還是不要用系統的一些名稱如system,這樣會有安全隱患
 FMOD::Sound sound;
 FMOD::Channel channel;
 FMOD_RESULT result;

 

   3.Application類的 createScene函數返回之前添加下面代碼,本來每執行一條就應該檢查result的建立結果的,爲了篇幅我省掉了,要是編寫發行版的遊戲,應該檢查每次result結果是否等於 FMOD_OK,調試程序運行不正確的話我們也可以這樣一個個來檢查哪一步出了問題:
FMOD::System_Create(&soundsystem);
result = soundsystem->init(50, FMOD_INIT_NORMAL, 0);
//因爲程序相機移動速度太快,每秒上百米多普勒效應太明顯,設置成0.1來減少
result = soundsystem->set3DSettings(0.1f, 1.0, 1.0);
//創建聲音
result = soundsystem->createSound("../../media/sound/qpl.wma", FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
//播放聲音。參數3表示允許在發出聲音之前設置聲音參數,設置完了之後記得關掉paused
result = soundsystem->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
//0~100聲音最大,100~2000爲衰減距離,2000以上聲音不再減小
channel->set3DMinMaxDistance(100.0f, 2000.0f);
//關掉paused,開始發出聲音
result = channel->setPaused(false);
   這時候應該能播放音樂了,如果播放不了,請檢查聲音源文件的位置,或者直接把聲音文件qpl.wma放到.exe的文件夾中"../../media/sound/qpl.wma"改成 "qpl.wma"表示在當前目錄下尋找名爲qpl.wma的聲音文件。

 

   4.我們設計一個函數soundFrameStart()來實現簡單的3D音效效果
在myFrameListener類public中添加這個函數,然後在frameStarted()函數中,每一幀都執行它進行更新

  void soundFrameStart(Real timeElapsed)
 {
   FMOD_VECTOR    listenerPosition;
   FMOD_VECTOR    listenerForward;
   FMOD_VECTOR    listenerUp;
   FMOD_VECTOR    listenerVelocity;
   Ogre::Vector3  vectorVelocity;
   Ogre::Vector3  vectorForward;
   Ogre::Vector3  vectorUp;

   if (timeElapsed > 0)
    vectorVelocity = (mCamNode->getPosition() - prevListenerPosition) / timeElapsed;
   else
      vectorVelocity = Vector3(0, 0, 0);

   vectorForward = mCamNode->getOrientation().zAxis();
   vectorForward.normalise();
   vectorUp = mCamNode->getOrientation().yAxis();
   vectorUp.normalise();
  
   listenerForward.x = vectorForward.x;
   listenerForward.y = vectorForward.y;
   listenerForward.z = vectorForward.z;
   listenerUp.x = vectorUp.x;
   listenerUp.y = vectorUp.y;
   listenerUp.z = vectorUp.z;

   listenerPosition.x = mCamNode->getPosition() .x;
   listenerPosition.y = mCamNode->getPosition() .y;
   listenerPosition.z = mCamNode->getPosition() .z;

   listenerVelocity.x = vectorVelocity.x;
   listenerVelocity.y = vectorVelocity.y;
   listenerVelocity.z = vectorVelocity.z;

   soundsystem->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
   prevListenerPosition = mCamNode->getPosition() ;
   soundsystem->update();
 }

 

 5.在frameStarted()函數中,return語句之前,添加如下代碼:

soundFrameStart(evt.timeSinceLastFrame);

 

   現在運行程序對着怪物頭一動是不是聲音會變尖銳和音量變大了呢?
   把頭偏向一邊(相機視角),聲源在視角的左邊或者右邊看能辨別音源位置不?

   這裏只實現了一個聲音的效果,要是有多個聲音,你還得建立更多的sound和channel,或者使用SoundManager來管理。Fmod還提供了很多功能參數來給我們使用,這個例子中用到的函數只是其中很小一部分,要進一步深入學習只能靠自己多看代碼和例子了,
樓主水平有限,只能爲大家入門拋磚引玉。

 

最後把代碼貼出來
【附錄1:】

OgreTemplate.cpp

#include "ExampleApplication.h"
#include "fmod.hpp"
#include "fmod_errors.h"

 SceneNode *mCamNode;
 SceneNode *soundNode;
 FMOD::System *soundsystem;
 FMOD::Channel *channel;
 FMOD::Sound  *sound;
 FMOD_RESULT result;
 Ogre::Vector3  prevListenerPosition;

 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);

  soundFrameStart(evt.timeSinceLastFrame);
  return mContinue;
    }

 void soundFrameStart(Real timeElapsed)
 {
   FMOD_VECTOR    listenerPosition;
   FMOD_VECTOR    listenerForward;
   FMOD_VECTOR    listenerUp;
   FMOD_VECTOR    listenerVelocity;
   Ogre::Vector3  vectorVelocity;
   Ogre::Vector3  vectorForward;
   Ogre::Vector3  vectorUp;

     if (timeElapsed > 0)
   {
    vectorVelocity = (mCamNode->getPosition() - prevListenerPosition) / timeElapsed;
       vectorVelocity/=5;
   }
   else
      vectorVelocity = Vector3(0, 0, 0);

   vectorForward = mCamNode->getOrientation().zAxis();
   vectorForward.normalise();
   vectorUp = mCamNode->getOrientation().yAxis();
   vectorUp.normalise();
  
   listenerForward.x = vectorForward.x;
   listenerForward.y = vectorForward.y;
   listenerForward.z = vectorForward.z;
   listenerUp.x = vectorUp.x;
   listenerUp.y = vectorUp.y;
   listenerUp.z = vectorUp.z;

   listenerPosition.x = mCamNode->getPosition() .x;
   listenerPosition.y = mCamNode->getPosition() .y;
   listenerPosition.z = mCamNode->getPosition() .z;

   listenerVelocity.x = vectorVelocity.x;
   listenerVelocity.y = vectorVelocity.y;
   listenerVelocity.z = vectorVelocity.z;

   soundsystem->set3DListenerAttributes(0, &listenerPosition, &listenerVelocity, &listenerForward, &listenerUp);
   prevListenerPosition = mCamNode->getPosition() ;
   soundsystem->update();
 }

 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,20,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
  FMOD::System_Create(&soundsystem);
  result = soundsystem->init(50, FMOD_INIT_NORMAL, 0);
  result = soundsystem->set3DSettings(0.1f, 1.0, 1.0);
  result = soundsystem->createSound("qpl.wma", FMOD_LOOP_NORMAL | FMOD_3D | FMOD_HARDWARE, 0, &sound);
  result = soundsystem->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);
  channel->set3DMinMaxDistance(100.0f, 2000.0f);
  result = channel->setPaused(false);
 }

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

#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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章