DirectSoundを利用してWAVファイルを再生する
(その3)
DirectSoundでは、SetNotificationPositions()にて事前に登録した再生位置までサウンド再生が実行されるとアプリケーションに対してイベントが通知されます。
このイベントの通知を受けたアプリケーション側は、直前にサウンドデータを書き込んだ最終位置から現在の再生位置までを計算してストリームバッファの空き容量を調べて、次のサウンドデータをストリームバッファに書き込みます
サンプルコードを以下に示します。
/****************************************************************************/
/*!
* @brief データ出力イベントを監視するタスク.
*
* @param [in] user ダイアログのウインドウ・ハンドル.
*
* @retval TRUE = OK. / FALSE = NG.
*/
static DWORD WINAPI OutputEventTask( void* user )
{
while( OutState ){
switch( WaitForMultipleObjects( 2, OutEvent, 0, INFINITE ) ){
case WAIT_OBJECT_0 + 0:
case WAIT_OBJECT_0 + 1:
if( OutState ){
//! 現在の再生カーソルの位置を取得する.
DWORD n = OutBufSize - OutPos;
DWORD play_pos = 0;
DWORD dummy = 0;
OutBuf->GetCurrentPosition( &play_pos, &dummy );
/*
* 再生カーソルとバッファの次のデータの書き込み開始位置から
* 書き込み可能な領域のバイト数を計算する.
*/
if( play_pos > OutPos ){
n = play_pos - OutPos;
}else
if( play_pos < OutPos ){
n = (OutBufSize - OutPos) + play_pos;
}
if( n > DataSize ) n = DataSize;
//! WAVファイルから再生データを読み込む.
memset( DataBuff, 0, n );
DWORD len = ReadWaveFile( DataBuff, n );
if( len < n ){
memset( &DataBuff[len], 0, n - len );
}
if( len == 0 && !WaveEnd ){
WaveEnd = TRUE;
EndDelay = 3;
}
//! データを書き込む領域をロックする.
void* buf1 = NULL;
void* buf2 = NULL;
DWORD len1 = 0;
DWORD len2 = 0;
OutBuf->Lock( OutPos, n, &buf1, &len1, &buf2, &len2, 0 );
//! 書き込みバッファに送信データをコピーする.
if( buf1 && len1 ){
memcpy( buf1, DataBuff, len1 );
OutPos += len1;
}
//! 書き込み領域がバッファの先頭に戻った場合、残りのデータをコピーする.
if( buf2 && len2 ){
memcpy( buf2, &DataBuff[len1], len2 );
OutPos += len2;
}
//! 次のデータの書き込み開始位置を更新する.
if( OutPos >= OutBufSize ){
OutPos -= OutBufSize;
}
//! バッファのロックを解除する.
OutBuf->Unlock( buf1, len1, buf2, len2 );
//! 最終データの再生が終了したので、STOPコマンドを発行する.
if( WaveEnd ){
if( EndDelay == 0 ){
PostMessage( (HWND)user, WM_COMMAND, IDC_STOP, 0 );
}else{
EndDelay--;
}
}
}
break;
}
}
return 0;
}
イベント通知を受けた時点でのストリームバッファの再生位置を知る為に、IDirectSoundBuffer8のメソッドGetCurrentPosition()を呼び出して、現在の再生カーソルの位置を取得します。
HRESULT GetCurrentPosition(
LPDWORD pdwCurrentPlayCursor,
LPDWORD pdwCurrentWriteCursor )
引数 | 説明 |
---|---|
pdwCurrentPlayCursor | 再生カーソル(バイト単位)を受け取る変数のポインタ。 |
pdwCurrentWriteCursor | 書き込みカーソル(バイト単位)を受け取る変数のポインタ。 |
GetCurrentPosition()からの戻り値がDS_OKであれば、ストリームバッファのカーソル位置が取得できます。
DirectSoundではストリームバッファの書き込みは排他制御で行う為に、IDirectSoundBuffer8のメソッドLock()を呼び出して、書き込みを行うストリームバッファのポインタを取得します。
取得したストリームバッファにオーディオデータを書き込んだら、Unlock()メソッドを呼び出して、ストリームバッファの排他制御を解除します。
HRESULT IDirectSoundBuffer8::Lock(
DWORD dwOffset,
DWORD dwBytes,
LPVOID * ppvAudioPtr1,
LPDWORD pdwAudioBytes1,
LPVOID * ppvAudioPtr2,
LPDWORD pdwAudioBytes2,
DWORD dwFlags )
引数 | 説明 |
---|---|
dwOffset | バッファの先頭からロック開始位置までのオフセット(バイト)。 |
dwBytes | ロックするバッファ部分のバイト数。 |
ppvAudioPtr1 | バッファのロックされた部分へのポインタを受け取る変数のポインタ。 |
pdwAudioBytes1 | ppvAudioPtr1 のブロックのバイト数を受け取る変数のポインタ。 |
ppvAudioPtr2 | 引数 dwBytes で指定したバイト数の途中でバッファの最後になってしまった場合、2番目にロックされた部分へのポインタを受け取る変数のポインタ。 |
pdwAudioBytes2 | ppvAudioPtr2 のブロックのバイト数を受け取る変数のポインタ。 |
dwFlags | 引数dwOffsetからロックするので0を指定します。 |
Lock()からの戻り値がDS_OKであれば、書き込み可能なストリームバッファのポインタが取得できます。
ストリームバッファの排他制御を解除するUnlock()メソッドは以下のとおりです。
HRESULT IDirectSoundBuffer8::Unlock(
LPVOID pvAudioPtr1,
DWORD dwAudioBytes1,
LPVOID pvAudioPtr2,
DWORD dwAudioBytes2 )
引数 | 説明 |
---|---|
pvAudioPtr1 | Lock()の ppvAudioPtr1 で取得したポインタ。 |
dwAudioBytes1 | pvAudioPtr1 のアドレスに書き込んだバイト数。 |
pvAudioPtr2 | Lock()の ppvAudioPtr2 で取得したポインタ。 |
dwAudioBytes2 | pvAudioPtr2 のアドレスに書き込んだバイト数。 |
Unlock()からの戻り値がDS_OKであれば、ストリームバッファの排他制御を解除できました。