Microsoft DirectX 8.0 (C++) |
セカンダリ バッファがサポートしていない特別なミキシングやほかのエフェクトを必要とするアプリケーションに対して、DirectSound はプライマリ サウンド バッファへの直接アクセスを許可する。
プライマリ バッファへの書き込みアクセスを取得すると、DirectSound のほかの機能は利用できなくなる。セカンダリ バッファのミキシングも行われないので、ハードウェア アクセラレーション ミキシングも利用できなくなる。
ほとんどのアプリケーションは、プライマリ バッファへ直接アクセスせずにセカンダリ バッファを使う。セカンダリ バッファへの書き込みは簡単に行うことができる。より大きなバッファにより、次のデータ ブロックの書き込みまでの時間が長くなり、オーディオの音が途切れる危険が最小限に抑えられる。アプリケーションが必要とするオーディオが、ミキシングを必要としない 1 つのオーディオ ストリームのような単純なものであっても、セカンダリ バッファを使ってオーディオ データを再生することにより、より高い性能が実現できる。
注 プライマリ バッファへの直接の書き込みは、WDM ドライバ モデルでは利点がない。WDM では、プライマリ バッファは、事実上、カーネル ミキサによってミキシングされるセカンダリ バッファである。WDM の詳細については、「DirectSound ドライバ モデル」を参照すること。
プライマリ バッファのサイズは指定できず、バッファを作成した後に返されるサイズを受け入れなければならない。通常、プライマリ バッファは極めて小さく、もしアプリケーションがこのようなバッファに直接書き込む場合は短い間隔でデータ ブロックを書き込み、以前のデータがもう一度再生されることを防がなければならない。
プライマリ バッファがハードウェア上に存在していない限り、そのバッファの書き込みアクセスは取得できない。これに該当するかどうかを確認するには、IDirectSoundBuffer8::GetCaps メソッドを呼び出して DSBCAPS 構造体の dwFlags メンバ内の DSBCAPS_LOCHARDWARE フラグの有無をチェックする。ソフトウェアでエミュレートしたプライマリ バッファをロックしようとすると、呼び出しは失敗する。
DSBUFFERDESC 構造体に DSBCAPS_PRIMARYBUFFER フラグを指定し、この構造体を IDirectSound8::CreateSoundBuffer メソッドに渡して、アクセス可能なプライマリ バッファを作成する。バッファに書き込みをしたい場合、協調レベルは DSSCL_WRITEPRIMARY でなければならない。
プライマリ サウンド バッファは、ループして再生しなければならない。その目的で、DSBPLAY_LOOPING フラグが設定されていることを確認する。
次の例は、プライマリ バッファへの書き込みアクセスを取得する方法を示している。プライマリ バッファは IDirectSoundBuffer インターフェイスだけをサポートし、IDirectSoundBuffer8 をサポートしていないことに注意すること。
BOOL AppCreateWritePrimaryBuffer( LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER *lplpDsb, LPDWORD lpdwBufferSize, HWND hwnd) { DSBUFFERDESC dsbdesc; DSBCAPS dsbcaps; HRESULT hr; WAVEFORMATEX wf; // ウェーブ フォーマット構造体を設定する。 memset(&wf, 0, sizeof(WAVEFORMATEX)); wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 2; wf.nSamplesPerSec = 22050; wf.nBlockAlign = 4; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.wBitsPerSample = 16; // DSBUFFERDESC 構造体を設定する。 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // バッファ サイズはサウンド ハードウェアによって決まる。 dsbdesc.dwBufferBytes = 0; dsbdesc.lpwfxFormat = NULL; // プライマリ バッファでは NULL でなければならない。 // 書き込み優先協調レベルを取得する。 hr = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_WRITEPRIMARY); if SUCCEEDED(hr) { // バッファの作成を試みる。 hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, lplpDsb, NULL); if SUCCEEDED(hr) { // プライマリ バッファを適切なフォーマットに設定する。 hr = (*lplpDsb)->SetFormat(&wf); if SUCCEEDED(hr) { // バッファ サイズを知りたい場合は、GetCaps を呼び出す。 dsbcaps.dwSize = sizeof(DSBCAPS); (*lplpDsb)->GetCaps(&dsbcaps); *lpdwBufferSize = dsbcaps.dwBufferBytes; return TRUE; } } } // 失敗。 *lplpDsb = NULL; *lpdwBufferSize = 0; return FALSE; }
次の例は、アプリケーションがカスタム ミキサを実装する方法を示している。サウンド デバイスがデータ ブロックの再生を繰り返さないよう、AppMixIntoPrimaryBuffer 関数を一定の間隔で呼び出さなければならない。CustomMixer 関数はアプリケーションが定義した関数であり、アプリケーションが定義した AppStreamInfo 構造体の指定に従って複数のストリームをミキシングし、次のように結果を指定のポインタに書き込む。
BOOL AppMixIntoPrimaryBuffer( LPAPPSTREAMINFO lpAppStreamInfo, LPDIRECTSOUNDBUFFER lpDsbPrimary, DWORD dwDataBytes, DWORD dwOldPos, LPDWORD lpdwNewPos) { LPVOID lpvPtr1; DWORD dwBytes1; LPVOID lpvPtr2; DWORD dwBytes2; HRESULT hr; // 書き込みポインタを取得する。 hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); // DSERR_BUFFERLOST が返された場合、復元してロックを再試行する。 if (DSERR_BUFFERLOST == hr) { lpDsbPrimary->Restore(); hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); } if SUCCEEDED(hr) { // 返されたポインタにデータをミキシングする。 CustomMixer(lpAppStreamInfo, lpvPtr1, dwBytes1); *lpdwNewPos = dwOldPos + dwBytes1; if (NULL != lpvPtr2) { CustomMixer(lpAppStreamInfo, lpvPtr2, dwBytes2); *lpdwNewPos = dwBytes2; // ラップアラウンドされるからである。 } // データを解放して DirectSound に戻す。 hr = lpDsbPrimary->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); if SUCCEEDED(hr) { return TRUE; } } // ロックまたはアンロックが失敗した。 return FALSE; }