DirectSoundを利用してマイクから録音する
(その1)
以前作成した『DirectSoundを利用してWAVファイルを再生する』サンプルプログラムを改造して、DirectSoundのキャプチャ機能を利用して外部から音声データを入力して、WAVEファイルに保存するサンプルプログラムを作ります。
DirectSoundの開発には『DirectX SDK』が別途必要になります。
DirectSoundを利用したサウンド・キャプチャ処理の流れを簡単に説明すると、以下のようになります。
- キャプチャを行うデバイスのオブジェクトを作成します。
- キャプチャを行うデバイスのオブジェクトからキャプチャバッファを作成します。
- キャプチャバッファの任意の位置にキャプチャ完了イベントを設定します。
- 入力サウンドのキャプチャを開始します。
- キャプチャバッファ上の任意の位置までキャプチャが完了したときにイベント通知が受けられるので、キャプチャバッファからサウンドデータを取り出します。
- キャプチャ処理を停止するまで上記の処理を繰り返します。
DirectSoundを利用するには、以下のインクルードファイルとライブラリが必要になります。
追加するインクルード | dsound.h |
追加するライブラリ | dsound.lib, dxguid.lib |
作成するサンプルプログラムは以下の仕様にします。
- ウインドウ・スタイルはダイアログにする。
- サウンドを入出力するデバイスが複数ある場合、ユーザーが選択できる。
- 録音するWAVファイルは、ユーザーが選択できる。
- WAVファイルを選択後、[録音]ボタンでマイクからの録音を開始する。
- マイクからの入力をモニタする為に、入力した音声データをそのままサウンド出力デバイスに送る。
- 録音中に[一時停止]ボタンを押すと、録音を一時中断し、再度[録音]ボタンを押すとマイクからの録音を再開する。
- 録音の一時停止中であっても、マイクのモニタ出力は継続する。
- [停止]ボタンを押すと録音を終了する。
- 音量調節および左右のバランス調節をダイアログから行えるようにする。
(但しファイルに記録する音声データの音量は変化させない。)
サンプルプログラムの外観は以下のようにします。
WM_INITDIALOGメッセージを受けたときに、ダイアログの初期処理を行います。
ダイアログの初期処理では、サウンド入出力デバイスのリストの作成と、音量ボリュームおよび左右バランスのトラックバーの初期化を行います。
サウンド出力デバイスのリスト作成は、『DirectSoundを利用してWAVファイルを再生する』を参考にしてください。
サウンド入力デバイスのリスト作成は、DirectSoundCaptureEnumerate()呼び出して、コールバック関数でデバイス名とGUIDの対応テーブルを作成し、作成したテーブルからデバイス名を取り出してコンボボックスを作成します。
(よってコンボボックスのインデックスが、デバイス名テーブルのインデックスになるようにコンボボックスにデバイス名を登録します。)
サンプルコードを以下に示します。
/****************************************************************************/
/*!
* @brief ダイアログの初期処理.
*
* @param [in] hDlg ダイアログのウインドウ・ハンドル.
*
* @retval なし.
*/
static void OnInitDialog( HWND hDlg )
{
//! DirectSoundの出力デバイスをリストアップする.
DevCnt[0] = 0;
DirectSoundEnumerate( DSEnumCallback, (void*)0 );
//! 出力デバイスリストを作成する.
if( DevCnt[0] != 0 ){
HWND hlist = GetDlgItem( hDlg, IDC_OUTPUT_DEVICE );
ComboBox_ResetContent( hlist );
for( DWORD i = 0; i < DevCnt[0]; i++ ){
ComboBox_AddString( hlist, DevName[0][i] );
}
ComboBox_SetCurSel( hlist, 0 );
}
// DirectSoundの入力デバイスをリストアップする.
DevCnt[1] = 0;
DirectSoundCaptureEnumerate( DSEnumCallback, (void*)1 );
// 入力デバイスリストを作成する.
if( DevCnt[1] != 0 ){
HWND hlist = GetDlgItem( hDlg, IDC_INPUT_DEVICE );
ComboBox_ResetContent( hlist );
for( DWORD i = 0; i < DevCnt[1]; i++ ){
ComboBox_AddString( hlist, DevName[1][i] );
}
ComboBox_SetCurSel( hlist, 0 );
}
//! 音量ボリュームのトラックバーを設定する.
HWND hbar = GetDlgItem( hDlg, IDC_VOLUME );
SendMessage( hbar, TBM_SETRANGE, TRUE, MAKELONG( 0, 100 ) );
SendMessage( hbar, TBM_SETPOS , TRUE, 50 );
//! 音量バランスのトラックバーを設定する.
hbar = GetDlgItem( hDlg, IDC_BALANCE );
SendMessage( hbar, TBM_SETRANGE, TRUE, MAKELONG( 0, 100 ) );
SendMessage( hbar, TBM_SETPOS , TRUE, 50 );
}
サウンド入出力デバイスのリストアップでコールバックされる関数は、入力デバイスのリストアップ時と出力デバイスのリストアップ時で共通の関数を使います。
サンプルプログラムでは、DirectSoundEnumerate()およびDirectSoundCaptureEnumerate()の第2引数で出力デバイスか入力デバイスかを判定させています。
/****************************************************************************/
/*!
* @brief 入出力デバイス列挙時のコールバック関数.
*
* @param [in] guid デバイスを識別するGIUD.
* @param [in] desc DirectSoundデバイスの説明文.
* @param [in] module DirectSoundドライバのモジュール名.
* @param [in] user ユーザー定義変数.
*
* @retval TRUE = 次のデバイスを取得 / FALSE = デバイスの列挙を終了する.
*/
static BOOL CALLBACK DSEnumCallback( LPGUID guid, LPCTSTR desc, LPCTSTR module, LPVOID user )
{
DWORD idx = (DWORD)user;
DWORD num = DevCnt[idx];
//! 確保しているリストサイズを超えてしまう場合、デバイスの列挙を終了する.
if( num >= 10 ){
return FALSE;
}
//! "プライマリ デバイス"はリストに入れない.
if( !guid || !desc ){
return TRUE;
}
//! 出力デバイスのGUIDを保存する.
DevGuid[idx][num] = *guid;
//! 出力デバイス名を保存する.
_tcsncpy_s( DevName[idx][num], desc, 63 );
//! 出力デバイスの個数を+1する.
DevCnt[idx]++;
return TRUE;
}
使用するオーディオドライバをユーザーが選択できるように、キャプチャ可能なDirectSoundドライバをDirectSoundCaptureEnumerate()を呼び出してリストアップします。
HRESULT WINAPI DirectSoundCaptureEnumerate(
LPDSENUMCALLBACK lpDSEnumCallback,
LPVOID lpContext )
引数 | 説明 |
---|---|
lpDSEnumCallback |
キャプチャ可能なDirectSoundドライバの情報が通知されるコールバック関数のポインタ。 |
lpContext |
アプリケーション定義データ |
DirectSoundCaptureEnumerate()からの戻り値がS_OKであれば、DirectSoundの入力デバイスをリストアップは完了です。
DirectSoundドライバの情報が通知されるコールバックは以下のとおりです。
BOOL CALLBACK DSEnumCallback(
LPGUID lpGuid,
LPCSTR lpcstrDescription,
LPCSTR lpcstrModule,
LPVOID lpContext )
引数 | 説明 |
---|---|
lpGuid |
ドライバのGUIDを示す変数のポインタ。 |
lpcstrDescription | デバイスの説明文を示す文字列のポインタ。 |
lpcstrModule | モジュール名を示す文字列のポインタ。 |
lpContext | アプリケーション定義データ。 |
引き続きデバイスの列挙を行う場合はTRUEを、デバイスの列挙を中断する場合はFALSEを返します。
サンプルコードでは、入出力デバイスのGUIDと表示用にデバイスの説明文を示す文字列をテーブルに保存しています。