aboutsummaryrefslogtreecommitdiff
path: root/samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp
diff options
context:
space:
mode:
authorDave Clark <[email protected]>2018-02-28 17:22:22 -0500
committerDave Clark <[email protected]>2018-02-28 17:22:22 -0500
commit25528fd230f5f4298c35123a833cdb112675808e (patch)
treef5aca3f5ee5a7734df41e7b974a04c37ddff528e /samples/DX_APIUsage/DXUT/Optional/SDKsound.cpp
parentPush GfeSDK #173 (diff)
downloadgfesdk-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.cpp1053
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 );
+}