DirectSoundを利用してマイクから録音する
(その2)
ダイアログの[録音]ボタンを押されたときに、外部入力(マイク)からの録音を開始します。
[録音]ボタンは、録音開始時と一時停止からの録音復帰時に押されますので、入出力デバイス等の二重オープンを防止する為に、入力デバイスの有無でデバイスのオープン済みかどうかを判定します。
(よって、何れかのデバイスでオープンに失敗したときは、オープンに成功した他のデバイスもクローズするようにしておきます。)
録音開始までの手順は以下のようになります。
- 音声データを記録する為にWAVファイルをオープンします。
- DirectSoundのキャプチャ・オブジェクトを作成します。
- キャプチャバッファ・オブジェクトを作成します。
- キャプチャ完了イベントを設定します。
- キャプチャ・イベント監視するタスクを起動します。
- マイクからの音声データサンプリング(サウンド・キャプチャ処理)を開始します。
最後に録音の一時停止中を判定する為に録画中フラグをセットしておきます。
サンプルコードを以下に示します。
/****************************************************************************/
/*!
* @brief 外部音声の録音を開始する.
*
* @param [in] hDlg ダイアログのウインドウ・ハンドル.
*
* @retval TRUE = OK. / FALSE = NG.
*/
static BOOL RecSound( HWND hDlg )
{
if( !CapDev ){
//! オーディオ・データ・フォーマットを設定する.
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( !OpenSoundCapDevice( hDlg, num, &wf ) ){
return FALSE;
}
//! サウンド出力デバイスをオープンする.
num = ComboBox_GetCurSel( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ) );
if( !OpenSoundOutDevice( hDlg, num, &wf ) ){
return FALSE;
}
//! 読み込みバッファを確保する.
if( !DataBuff ){
DataBuff = new BYTE[CapBufSize];
memset( DataBuff, 0, CapBufSize );
}
//! ボリューム設定をダイアログの設定にあわせる.
VolumeControl( GetDlgItem( hDlg, IDC_VOLUME ) );
//! バランス設定をダイアログの設定にあわせる.
VolumeControl( GetDlgItem( hDlg, IDC_BALANCE ) );
//! 再生カーソルの位置を初期化する.
OutPos = 0;
OutBuf->SetCurrentPosition( 0 );
//! サウンド出力を開始する.
OutBuf->Play( 0, 0, DSBPLAY_LOOPING );
//! キャプチャを開始する.
GetPos = (CapBufSize >> 1);
PutPos = 0;
CapBuf->Start( DSCBSTART_LOOPING );
}
//! 録画中フラグをセットする.
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 再生するWAVファイルのオーディオ情報構造体のポインタ.
*
* @retval TRUE = OK. / FALSE = NG.
*/
static BOOL OpenSoundCapDevice( HWND hDlg, UINT num, WAVEFORMATEX* wf )
{
//! DirectSoundのキャプチャ・オブジェクトを作成する.
if( DirectSoundCaptureCreate8( &DevGuid[1][num], &CapDev, NULL ) != S_OK ){
return FALSE;
}
//! キャプチャバッファ・オブジェクトを作成する.
DSCBUFFERDESC desc;
memset( &desc, 0, sizeof(desc) );
desc.dwSize = sizeof(desc);
desc.dwFlags = 0;
desc.dwBufferBytes = BUFSIZE;
desc.lpwfxFormat = wf;
if( CapDev->CreateCaptureBuffer( &desc, &CapBuf, NULL ) != DS_OK ){
return FALSE;
}
// 確保されたキャプチャ・バッファのサイズを取得する.
DSCBCAPS caps;
memset( &caps, 0, sizeof(caps) );
caps.dwSize = sizeof(caps);
if( CapBuf->GetCaps( &caps ) == DS_OK ){
CapBufSize = caps.dwBufferBytes;
}
// キャプチャ・イベントのオブジェクトを作成する.
for( int i = 0; i < 2; i++ ){
CapEvent[i] = CreateEvent( NULL, FALSE, FALSE, NULL );
}
LPDIRECTSOUNDNOTIFY8 Notify;
if( CapBuf->QueryInterface( IID_IDirectSoundNotify, (LPVOID*)&Notify ) != DS_OK ){
return FALSE;
}
//! キャプチャ・イベントを設定する.
DSBPOSITIONNOTIFY pos[2];
pos[0].dwOffset = (CapBufSize / 2) - 1;
pos[0].hEventNotify = CapEvent[0];
pos[1].dwOffset = CapBufSize - 1;
pos[1].hEventNotify = CapEvent[1];
Notify->SetNotificationPositions( 2, pos );
Notify->Release();
CapState = TRUE;
//! イベントタスクを起動する.
DWORD tid;
CreateThread( NULL, 0, CaptureEventTask, NULL, 0, &tid );
return TRUE;
}
IDirectSoundCapture8インターフェイスのオブジェクトを作成する
DirectSoundCaptureCreate8()を呼び出して、IDirectSoundCapture8インターフェイスのオブジェクト(キャプチャ・オブジェクト)を作成します。
HRESULT WINAPI DirectSoundCaptureCreate8(
LPCGUID lpcGUID,
LPDIRECTSOUNDCAPTURE8* lplpDSC,
LPUNKNOWN pUnkOuter )
引数 | 説明 |
---|---|
lpcGUID |
オーディオ入力デバイスのGUIDのポインタ |
lplpDSC | IDirectSoundCapture8インターフェイスのオブジェクトのポインタを受け取る変数のポインタ。 |
pUnkOuter | NULLを指定します。 |
DirectSoundCaptureCreate8()からの戻り値がS_OKであれば、オブジェクトの作成は成功です。
IDirectSoundCapture8インターフェイスの各メソッドは以下のとおりです。
クラス識別子 | CLSID_DirectSoundCapture8 |
---|---|
インターフェイス識別子 | IID_IDirectSoundCapture8 |
継承クラス | IUnknown |
メソッド名 | 説明 |
CreateCaptureBuffer | キャプチャデータを格納するバッファを作成します。 |
GetCaps | デバイスの性能を取得します。 |
Initialize | オブジェクトを初期化します。 |
IDirectSoundCapture8のメソッドCreateCaptureBuffer()を呼び出して、キャプチャしたオーディオデータを格納するバッファのオブジェクトを作成します。
HRESULT IDirectSoundCapture8::CreateCaptureBuffer(
LPCDSCBUFFERDESC pcDSCBufferDesc,
LPDIRECTSOUNDCAPTUREBUFFER * ppDSCBuffer,
LPUNKNOWN pUnkOuter )
引数 | 説明 |
---|---|
pcDSCBufferDesc | 作成するバッファの設定を記述した構造体のポインタ。 |
ppDSCBuffer | 作成したバッファ・オブジェクト(IDirectSoundCaptureBufferインターフェイス)のポインタを受け取る変数のポインタ。 |
pUnkOuter | NULLを指定します。 |
CreateCaptureBuffer()からの戻り値がDS_OKであれば、キャプチャバッファの作成は成功です。
IDirectSoundCaptureBufferインターフェイスの各メソッドは以下のとおりです。
インターフェイス識別子 | IID_IDirectSoundCaptureBuffer8 |
---|---|
継承クラス | IDirectSoundCaptureBuffer |
メソッド名 | 説明 |
Start | オーディオデータのキャプチャを開始します。 |
Stop | オーディオデータのキャプチャを停止します。 |
Lock | キャプチャバッファをロックします。 |
Unlock | キャプチャバッファをアンロックします。 |
GetStatus | キャプチャバッファのステータスを取得します。 |
GetFormat | オーディオ・フォーマットを取得します。 |
GetCaps | バッファの能力を取得します。 |
GetCurrentPosition | バッファのキャプチャ・カーソルおよび読み込みカーソルの位置を取得します。 |
Initialize | オブジェクトを初期化します。 |
GetFXStatus | キャプチャ・エフェクトのステータスを取得します。 |
GetObjectInPath | バッファに関連付けられたエフェクト・オブジェクトのインターフェイスを取得します。 |
キャプチャ・バッファ設定構造体(DSCBUFFERDESC構造体)は以下のとおりです。
メンバ名 | 説明 |
---|---|
dwSize | DSCBUFFERDESC構造体のサイズ (バイト数)。 |
dwFlags |
- DSCBCAPS_CTRLFX |
dwBufferBytes | 作成するバッファのサイズ(バイト数)。 |
dwReserved | 0を指定します。 |
lpwfxFormat | オーディオ・フォーマット(WAVEFORMATEX構造体またはWAVEFORMATEXTENSIBLE構造体)のポインタを指定します。 |
dwFXCount | サポートするエフェクトの個数。 |
lpDSCFXDesc | サポートするエフェクトを表すDSCEFFECTDESC構造体配列のポインタ。 |
作成したキャプチャバッファのサイズを取得するには、IDirectSoundCaptureBufferのメソッドGetCaps()を使用します。
HRESULT IDirectSoundCapture8::GetCaps( LPDSCCAPS pDSCCaps )
引数 | 説明 |
---|---|
pDSCCaps |
バッファの能力が格納されるデータ(DSCCAPS構造体)のポインタ。 |
GetCaps()からの戻り値がDS_OKであれば成功です。
メンバ名 | 説明 |
---|---|
dwSize | DSCCAPS構造体のサイズ (バイト数)。 |
dwFlags | バッファの能力を示すフラグ。 |
dwBufferBytes | バッファのサイズ(バイト数)。 |
dwReserved | 未使用。 |
IDirectSoundCapture8のメソッドQueryInterface()を呼び出して、イベント設定用のIDirectSoundNotify8インターフェースのオブジェクトを取得します。
IDirectSoundNotify8のメソッドSetNotificationPositions()を呼び出してキャプチャ完了イベントの通知位置を設定します。
キャプチャ中に、キャプチャ・カーソルが指定されたオフセットに到達するたびに、イベントが通知されます。
HRESULT IDirectSoundNotify8::SetNotificationPositions(
DWORD dwPositionNotifies,
LPCDSBPOSITIONNOTIFY pcPositionNotifies )
引数 | 説明 |
---|---|
dwPositionNotifies | DSBPOSITIONNOTIFY構造体配列の個数。 |
pcPositionNotifies | DSBPOSITIONNOTIFY構造体配列のポインタ。 |
SetNotificationPositions()からの戻り値がDS_OKであればイベント設定は完了です。
※SetNotificationPositions()を呼び出すと、以前に設定していたイベント通知は上書きされて無効になります。
イベント設定構造体(DSBPOSITIONNOTIFY構造体)は以下のとおりです。
メンバ名 | 説明 |
---|---|
dwOffset |
バッファの先頭から通知イベントが発生する場所までのオフセット。 |
hEventNotify | 通知されるイベントのハンドル。 |
サンプルコードではキャプチャバッファの中間点と最後の2点にキャプチャ完了イベントを設定しています。
IDirectSoundCaptureBuffer8のメソッドStart()を呼び出してキャプチャを開始します。
HRESULT IDirectSoundCaptureBuffer8::Start( DWORD dwFlags )
引数 | 説明 |
---|---|
dwFlags | キャプチャバッファをリングバッファとして動作させますのでDSCBSTART_LOOPINGを指定します。 |
Start()からの戻り値がDS_OKであればキャプチャが開始されます。