マルチメディアAPI(MME)利用してマイクから録音する
(その2)

マイクからの録音を開始する

ダイアログの[録音]ボタンを押されたときに、外部入力(マイク)からの録音を開始します。

[録音]ボタンは、録音開始時と一時停止からの録音復帰時に押されますので、入出力デバイス等の二重オープンを防止する為に、入力デバイスの有無でデバイスのオープン済みかどうかを判定します。
(よって、何れかのデバイスでオープンに失敗したときは、オープンに成功した他のデバイスもクローズするようにしておきます。)

録音開始までの手順は以下のようになります。

最後に録音の一時停止中を判定する為に録画中フラグをセットしておきます。

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


/****************************************************************************/
/*!
 *  @brief  外部音声の録音を開始する.
 *
 *  @param  [in]    hDlg    ダイアログのウインドウ・ハンドル.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
static BOOL     RecSound( HWND hDlg )
{
    if( !hWI ){
        //! オーディオ・データ・フォーマットを設定する.
        WAVEFORMATEX wf;
        wf.wFormatTag      = WAVE_FORMAT_PCM;
        wf.nChannels       = 2;
        wf.wBitsPerSample  = 16;
        wf.nBlockAlign     = 4;
        wf.nSamplesPerSec  = SMPFRQ;
        wf.nAvgBytesPerSec = SMPFRQ * wf.nBlockAlign;
        wf.cbSize          = 0;

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

        //! 保存するWAVファイルを新規作成でオープンする.
        int result = SaveOpenWaveFile( FileName, &wf, FALSE );
        if( result == -2 ){
            //! 同一のファイル名がある場合、上書きするかどうかを問い合わせる.
            if( MessageBox( hDlg, _T("既にファイルが存在しています。上書きしますか?"), _T("WAVファイルの保存"), MB_YESNO ) == IDYES ){

                //! 上書きモードでファイルをオープンする.
                result = SaveOpenWaveFile( FileName, &wf, TRUE );
            }else{
                return FALSE;
            }
        }
        if( result != 0 ){
            MessageBox( hDlg, _T("ファイルをオープンできません"), _T("WAVファイルの保存"), MB_OK );
            return FALSE;
        }

        //! サウンド入力デバイスをオープンする.
        UINT num = ComboBox_GetCurSel( GetDlgItem( hDlg, IDC_INPUT_DEVICE ) );
        if( !OpenSoundInputDevice( hDlg, num, &wf ) ){
            return FALSE;
        }
        //! サウンド出力デバイスをオープンする.
        num = ComboBox_GetCurSel( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ) );
        if( !OpenSoundOutDevice( hDlg, num, &wf ) ){
            return FALSE;
        }
        //! ボリューム設定をダイアログの設定にあわせる.
        VolumeControl( GetDlgItem( hDlg, IDC_VOLUME ) );

        //! サウンド入力バッファをリセットする.
        waveInReset( hWI );

        //! データブロックをサウンド入力デバイスにキューイングする.
        for( DWORD i = 0; i < 2; i++ ){
            InHdr[i].dwBytesRecorded = 0;
            waveInAddBuffer( hWI, &InHdr[i], sizeof(WAVEHDR) );
        }

        //! サウンド入力処理を開始する.
        waveInStart( hWI );
    }
    //! 録画中フラグをセットする.
    RecFlag = TRUE;

    EnableWindow( GetDlgItem( hDlg, IDC_REC           ), 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 );
    EnableWindow( GetDlgItem( hDlg, IDC_INPUT_DEVICE  ), FALSE );
    return TRUE;
}
	


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

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

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


    MMRESULT waveInOpen(
                   LPHWAVEIN phwi,
                   UINT uDeviceID,
                   LPWAVEFORMATEX pwfx,
                   DWORD dwCallback,
                   DWORD dwCallbackInstance,
                   DWORD fdwOpen )
	

引数説明
phwi

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

uDeviceID

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

pwfx

WAVEFORMATEX構造体のポインタ

dwCallback

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

dwCallbackInstance

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

fdwOpen

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

waveInOpen()からの戻り値が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)

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

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


    MMRESULT waveInPrepareHeader( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh )
	

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

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

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

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

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


サウンド入力が開始されると、キューイングされているデータブロックにサンプリングした音声データを格納していきます。

データブロックのサイズ分の音声データの格納が完了すると、アプリケーションに対してデータ入力完了のコールバックを行います。

このときデバイスは、次にキューイングされているデータブロックがあれば、そのデータブロックから継続してサンプリングした音声データを格納していきます。

キューイングされているデータブロックが無い場合、次のデータブロックがキューイングされるまでサンプリングを中断してしまいます。

よって、連続してデータ入力を行いたい場合、データブロックは2つ以上準備します。

サウンド入力処理をリセットする

waveInReset()を呼び出して、サウンド入力処理のリセットを行います。


    MMRESULT waveInReset( HWAVEIN hwi )
	

waveInReset()からの戻り値がMMSYSERR_NOERRORであれば、成功です。

データブロックをサウンド入力デバイスに投入する

waveInAddBuffer()を呼び出して、データブロックをサウンド入力デバイスのデータ・キューにキューイングします。


    MMRESULT waveInAddBuffer( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh )
	

引数説明
hwiサウンド入力デバイスのハンドル
pwh

キューイングするデータブロック(WAVEHDR構造体)のポインタ

cbwhWAVEHDR構造体のバイト数

waveInAddBuffer()からの戻り値がMMSYSERR_NOERRORであれば、データブロックがキューイングされます。

音声データのサンプリングを開始する

音声データのサンプリング開始は、サウンド出力のようにデータブロックをデバイスにキューイングしただけでは開始されません。

waveInStart()を呼び出して、音声データのサンプリング開始します。


    MMRESULT waveInStart( HWAVEIN hwi )
	

waveInStart()からの戻り値がMMSYSERR_NOERRORであれば、音声データのサンプリングが開始されます。



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