DirectSoundを利用してWAVファイルを再生する
(その2)
ダイアログの[再生]ボタンを押されたときに、WAVファイルの再生を開始します。
WAVファイルがオープンされていない場合、WAVファイルをオープンします。
次にオープンしたWAVファイルの情報を元に以下の手順でサウンド出力デバイスに対応したストリームバッファを作成します。
- DirectSoundの再生オブジェクトを作成。
- 再生オブジェクトに対するアプリケーションの協調レベルを設定。
- ストリームバッファ・オブジェクトを作成。
- 再生イベントを設定。
- 再生イベントを監視するタスクを起動。
サンプルコードを以下に示します。
/****************************************************************************/
/*!
* @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;
}
WaveEnd = FALSE;
EndDelay = 0;
}
if( !OutDev ){
//! オープンした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 ) );
//! バランス設定をダイアログの設定にあわせる.
VolumeControl( GetDlgItem( hDlg, IDC_BALANCE ) );
//! 再生カーソルの位置を初期化する.
OutPos = 0;
OutBuf->SetCurrentPosition( 0 );
}
}
//! サウンド出力を開始する.
OutBuf->Play( 0, 0, DSBPLAY_LOOPING );
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 )
{
//! DirectSoundの再生オブジェクトを作成する.
if( DirectSoundCreate8( &DevGuid[num], &OutDev, NULL ) != S_OK ){
return FALSE;
}
//! 再生オブジェクトに対するアプリケーションの協調レベルを設定する.
if( OutDev->SetCooperativeLevel( hDlg, DSSCL_PRIORITY ) != DS_OK ){
return FALSE;
}
//! ストリームバッファ・オブジェクトを作成する.
DSBUFFERDESC desc;
memset( &desc, 0, sizeof(desc) );
desc.dwSize = sizeof(desc);
desc.dwFlags = DSBCAPS_GLOBALFOCUS
| DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_CTRLVOLUME
| DSBCAPS_CTRLPAN;
desc.dwBufferBytes = wf->nAvgBytesPerSec >> 2;
desc.lpwfxFormat = wf;
if( OutDev->CreateSoundBuffer( &desc, &OutBuf, NULL ) != DS_OK ){
return FALSE;
}
//! 確保されたストリームバッファのサイズを取得する.
DSBCAPS caps;
memset( &caps, 0, sizeof(caps) );
caps.dwSize = sizeof(caps);
if( OutBuf->GetCaps( &caps ) == DS_OK ){
OutBufSize = caps.dwBufferBytes;
}
//! 再生イベントのオブジェクトを作成する.
for( int i = 0; i < 2; i++ ){
OutEvent[i] = CreateEvent( NULL, FALSE, FALSE, NULL );
}
LPDIRECTSOUNDNOTIFY8 Notify;
if( OutBuf->QueryInterface( IID_IDirectSoundNotify, (LPVOID*)&Notify )
!= DS_OK ){
return FALSE;
}
//! 再生イベントを設定する.
DSBPOSITIONNOTIFY pos[2];
pos[0].dwOffset = (OutBufSize / 2) - 1;
pos[0].hEventNotify = OutEvent[0];
pos[1].dwOffset = OutBufSize - 1;
pos[1].hEventNotify = OutEvent[1];
Notify->SetNotificationPositions( 2, pos );
Notify->Release();
OutState = TRUE;
//! イベントタスクを起動する.
DWORD tid;
CreateThread( NULL, 0, OutputEventTask, (void*)hDlg, 0, &tid );
return TRUE;
}
IDirectSound8インターフェイスのオブジェクトを作成する
DirectSoundCreate8()を呼び出して、IDirectSound8インターフェイスのオブジェクトを作成します。
HRESULT WINAPI DirectSoundCreate8(
LPCGUID lpcGuidDevice,
LPDIRECTSOUND8* ppDS8,
LPUNKNOWN pUnkOuter )
引数 | 説明 |
---|---|
lpcGuidDevice |
オーディオ出力デバイスのGUIDのポインタ |
ppDS8 | IDirectSound8インターフェイスのオブジェクトのポインタを受け取る変数のポインタ。 |
pUnkOuter | NULLを指定します。 |
DirectSoundCreate8()からの戻り値がS_OKであれば、オブジェクトの作成は成功です。
IDirectSound8インターフェイスの各メソッドは以下のとおりです。
クラス識別子 | CLSID_DirectSound8 |
---|---|
インターフェイス識別子 | IID_IDirectSound8 |
継承クラス | IDirectSound |
メソッド名 | 説明 |
SetCooperativeLevel | デバイスに対するアプリケーションの協調レベルを設定します。 |
CreateSoundBuffer | オーディオデータを格納するバッファを作成します。 |
DuplicateSoundBuffer | 新しいセカンダリ・バッファを作成します。 |
GetCaps | デバイスの性能を取得します。 |
GetSpeakerConfig | スピーカの構成を取得します。 |
SetSpeakerConfig | スピーカ構成を設定します。 |
Initialize | オブジェクトを初期化します。 |
Compact | オンボード・メモリで利用できる空きメモリ領域を最大にします。 |
VerifyCertification | ドライバが DirectX で認証されているかどうかを確認します。 |
DirectSoundでは1つの出力デバイスを複数のアプリケーションから同時に利用できる為、IDirectSound8のメソッドSetCooperativeLevel()を呼び出して、使用するサウンド出力デバイスのアプリケーション間の協調レベルを設定します。
HRESULT IDirectSound8::SetCooperativeLevel(
HWND hwnd,
DWORD dwLevel )
引数 | 説明 |
---|---|
hwnd | アプリケーションのウインドウ・ハンドル。 |
dwLevel |
アプリケーションの協調レベル。 |
SetCooperativeLevel()からの戻り値がDS_OKであれば、協調レベルの設定は成功です。
IDirectSound8のメソッドCreateSoundBuffer()を呼び出して、オーディオデータを格納するストリームバッファのオブジェクトを作成します。
HRESULT IDirectSound8::CreateSoundBuffer(
LPCDSBUFFERDESC pcDSBufferDesc,
LPDIRECTSOUNDBUFFER* ppDSBuffer,
LPUNKNOWN pUnkOuter )
引数 | 説明 |
---|---|
pcDSBufferDesc | 作成するバッファの設定を記述した構造体のポインタ。 |
ppDSBuffer |
作成したストリームバッファ・オブジェクト(IDirectSoundBuffer8インターフェイス)のポインタを受け取る変数のポインタ。 |
pUnkOuter |
NULLを指定します。 |
CreateSoundBuffer()からの戻り値がDS_OKであれば、オーディオデータ・バッファの作成は成功です。
IDirectSoundBuffer8インターフェイスの各メソッドは以下のとおりです。
インターフェイス識別子 | IID_IDirectSoundBuffer8 |
---|---|
継承クラス | IDirectSoundBuffer |
メソッド名 | 説明 |
Play | オーディオの再生を開始します。 |
Stop | オーディオの再生を停止します。 |
Lock | バッファをロックします。 |
Unlock | バッファをアンロックします。 |
GetFormat | オーディオ・フォーマットを取得します。 |
SetFormat | プライマリ・バッファのオーディオ・フォーマットを設定します。 |
GetFrequency | サンプルレートを取得します。 |
SetFrequency | サンプルレートを設定します。 |
GetCaps | バッファの能力を取得します。 |
GetStatus | バッファのステータスを取得します。 |
GetCurrentPosition | バッファの再生カーソルおよび読み込みカーソルの位置を取得します。 |
SetCurrentPosition | バッファの再生カーソルの位置を設定します。 |
GetVolume | ボリュームレベルを取得します。 |
SetVolume | ボリュームレベルを設定します。 |
GetPan | 左右のボリュームバランスを取得します。 |
SetPan | 左右のボリュームバランスを設定します。 |
Restore | 失われたバッファへのメモリ割り当てを復元します。 |
AcquireResources | DSBCAPS_LOCDEFER フラグで作成されたバッファにリソースを割り当てます。 |
Initialize | オブジェクトを初期化します。 |
GetObjectInPath | エフェクト・オブジェクトのインターフェイスを取得します。 |
SetFX | バッファのエフェクトを有効にします。 |
オーディオデータ・バッファ設定構造体(DSBUFFERDESC構造体)は以下のとおりです。
メンバ名 | 説明 |
---|---|
dwSize |
DSBUFFERDESC構造体のサイズ (バイト数)。 |
dwFlags |
サンプルコードで設定しているフラグは以下のとおりです。 |
dwBufferBytes |
作成するバッファのサイズ(バイト数)。 |
dwReserved | 0を指定します。 |
lpwfxFormat |
オーディオ・フォーマット(WAVEFORMATEX構造体またはWAVEFORMATEXTENSIBLE構造体)のポインタを指定します。 |
guid3DAlgorithm |
DirectSound3Dの2スピーカ仮想化アルゴリズムの指定。 |
作成したストリームバッファのサイズを取得するには、IDirectSoundBuffer8のメソッドGetCaps()を使用します。
HRESULT IDirectSoundBuffer8::GetCaps( LPDSBCAPS pDSBufferCaps )
引数 | 説明 |
---|---|
pDSBufferCaps |
バッファの能力が格納されるデータ(DSBCAPS構造体)のポインタ。 |
GetCaps()からの戻り値がDS_OKであれば成功です。
メンバ名 | 説明 |
---|---|
dwSize | DSBCAPS構造体のサイズ (バイト数)。 |
dwFlags | バッファの能力を示すフラグ。 |
dwBufferBytes | バッファのサイズ(バイト数)。 |
dwUnlockTransferRate | Unlock()を呼び出したときにデータをバッファ・メモリに転送する速度 (KB/秒)。 |
dwPlayCpuOverhead | サウンド・バッファをミキシングするために必要な時間。CPUサイクルのパーセンテージで表します。 |
IDirectSound8のメソッドQueryInterface()を呼び出して、イベント設定用のIDirectSoundNotify8インターフェースのオブジェクトを取得します。
IDirectSoundNotify8のメソッドSetNotificationPositions()を呼び出して再生イベントの通知位置を設定します。
再生中に、再生カーソルが指定されたオフセットに到達するたびに、イベントが通知されます。
HRESULT IDirectSoundNotify8::SetNotificationPositions(
DWORD dwPositionNotifies,
LPCDSBPOSITIONNOTIFY pcPositionNotifies )
引数 | 説明 |
---|---|
dwPositionNotifies | DSBPOSITIONNOTIFY構造体配列の個数。 |
pcPositionNotifies | DSBPOSITIONNOTIFY構造体配列のポインタ。 |
SetNotificationPositions()からの戻り値がDS_OKであればイベント設定は完了です。
※SetNotificationPositions()を呼び出すと、以前に設定していたイベント通知は上書きされて無効になります。
イベント設定構造体(DSBPOSITIONNOTIFY構造体)は以下のとおりです。
メンバ名 | 説明 |
---|---|
dwOffset |
バッファの先頭から通知イベントが発生する場所までのオフセット。 |
hEventNotify | 通知されるイベントのハンドル。 |
サンプルコードではストリームバッファの中間点と最後の2点に再生イベントを設定しています。
IDirectSoundBuffer8のメソッドSetCurrentPosition()を呼び出して再生開始位置を設定します。
サンプルコードでは再生開始位置の初期化なので、引数に0を設定します。
HRESULT IDirectSoundBuffer8::SetCurrentPosition( DWORD dwNewPosition )
引数 | 説明 |
---|---|
dwNewPosition | バッファの先頭からの再生カーソルのオフセット(バイト単位)。 |
SetCurrentPosition()からの戻り値がDS_OKであれば再生開始位置の設定は完了です。
IDirectSoundBuffer8のメソッドPlay()を呼び出して再生を開始します。
HRESULT IDirectSoundBuffer8::Play(
DWORD dwReserved1,
DWORD dwPriority,
DWORD dwFlags )
引数 | 説明 |
---|---|
dwReserved1 | 0を指定。 |
dwPriority | サウンドの優先度。通常は0を指定します。 |
dwFlags | ストリームバッファをリングバッファとして動作させますのでDSBPLAY_LOOPINGを指定します。 |
Play()からの戻り値がDS_OKであれば再生が開始されます。