2018年12月3日 星期一

初探XAudio2

初探XAudio2

前言

  最近要在遊戲引擎裡播放聲音,本想繼續使用以前的DirectSound來播,但查了一下DirectSound的wiki得知DirectSound已不建議使用,所以打算改成XAudio2來播放聲音,以下是學習心得,在此做個紀錄。

內容

  在使用XAudio2的開始需要一些範例,但不幸的是Windows10本身沒給範例,所以請到舊的DirectX範例裡下載,裡面有個"XAudio2"的資料夾,範例就在裡面。

  使用XAudio2時需要"IXAudio2",所有的資源都由這個COM來管理,有點類似OpenGL的context,範例如下
HRESULT hr = CoInitializeEx( nullptr, COINIT_MULTITHREADED );
if (FAILED(hr))
{
  wprintf(L"Failed to init COM: %#X\n", hr);
  return 0;
}
//
IXAudio2* pXAudio2 = nullptr;
hr = XAudio2Create( &pXAudio2 , 0);
if( FAILED( hr ) )
{
  wprintf( L"Failed to init XAudio2 engine: %#X\n", hr );
  CoUninitialize();
  return 0;
}

在CoInitializeEx()的部分並不是每次要XAudio2Create()之前都要喚起,而是整個程式只要在最開頭喚起一次就好,當然在程式最後也請喚起CoUninitialize()。XAudio2Create()可以依據需要填 入debug的flag,範例裡有示範。

  接著來製造"IXAudio2MasteringVoice",這個COM其實我並不是很了解它的作用,但一定要製造,製造的方法很簡單,範例如下
//IXAudio2* pXAudio2;
IXAudio2MasteringVoice* pMasteringVoice = nullptr;
if( FAILED( pXAudio2->CreateMasteringVoice( &pMasteringVoice ) ) )
{
  pXAudio2->Release();
  CoUninitialize();
  return 0;
}

  接著來製造"IXAudio2SourceVoice",這個就是在播放聲音的COM,依據需要"同時"播放幾個聲音來決定需要的數量,製造前需要決定播放的格式,這個結構是"WAVEFORMATEX",這個結構的資料可以在WAV檔裡的"fmt"的chunk裡找到,可以參考前篇WAV檔案格式學習心得,範例如下
//IXAudio2* pXAudio2;
IXAudio2MasteringVoice* pMasteringVoice;
WAVEFORMATEX format;
//Fill struct data
//...
IXAudio2SourceVoice* pSourceVoice = nullptr;
if( FAILED( pXaudio2->CreateSourceVoice( &pSourceVoice, &format) ) )
{
  return 0;
}

接著將WAV的raw data送到"IXAudio2SourceVoice",範例如下
//IXAudio2* pXAudio2;
IXAudio2MasteringVoice* pMasteringVoice;
IXAudio2SourceVoice* pSourceVoice;

unsigned char* pWAVRawData;
size_t wavRawDataSize;
XAUDIO2_BUFFER buffer = {0};
buffer.pAudioData = pWAVRawData;
buffer.Flags = XAUDIO2_END_OF_STREAM;
buffer.AudioBytes = wavRawDataSize;
if( FAILED(pSourceVoice->SubmitSourceBuffer( &buffer ) ) )
{
  pSourceVoice->DestroyVoice();
  return 0;
}
這裡要注意"IXAudio2SourceVoice"的釋放不是用Release(),而是用DestroyVoice(),接著只要使用Start()
來播放即可,要停止播放使用Stop()。

  最後來說說Stream play,請看下圖
Stream play
播放聲音時可以不斷地往後送Buffer,在XAudio2裡這個動作就是SubmitSourceBuffer(),也就是圖中的"Enqueue",圖中的"Dequeue"這個動作在XAudio2是"自動完成"的,可以利用"IXAudio2SourceVoice"下的GetState()來取得剩下的Buffer數量,這就是Stream play。這種機制幾乎是必要的,想想如果聲音檔案很長,用一般的做法(只播一個Buffer),這個Buffer就會很大,而且考量到壓縮格式的聲音檔(如Ogg),必須是解一小段播一小段(這樣很省記憶體),同樣的機制在DirectSound要實現的話相當麻煩,還必須處理Event,程式碼相當長,但XAudio2在這方面的實現就相當簡單,最後我想以後我應該會淘汰DirectSound改用XAudio2。

參考資料

DirectSound的wiki
舊的DirectX範例

相關文章

WAV檔案格式學習心得

沒有留言:

張貼留言