マルチメディアAPI(MME)を利用してWAVファイルを再生する
(その2)

WAVファイルの再生を開始する

ダイアログの[再生]ボタンを押されたときに、WAVファイルの再生を開始します。

WAVファイルがオープンされていない場合、WAVファイルをオープンします。

次にオープンしたWAVファイルの情報を元にサウンド出力デバイスのオープンを行います。

最後にWAVファイルから再生するオーディオデータを読み込んでサウンド出力デバイスに投入します。

これでWAVファイルの再生が開始されます。

サンプルコードを以下に示します。


/****************************************************************************/
/*!
 *  @brief  WAVファイルを再生する.
 *
 *  @param  [in]    hDlg    ダイアログのウインドウ・ハンドル.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
static BOOL     PlaySoundOutput( HWND hDlg )
{
    //! WAVファイルをオープンしていない場合、オープンする.
    if( !IsOpenWaveFile() ){

        //! -1.ダイアログから再生するWAVファイル名を取得する.
        GetWindowText( GetDlgItem( hDlg, IDC_FILENAME ),
                                 FileName, sizeof(FileName) - 1 );

        //! -2.再生するWAVファイル名をオープンする.
        if( !ReadOpenWaveFile( FileName ) ){
            MessageBox( hDlg, _T("ファイルをオープンできません"),
                              _T("WAVファイルの再生"), MB_OK );
            return FALSE;
        }
    }
    if( !hWO ){
        //! オープンしたWAVファイルのオーディオ・フィーマットを取得する.
        WAVEFORMATEX wf;
        GetAudioFormat( &wf );

        //! サウンド出力デバイスをオープンする.
        UINT num = ComboBox_GetCurSel( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ) );
        if( OpenSoundOutDevice( hDlg, num, &wf ) ){
            //! WAVファイルの読み込み用バッファを確保する.
            if( !DataBuff ){
                DataSize = wf.nAvgBytesPerSec >> 2;     // 1sec / 4 = 250ms.
                DataBuff = new BYTE[DataSize];
            }
            //! ボリューム設定をダイアログの設定にあわせる.
            VolumeControl( GetDlgItem( hDlg, IDC_VOLUME ) );
        }
    }

    //! WAVファイルを読み込んで、サウンド出力を開始する.
    for( DWORD i = 0; i < 2; i++ ){
        //! -1.データブロックをクリアする.
        memset( OutHdr[i].lpData, 0, DataSize );

        //! -2.WAVファイルからデータブロック分のデータを読み込む.
        ReadWaveFile( OutHdr[i].lpData, DataSize );

        //! -3.読み込んだオーディオ・データを出力デバイスに投入する.
        OutHdr[i].dwBufferLength = DataSize;
        waveOutWrite( hWO, &OutHdr[i], sizeof(WAVEHDR) );
    }
    EnableWindow( GetDlgItem( hDlg, IDC_PLAY          ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_PAUSE         ), TRUE  );
    EnableWindow( GetDlgItem( hDlg, IDC_STOP          ), TRUE  );
    EnableWindow( GetDlgItem( hDlg, IDC_FILENAME      ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_FILE_SELECT   ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ), FALSE );
    return TRUE;
}
	


/****************************************************************************/
/*!
 *  @brief  サウンド出力デバイスをオープンする.
 *
 *  @param  [in]    hDlg    ダイアログのウインドウ・ハンドル.
 *  @param  [in]    num     オープンするデバイス番号.
 *  @param  [in]    wf      再生するWAVファイルのオーディオ情報構造体のポインタ.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
static BOOL     OpenSoundOutDevice( HWND hDlg, UINT num, WAVEFORMATEX* wf )
{
    //! 出力デバイスをオープンする.
    if( waveOutOpen( &hWO, num, wf, (DWORD)hDlg, 0, CALLBACK_WINDOW )
                                                     != MMSYSERR_NOERROR ){
        return FALSE;
    }
    //! データブロックを出力デバイスに登録する.
    memset( OutHdr, 0, sizeof(OutHdr) );
    for( int i = 0; i < 2; i++ ){
        OutHdr[i].lpData = new char[wf->nAvgBytesPerSec];
        if( waveOutPrepareHeader( hWO, &OutHdr[i], sizeof(WAVEHDR) )
                                                     != MMSYSERR_NOERROR ){
            return FALSE;
        }
    }
    return TRUE;
}
	

サウンド出力デバイスをオープンする

waveOutOpen()を呼び出して、サウンド出力デバイスをオープンします。


    MMRESULT waveOutOpen(
                   LPHWAVEOUT phwo,
                   UINT uDeviceID,
                   LPWAVEFORMATEX pwfx,
                   DWORD dwCallback,
                   DWORD dwCallbackInstance,
                   DWORD fdwOpen )
	

引数説明
phwo

オープンしたサウンド出力デバイスのハンドルを格納する変数へのポインタ

uDeviceID

オープンするサウンド出力デバイスの識別子
コントロールパネルで設定されている出力デバイス(デフォルトのデバイス)を使用する場合、WAVE_MAPPERを指定する

pwfxWAVEFORMATEX構造体のポインタ
dwCallback

サウンド出力デバイスからのWM_WOM_DONEメッセージを受け取るウインドウのハンドル

dwCallbackInstancefdwOpen に CALLBACK_WINDOW を指定するので 0 を設定
fdwOpen

サウンド出力デバイスからの通知をウインドウ・メッセージで受け取る方法にするのでCALLBACK_WINDOWを指定

waveOutOpen()からの戻り値がMMSYSERR_NOERRORであれば、デバイスのオープンは成功です。

出力するサウンドデータのデータ形式(WAVEFORMATEX構造体)は以下のとおりです。

メンバ名説明
wFormatTag

オーディオデータのデータ形式
(PCMの場合WAVE_FORMAT_PCM)

nChannels

オーディオデータのチャンネル数
 - ステレオ(2チャンネル)の場合は2
 - モノラル(1チャンネル)の場合は1

wBitsPerSampleオーディオデータのビット数
nBlockAlign

1サンプルあたりのバイト数

チャンネル数(nChannels)とデータビット長(wBitsPerSample)により、以下のようになる

 - 2チャンネル16ビットの場合、4バイト
 - 2チャンネル 8ビットの場合、2バイト
 - 1チャンネル16ビットの場合、2バイト
 - 1チャンネル 8ビットの場合、1バイト

nBlockAlign = nChannels * ((wBitsPerSample + 7) >> 3)となる

nSamplesPerSecサンプリング周波数(1秒間あたりのサンプル数)
nAvgBytesPerSec

1秒間あたりのサウンドデータのバイト数
nAvgBytesPerSec = nSamplesPerSec * nBlockAlign

cbSize

拡張データのバイト数の指定。
(MMEでは使用しないので0)

データブロックをサウンド出力デバイスに関連付ける

waveOutPrepareHeader()を呼び出して、出力するサウンドデータを格納するデータブロックを、オープンしたサウンド出力デバイスで使用できるようにします。


    MMRESULT waveOutPrepareHeader( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh )
	

引数説明
hwoサウンド出力デバイスのハンドル
pwh初期化するWAVEHDR構造体のポインタ
cbwhWAVEHDR構造体のバイト数

waveOutPrepareHeader()からの戻り値がMMSYSERR_NOERRORであれば、データブロックの利用が可能です。

WAVEHDR構造体の各メンバは以下のとおりです。

メンバ名説明
lpDataデータバッファのポインタ
dwBufferLengthデータバッファ中のサウンド出力するデータのバイト数
dwBytesRecordedサウンド出力時は使用しない
dwUserユーザーが自由に使用できるデータ領域
dwFlagsデータブロックの状態を示す為のフラグ
dwLoops

ループ再生時のループ回数
(ループ再生しない場合は0)

lpNext

ドライバが使用する領域

使用するデータブロックはwaveOutUnprepareHeader()によってクリーンアップされるまでは有効な変数でないといけません。
(要するにデータブロックは、静的変数として確保しておくという事です。)

サウンド出力が開始されると、キューイングされているデータブロックから出力するサウンドデータを取り出して、 指定されたデータバイト数のデータ出力を行います。

デバイスはデータ出力完了後に、アプリケーションに対してデータ出力完了のコールバックを行います。
このときデバイスは、次にキューイングされているデータブロックがあれば、そのデータブロックから継続してデータ出力を行います。
キューイングされているデータブロックが無い場合、次のデータブロックがキューイングされるまでデータ出力を中断してしまいます。
よって、連続してデータ出力を行いたい場合、データブロックは2つ以上準備します。



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