スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【コード】XAudio2 クラス Part1

お久しぶりです。
ようやく XAudio2 を使って WAVE データを鳴らす事が出来ました。
DirectMusic からいきなり XAudio2 に飛んだので、DirectSound や、XAudio は知りません。
MSDN 様、もとい英語様とかなりの死闘を繰り広げました。

【追記】ソースアップしました。
↓コピーしてアドレスバーに貼り付けて下さい。
http://www.geocities.jp/dvdmvoid/XAudio2_WAVE.zip


XAudio2 の資料が全然なかったので自分も貢献しようかなと思いました。
Part1 なので勿論 Part2 もやる予定です。




XAudio2 は、やること自体はそんなに難しくないです。(当社比)
とりあえずいきなり新しい単語が増えたなってのが最初の印象です。
はい・・・意味不明な単語が良く出てきます。


早速 XAudio2 クラスを載せてみました。


class CXAUDIO2
{
private:
static IXAudio2 *m_pXAudio2; // XAudio2インターフェース
static IXAudio2MasteringVoice *m_pMVoice; // マスターヴォイス

IXAudio2SourceVoice *m_pSVoice; // ソースヴォイス
CWAVE m_WaveSound; // WAVE 音楽データ

private:
CXAUDIO2();
CXAUDIO2(const CXAUDIO2&);
CXAUDIO2& operator=(const CXAUDIO2&);

void Exit(); // 終了
bool Submit(); // 音楽データをキューに送信
bool CreateXAudio2(); // XAudio2 インターフェースの作成
bool CreateMVoice(); // マスターヴォイスの作成
bool CreateSVoice(); // ソースヴォイスの作成

public:
~CXAUDIO2();

static CXAUDIO2* GetInst(); // インスタンスの生成

bool LoadWaveFile(const std::string &FineName); // 音楽のロード
bool Init(); // 初期化
void Play(); // 再生
void Stop(); // 停止
void Pause(); // 一時停止

void SetVolume(float Volume); // ボリュームの設定
float GetVolume()const; // ボリュームを取得

};



CXAudio2 クラスのヘッダ部分です。
CWAVE m_WaveSound は WAVE クラスです。
ロード関数にファイル名を渡すと WAVE ファイルを読み込んだりしてくれますが、
WAVE クラスとして別記事に分けようと思っていますので、今は気にしなくてもOKです。


この XAudio2 クラスですが、
二つ以上インスタンスを必要とするケースが解らなかったのでシングルトンで実装してます。
なので、どこかで CXAUDIO2::GetInst を呼び、インスタンスを生成する必要があります。

インスタンスの生成はこんな感じです。

CXAUDIO2 *m_pCXAudio2 = CXAUDIO2::GetInst();

これで XAudio2 クラスのインスタンスを生成できます。



で、初期化ですが、
パブリックなメンバメソッド CXAUDIO2::Init が呼ばれると XAudio2 の初期化が始まります。
私は初期化をこのように実装してます。


//*******************************************
// 初期化
// 引数 : なし
// 戻り値 : TRUE:初期化成功 FALSE:初期化失敗
//*******************************************

bool CXAUDIO2::Init()
{
// COM の初期化
if( FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
{
if( FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) )
{
DEBUGOUT(_T("[XAudio2]COMの初期化に失敗しました。\n"));
return FALSE;
}
}

// XAudio2 インターフェースの作成
if(!this->CreateXAudio2())
return FALSE;

// マスターヴォイスの作成
if(!this->CreateMVoice())
return FALSE;

// WAVE ファイルの読み込み
if(!this->LoadWaveFile("Data/Sound/BGM/BGM_Title.wav"))
return FALSE;

DEBUGOUT(_T("XAudio2 関連の初期化に成功しました。\n"));
return TRUE;
}

DEBUGOUT っていうのは DirecX のメソッドである
OutputDebugString をマクロ化したものです。
デバッグウインドウに指定した文字を出力するだけのものなので、
特に気にする必要はないと思います。


初期化の流れとしては
・COM (Component Object Model)の初期化
・XAudio2 インターフェースの作成
・マスターヴォイスの作成
・WAVE の読み込み
・ソースヴォイスの作成
・ソースヴォイスキューにデータを送信


という感じです。



COM の初期化はあれで終わりなので
次は XAudio2 インターフェースを作成するメンバメソッドです。


【XAudio2 インターフェースの作成】


//*******************************************
// XAudio2 インターフェースの作成
// 引数 : なし
// 戻り値 : TRUE:成功 FALSE:失敗
//*******************************************

bool CXAUDIO2::CreateXAudio2()
{
UINT wFlag = 0;

#ifndef NDEBUG
wFlag |= XAUDIO2_DEBUG_ENGINE;
#endif

if( FAILED( XAudio2Create(&m_pXAudio2, wFlag)) )
{
DEBUGOUT(_T("XAudio2 インターフェスの作成に失敗しました。\n"));
return FALSE;
}

DEBUGOUT(_T("XAudio2 インターフェスの作成に成功しました。\n"));
return TRUE;
}


XAudio2Create メソッドを呼ぶだけです。
第一引数に IXAudio2* オブジェクトのアドレスを渡しています。
第二引数がポイントで、MSDN によると


Flags that specify the behavior of the XAudio2 object.
The value of this parameter can be 0 or the following:

(XAudio2 オブジェクトの動作フラグを指定します。
このパラメーターには、0 または次にの値を指定することが出来ます。)

XAUDIO2_DEBUG_ENGINE

Windows : Requests the debug version of the XAudio2 library.
Xbox 360 : No effect.

(Windows : デバッグバージョンの XAudio2 ライブラリをリクエストします。
Xbox 360: 効果なし)


と書かれています・・・なので、

#ifndef NDEBUG
wFlag |= XAUDIO2_DEBUG_ENGINE;
#endif

としています。
どうやらデバッグバージョンの時は
XAUDIO2_DEBUG_ENGINE フラグを指定する必要があるようです。
Xbox360 は効果ないようですのでこのフラグ指定は要らないってことですかね。
良く解りません。
これでエラーが出なければ XAudio2 インターフェースの作成に成功します。


【マスターヴォイスの作成】

//*******************************************
// マスターヴォイスの作成
// 引数 : なし
// 戻り値 : TRUE:成功 FALSE:失敗
//*******************************************

bool CXAUDIO2::CreateMVoice()
{
if( FAILED(m_pXAudio2->CreateMasteringVoice(&m_pMVoice)) )
{
DEBUGOUT(_T("マスターヴォイスの作成に失敗しました。\n"));
return FALSE;
}

DEBUGOUT(_T("マスターヴォイスの作成に成功しました。\n"));

return TRUE;
}


IXAudio2::CreateMasteringVoice メソッドでマスターヴォイスを作成します。
第一引数に IXAudio2MasteringVoice* オブジェクトのアドレスを渡します。
後に引数があるのですが、デフォルトの値を使用しました。

MSDN によると
Several mastering voices can exist at once, but only one of them can be started.
Only one device can be used by XAudio2 at a time.

(二つ以上のマスターヴォイスがが同時に存在しても大丈夫で、
スタートできるのは一度につき一個だけです。
XAudio2 が使用できるデバイスは一度に一個だけです。)



らしいので、作っても問題はないそうですが二つ作る意味はないようです。

また、
It is illegal to call CreateMasteringVoice from within a callback.
If CreateMasteringVoice is called within a callback XAUDIO2_E_INVALID_CALL will be returned.

(CreateMasteringVoice メソッドは、コールバック関数の中から呼び出すと無効になる。
もし呼び出した場合、XAUDIO2_E_INVALID_CALL が返されます。)



というのもあるので コールバック内からのメソッド呼び出しもやっては駄目なようです。
その辺注意しておけばOKみたいなので便利ですよね~。




次は、this->LoadWaveFile メンバメソッドを呼んでいますが
・WAVE 読み込み
・ソースヴォイスの作成
・データ送信


をやってます。

WAVE の読み込みには WAVE クラスである
CWAVE::Load(const std::string &FileName) で読み込むのですが
今回は WAVE 読み込みは既に出来ているものとして話を進めます。
コレに関しては Part2 くらいで WAVE クラスを紹介する時にでもお話します。


余談ですが、CXAUDIO2 クラスのメンバに CWAVE クラスを持たせてますが
一緒に入れない方がいいと思ってます。
やるなら WAVE を管理するクラスなんかあってもいいかもしれませんね。
ま、今回は中に入れときました。


【ソースヴォイスの作成】

//*******************************************
// ソースヴォイスの作成
// 引数 : なし
// 戻り値 : TRUE:成功 FALSE:失敗
//*******************************************

bool CXAUDIO2::CreateSVoice()
{
if( FAILED(m_pXAudio2->CreateSourceVoice(&m_pSVoice, WAVEFORMATEX へのポインタ)) )
{
DEBUGOUT(_T("ソースヴォイスの作成に失敗しました。\n"));
return FALSE;
}

DEBUGOUT(_T("ソースヴォイスの作成に成功しました。\n"));
return TRUE;
}


ソースヴォイスの作成には
IXAudio2::CreateSourceVoice メソッドを使います。
第一引数に IXAudio2SourceVoice* オブジェクトのアドレスを渡します。
第二引数に WAVEFORMATEX 構造体を渡します。
既に WAVE ファイルを読み込んでいる前提なのでココにそれを渡します。

これで、音を鳴らす準備が出来たので、ソースヴォイスキューに送信します。


【ソースヴォイスの作成】

//*******************************************
// ソースヴォイスキューに音楽データを送信
// 引数 : なし
// 戻り値 : TRUE:成功 FALSE:失敗
//*******************************************

bool CXAUDIO2::Submit()
{
XAUDIO2_BUFFER wSubmit = { 0 };
wSubmit.AudioBytes = WAVE データのサイズ;
wSubmit.pAudioData = WAVE データ;
wSubmit.Flags = XAUDIO2_END_OF_STREAM;

if( m_pSVoice->SubmitSourceBuffer(&wSubmit) != S_OK )
{
DEBUGOUT(_T("音楽データの送信に失敗しました。\n"));
return FALSE;
}

DEBUGOUT(_T("音楽データの送信に成功しました。\n"));
return TRUE;
}



ソースヴォイスキューに送信するには
IXAudio2SourceVoice::SubmitSourceBuffer メソッドを使います。
第一引数に XAUDIO2_BUFFER 構造体のアドレス を渡します。
第二引数ですが、今回は使いませんので指定しません。(デフォルトで NULL です)


さて、
wSubmit.AudioBytes = WAVE データのサイズ;
wSubmit.pAudioData = WAVE データ;

この二行ですが、
WAVE データのサイズ は 「dataチャンク」を読み込んだ時に
WAVE のサイズも一緒に読み込んでいると思います。
MMCKINFO::cksize に WAVE データのサイズが入れられてます。


WAVE データ は実際に WAVE の情報を読み込んだオブジェクトです。
例えば mmioRead で読み込んだ WAVE データをここに指定します。



ここまでエラーがなければ後は再生・停止するだけです。

m_pSVoice->Start(0);
m_pSVoice->Stop(0);

ソースヴォイスオブジェクトのメンバメソッドである
再生・停止メソッドを呼べばOKです。


使い終わったら後処理です。


//*******************************************
// 終了
// 引数 : なし
// 戻り値 : なし
//*******************************************

void CXAUDIO2::Exit()
{
// ソースヴォイスの破棄
if(m_pSVoice)
{
m_pSVoice->Stop(0);
m_pSVoice->DestroyVoice();
m_pSVoice = NULL;
}

// マスタリングヴォイスの破棄
if(m_pMVoice)
{
m_pMVoice->DestroyVoice();
m_pMVoice = NULL;
}
if(m_pXAudio2)
{
m_pXAudio2->Release(); // XAudio2 インターフェースの破棄
pXAudio2 = NULL;
}

CoUninitialize();
}



ちょっと WAVE データの所がうやむやなので、次回は WAVE クラスを取り上げます。


花粉ヤバいですけど元気です。
地震起こりまくってますけど元気です。

では、またノシ
スポンサーサイト
NEXT≫
検索フォーム
プロフィール

DVDM

Author:DVDM
自作ゲームの開発過程ブログ。
赤髪愛なら誰にも負けない。

 
Pixiv バナー


ブロとも申請フォーム
最新記事
カテゴリ
最新コメント
最新トラックバック
RSSリンクの表示
リンク
ブロとも一覧
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。