[XAudio2] Singleton クラスを作る

しばらくXAudio2関連で話をしようと思います。

XAudio2はDirectSoundとは違って、

  • ハードウェア処理からソフトウェア処理(CPU処理)へ
  • ボイスという概念を用いて統一的に音を管理
  • ソフトウェア処理の関係でMIDIが鳴らせなくなった
  • デフォルトでストリーミング再生になったので、一々API専用バッファへ書き込む必要がなくなった
  • DirectSoundで必要だったウィンドウハンドルが不要になった為、ウィンドウを作る必要がなくなった

などなど、DirectSoundから大分変わったんじゃないかと思います。

DirectSoundだとどうもめんどくさかったらしいですが(私はDirectSound使ったことありません)XAudio2の初期化自体はそんなに面倒なものじゃないです。
エンジンは複数個作れるかもしれませんが(作れてもあまり意味を成さないはず)、
マスターボイスは作動しているのが1つじゃないといけない(複数個作れるけど同時には動かせない)
ので、Singletonクラスを作って2つとも入れてしまっても大丈夫だと思います。

#define _WIN32_DCOM   // CoInitalizeEx関数使用を宣言
#include <xaudio2.h>

class xaudio2_singleton
{
public:
   static xaudio2_singleton& get_instance()
   {
      static xaudio2_singleton _Instance;
      return _Instance;
   }
   IXAudio2*               get_engine() { return engine_; };
   IXAudio2MasteringVoice* get_mastervoice() { return master_voice_; };
   HRESULT initialize()
   {
      HRESULT hr;
      hr = CoInitializeEx( NULL, COINIT_MULTITHREADED ); // COM の初期化
      if( FAILED(hr) ) return hr;

      UINT Flag = 0;
#ifdef _DEBUG
      Flag |= XAUDIO2_DEBUG_ENGINE;   // デバッグビルドの場合は、デバッグ版エンジンを使用します
#endif
      hr = XAudio2Create( &engine_, Flag );  // エンジンの作成
      if( FAILED(hr) )
      {
         release(); return hr;
      }

      hr = engine_->CreateMasteringVoice( &master_voice_ );  // マスターボイスの作成
      if( FAILED(hr) )
      {
         release(); return hr;
      }
      return S_OK;
   }
   void release()
   {
      if( master_voice_ ){ master_voice_->DestroyVoice(); master_voice_ = NULL; };
      if( engine_ ) { engine_->Release(); engine_ = NULL; };
      CoUninitialize();
   }

private:
   IXAudio2*               engine_;
   IXAudio2MasteringVoice* master_voice_;

   xaudio2_singleton() : engine_( NULL ), master_voice_( NULL ) {};
   xaudio2_singleton( const xaudio2_singleton& );
   xaudio2_singleton& operator=( const xaudio2_singleton& );
};

int main()
{
   xaudio2_singleton& xaudio2_ = xaudio2_singleton_::get_instance();

   if( FAILED(xaudio2_.initialize()) )
   {
      return -1;
   }

   xaudio2_.release();
   return 0;
}

インクルードするヘッダはxaudio2.hだけなので凄く簡単ですね。
XAudio2でもなんですが、COMを使っているので初期化が必要です。
CoInitializeEx関数で初期化しますが、この関数を使うために_WIN32_DCOMマクロを先に定義しておく必要があります。
CoInitializeEx関数の引数ですが、第1引数はどうもデフォルトがNULLだそうです。
第2引数にはCOMの利用範囲、との事なのですがCOINIT_MULTITHREADED(マルチスレッドで使用)で良いそうです。
DirectShow等を一緒に使う場合はここをCOINIT_APARTMENTTHREADEDにする必要がありそうです。

次にエンジンを初期化するわけですが、デバッグエンジンを使う指定が可能です。
今回は_DEBUGマクロがあればデバッグエンジンを使用、そうでない場合リリース版のエンジンを使う設定にしました。
XAudio2Create関数で初期化するのですが、これはインライン関数でWindowsではCoCreateInstance関数を呼び出しているとの事。

エンジンが作成できれば、マスターボイスを作るだけです。
これは、IXAudio2::CreateMasteringVoiceメソッドで作ります。
第1引数以外は全てデフォルトが与えられていて、通常はデフォルトパラメータで大丈夫なはずです。
これで初期化は終わりです。

削除する際は、作成順序と逆に削除するだけなので、マスターボイス→エンジン→COMの順番に消します。
マスターボイスの削除にはIXAudio2Voice::DestroyVoice()、
エンジンの削除にはIUnknown::Release()、
COMの削除にはCoUninitialize()を使います。

XAudio2の初期化と終了はこれだけです。
多分、DirectSoundよりは簡単なんじゃないかなあ、と。

色々説明はしょってますが、MSDNのドキュメントを見ると大体分かると思います。
というか、一応公式なので見たほうがいいかと。
日本語版はちょっと古いので、最新のドキュメントを見たい場合は必然的に英語になってしまいますが。

で、次からソースボイスを使って流すことをやりたいと思っているんですが、WAVEファイルの解析等は非常にめんどくさいのと、
DirectSoundでも同じ方法だと思うので、こちらでは解説しません。
インターフェイスで表記しておきますので、自作したクラス等に置き換えてみてください。


追記)
コードに色付けするのどうやるんだ…で四苦八苦。
http://d.hatena.ne.jp/hatenadiary/20061215/1166155734
を参考にしてrubyの所をcppに置き換えてやるだけでした。

後、インターフェイスへのポインタゲッターをconstメンバにするか否かってのがちょっと微妙です。
今回で言うとget_engineとget_mastervoiceなのですが。
クラスが持っているメンバはインスタンスへのポインタなので、実質インスタンスはクラス外に存在するという事は、constメンバでもエラーにはならないと思うのですが…。
意味的にはconstメンバじゃなくて良いと思うんですが、どうでしょうか。