Imported Upstream version 2.81
[platform/upstream/libbullet.git] / Demos / DX11ClothDemo / DXUT / Optional / SDKsound.cpp
1 //-----------------------------------------------------------------------------\r
2 // File: DXUTsound.cpp\r
3 //\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
7 //\r
8 // Copyright (c) Microsoft Corp. All rights reserved.\r
9 //-----------------------------------------------------------------------------\r
10 #define STRICT\r
11 #include "DXUT.h"\r
12 #include <mmsystem.h>\r
13 #include <dsound.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
18 \r
19 \r
20 //-----------------------------------------------------------------------------\r
21 // Name: CSoundManager::CSoundManager()\r
22 // Desc: Constructs the class\r
23 //-----------------------------------------------------------------------------\r
24 CSoundManager::CSoundManager()\r
25 {\r
26     m_pDS = NULL;\r
27 }\r
28 \r
29 \r
30 //-----------------------------------------------------------------------------\r
31 // Name: CSoundManager::~CSoundManager()\r
32 // Desc: Destroys the class\r
33 //-----------------------------------------------------------------------------\r
34 CSoundManager::~CSoundManager()\r
35 {\r
36     SAFE_RELEASE( m_pDS );\r
37 }\r
38 \r
39 \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
46                                    DWORD dwCoopLevel )\r
47 {\r
48     HRESULT hr;\r
49 \r
50     SAFE_RELEASE( m_pDS );\r
51 \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
55 \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
59 \r
60     return S_OK;\r
61 }\r
62 \r
63 \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
69 //                   DirectMusic!\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
78 {\r
79     HRESULT hr;\r
80     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;\r
81 \r
82     if( m_pDS == NULL )\r
83         return CO_E_NOTINITIALIZED;\r
84 \r
85     // Get the primary buffer\r
86     DSBUFFERDESC dsbd;\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
92 \r
93     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )\r
94         return DXUT_ERR( L"CreateSoundBuffer", hr );\r
95 \r
96     WAVEFORMATEX wfx;\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
104 \r
105     if( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) )\r
106         return DXUT_ERR( L"SetFormat", hr );\r
107 \r
108     SAFE_RELEASE( pDSBPrimary );\r
109 \r
110     return S_OK;\r
111 }\r
112 \r
113 \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
119 {\r
120     HRESULT hr;\r
121     DSBUFFERDESC dsbdesc;\r
122     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;\r
123 \r
124     if( ppDSListener == NULL )\r
125         return E_INVALIDARG;\r
126     if( m_pDS == NULL )\r
127         return CO_E_NOTINITIALIZED;\r
128 \r
129     *ppDSListener = NULL;\r
130 \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
137 \r
138     if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener,\r
139                                                   ( VOID** )ppDSListener ) ) )\r
140     {\r
141         SAFE_RELEASE( pDSBPrimary );\r
142         return DXUT_ERR( L"QueryInterface", hr );\r
143     }\r
144 \r
145     // Release the primary buffer, since it is not need anymore\r
146     SAFE_RELEASE( pDSBPrimary );\r
147 \r
148     return S_OK;\r
149 }\r
150 \r
151 \r
152 //-----------------------------------------------------------------------------\r
153 // Name: CSoundManager::Create()\r
154 // Desc:\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
161 {\r
162     HRESULT hr;\r
163     HRESULT hrRet = S_OK;\r
164     DWORD i;\r
165     LPDIRECTSOUNDBUFFER* apDSBuffer = NULL;\r
166     DWORD dwDSBufferSize = NULL;\r
167     CWaveFile* pWaveFile = NULL;\r
168 \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
173 \r
174     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];\r
175     if( apDSBuffer == NULL )\r
176     {\r
177         hr = E_OUTOFMEMORY;\r
178         goto LFail;\r
179     }\r
180 \r
181     pWaveFile = new CWaveFile();\r
182     if( pWaveFile == NULL )\r
183     {\r
184         hr = E_OUTOFMEMORY;\r
185         goto LFail;\r
186     }\r
187 \r
188     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );\r
189 \r
190     if( pWaveFile->GetSize() == 0 )\r
191     {\r
192         // Wave is blank, so don't create it.\r
193         hr = E_FAIL;\r
194         goto LFail;\r
195     }\r
196 \r
197     // Make the DirectSound buffer the same size as the wav file\r
198     dwDSBufferSize = pWaveFile->GetSize();\r
199 \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
203     DSBUFFERDESC dsbd;\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
210 \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
214 \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
219 \r
220     if( FAILED( hr ) )\r
221     {\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
225 \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
229 \r
230         goto LFail;\r
231     }\r
232 \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
237     {\r
238         for( i = 1; i < dwNumBuffers; i++ )\r
239         {\r
240             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )\r
241             {\r
242                 DXUT_ERR( L"DuplicateSoundBuffer", hr );\r
243                 goto LFail;\r
244             }\r
245         }\r
246     }\r
247     else\r
248     {\r
249         for( i = 1; i < dwNumBuffers; i++ )\r
250         {\r
251             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );\r
252             if( FAILED( hr ) )\r
253             {\r
254                 DXUT_ERR( L"CreateSoundBuffer", hr );\r
255                 goto LFail;\r
256             }\r
257         }\r
258     }\r
259 \r
260     // Create the sound\r
261     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );\r
262 \r
263     SAFE_DELETE_ARRAY( apDSBuffer );\r
264     return hrRet;\r
265 \r
266 LFail:\r
267     // Cleanup\r
268     SAFE_DELETE( pWaveFile );\r
269     SAFE_DELETE_ARRAY( apDSBuffer );\r
270     return hr;\r
271 }\r
272 \r
273 \r
274 //-----------------------------------------------------------------------------\r
275 // Name: CSoundManager::CreateFromMemory()\r
276 // Desc:\r
277 //-----------------------------------------------------------------------------\r
278 HRESULT CSoundManager::CreateFromMemory( CSound** ppSound,\r
279                                          BYTE* pbData,\r
280                                          ULONG ulDataSize,\r
281                                          LPWAVEFORMATEX pwfx,\r
282                                          DWORD dwCreationFlags,\r
283                                          GUID guid3DAlgorithm,\r
284                                          DWORD dwNumBuffers )\r
285 {\r
286     HRESULT hr;\r
287     DWORD i;\r
288     LPDIRECTSOUNDBUFFER* apDSBuffer = NULL;\r
289     DWORD dwDSBufferSize = NULL;\r
290     CWaveFile* pWaveFile = NULL;\r
291 \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
296 \r
297     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];\r
298     if( apDSBuffer == NULL )\r
299     {\r
300         hr = E_OUTOFMEMORY;\r
301         goto LFail;\r
302     }\r
303 \r
304     pWaveFile = new CWaveFile();\r
305     if( pWaveFile == NULL )\r
306     {\r
307         hr = E_OUTOFMEMORY;\r
308         goto LFail;\r
309     }\r
310 \r
311     pWaveFile->OpenFromMemory( pbData, ulDataSize, pwfx, WAVEFILE_READ );\r
312 \r
313 \r
314     // Make the DirectSound buffer the same size as the wav file\r
315     dwDSBufferSize = ulDataSize;\r
316 \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
320     DSBUFFERDESC dsbd;\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
327 \r
328     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) )\r
329     {\r
330         DXUT_ERR( L"CreateSoundBuffer", hr );\r
331         goto LFail;\r
332     }\r
333 \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
338     {\r
339         for( i = 1; i < dwNumBuffers; i++ )\r
340         {\r
341             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )\r
342             {\r
343                 DXUT_ERR( L"DuplicateSoundBuffer", hr );\r
344                 goto LFail;\r
345             }\r
346         }\r
347     }\r
348     else\r
349     {\r
350         for( i = 1; i < dwNumBuffers; i++ )\r
351         {\r
352             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );\r
353             if( FAILED( hr ) )\r
354             {\r
355                 DXUT_ERR( L"CreateSoundBuffer", hr );\r
356                 goto LFail;\r
357             }\r
358         }\r
359     }\r
360 \r
361     // Create the sound\r
362     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );\r
363 \r
364     SAFE_DELETE_ARRAY( apDSBuffer );\r
365     return S_OK;\r
366 \r
367 LFail:\r
368     // Cleanup\r
369 \r
370     SAFE_DELETE_ARRAY( apDSBuffer );\r
371     return hr;\r
372 }\r
373 \r
374 \r
375 //-----------------------------------------------------------------------------\r
376 // Name: CSoundManager::CreateStreaming()\r
377 // Desc:\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
386 {\r
387     HRESULT hr;\r
388 \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
393 \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
399 \r
400     pWaveFile = new CWaveFile();\r
401     if( pWaveFile == NULL )\r
402         return E_OUTOFMEMORY;\r
403     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );\r
404 \r
405     // Figure out how big the DirectSound buffer should be\r
406     dwDSBufferSize = dwNotifySize * dwNotifyCount;\r
407 \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
411     DSBUFFERDESC dsbd;\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
420 \r
421     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) )\r
422     {\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
427 \r
428         return DXUT_ERR( L"CreateSoundBuffer", hr );\r
429     }\r
430 \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
435     {\r
436         SAFE_DELETE_ARRAY( aPosNotify );\r
437         return DXUT_ERR( L"QueryInterface", hr );\r
438     }\r
439 \r
440     aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ];\r
441     if( aPosNotify == NULL )\r
442         return E_OUTOFMEMORY;\r
443 \r
444     for( DWORD i = 0; i < dwNotifyCount; i++ )\r
445     {\r
446         aPosNotify[i].dwOffset = ( dwNotifySize * i ) + dwNotifySize - 1;\r
447         aPosNotify[i].hEventNotify = hNotifyEvent;\r
448     }\r
449 \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
453                                                           aPosNotify ) ) )\r
454     {\r
455         SAFE_RELEASE( pDSNotify );\r
456         SAFE_DELETE_ARRAY( aPosNotify );\r
457         return DXUT_ERR( L"SetNotificationPositions", hr );\r
458     }\r
459 \r
460     SAFE_RELEASE( pDSNotify );\r
461     SAFE_DELETE_ARRAY( aPosNotify );\r
462 \r
463     // Create the sound\r
464     *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize );\r
465 \r
466     return S_OK;\r
467 }\r
468 \r
469 \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
476 {\r
477     DWORD i;\r
478 \r
479     if( dwNumBuffers <= 0 )\r
480         return;\r
481 \r
482     m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];\r
483     if( NULL != m_apDSBuffer )\r
484     {\r
485         for( i = 0; i < dwNumBuffers; i++ )\r
486             m_apDSBuffer[i] = apDSBuffer[i];\r
487 \r
488         m_dwDSBufferSize = dwDSBufferSize;\r
489         m_dwNumBuffers = dwNumBuffers;\r
490         m_pWaveFile = pWaveFile;\r
491         m_dwCreationFlags = dwCreationFlags;\r
492 \r
493         FillBufferWithSound( m_apDSBuffer[0], FALSE );\r
494     }\r
495 }\r
496 \r
497 \r
498 //-----------------------------------------------------------------------------\r
499 // Name: CSound::~CSound()\r
500 // Desc: Destroys the class\r
501 //-----------------------------------------------------------------------------\r
502 CSound::~CSound()\r
503 {\r
504     for( DWORD i = 0; i < m_dwNumBuffers; i++ )\r
505     {\r
506         SAFE_RELEASE( m_apDSBuffer[i] );\r
507     }\r
508 \r
509     SAFE_DELETE_ARRAY( m_apDSBuffer );\r
510     SAFE_DELETE( m_pWaveFile );\r
511 }\r
512 \r
513 \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
519 {\r
520     HRESULT hr;\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
524 \r
525     if( pDSB == NULL )\r
526         return CO_E_NOTINITIALIZED;\r
527 \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
532 \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
538 \r
539     // Reset the wave file to the beginning\r
540     m_pWaveFile->ResetFile();\r
541 \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
546 \r
547     if( dwWavDataRead == 0 )\r
548     {\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
553     }\r
554     else if( dwWavDataRead < dwDSLockedBufferSize )\r
555     {\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
559         {\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
563             {\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
568 \r
569                 hr = m_pWaveFile->Read( ( BYTE* )pDSLockedBuffer + dwReadSoFar,\r
570                                         dwDSLockedBufferSize - dwReadSoFar,\r
571                                         &dwWavDataRead );\r
572                 if( FAILED( hr ) )\r
573                     return DXUT_ERR( L"Read", hr );\r
574 \r
575                 dwReadSoFar += dwWavDataRead;\r
576             }\r
577         }\r
578         else\r
579         {\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
584         }\r
585     }\r
586 \r
587     // Unlock the buffer, we don't need it anymore.\r
588     pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );\r
589 \r
590     return S_OK;\r
591 }\r
592 \r
593 \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
600 {\r
601     HRESULT hr;\r
602 \r
603     if( pDSB == NULL )\r
604         return CO_E_NOTINITIALIZED;\r
605     if( pbWasRestored )\r
606         *pbWasRestored = FALSE;\r
607 \r
608     DWORD dwStatus;\r
609     if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) )\r
610         return DXUT_ERR( L"GetStatus", hr );\r
611 \r
612     if( dwStatus & DSBSTATUS_BUFFERLOST )\r
613     {\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
618         do\r
619         {\r
620             hr = pDSB->Restore();\r
621             if( hr == DSERR_BUFFERLOST )\r
622                 Sleep( 10 );\r
623         }        while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST );\r
624 \r
625         if( pbWasRestored != NULL )\r
626             *pbWasRestored = TRUE;\r
627 \r
628         return S_OK;\r
629     }\r
630     else\r
631     {\r
632         return S_FALSE;\r
633     }\r
634 }\r
635 \r
636 \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
643 {\r
644     if( m_apDSBuffer == NULL )\r
645         return FALSE;\r
646 \r
647     DWORD i;\r
648     for( i = 0; i < m_dwNumBuffers; i++ )\r
649     {\r
650         if( m_apDSBuffer[i] )\r
651         {\r
652             DWORD dwStatus = 0;\r
653             m_apDSBuffer[i]->GetStatus( &dwStatus );\r
654             if( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )\r
655                 break;\r
656         }\r
657     }\r
658 \r
659     if( i != m_dwNumBuffers )\r
660         return m_apDSBuffer[ i ];\r
661     else\r
662         return m_apDSBuffer[ rand() % m_dwNumBuffers ];\r
663 }\r
664 \r
665 \r
666 //-----------------------------------------------------------------------------\r
667 // Name: CSound::GetBuffer()\r
668 // Desc:\r
669 //-----------------------------------------------------------------------------\r
670 LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex )\r
671 {\r
672     if( m_apDSBuffer == NULL )\r
673         return NULL;\r
674     if( dwIndex >= m_dwNumBuffers )\r
675         return NULL;\r
676 \r
677     return m_apDSBuffer[dwIndex];\r
678 }\r
679 \r
680 \r
681 //-----------------------------------------------------------------------------\r
682 // Name: CSound::Get3DBufferInterface()\r
683 // Desc:\r
684 //-----------------------------------------------------------------------------\r
685 HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer )\r
686 {\r
687     if( m_apDSBuffer == NULL )\r
688         return CO_E_NOTINITIALIZED;\r
689     if( dwIndex >= m_dwNumBuffers )\r
690         return E_INVALIDARG;\r
691 \r
692     *ppDS3DBuffer = NULL;\r
693 \r
694     return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer,\r
695                                                   ( VOID** )ppDS3DBuffer );\r
696 }\r
697 \r
698 \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
705 {\r
706     HRESULT hr;\r
707     BOOL bRestored;\r
708 \r
709     if( m_apDSBuffer == NULL )\r
710         return CO_E_NOTINITIALIZED;\r
711 \r
712     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();\r
713 \r
714     if( pDSB == NULL )\r
715         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );\r
716 \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
720 \r
721     if( bRestored )\r
722     {\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
726     }\r
727 \r
728     if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME )\r
729     {\r
730         pDSB->SetVolume( lVolume );\r
731     }\r
732 \r
733     if( lFrequency != -1 &&\r
734         ( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY ) )\r
735     {\r
736         pDSB->SetFrequency( lFrequency );\r
737     }\r
738 \r
739     if( m_dwCreationFlags & DSBCAPS_CTRLPAN )\r
740     {\r
741         pDSB->SetPan( lPan );\r
742     }\r
743 \r
744     return pDSB->Play( 0, dwPriority, dwFlags );\r
745 }\r
746 \r
747 \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
754 {\r
755     HRESULT hr;\r
756     BOOL bRestored;\r
757     DWORD dwBaseFrequency;\r
758 \r
759     if( m_apDSBuffer == NULL )\r
760         return CO_E_NOTINITIALIZED;\r
761 \r
762     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();\r
763     if( pDSB == NULL )\r
764         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );\r
765 \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
769 \r
770     if( bRestored )\r
771     {\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
775     }\r
776 \r
777     if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY )\r
778     {\r
779         pDSB->GetFrequency( &dwBaseFrequency );\r
780         pDSB->SetFrequency( dwBaseFrequency + lFrequency );\r
781     }\r
782 \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
787     {\r
788         hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE );\r
789         if( SUCCEEDED( hr ) )\r
790         {\r
791             hr = pDSB->Play( 0, dwPriority, dwFlags );\r
792         }\r
793 \r
794         pDS3DBuffer->Release();\r
795     }\r
796 \r
797     return hr;\r
798 }\r
799 \r
800 \r
801 //-----------------------------------------------------------------------------\r
802 // Name: CSound::Stop()\r
803 // Desc: Stops the sound from playing\r
804 //-----------------------------------------------------------------------------\r
805 HRESULT CSound::Stop()\r
806 {\r
807     if( m_apDSBuffer == NULL )\r
808         return CO_E_NOTINITIALIZED;\r
809 \r
810     HRESULT hr = 0;\r
811 \r
812     for( DWORD i = 0; i < m_dwNumBuffers; i++ )\r
813         hr |= m_apDSBuffer[i]->Stop();\r
814 \r
815     return hr;\r
816 }\r
817 \r
818 \r
819 //-----------------------------------------------------------------------------\r
820 // Name: CSound::Reset()\r
821 // Desc: Reset all of the sound buffers\r
822 //-----------------------------------------------------------------------------\r
823 HRESULT CSound::Reset()\r
824 {\r
825     if( m_apDSBuffer == NULL )\r
826         return CO_E_NOTINITIALIZED;\r
827 \r
828     HRESULT hr = 0;\r
829 \r
830     for( DWORD i = 0; i < m_dwNumBuffers; i++ )\r
831         hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );\r
832 \r
833     return hr;\r
834 }\r
835 \r
836 \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
842 {\r
843     BOOL bIsPlaying = FALSE;\r
844 \r
845     if( m_apDSBuffer == NULL )\r
846         return FALSE;\r
847 \r
848     for( DWORD i = 0; i < m_dwNumBuffers; i++ )\r
849     {\r
850         if( m_apDSBuffer[i] )\r
851         {\r
852             DWORD dwStatus = 0;\r
853             m_apDSBuffer[i]->GetStatus( &dwStatus );\r
854             bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );\r
855         }\r
856     }\r
857 \r
858     return bIsPlaying;\r
859 }\r
860 \r
861 \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
872                                                                                        pWaveFile, 0 )\r
873 {\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
879 }\r
880 \r
881 \r
882 //-----------------------------------------------------------------------------\r
883 // Name: CStreamingSound::~CStreamingSound()\r
884 // Desc: Destroys the class\r
885 //-----------------------------------------------------------------------------\r
886 CStreamingSound::~CStreamingSound()\r
887 {\r
888 }\r
889 \r
890 \r
891 //-----------------------------------------------------------------------------\r
892 // Name: CStreamingSound::HandleWaveStreamNotification()\r
893 // Desc: Handle the notification that tells us to put more wav data in the\r
894 //       circular buffer\r
895 //-----------------------------------------------------------------------------\r
896 HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay )\r
897 {\r
898     HRESULT hr;\r
899     DWORD dwCurrentPlayPos;\r
900     DWORD dwPlayDelta;\r
901     DWORD dwBytesWrittenToBuffer;\r
902     VOID* pDSLockedBuffer = NULL;\r
903     VOID* pDSLockedBuffer2 = NULL;\r
904     DWORD dwDSLockedBufferSize;\r
905     DWORD dwDSLockedBufferSize2;\r
906 \r
907     if( m_apDSBuffer == NULL || m_pWaveFile == NULL )\r
908         return CO_E_NOTINITIALIZED;\r
909 \r
910     // Restore the buffer if it was lost\r
911     BOOL bRestored;\r
912     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )\r
913         return DXUT_ERR( L"RestoreBuffer", hr );\r
914 \r
915     if( bRestored )\r
916     {\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
920         return S_OK;\r
921     }\r
922 \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
928 \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
933 \r
934     if( !m_bFillNextNotificationWithSilence )\r
935     {\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
941     }\r
942     else\r
943     {\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
948     }\r
949 \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
953     {\r
954         if( !bLoopedPlay )\r
955         {\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
960 \r
961             // Any future notifications should just fill the buffer with silence\r
962             m_bFillNextNotificationWithSilence = TRUE;\r
963         }\r
964         else\r
965         {\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
969             {\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
973 \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
978 \r
979                 dwReadSoFar += dwBytesWrittenToBuffer;\r
980             }\r
981         }\r
982     }\r
983 \r
984     // Unlock the DirectSound buffer\r
985     m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );\r
986 \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
993 \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
997     else\r
998         dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;\r
999 \r
1000     m_dwPlayProgress += dwPlayDelta;\r
1001     m_dwLastPlayPos = dwCurrentPlayPos;\r
1002 \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
1006     {\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
1009         {\r
1010             m_apDSBuffer[0]->Stop();\r
1011         }\r
1012     }\r
1013 \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
1017 \r
1018     return S_OK;\r
1019 }\r
1020 \r
1021 \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
1027 {\r
1028     HRESULT hr;\r
1029 \r
1030     if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL )\r
1031         return CO_E_NOTINITIALIZED;\r
1032 \r
1033     m_dwLastPlayPos = 0;\r
1034     m_dwPlayProgress = 0;\r
1035     m_dwNextWriteOffset = 0;\r
1036     m_bFillNextNotificationWithSilence = FALSE;\r
1037 \r
1038     // Restore the buffer if it was lost\r
1039     BOOL bRestored;\r
1040     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )\r
1041         return DXUT_ERR( L"RestoreBuffer", hr );\r
1042 \r
1043     if( bRestored )\r
1044     {\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
1048     }\r
1049 \r
1050     m_pWaveFile->ResetFile();\r
1051 \r
1052     return m_apDSBuffer[0]->SetCurrentPosition( 0L );\r
1053 }\r