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

再生カーソル(バイト単位)を受け取る変数のポインタ。
NULLを指定すると、再生カーソルを取得しません。

pdwCurrentWriteCursor

書き込みカーソル(バイト単位)を受け取る変数のポインタ。
NULLを指定すると、書き込みカーソルを取得しません。

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であれば、ストリームバッファの排他制御を解除できました。



Windowsは米国Microsoft Corporationの登録商標です。
DirectXは米国Microsoft Corporationの登録商標です。
DirectSoundは米国Microsoft Corporationの登録商標です。