1 //-----------------------------------------------------------------------------
\r
2 // File: DXUTsound.cpp
\r
4 // Desc: DirectSound framework classes for playing wav files in DirectSound
\r
5 // buffers. Feel free to use these classes as a starting point for adding
\r
6 // extra functionality.
\r
8 // Copyright (c) Microsoft Corp. All rights reserved.
\r
9 //-----------------------------------------------------------------------------
\r
12 #include <mmsystem.h>
\r
14 #include "SDKsound.h"
\r
15 #include "SDKwavefile.h"
\r
16 #undef min // use __min instead
\r
17 #undef max // use __max instead
\r
20 //-----------------------------------------------------------------------------
\r
21 // Name: CSoundManager::CSoundManager()
\r
22 // Desc: Constructs the class
\r
23 //-----------------------------------------------------------------------------
\r
24 CSoundManager::CSoundManager()
\r
30 //-----------------------------------------------------------------------------
\r
31 // Name: CSoundManager::~CSoundManager()
\r
32 // Desc: Destroys the class
\r
33 //-----------------------------------------------------------------------------
\r
34 CSoundManager::~CSoundManager()
\r
36 SAFE_RELEASE( m_pDS );
\r
40 //-----------------------------------------------------------------------------
\r
41 // Name: CSoundManager::Initialize()
\r
42 // Desc: Initializes the IDirectSound object and also sets the primary buffer
\r
43 // format. This function must be called before any others.
\r
44 //-----------------------------------------------------------------------------
\r
45 HRESULT CSoundManager::Initialize( HWND hWnd,
\r
50 SAFE_RELEASE( m_pDS );
\r
52 // Create IDirectSound using the primary sound device
\r
53 if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) )
\r
54 return DXUT_ERR( L"DirectSoundCreate8", hr );
\r
56 // Set DirectSound coop level
\r
57 if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) )
\r
58 return DXUT_ERR( L"SetCooperativeLevel", hr );
\r
64 //-----------------------------------------------------------------------------
\r
65 // Name: CSoundManager::SetPrimaryBufferFormat()
\r
66 // Desc: Set primary buffer to a specified format
\r
67 // !WARNING! - Setting the primary buffer format and then using this
\r
68 // same DirectSound object for DirectMusic messes up
\r
70 // For example, to set the primary buffer format to 22kHz stereo, 16-bit
\r
71 // then: dwPrimaryChannels = 2
\r
72 // dwPrimaryFreq = 22050,
\r
73 // dwPrimaryBitRate = 16
\r
74 //-----------------------------------------------------------------------------
\r
75 HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels,
\r
76 DWORD dwPrimaryFreq,
\r
77 DWORD dwPrimaryBitRate )
\r
80 LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
\r
83 return CO_E_NOTINITIALIZED;
\r
85 // Get the primary buffer
\r
87 ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) );
\r
88 dsbd.dwSize = sizeof( DSBUFFERDESC );
\r
89 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
\r
90 dsbd.dwBufferBytes = 0;
\r
91 dsbd.lpwfxFormat = NULL;
\r
93 if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
\r
94 return DXUT_ERR( L"CreateSoundBuffer", hr );
\r
97 ZeroMemory( &wfx, sizeof( WAVEFORMATEX ) );
\r
98 wfx.wFormatTag = ( WORD )WAVE_FORMAT_PCM;
\r
99 wfx.nChannels = ( WORD )dwPrimaryChannels;
\r
100 wfx.nSamplesPerSec = ( DWORD )dwPrimaryFreq;
\r
101 wfx.wBitsPerSample = ( WORD )dwPrimaryBitRate;
\r
102 wfx.nBlockAlign = ( WORD )( wfx.wBitsPerSample / 8 * wfx.nChannels );
\r
103 wfx.nAvgBytesPerSec = ( DWORD )( wfx.nSamplesPerSec * wfx.nBlockAlign );
\r
105 if( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) )
\r
106 return DXUT_ERR( L"SetFormat", hr );
\r
108 SAFE_RELEASE( pDSBPrimary );
\r
114 //-----------------------------------------------------------------------------
\r
115 // Name: CSoundManager::Get3DListenerInterface()
\r
116 // Desc: Returns the 3D listener interface associated with primary buffer.
\r
117 //-----------------------------------------------------------------------------
\r
118 HRESULT CSoundManager::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener )
\r
121 DSBUFFERDESC dsbdesc;
\r
122 LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
\r
124 if( ppDSListener == NULL )
\r
125 return E_INVALIDARG;
\r
126 if( m_pDS == NULL )
\r
127 return CO_E_NOTINITIALIZED;
\r
129 *ppDSListener = NULL;
\r
131 // Obtain primary buffer, asking it for 3D control
\r
132 ZeroMemory( &dsbdesc, sizeof( DSBUFFERDESC ) );
\r
133 dsbdesc.dwSize = sizeof( DSBUFFERDESC );
\r
134 dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
\r
135 if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) )
\r
136 return DXUT_ERR( L"CreateSoundBuffer", hr );
\r
138 if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener,
\r
139 ( VOID** )ppDSListener ) ) )
\r
141 SAFE_RELEASE( pDSBPrimary );
\r
142 return DXUT_ERR( L"QueryInterface", hr );
\r
145 // Release the primary buffer, since it is not need anymore
\r
146 SAFE_RELEASE( pDSBPrimary );
\r
152 //-----------------------------------------------------------------------------
\r
153 // Name: CSoundManager::Create()
\r
155 //-----------------------------------------------------------------------------
\r
156 HRESULT CSoundManager::Create( CSound** ppSound,
\r
157 LPWSTR strWaveFileName,
\r
158 DWORD dwCreationFlags,
\r
159 GUID guid3DAlgorithm,
\r
160 DWORD dwNumBuffers )
\r
163 HRESULT hrRet = S_OK;
\r
165 LPDIRECTSOUNDBUFFER* apDSBuffer = NULL;
\r
166 DWORD dwDSBufferSize = NULL;
\r
167 CWaveFile* pWaveFile = NULL;
\r
169 if( m_pDS == NULL )
\r
170 return CO_E_NOTINITIALIZED;
\r
171 if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 )
\r
172 return E_INVALIDARG;
\r
174 apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
\r
175 if( apDSBuffer == NULL )
\r
177 hr = E_OUTOFMEMORY;
\r
181 pWaveFile = new CWaveFile();
\r
182 if( pWaveFile == NULL )
\r
184 hr = E_OUTOFMEMORY;
\r
188 pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
\r
190 if( pWaveFile->GetSize() == 0 )
\r
192 // Wave is blank, so don't create it.
\r
197 // Make the DirectSound buffer the same size as the wav file
\r
198 dwDSBufferSize = pWaveFile->GetSize();
\r
200 // Create the direct sound buffer, and only request the flags needed
\r
201 // since each requires some overhead and limits if the buffer can
\r
202 // be hardware accelerated
\r
204 ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) );
\r
205 dsbd.dwSize = sizeof( DSBUFFERDESC );
\r
206 dsbd.dwFlags = dwCreationFlags;
\r
207 dsbd.dwBufferBytes = dwDSBufferSize;
\r
208 dsbd.guid3DAlgorithm = guid3DAlgorithm;
\r
209 dsbd.lpwfxFormat = pWaveFile->m_pwfx;
\r
211 // DirectSound is only guarenteed to play PCM data. Other
\r
212 // formats may or may not work depending the sound card driver.
\r
213 hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL );
\r
215 // Be sure to return this error code if it occurs so the
\r
216 // callers knows this happened.
\r
217 if( hr == DS_NO_VIRTUALIZATION )
\r
218 hrRet = DS_NO_VIRTUALIZATION;
\r
222 // DSERR_BUFFERTOOSMALL will be returned if the buffer is
\r
223 // less than DSBSIZE_FX_MIN and the buffer is created
\r
224 // with DSBCAPS_CTRLFX.
\r
226 // It might also fail if hardware buffer mixing was requested
\r
227 // on a device that doesn't support it.
\r
228 DXUT_ERR( L"CreateSoundBuffer", hr );
\r
233 // Default to use DuplicateSoundBuffer() when created extra buffers since always
\r
234 // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if
\r
235 // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
\r
236 if( ( dwCreationFlags & DSBCAPS_CTRLFX ) == 0 )
\r
238 for( i = 1; i < dwNumBuffers; i++ )
\r
240 if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
\r
242 DXUT_ERR( L"DuplicateSoundBuffer", hr );
\r
249 for( i = 1; i < dwNumBuffers; i++ )
\r
251 hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
\r
254 DXUT_ERR( L"CreateSoundBuffer", hr );
\r
260 // Create the sound
\r
261 *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
\r
263 SAFE_DELETE_ARRAY( apDSBuffer );
\r
268 SAFE_DELETE( pWaveFile );
\r
269 SAFE_DELETE_ARRAY( apDSBuffer );
\r
274 //-----------------------------------------------------------------------------
\r
275 // Name: CSoundManager::CreateFromMemory()
\r
277 //-----------------------------------------------------------------------------
\r
278 HRESULT CSoundManager::CreateFromMemory( CSound** ppSound,
\r
281 LPWAVEFORMATEX pwfx,
\r
282 DWORD dwCreationFlags,
\r
283 GUID guid3DAlgorithm,
\r
284 DWORD dwNumBuffers )
\r
288 LPDIRECTSOUNDBUFFER* apDSBuffer = NULL;
\r
289 DWORD dwDSBufferSize = NULL;
\r
290 CWaveFile* pWaveFile = NULL;
\r
292 if( m_pDS == NULL )
\r
293 return CO_E_NOTINITIALIZED;
\r
294 if( pbData == NULL || ppSound == NULL || dwNumBuffers < 1 )
\r
295 return E_INVALIDARG;
\r
297 apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
\r
298 if( apDSBuffer == NULL )
\r
300 hr = E_OUTOFMEMORY;
\r
304 pWaveFile = new CWaveFile();
\r
305 if( pWaveFile == NULL )
\r
307 hr = E_OUTOFMEMORY;
\r
311 pWaveFile->OpenFromMemory( pbData, ulDataSize, pwfx, WAVEFILE_READ );
\r
314 // Make the DirectSound buffer the same size as the wav file
\r
315 dwDSBufferSize = ulDataSize;
\r
317 // Create the direct sound buffer, and only request the flags needed
\r
318 // since each requires some overhead and limits if the buffer can
\r
319 // be hardware accelerated
\r
321 ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) );
\r
322 dsbd.dwSize = sizeof( DSBUFFERDESC );
\r
323 dsbd.dwFlags = dwCreationFlags;
\r
324 dsbd.dwBufferBytes = dwDSBufferSize;
\r
325 dsbd.guid3DAlgorithm = guid3DAlgorithm;
\r
326 dsbd.lpwfxFormat = pwfx;
\r
328 if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) )
\r
330 DXUT_ERR( L"CreateSoundBuffer", hr );
\r
334 // Default to use DuplicateSoundBuffer() when created extra buffers since always
\r
335 // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if
\r
336 // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
\r
337 if( ( dwCreationFlags & DSBCAPS_CTRLFX ) == 0 )
\r
339 for( i = 1; i < dwNumBuffers; i++ )
\r
341 if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
\r
343 DXUT_ERR( L"DuplicateSoundBuffer", hr );
\r
350 for( i = 1; i < dwNumBuffers; i++ )
\r
352 hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
\r
355 DXUT_ERR( L"CreateSoundBuffer", hr );
\r
361 // Create the sound
\r
362 *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
\r
364 SAFE_DELETE_ARRAY( apDSBuffer );
\r
370 SAFE_DELETE_ARRAY( apDSBuffer );
\r
375 //-----------------------------------------------------------------------------
\r
376 // Name: CSoundManager::CreateStreaming()
\r
378 //-----------------------------------------------------------------------------
\r
379 HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound,
\r
380 LPWSTR strWaveFileName,
\r
381 DWORD dwCreationFlags,
\r
382 GUID guid3DAlgorithm,
\r
383 DWORD dwNotifyCount,
\r
384 DWORD dwNotifySize,
\r
385 HANDLE hNotifyEvent )
\r
389 if( m_pDS == NULL )
\r
390 return CO_E_NOTINITIALIZED;
\r
391 if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL )
\r
392 return E_INVALIDARG;
\r
394 LPDIRECTSOUNDBUFFER pDSBuffer = NULL;
\r
395 DWORD dwDSBufferSize = NULL;
\r
396 CWaveFile* pWaveFile = NULL;
\r
397 DSBPOSITIONNOTIFY* aPosNotify = NULL;
\r
398 LPDIRECTSOUNDNOTIFY pDSNotify = NULL;
\r
400 pWaveFile = new CWaveFile();
\r
401 if( pWaveFile == NULL )
\r
402 return E_OUTOFMEMORY;
\r
403 pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
\r
405 // Figure out how big the DirectSound buffer should be
\r
406 dwDSBufferSize = dwNotifySize * dwNotifyCount;
\r
408 // Set up the direct sound buffer. Request the NOTIFY flag, so
\r
409 // that we are notified as the sound buffer plays. Note, that using this flag
\r
410 // may limit the amount of hardware acceleration that can occur.
\r
412 ZeroMemory( &dsbd, sizeof( DSBUFFERDESC ) );
\r
413 dsbd.dwSize = sizeof( DSBUFFERDESC );
\r
414 dsbd.dwFlags = dwCreationFlags |
\r
415 DSBCAPS_CTRLPOSITIONNOTIFY |
\r
416 DSBCAPS_GETCURRENTPOSITION2;
\r
417 dsbd.dwBufferBytes = dwDSBufferSize;
\r
418 dsbd.guid3DAlgorithm = guid3DAlgorithm;
\r
419 dsbd.lpwfxFormat = pWaveFile->m_pwfx;
\r
421 if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) )
\r
423 // If wave format isn't then it will return
\r
424 // either DSERR_BADFORMAT or E_INVALIDARG
\r
425 if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG )
\r
426 return DXUT_ERR( L"CreateSoundBuffer", hr );
\r
428 return DXUT_ERR( L"CreateSoundBuffer", hr );
\r
431 // Create the notification events, so that we know when to fill
\r
432 // the buffer as the sound plays.
\r
433 if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify,
\r
434 ( VOID** )&pDSNotify ) ) )
\r
436 SAFE_DELETE_ARRAY( aPosNotify );
\r
437 return DXUT_ERR( L"QueryInterface", hr );
\r
440 aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ];
\r
441 if( aPosNotify == NULL )
\r
442 return E_OUTOFMEMORY;
\r
444 for( DWORD i = 0; i < dwNotifyCount; i++ )
\r
446 aPosNotify[i].dwOffset = ( dwNotifySize * i ) + dwNotifySize - 1;
\r
447 aPosNotify[i].hEventNotify = hNotifyEvent;
\r
450 // Tell DirectSound when to notify us. The notification will come in the from
\r
451 // of signaled events that are handled in WinMain()
\r
452 if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount,
\r
455 SAFE_RELEASE( pDSNotify );
\r
456 SAFE_DELETE_ARRAY( aPosNotify );
\r
457 return DXUT_ERR( L"SetNotificationPositions", hr );
\r
460 SAFE_RELEASE( pDSNotify );
\r
461 SAFE_DELETE_ARRAY( aPosNotify );
\r
463 // Create the sound
\r
464 *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize );
\r
470 //-----------------------------------------------------------------------------
\r
471 // Name: CSound::CSound()
\r
472 // Desc: Constructs the class
\r
473 //-----------------------------------------------------------------------------
\r
474 CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize,
\r
475 DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags )
\r
479 if( dwNumBuffers <= 0 )
\r
482 m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
\r
483 if( NULL != m_apDSBuffer )
\r
485 for( i = 0; i < dwNumBuffers; i++ )
\r
486 m_apDSBuffer[i] = apDSBuffer[i];
\r
488 m_dwDSBufferSize = dwDSBufferSize;
\r
489 m_dwNumBuffers = dwNumBuffers;
\r
490 m_pWaveFile = pWaveFile;
\r
491 m_dwCreationFlags = dwCreationFlags;
\r
493 FillBufferWithSound( m_apDSBuffer[0], FALSE );
\r
498 //-----------------------------------------------------------------------------
\r
499 // Name: CSound::~CSound()
\r
500 // Desc: Destroys the class
\r
501 //-----------------------------------------------------------------------------
\r
504 for( DWORD i = 0; i < m_dwNumBuffers; i++ )
\r
506 SAFE_RELEASE( m_apDSBuffer[i] );
\r
509 SAFE_DELETE_ARRAY( m_apDSBuffer );
\r
510 SAFE_DELETE( m_pWaveFile );
\r
514 //-----------------------------------------------------------------------------
\r
515 // Name: CSound::FillBufferWithSound()
\r
516 // Desc: Fills a DirectSound buffer with a sound file
\r
517 //-----------------------------------------------------------------------------
\r
518 HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger )
\r
521 VOID* pDSLockedBuffer = NULL; // Pointer to locked buffer memory
\r
522 DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
\r
523 DWORD dwWavDataRead = 0; // Amount of data read from the wav file
\r
526 return CO_E_NOTINITIALIZED;
\r
528 // Make sure we have focus, and we didn't just switch in from
\r
529 // an app which had a DirectSound device
\r
530 if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) )
\r
531 return DXUT_ERR( L"RestoreBuffer", hr );
\r
533 // Lock the buffer down
\r
534 if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize,
\r
535 &pDSLockedBuffer, &dwDSLockedBufferSize,
\r
536 NULL, NULL, 0L ) ) )
\r
537 return DXUT_ERR( L"Lock", hr );
\r
539 // Reset the wave file to the beginning
\r
540 m_pWaveFile->ResetFile();
\r
542 if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer,
\r
543 dwDSLockedBufferSize,
\r
544 &dwWavDataRead ) ) )
\r
545 return DXUT_ERR( L"Read", hr );
\r
547 if( dwWavDataRead == 0 )
\r
549 // Wav is blank, so just fill with silence
\r
550 FillMemory( ( BYTE* )pDSLockedBuffer,
\r
551 dwDSLockedBufferSize,
\r
552 ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
\r
554 else if( dwWavDataRead < dwDSLockedBufferSize )
\r
556 // If the wav file was smaller than the DirectSound buffer,
\r
557 // we need to fill the remainder of the buffer with data
\r
558 if( bRepeatWavIfBufferLarger )
\r
560 // Reset the file and fill the buffer with wav data
\r
561 DWORD dwReadSoFar = dwWavDataRead; // From previous call above.
\r
562 while( dwReadSoFar < dwDSLockedBufferSize )
\r
564 // This will keep reading in until the buffer is full
\r
565 // for very short files
\r
566 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
\r
567 return DXUT_ERR( L"ResetFile", hr );
\r
569 hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer + dwReadSoFar,
\r
570 dwDSLockedBufferSize - dwReadSoFar,
\r
573 return DXUT_ERR( L"Read", hr );
\r
575 dwReadSoFar += dwWavDataRead;
\r
580 // Don't repeat the wav file, just fill in silence
\r
581 FillMemory( ( BYTE* )pDSLockedBuffer + dwWavDataRead,
\r
582 dwDSLockedBufferSize - dwWavDataRead,
\r
583 ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
\r
587 // Unlock the buffer, we don't need it anymore.
\r
588 pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
\r
594 //-----------------------------------------------------------------------------
\r
595 // Name: CSound::RestoreBuffer()
\r
596 // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was
\r
597 // restored. It can also NULL if the information is not needed.
\r
598 //-----------------------------------------------------------------------------
\r
599 HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored )
\r
604 return CO_E_NOTINITIALIZED;
\r
605 if( pbWasRestored )
\r
606 *pbWasRestored = FALSE;
\r
609 if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) )
\r
610 return DXUT_ERR( L"GetStatus", hr );
\r
612 if( dwStatus & DSBSTATUS_BUFFERLOST )
\r
614 // Since the app could have just been activated, then
\r
615 // DirectSound may not be giving us control yet, so
\r
616 // the restoring the buffer may fail.
\r
617 // If it does, sleep until DirectSound gives us control.
\r
620 hr = pDSB->Restore();
\r
621 if( hr == DSERR_BUFFERLOST )
\r
623 } while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST );
\r
625 if( pbWasRestored != NULL )
\r
626 *pbWasRestored = TRUE;
\r
637 //-----------------------------------------------------------------------------
\r
638 // Name: CSound::GetFreeBuffer()
\r
639 // Desc: Finding the first buffer that is not playing and return a pointer to
\r
640 // it, or if all are playing return a pointer to a randomly selected buffer.
\r
641 //-----------------------------------------------------------------------------
\r
642 LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer()
\r
644 if( m_apDSBuffer == NULL )
\r
648 for( i = 0; i < m_dwNumBuffers; i++ )
\r
650 if( m_apDSBuffer[i] )
\r
652 DWORD dwStatus = 0;
\r
653 m_apDSBuffer[i]->GetStatus( &dwStatus );
\r
654 if( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
\r
659 if( i != m_dwNumBuffers )
\r
660 return m_apDSBuffer[ i ];
\r
662 return m_apDSBuffer[ rand() % m_dwNumBuffers ];
\r
666 //-----------------------------------------------------------------------------
\r
667 // Name: CSound::GetBuffer()
\r
669 //-----------------------------------------------------------------------------
\r
670 LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex )
\r
672 if( m_apDSBuffer == NULL )
\r
674 if( dwIndex >= m_dwNumBuffers )
\r
677 return m_apDSBuffer[dwIndex];
\r
681 //-----------------------------------------------------------------------------
\r
682 // Name: CSound::Get3DBufferInterface()
\r
684 //-----------------------------------------------------------------------------
\r
685 HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer )
\r
687 if( m_apDSBuffer == NULL )
\r
688 return CO_E_NOTINITIALIZED;
\r
689 if( dwIndex >= m_dwNumBuffers )
\r
690 return E_INVALIDARG;
\r
692 *ppDS3DBuffer = NULL;
\r
694 return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer,
\r
695 ( VOID** )ppDS3DBuffer );
\r
699 //-----------------------------------------------------------------------------
\r
700 // Name: CSound::Play()
\r
701 // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING
\r
702 // in the dwFlags to loop the sound
\r
703 //-----------------------------------------------------------------------------
\r
704 HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan )
\r
709 if( m_apDSBuffer == NULL )
\r
710 return CO_E_NOTINITIALIZED;
\r
712 LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
\r
715 return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
\r
717 // Restore the buffer if it was lost
\r
718 if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
\r
719 return DXUT_ERR( L"RestoreBuffer", hr );
\r
723 // The buffer was restored, so we need to fill it with new data
\r
724 if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
\r
725 return DXUT_ERR( L"FillBufferWithSound", hr );
\r
728 if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME )
\r
730 pDSB->SetVolume( lVolume );
\r
733 if( lFrequency != -1 &&
\r
734 ( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY ) )
\r
736 pDSB->SetFrequency( lFrequency );
\r
739 if( m_dwCreationFlags & DSBCAPS_CTRLPAN )
\r
741 pDSB->SetPan( lPan );
\r
744 return pDSB->Play( 0, dwPriority, dwFlags );
\r
748 //-----------------------------------------------------------------------------
\r
749 // Name: CSound::Play3D()
\r
750 // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING
\r
751 // in the dwFlags to loop the sound
\r
752 //-----------------------------------------------------------------------------
\r
753 HRESULT CSound::Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority, DWORD dwFlags, LONG lFrequency )
\r
757 DWORD dwBaseFrequency;
\r
759 if( m_apDSBuffer == NULL )
\r
760 return CO_E_NOTINITIALIZED;
\r
762 LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
\r
764 return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
\r
766 // Restore the buffer if it was lost
\r
767 if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
\r
768 return DXUT_ERR( L"RestoreBuffer", hr );
\r
772 // The buffer was restored, so we need to fill it with new data
\r
773 if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
\r
774 return DXUT_ERR( L"FillBufferWithSound", hr );
\r
777 if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY )
\r
779 pDSB->GetFrequency( &dwBaseFrequency );
\r
780 pDSB->SetFrequency( dwBaseFrequency + lFrequency );
\r
783 // QI for the 3D buffer
\r
784 LPDIRECTSOUND3DBUFFER pDS3DBuffer;
\r
785 hr = pDSB->QueryInterface( IID_IDirectSound3DBuffer, ( VOID** )&pDS3DBuffer );
\r
786 if( SUCCEEDED( hr ) )
\r
788 hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE );
\r
789 if( SUCCEEDED( hr ) )
\r
791 hr = pDSB->Play( 0, dwPriority, dwFlags );
\r
794 pDS3DBuffer->Release();
\r
801 //-----------------------------------------------------------------------------
\r
802 // Name: CSound::Stop()
\r
803 // Desc: Stops the sound from playing
\r
804 //-----------------------------------------------------------------------------
\r
805 HRESULT CSound::Stop()
\r
807 if( m_apDSBuffer == NULL )
\r
808 return CO_E_NOTINITIALIZED;
\r
812 for( DWORD i = 0; i < m_dwNumBuffers; i++ )
\r
813 hr |= m_apDSBuffer[i]->Stop();
\r
819 //-----------------------------------------------------------------------------
\r
820 // Name: CSound::Reset()
\r
821 // Desc: Reset all of the sound buffers
\r
822 //-----------------------------------------------------------------------------
\r
823 HRESULT CSound::Reset()
\r
825 if( m_apDSBuffer == NULL )
\r
826 return CO_E_NOTINITIALIZED;
\r
830 for( DWORD i = 0; i < m_dwNumBuffers; i++ )
\r
831 hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );
\r
837 //-----------------------------------------------------------------------------
\r
838 // Name: CSound::IsSoundPlaying()
\r
839 // Desc: Checks to see if a buffer is playing and returns TRUE if it is.
\r
840 //-----------------------------------------------------------------------------
\r
841 BOOL CSound::IsSoundPlaying()
\r
843 BOOL bIsPlaying = FALSE;
\r
845 if( m_apDSBuffer == NULL )
\r
848 for( DWORD i = 0; i < m_dwNumBuffers; i++ )
\r
850 if( m_apDSBuffer[i] )
\r
852 DWORD dwStatus = 0;
\r
853 m_apDSBuffer[i]->GetStatus( &dwStatus );
\r
854 bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
\r
862 //-----------------------------------------------------------------------------
\r
863 // Name: CStreamingSound::CStreamingSound()
\r
864 // Desc: Setups up a buffer so data can be streamed from the wave file into
\r
865 // a buffer. This is very useful for large wav files that would take a
\r
866 // while to load. The buffer is initially filled with data, then
\r
867 // as sound is played the notification events are signaled and more data
\r
868 // is written into the buffer by calling HandleWaveStreamNotification()
\r
869 //-----------------------------------------------------------------------------
\r
870 CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize,
\r
871 CWaveFile* pWaveFile, DWORD dwNotifySize ) : CSound( &pDSBuffer, dwDSBufferSize, 1,
\r
874 m_dwLastPlayPos = 0;
\r
875 m_dwPlayProgress = 0;
\r
876 m_dwNotifySize = dwNotifySize;
\r
877 m_dwNextWriteOffset = 0;
\r
878 m_bFillNextNotificationWithSilence = FALSE;
\r
882 //-----------------------------------------------------------------------------
\r
883 // Name: CStreamingSound::~CStreamingSound()
\r
884 // Desc: Destroys the class
\r
885 //-----------------------------------------------------------------------------
\r
886 CStreamingSound::~CStreamingSound()
\r
891 //-----------------------------------------------------------------------------
\r
892 // Name: CStreamingSound::HandleWaveStreamNotification()
\r
893 // Desc: Handle the notification that tells us to put more wav data in the
\r
895 //-----------------------------------------------------------------------------
\r
896 HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay )
\r
899 DWORD dwCurrentPlayPos;
\r
901 DWORD dwBytesWrittenToBuffer;
\r
902 VOID* pDSLockedBuffer = NULL;
\r
903 VOID* pDSLockedBuffer2 = NULL;
\r
904 DWORD dwDSLockedBufferSize;
\r
905 DWORD dwDSLockedBufferSize2;
\r
907 if( m_apDSBuffer == NULL || m_pWaveFile == NULL )
\r
908 return CO_E_NOTINITIALIZED;
\r
910 // Restore the buffer if it was lost
\r
912 if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
\r
913 return DXUT_ERR( L"RestoreBuffer", hr );
\r
917 // The buffer was restored, so we need to fill it with new data
\r
918 if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
\r
919 return DXUT_ERR( L"FillBufferWithSound", hr );
\r
923 // Lock the DirectSound buffer
\r
924 if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize,
\r
925 &pDSLockedBuffer, &dwDSLockedBufferSize,
\r
926 &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) )
\r
927 return DXUT_ERR( L"Lock", hr );
\r
929 // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize,
\r
930 // it should the second buffer, so it should never be valid
\r
931 if( pDSLockedBuffer2 != NULL )
\r
932 return E_UNEXPECTED;
\r
934 if( !m_bFillNextNotificationWithSilence )
\r
936 // Fill the DirectSound buffer with wav data
\r
937 if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer,
\r
938 dwDSLockedBufferSize,
\r
939 &dwBytesWrittenToBuffer ) ) )
\r
940 return DXUT_ERR( L"Read", hr );
\r
944 // Fill the DirectSound buffer with silence
\r
945 FillMemory( pDSLockedBuffer, dwDSLockedBufferSize,
\r
946 ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
\r
947 dwBytesWrittenToBuffer = dwDSLockedBufferSize;
\r
950 // If the number of bytes written is less than the
\r
951 // amount we requested, we have a short file.
\r
952 if( dwBytesWrittenToBuffer < dwDSLockedBufferSize )
\r
956 // Fill in silence for the rest of the buffer.
\r
957 FillMemory( ( BYTE* )pDSLockedBuffer + dwBytesWrittenToBuffer,
\r
958 dwDSLockedBufferSize - dwBytesWrittenToBuffer,
\r
959 ( BYTE )( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
\r
961 // Any future notifications should just fill the buffer with silence
\r
962 m_bFillNextNotificationWithSilence = TRUE;
\r
966 // We are looping, so reset the file and fill the buffer with wav data
\r
967 DWORD dwReadSoFar = dwBytesWrittenToBuffer; // From previous call above.
\r
968 while( dwReadSoFar < dwDSLockedBufferSize )
\r
970 // This will keep reading in until the buffer is full (for very short files).
\r
971 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
\r
972 return DXUT_ERR( L"ResetFile", hr );
\r
974 if( FAILED( hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer + dwReadSoFar,
\r
975 dwDSLockedBufferSize - dwReadSoFar,
\r
976 &dwBytesWrittenToBuffer ) ) )
\r
977 return DXUT_ERR( L"Read", hr );
\r
979 dwReadSoFar += dwBytesWrittenToBuffer;
\r
984 // Unlock the DirectSound buffer
\r
985 m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
\r
987 // Figure out how much data has been played so far. When we have played
\r
988 // past the end of the file, we will either need to start filling the
\r
989 // buffer with silence or starting reading from the beginning of the file,
\r
990 // depending if the user wants to loop the sound
\r
991 if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) )
\r
992 return DXUT_ERR( L"GetCurrentPosition", hr );
\r
994 // Check to see if the position counter looped
\r
995 if( dwCurrentPlayPos < m_dwLastPlayPos )
\r
996 dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos;
\r
998 dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;
\r
1000 m_dwPlayProgress += dwPlayDelta;
\r
1001 m_dwLastPlayPos = dwCurrentPlayPos;
\r
1003 // If we are now filling the buffer with silence, then we have found the end so
\r
1004 // check to see if the entire sound has played, if it has then stop the buffer.
\r
1005 if( m_bFillNextNotificationWithSilence )
\r
1007 // We don't want to cut off the sound before it's done playing.
\r
1008 if( m_dwPlayProgress >= m_pWaveFile->GetSize() )
\r
1010 m_apDSBuffer[0]->Stop();
\r
1014 // Update where the buffer will lock (for next time)
\r
1015 m_dwNextWriteOffset += dwDSLockedBufferSize;
\r
1016 m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer
\r
1022 //-----------------------------------------------------------------------------
\r
1023 // Name: CStreamingSound::Reset()
\r
1024 // Desc: Resets the sound so it will begin playing at the beginning
\r
1025 //-----------------------------------------------------------------------------
\r
1026 HRESULT CStreamingSound::Reset()
\r
1030 if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL )
\r
1031 return CO_E_NOTINITIALIZED;
\r
1033 m_dwLastPlayPos = 0;
\r
1034 m_dwPlayProgress = 0;
\r
1035 m_dwNextWriteOffset = 0;
\r
1036 m_bFillNextNotificationWithSilence = FALSE;
\r
1038 // Restore the buffer if it was lost
\r
1040 if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
\r
1041 return DXUT_ERR( L"RestoreBuffer", hr );
\r
1045 // The buffer was restored, so we need to fill it with new data
\r
1046 if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
\r
1047 return DXUT_ERR( L"FillBufferWithSound", hr );
\r
1050 m_pWaveFile->ResetFile();
\r
1052 return m_apDSBuffer[0]->SetCurrentPosition( 0L );
\r