diff options
| author | Dave Clark <[email protected]> | 2018-02-28 17:22:22 -0500 |
|---|---|---|
| committer | Dave Clark <[email protected]> | 2018-02-28 17:22:22 -0500 |
| commit | 25528fd230f5f4298c35123a833cdb112675808e (patch) | |
| tree | f5aca3f5ee5a7734df41e7b974a04c37ddff528e /samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp | |
| parent | Push GfeSDK #173 (diff) | |
| download | gfesdk-25528fd230f5f4298c35123a833cdb112675808e.tar.xz gfesdk-25528fd230f5f4298c35123a833cdb112675808e.zip | |
Push SDK # 1.1.186
Documentation updates.
Diffstat (limited to 'samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp')
| -rw-r--r-- | samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp b/samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp new file mode 100644 index 0000000..a124c77 --- /dev/null +++ b/samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp @@ -0,0 +1,1053 @@ +//----------------------------------------------------------------------------- +// File: DXUTsound.cpp +// +// Desc: DirectSound framework classes for playing wav files in DirectSound +// buffers. Feel free to use these classes as a starting point for adding +// extra functionality. +// +// Copyright (c) Microsoft Corp. All rights reserved. +//----------------------------------------------------------------------------- +#define STRICT +#include "DXUT.h" +#include <mmsystem.h> +#include <dsound.h> +#include "SDKsound.h" +#include "SDKwavefile.h" +#undef min // use __min instead +#undef max // use __max instead + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::CSoundManager() +// Desc: Constructs the class +//----------------------------------------------------------------------------- +CSoundManager::CSoundManager() +{ + m_pDS = NULL; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::~CSoundManager() +// Desc: Destroys the class +//----------------------------------------------------------------------------- +CSoundManager::~CSoundManager() +{ + SAFE_RELEASE( m_pDS ); +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::Initialize() +// Desc: Initializes the IDirectSound object and also sets the primary buffer +// format. This function must be called before any others. +//----------------------------------------------------------------------------- +HRESULT CSoundManager::Initialize( HWND hWnd, + DWORD dwCoopLevel ) +{ + HRESULT hr; + + SAFE_RELEASE( m_pDS ); + + // Create IDirectSound using the primary sound device + if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) ) + return DXUT_ERR( L"DirectSoundCreate8", hr ); + + // Set DirectSound coop level + if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) ) + return DXUT_ERR( L"SetCooperativeLevel", hr ); + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::SetPrimaryBufferFormat() +// Desc: Set primary buffer to a specified format +// !WARNING! - Setting the primary buffer format and then using this +// same DirectSound object for DirectMusic messes up +// DirectMusic! +// For example, to set the primary buffer format to 22kHz stereo, 16-bit +// then: dwPrimaryChannels = 2 +// dwPrimaryFreq = 22050, +// dwPrimaryBitRate = 16 +//----------------------------------------------------------------------------- +HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, + DWORD dwPrimaryFreq, + DWORD dwPrimaryBitRate ) +{ + HRESULT hr; + LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; + + if( m_pDS == NULL ) + return CO_E_NOTINITIALIZED; + + // Get the primary buffer + DSBUFFERDESC dsbd; + ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) ); + dsbd.dwSize = sizeof( DSBUFFERDESC ); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) + return DXUT_ERR( L"CreateSoundBuffer", hr ); + + WAVEFORMATEX wfx; + ZeroMemory( &wfx, sizeof( WAVEFORMATEX ) ); + wfx.wFormatTag = ( WORD )WAVE_FORMAT_PCM; + wfx.nChannels = ( WORD )dwPrimaryChannels; + wfx.nSamplesPerSec = ( DWORD )dwPrimaryFreq; + wfx.wBitsPerSample = ( WORD )dwPrimaryBitRate; + wfx.nBlockAlign = ( WORD )( wfx.wBitsPerSample / 8 * wfx.nChannels ); + wfx.nAvgBytesPerSec = ( DWORD )( wfx.nSamplesPerSec * wfx.nBlockAlign ); + + if( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) ) + return DXUT_ERR( L"SetFormat", hr ); + + SAFE_RELEASE( pDSBPrimary ); + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::Get3DListenerInterface() +// Desc: Returns the 3D listener interface associated with primary buffer. +//----------------------------------------------------------------------------- +HRESULT CSoundManager::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener ) +{ + HRESULT hr; + DSBUFFERDESC dsbdesc; + LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; + + if( ppDSListener == NULL ) + return E_INVALIDARG; + if( m_pDS == NULL ) + return CO_E_NOTINITIALIZED; + + *ppDSListener = NULL; + + // Obtain primary buffer, asking it for 3D control + ZeroMemory( &dsbdesc, sizeof( DSBUFFERDESC ) ); + dsbdesc.dwSize = sizeof( DSBUFFERDESC ); + dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER; + if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) ) + return DXUT_ERR( L"CreateSoundBuffer", hr ); + + if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener, + ( VOID** )ppDSListener ) ) ) + { + SAFE_RELEASE( pDSBPrimary ); + return DXUT_ERR( L"QueryInterface", hr ); + } + + // Release the primary buffer, since it is not need anymore + SAFE_RELEASE( pDSBPrimary ); + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::Create() +// Desc: +//----------------------------------------------------------------------------- +HRESULT CSoundManager::Create( CSound** ppSound, + LPWSTR strWaveFileName, + DWORD dwCreationFlags, + GUID guid3DAlgorithm, + DWORD dwNumBuffers ) +{ + HRESULT hr; + HRESULT hrRet = S_OK; + DWORD i; + LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; + DWORD dwDSBufferSize = NULL; + CWaveFile* pWaveFile = NULL; + + if( m_pDS == NULL ) + return CO_E_NOTINITIALIZED; + if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 ) + return E_INVALIDARG; + + apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; + if( apDSBuffer == NULL ) + { + hr = E_OUTOFMEMORY; + goto LFail; + } + + pWaveFile = new CWaveFile(); + if( pWaveFile == NULL ) + { + hr = E_OUTOFMEMORY; + goto LFail; + } + + pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); + + if( pWaveFile->GetSize() == 0 ) + { + // Wave is blank, so don't create it. + hr = E_FAIL; + goto LFail; + } + + // Make the DirectSound buffer the same size as the wav file + dwDSBufferSize = pWaveFile->GetSize(); + + // Create the direct sound buffer, and only request the flags needed + // since each requires some overhead and limits if the buffer can + // be hardware accelerated + DSBUFFERDESC dsbd; + ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) ); + dsbd.dwSize = sizeof( DSBUFFERDESC ); + dsbd.dwFlags = dwCreationFlags; + dsbd.dwBufferBytes = dwDSBufferSize; + dsbd.guid3DAlgorithm = guid3DAlgorithm; + dsbd.lpwfxFormat = pWaveFile->m_pwfx; + + // DirectSound is only guarenteed to play PCM data. Other + // formats may or may not work depending the sound card driver. + hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ); + + // Be sure to return this error code if it occurs so the + // callers knows this happened. + if( hr == DS_NO_VIRTUALIZATION ) + hrRet = DS_NO_VIRTUALIZATION; + + if( FAILED( hr ) ) + { + // DSERR_BUFFERTOOSMALL will be returned if the buffer is + // less than DSBSIZE_FX_MIN and the buffer is created + // with DSBCAPS_CTRLFX. + + // It might also fail if hardware buffer mixing was requested + // on a device that doesn't support it. + DXUT_ERR( L"CreateSoundBuffer", hr ); + + goto LFail; + } + + // Default to use DuplicateSoundBuffer() when created extra buffers since always + // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if + // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case. + if( ( dwCreationFlags & DSBCAPS_CTRLFX ) == 0 ) + { + for( i = 1; i < dwNumBuffers; i++ ) + { + if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) + { + DXUT_ERR( L"DuplicateSoundBuffer", hr ); + goto LFail; + } + } + } + else + { + for( i = 1; i < dwNumBuffers; i++ ) + { + hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL ); + if( FAILED( hr ) ) + { + DXUT_ERR( L"CreateSoundBuffer", hr ); + goto LFail; + } + } + } + + // Create the sound + *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags ); + + SAFE_DELETE_ARRAY( apDSBuffer ); + return hrRet; + +LFail: + // Cleanup + SAFE_DELETE( pWaveFile ); + SAFE_DELETE_ARRAY( apDSBuffer ); + return hr; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::CreateFromMemory() +// Desc: +//----------------------------------------------------------------------------- +HRESULT CSoundManager::CreateFromMemory( CSound** ppSound, + BYTE* pbData, + ULONG ulDataSize, + LPWAVEFORMATEX pwfx, + DWORD dwCreationFlags, + GUID guid3DAlgorithm, + DWORD dwNumBuffers ) +{ + HRESULT hr; + DWORD i; + LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; + DWORD dwDSBufferSize = NULL; + CWaveFile* pWaveFile = NULL; + + if( m_pDS == NULL ) + return CO_E_NOTINITIALIZED; + if( pbData == NULL || ppSound == NULL || dwNumBuffers < 1 ) + return E_INVALIDARG; + + apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; + if( apDSBuffer == NULL ) + { + hr = E_OUTOFMEMORY; + goto LFail; + } + + pWaveFile = new CWaveFile(); + if( pWaveFile == NULL ) + { + hr = E_OUTOFMEMORY; + goto LFail; + } + + pWaveFile->OpenFromMemory( pbData, ulDataSize, pwfx, WAVEFILE_READ ); + + + // Make the DirectSound buffer the same size as the wav file + dwDSBufferSize = ulDataSize; + + // Create the direct sound buffer, and only request the flags needed + // since each requires some overhead and limits if the buffer can + // be hardware accelerated + DSBUFFERDESC dsbd; + ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) ); + dsbd.dwSize = sizeof( DSBUFFERDESC ); + dsbd.dwFlags = dwCreationFlags; + dsbd.dwBufferBytes = dwDSBufferSize; + dsbd.guid3DAlgorithm = guid3DAlgorithm; + dsbd.lpwfxFormat = pwfx; + + if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) ) + { + DXUT_ERR( L"CreateSoundBuffer", hr ); + goto LFail; + } + + // Default to use DuplicateSoundBuffer() when created extra buffers since always + // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if + // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case. + if( ( dwCreationFlags & DSBCAPS_CTRLFX ) == 0 ) + { + for( i = 1; i < dwNumBuffers; i++ ) + { + if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) + { + DXUT_ERR( L"DuplicateSoundBuffer", hr ); + goto LFail; + } + } + } + else + { + for( i = 1; i < dwNumBuffers; i++ ) + { + hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL ); + if( FAILED( hr ) ) + { + DXUT_ERR( L"CreateSoundBuffer", hr ); + goto LFail; + } + } + } + + // Create the sound + *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags ); + + SAFE_DELETE_ARRAY( apDSBuffer ); + return S_OK; + +LFail: + // Cleanup + + SAFE_DELETE_ARRAY( apDSBuffer ); + return hr; +} + + +//----------------------------------------------------------------------------- +// Name: CSoundManager::CreateStreaming() +// Desc: +//----------------------------------------------------------------------------- +HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound, + LPWSTR strWaveFileName, + DWORD dwCreationFlags, + GUID guid3DAlgorithm, + DWORD dwNotifyCount, + DWORD dwNotifySize, + HANDLE hNotifyEvent ) +{ + HRESULT hr; + + if( m_pDS == NULL ) + return CO_E_NOTINITIALIZED; + if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL ) + return E_INVALIDARG; + + LPDIRECTSOUNDBUFFER pDSBuffer = NULL; + DWORD dwDSBufferSize = NULL; + CWaveFile* pWaveFile = NULL; + DSBPOSITIONNOTIFY* aPosNotify = NULL; + LPDIRECTSOUNDNOTIFY pDSNotify = NULL; + + pWaveFile = new CWaveFile(); + if( pWaveFile == NULL ) + return E_OUTOFMEMORY; + pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); + + // Figure out how big the DirectSound buffer should be + dwDSBufferSize = dwNotifySize * dwNotifyCount; + + // Set up the direct sound buffer. Request the NOTIFY flag, so + // that we are notified as the sound buffer plays. Note, that using this flag + // may limit the amount of hardware acceleration that can occur. + DSBUFFERDESC dsbd; + ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) ); + dsbd.dwSize = sizeof( DSBUFFERDESC ); + dsbd.dwFlags = dwCreationFlags | + DSBCAPS_CTRLPOSITIONNOTIFY | + DSBCAPS_GETCURRENTPOSITION2; + dsbd.dwBufferBytes = dwDSBufferSize; + dsbd.guid3DAlgorithm = guid3DAlgorithm; + dsbd.lpwfxFormat = pWaveFile->m_pwfx; + + if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) ) + { + // If wave format isn't then it will return + // either DSERR_BADFORMAT or E_INVALIDARG + if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG ) + return DXUT_ERR( L"CreateSoundBuffer", hr ); + + return DXUT_ERR( L"CreateSoundBuffer", hr ); + } + + // Create the notification events, so that we know when to fill + // the buffer as the sound plays. + if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify, + ( VOID** )&pDSNotify ) ) ) + { + SAFE_DELETE_ARRAY( aPosNotify ); + return DXUT_ERR( L"QueryInterface", hr ); + } + + aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ]; + if( aPosNotify == NULL ) + return E_OUTOFMEMORY; + + for( DWORD i = 0; i < dwNotifyCount; i++ ) + { + aPosNotify[i].dwOffset = ( dwNotifySize * i ) + dwNotifySize - 1; + aPosNotify[i].hEventNotify = hNotifyEvent; + } + + // Tell DirectSound when to notify us. The notification will come in the from + // of signaled events that are handled in WinMain() + if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, + aPosNotify ) ) ) + { + SAFE_RELEASE( pDSNotify ); + SAFE_DELETE_ARRAY( aPosNotify ); + return DXUT_ERR( L"SetNotificationPositions", hr ); + } + + SAFE_RELEASE( pDSNotify ); + SAFE_DELETE_ARRAY( aPosNotify ); + + // Create the sound + *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize ); + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::CSound() +// Desc: Constructs the class +//----------------------------------------------------------------------------- +CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, + DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags ) +{ + DWORD i; + + if( dwNumBuffers <= 0 ) + return; + + m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; + if( NULL != m_apDSBuffer ) + { + for( i = 0; i < dwNumBuffers; i++ ) + m_apDSBuffer[i] = apDSBuffer[i]; + + m_dwDSBufferSize = dwDSBufferSize; + m_dwNumBuffers = dwNumBuffers; + m_pWaveFile = pWaveFile; + m_dwCreationFlags = dwCreationFlags; + + FillBufferWithSound( m_apDSBuffer[0], FALSE ); + } +} + + +//----------------------------------------------------------------------------- +// Name: CSound::~CSound() +// Desc: Destroys the class +//----------------------------------------------------------------------------- +CSound::~CSound() +{ + for( DWORD i = 0; i < m_dwNumBuffers; i++ ) + { + SAFE_RELEASE( m_apDSBuffer[i] ); + } + + SAFE_DELETE_ARRAY( m_apDSBuffer ); + SAFE_DELETE( m_pWaveFile ); +} + + +//----------------------------------------------------------------------------- +// Name: CSound::FillBufferWithSound() +// Desc: Fills a DirectSound buffer with a sound file +//----------------------------------------------------------------------------- +HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger ) +{ + HRESULT hr; + VOID* pDSLockedBuffer = NULL; // Pointer to locked buffer memory + DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer + DWORD dwWavDataRead = 0; // Amount of data read from the wav file + + if( pDSB == NULL ) + return CO_E_NOTINITIALIZED; + + // Make sure we have focus, and we didn't just switch in from + // an app which had a DirectSound device + if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) + return DXUT_ERR( L"RestoreBuffer", hr ); + + // Lock the buffer down + if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, + &pDSLockedBuffer, &dwDSLockedBufferSize, + NULL, NULL, 0L ) ) ) + return DXUT_ERR( L"Lock", hr ); + + // Reset the wave file to the beginning + m_pWaveFile->ResetFile(); + + if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer, + dwDSLockedBufferSize, + &dwWavDataRead ) ) ) + return DXUT_ERR( L"Read", hr ); + + if( dwWavDataRead == 0 ) + { + // Wav is blank, so just fill with silence + FillMemory( ( BYTE* )pDSLockedBuffer, + dwDSLockedBufferSize, + ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); + } + else if( dwWavDataRead < dwDSLockedBufferSize ) + { + // If the wav file was smaller than the DirectSound buffer, + // we need to fill the remainder of the buffer with data + if( bRepeatWavIfBufferLarger ) + { + // Reset the file and fill the buffer with wav data + DWORD dwReadSoFar = dwWavDataRead; // From previous call above. + while( dwReadSoFar < dwDSLockedBufferSize ) + { + // This will keep reading in until the buffer is full + // for very short files + if( FAILED( hr = m_pWaveFile->ResetFile() ) ) + return DXUT_ERR( L"ResetFile", hr ); + + hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer + dwReadSoFar, + dwDSLockedBufferSize - dwReadSoFar, + &dwWavDataRead ); + if( FAILED( hr ) ) + return DXUT_ERR( L"Read", hr ); + + dwReadSoFar += dwWavDataRead; + } + } + else + { + // Don't repeat the wav file, just fill in silence + FillMemory( ( BYTE* )pDSLockedBuffer + dwWavDataRead, + dwDSLockedBufferSize - dwWavDataRead, + ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); + } + } + + // Unlock the buffer, we don't need it anymore. + pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::RestoreBuffer() +// Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was +// restored. It can also NULL if the information is not needed. +//----------------------------------------------------------------------------- +HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored ) +{ + HRESULT hr; + + if( pDSB == NULL ) + return CO_E_NOTINITIALIZED; + if( pbWasRestored ) + *pbWasRestored = FALSE; + + DWORD dwStatus; + if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) + return DXUT_ERR( L"GetStatus", hr ); + + if( dwStatus & DSBSTATUS_BUFFERLOST ) + { + // Since the app could have just been activated, then + // DirectSound may not be giving us control yet, so + // the restoring the buffer may fail. + // If it does, sleep until DirectSound gives us control. + do + { + hr = pDSB->Restore(); + if( hr == DSERR_BUFFERLOST ) + Sleep( 10 ); + } while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST ); + + if( pbWasRestored != NULL ) + *pbWasRestored = TRUE; + + return S_OK; + } + else + { + return S_FALSE; + } +} + + +//----------------------------------------------------------------------------- +// Name: CSound::GetFreeBuffer() +// Desc: Finding the first buffer that is not playing and return a pointer to +// it, or if all are playing return a pointer to a randomly selected buffer. +//----------------------------------------------------------------------------- +LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer() +{ + if( m_apDSBuffer == NULL ) + return FALSE; + + DWORD i; + for( i = 0; i < m_dwNumBuffers; i++ ) + { + if( m_apDSBuffer[i] ) + { + DWORD dwStatus = 0; + m_apDSBuffer[i]->GetStatus( &dwStatus ); + if( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) + break; + } + } + + if( i != m_dwNumBuffers ) + return m_apDSBuffer[ i ]; + else + return m_apDSBuffer[ rand() % m_dwNumBuffers ]; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::GetBuffer() +// Desc: +//----------------------------------------------------------------------------- +LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex ) +{ + if( m_apDSBuffer == NULL ) + return NULL; + if( dwIndex >= m_dwNumBuffers ) + return NULL; + + return m_apDSBuffer[dwIndex]; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::Get3DBufferInterface() +// Desc: +//----------------------------------------------------------------------------- +HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer ) +{ + if( m_apDSBuffer == NULL ) + return CO_E_NOTINITIALIZED; + if( dwIndex >= m_dwNumBuffers ) + return E_INVALIDARG; + + *ppDS3DBuffer = NULL; + + return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer, + ( VOID** )ppDS3DBuffer ); +} + + +//----------------------------------------------------------------------------- +// Name: CSound::Play() +// Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING +// in the dwFlags to loop the sound +//----------------------------------------------------------------------------- +HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan ) +{ + HRESULT hr; + BOOL bRestored; + + if( m_apDSBuffer == NULL ) + return CO_E_NOTINITIALIZED; + + LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer(); + + if( pDSB == NULL ) + return DXUT_ERR( L"GetFreeBuffer", E_FAIL ); + + // Restore the buffer if it was lost + if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) + return DXUT_ERR( L"RestoreBuffer", hr ); + + if( bRestored ) + { + // The buffer was restored, so we need to fill it with new data + if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) ) + return DXUT_ERR( L"FillBufferWithSound", hr ); + } + + if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME ) + { + pDSB->SetVolume( lVolume ); + } + + if( lFrequency != -1 && + ( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY ) ) + { + pDSB->SetFrequency( lFrequency ); + } + + if( m_dwCreationFlags & DSBCAPS_CTRLPAN ) + { + pDSB->SetPan( lPan ); + } + + return pDSB->Play( 0, dwPriority, dwFlags ); +} + + +//----------------------------------------------------------------------------- +// Name: CSound::Play3D() +// Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING +// in the dwFlags to loop the sound +//----------------------------------------------------------------------------- +HRESULT CSound::Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority, DWORD dwFlags, LONG lFrequency ) +{ + HRESULT hr; + BOOL bRestored; + DWORD dwBaseFrequency; + + if( m_apDSBuffer == NULL ) + return CO_E_NOTINITIALIZED; + + LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer(); + if( pDSB == NULL ) + return DXUT_ERR( L"GetFreeBuffer", E_FAIL ); + + // Restore the buffer if it was lost + if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) + return DXUT_ERR( L"RestoreBuffer", hr ); + + if( bRestored ) + { + // The buffer was restored, so we need to fill it with new data + if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) ) + return DXUT_ERR( L"FillBufferWithSound", hr ); + } + + if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY ) + { + pDSB->GetFrequency( &dwBaseFrequency ); + pDSB->SetFrequency( dwBaseFrequency + lFrequency ); + } + + // QI for the 3D buffer + LPDIRECTSOUND3DBUFFER pDS3DBuffer; + hr = pDSB->QueryInterface( IID_IDirectSound3DBuffer, ( VOID** )&pDS3DBuffer ); + if( SUCCEEDED( hr ) ) + { + hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE ); + if( SUCCEEDED( hr ) ) + { + hr = pDSB->Play( 0, dwPriority, dwFlags ); + } + + pDS3DBuffer->Release(); + } + + return hr; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::Stop() +// Desc: Stops the sound from playing +//----------------------------------------------------------------------------- +HRESULT CSound::Stop() +{ + if( m_apDSBuffer == NULL ) + return CO_E_NOTINITIALIZED; + + HRESULT hr = 0; + + for( DWORD i = 0; i < m_dwNumBuffers; i++ ) + hr |= m_apDSBuffer[i]->Stop(); + + return hr; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::Reset() +// Desc: Reset all of the sound buffers +//----------------------------------------------------------------------------- +HRESULT CSound::Reset() +{ + if( m_apDSBuffer == NULL ) + return CO_E_NOTINITIALIZED; + + HRESULT hr = 0; + + for( DWORD i = 0; i < m_dwNumBuffers; i++ ) + hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 ); + + return hr; +} + + +//----------------------------------------------------------------------------- +// Name: CSound::IsSoundPlaying() +// Desc: Checks to see if a buffer is playing and returns TRUE if it is. +//----------------------------------------------------------------------------- +BOOL CSound::IsSoundPlaying() +{ + BOOL bIsPlaying = FALSE; + + if( m_apDSBuffer == NULL ) + return FALSE; + + for( DWORD i = 0; i < m_dwNumBuffers; i++ ) + { + if( m_apDSBuffer[i] ) + { + DWORD dwStatus = 0; + m_apDSBuffer[i]->GetStatus( &dwStatus ); + bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 ); + } + } + + return bIsPlaying; +} + + +//----------------------------------------------------------------------------- +// Name: CStreamingSound::CStreamingSound() +// Desc: Setups up a buffer so data can be streamed from the wave file into +// a buffer. This is very useful for large wav files that would take a +// while to load. The buffer is initially filled with data, then +// as sound is played the notification events are signaled and more data +// is written into the buffer by calling HandleWaveStreamNotification() +//----------------------------------------------------------------------------- +CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize, + CWaveFile* pWaveFile, DWORD dwNotifySize ) : CSound( &pDSBuffer, dwDSBufferSize, 1, + pWaveFile, 0 ) +{ + m_dwLastPlayPos = 0; + m_dwPlayProgress = 0; + m_dwNotifySize = dwNotifySize; + m_dwNextWriteOffset = 0; + m_bFillNextNotificationWithSilence = FALSE; +} + + +//----------------------------------------------------------------------------- +// Name: CStreamingSound::~CStreamingSound() +// Desc: Destroys the class +//----------------------------------------------------------------------------- +CStreamingSound::~CStreamingSound() +{ +} + + +//----------------------------------------------------------------------------- +// Name: CStreamingSound::HandleWaveStreamNotification() +// Desc: Handle the notification that tells us to put more wav data in the +// circular buffer +//----------------------------------------------------------------------------- +HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay ) +{ + HRESULT hr; + DWORD dwCurrentPlayPos; + DWORD dwPlayDelta; + DWORD dwBytesWrittenToBuffer; + VOID* pDSLockedBuffer = NULL; + VOID* pDSLockedBuffer2 = NULL; + DWORD dwDSLockedBufferSize; + DWORD dwDSLockedBufferSize2; + + if( m_apDSBuffer == NULL || m_pWaveFile == NULL ) + return CO_E_NOTINITIALIZED; + + // Restore the buffer if it was lost + BOOL bRestored; + if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) ) + return DXUT_ERR( L"RestoreBuffer", hr ); + + if( bRestored ) + { + // The buffer was restored, so we need to fill it with new data + if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) ) + return DXUT_ERR( L"FillBufferWithSound", hr ); + return S_OK; + } + + // Lock the DirectSound buffer + if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize, + &pDSLockedBuffer, &dwDSLockedBufferSize, + &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) ) + return DXUT_ERR( L"Lock", hr ); + + // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize, + // it should the second buffer, so it should never be valid + if( pDSLockedBuffer2 != NULL ) + return E_UNEXPECTED; + + if( !m_bFillNextNotificationWithSilence ) + { + // Fill the DirectSound buffer with wav data + if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer, + dwDSLockedBufferSize, + &dwBytesWrittenToBuffer ) ) ) + return DXUT_ERR( L"Read", hr ); + } + else + { + // Fill the DirectSound buffer with silence + FillMemory( pDSLockedBuffer, dwDSLockedBufferSize, + ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); + dwBytesWrittenToBuffer = dwDSLockedBufferSize; + } + + // If the number of bytes written is less than the + // amount we requested, we have a short file. + if( dwBytesWrittenToBuffer < dwDSLockedBufferSize ) + { + if( !bLoopedPlay ) + { + // Fill in silence for the rest of the buffer. + FillMemory( ( BYTE* )pDSLockedBuffer + dwBytesWrittenToBuffer, + dwDSLockedBufferSize - dwBytesWrittenToBuffer, + ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); + + // Any future notifications should just fill the buffer with silence + m_bFillNextNotificationWithSilence = TRUE; + } + else + { + // We are looping, so reset the file and fill the buffer with wav data + DWORD dwReadSoFar = dwBytesWrittenToBuffer; // From previous call above. + while( dwReadSoFar < dwDSLockedBufferSize ) + { + // This will keep reading in until the buffer is full (for very short files). + if( FAILED( hr = m_pWaveFile->ResetFile() ) ) + return DXUT_ERR( L"ResetFile", hr ); + + if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer + dwReadSoFar, + dwDSLockedBufferSize - dwReadSoFar, + &dwBytesWrittenToBuffer ) ) ) + return DXUT_ERR( L"Read", hr ); + + dwReadSoFar += dwBytesWrittenToBuffer; + } + } + } + + // Unlock the DirectSound buffer + m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); + + // Figure out how much data has been played so far. When we have played + // past the end of the file, we will either need to start filling the + // buffer with silence or starting reading from the beginning of the file, + // depending if the user wants to loop the sound + if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) ) + return DXUT_ERR( L"GetCurrentPosition", hr ); + + // Check to see if the position counter looped + if( dwCurrentPlayPos < m_dwLastPlayPos ) + dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos; + else + dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos; + + m_dwPlayProgress += dwPlayDelta; + m_dwLastPlayPos = dwCurrentPlayPos; + + // If we are now filling the buffer with silence, then we have found the end so + // check to see if the entire sound has played, if it has then stop the buffer. + if( m_bFillNextNotificationWithSilence ) + { + // We don't want to cut off the sound before it's done playing. + if( m_dwPlayProgress >= m_pWaveFile->GetSize() ) + { + m_apDSBuffer[0]->Stop(); + } + } + + // Update where the buffer will lock (for next time) + m_dwNextWriteOffset += dwDSLockedBufferSize; + m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer + + return S_OK; +} + + +//----------------------------------------------------------------------------- +// Name: CStreamingSound::Reset() +// Desc: Resets the sound so it will begin playing at the beginning +//----------------------------------------------------------------------------- +HRESULT CStreamingSound::Reset() +{ + HRESULT hr; + + if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL ) + return CO_E_NOTINITIALIZED; + + m_dwLastPlayPos = 0; + m_dwPlayProgress = 0; + m_dwNextWriteOffset = 0; + m_bFillNextNotificationWithSilence = FALSE; + + // Restore the buffer if it was lost + BOOL bRestored; + if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) ) + return DXUT_ERR( L"RestoreBuffer", hr ); + + if( bRestored ) + { + // The buffer was restored, so we need to fill it with new data + if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) ) + return DXUT_ERR( L"FillBufferWithSound", hr ); + } + + m_pWaveFile->ResetFile(); + + return m_apDSBuffer[0]->SetCurrentPosition( 0L ); +} |