1 //-----------------------------------------------------------------------------
2 // File: DXUTsound.cpp
3 //
4 // Desc: DirectSound framework classes for reading and writing wav files and
5 //       playing them in DirectSound buffers. Feel free to use this class
6 //       as a starting point for adding extra functionality.
7 //
8 // Copyright (c) Microsoft Corp. All rights reserved.
9 //-----------------------------------------------------------------------------
10 #define STRICT
11 #include "dxstdafx.h"
12 #include <mmsystem.h>
13 #include <dsound.h>
14 #include "DXUTsound.h"
15 #undef min // use __min instead
16 #undef max // use __max instead
17 
18 
19 //-----------------------------------------------------------------------------
20 // Name: CSoundManager::CSoundManager()
21 // Desc: Constructs the class
22 //-----------------------------------------------------------------------------
CSoundManager()23 CSoundManager::CSoundManager()
24 {
25     m_pDS = NULL;
26 }
27 
28 
29 //-----------------------------------------------------------------------------
30 // Name: CSoundManager::~CSoundManager()
31 // Desc: Destroys the class
32 //-----------------------------------------------------------------------------
~CSoundManager()33 CSoundManager::~CSoundManager()
34 {
35     SAFE_RELEASE( m_pDS );
36 }
37 
38 
39 //-----------------------------------------------------------------------------
40 // Name: CSoundManager::Initialize()
41 // Desc: Initializes the IDirectSound object and also sets the primary buffer
42 //       format.  This function must be called before any others.
43 //-----------------------------------------------------------------------------
Initialize(HWND hWnd,DWORD dwCoopLevel)44 HRESULT CSoundManager::Initialize( HWND  hWnd,
45                                    DWORD dwCoopLevel )
46 {
47     HRESULT             hr;
48 
49     SAFE_RELEASE( m_pDS );
50 
51     // Create IDirectSound using the primary sound device
52     if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) )
53         return DXUT_ERR( L"DirectSoundCreate8", hr );
54 
55     // Set DirectSound coop level
56     if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) )
57         return DXUT_ERR( L"SetCooperativeLevel", hr );
58 
59     return S_OK;
60 }
61 
62 
63 //-----------------------------------------------------------------------------
64 // Name: CSoundManager::SetPrimaryBufferFormat()
65 // Desc: Set primary buffer to a specified format
66 //       !WARNING! - Setting the primary buffer format and then using this
67 //                   same DirectSound object for DirectMusic messes up
68 //                   DirectMusic!
69 //       For example, to set the primary buffer format to 22kHz stereo, 16-bit
70 //       then:   dwPrimaryChannels = 2
71 //               dwPrimaryFreq     = 22050,
72 //               dwPrimaryBitRate  = 16
73 //-----------------------------------------------------------------------------
SetPrimaryBufferFormat(DWORD dwPrimaryChannels,DWORD dwPrimaryFreq,DWORD dwPrimaryBitRate)74 HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels,
75                                                DWORD dwPrimaryFreq,
76                                                DWORD dwPrimaryBitRate )
77 {
78     HRESULT             hr;
79     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
80 
81     if( m_pDS == NULL )
82         return CO_E_NOTINITIALIZED;
83 
84     // Get the primary buffer
85     DSBUFFERDESC dsbd;
86     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
87     dsbd.dwSize        = sizeof(DSBUFFERDESC);
88     dsbd.dwFlags       = DSBCAPS_PRIMARYBUFFER;
89     dsbd.dwBufferBytes = 0;
90     dsbd.lpwfxFormat   = NULL;
91 
92     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) )
93         return DXUT_ERR( L"CreateSoundBuffer", hr );
94 
95     WAVEFORMATEX wfx;
96     ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
97     wfx.wFormatTag      = (WORD) WAVE_FORMAT_PCM;
98     wfx.nChannels       = (WORD) dwPrimaryChannels;
99     wfx.nSamplesPerSec  = (DWORD) dwPrimaryFreq;
100     wfx.wBitsPerSample  = (WORD) dwPrimaryBitRate;
101     wfx.nBlockAlign     = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
102     wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign);
103 
104     if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) )
105         return DXUT_ERR( L"SetFormat", hr );
106 
107     SAFE_RELEASE( pDSBPrimary );
108 
109     return S_OK;
110 }
111 
112 
113 //-----------------------------------------------------------------------------
114 // Name: CSoundManager::Get3DListenerInterface()
115 // Desc: Returns the 3D listener interface associated with primary buffer.
116 //-----------------------------------------------------------------------------
Get3DListenerInterface(LPDIRECTSOUND3DLISTENER * ppDSListener)117 HRESULT CSoundManager::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener )
118 {
119     HRESULT             hr;
120     DSBUFFERDESC        dsbdesc;
121     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
122 
123     if( ppDSListener == NULL )
124         return E_INVALIDARG;
125     if( m_pDS == NULL )
126         return CO_E_NOTINITIALIZED;
127 
128     *ppDSListener = NULL;
129 
130     // Obtain primary buffer, asking it for 3D control
131     ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) );
132     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
133     dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
134     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) )
135         return DXUT_ERR( L"CreateSoundBuffer", hr );
136 
137     if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener,
138                                                   (VOID**)ppDSListener ) ) )
139     {
140         SAFE_RELEASE( pDSBPrimary );
141         return DXUT_ERR( L"QueryInterface", hr );
142     }
143 
144     // Release the primary buffer, since it is not need anymore
145     SAFE_RELEASE( pDSBPrimary );
146 
147     return S_OK;
148 }
149 
150 
151 //-----------------------------------------------------------------------------
152 // Name: CSoundManager::Create()
153 // Desc:
154 //-----------------------------------------------------------------------------
Create(CSound ** ppSound,LPWSTR strWaveFileName,DWORD dwCreationFlags,GUID guid3DAlgorithm,DWORD dwNumBuffers)155 HRESULT CSoundManager::Create( CSound** ppSound,
156                                LPWSTR strWaveFileName,
157                                DWORD dwCreationFlags,
158                                GUID guid3DAlgorithm,
159                                DWORD dwNumBuffers )
160 {
161     HRESULT hr;
162     HRESULT hrRet = S_OK;
163     DWORD   i;
164     LPDIRECTSOUNDBUFFER* apDSBuffer     = NULL;
165     DWORD                dwDSBufferSize = NULL;
166     CWaveFile*           pWaveFile      = NULL;
167 
168     if( m_pDS == NULL )
169         return CO_E_NOTINITIALIZED;
170     if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 )
171         return E_INVALIDARG;
172 
173     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
174     if( apDSBuffer == NULL )
175     {
176         hr = E_OUTOFMEMORY;
177         goto LFail;
178     }
179 
180     pWaveFile = new CWaveFile();
181     if( pWaveFile == NULL )
182     {
183         hr = E_OUTOFMEMORY;
184         goto LFail;
185     }
186 
187     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
188 
189     if( pWaveFile->GetSize() == 0 )
190     {
191         // Wave is blank, so don't create it.
192         hr = E_FAIL;
193         goto LFail;
194     }
195 
196     // Make the DirectSound buffer the same size as the wav file
197     dwDSBufferSize = pWaveFile->GetSize();
198 
199     // Create the direct sound buffer, and only request the flags needed
200     // since each requires some overhead and limits if the buffer can
201     // be hardware accelerated
202     DSBUFFERDESC dsbd;
203     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
204     dsbd.dwSize          = sizeof(DSBUFFERDESC);
205     dsbd.dwFlags         = dwCreationFlags;
206     dsbd.dwBufferBytes   = dwDSBufferSize;
207     dsbd.guid3DAlgorithm = guid3DAlgorithm;
208     dsbd.lpwfxFormat     = pWaveFile->m_pwfx;
209 
210     // DirectSound is only guarenteed to play PCM data.  Other
211     // formats may or may not work depending the sound card driver.
212     hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL );
213 
214     // Be sure to return this error code if it occurs so the
215     // callers knows this happened.
216     if( hr == DS_NO_VIRTUALIZATION )
217         hrRet = DS_NO_VIRTUALIZATION;
218 
219     if( FAILED(hr) )
220     {
221         // DSERR_BUFFERTOOSMALL will be returned if the buffer is
222         // less than DSBSIZE_FX_MIN and the buffer is created
223         // with DSBCAPS_CTRLFX.
224 
225         // It might also fail if hardware buffer mixing was requested
226         // on a device that doesn't support it.
227         DXUT_ERR( L"CreateSoundBuffer", hr );
228 
229         goto LFail;
230     }
231 
232     // Default to use DuplicateSoundBuffer() when created extra buffers since always
233     // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if
234     // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
235     if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 )
236     {
237         for( i=1; i<dwNumBuffers; i++ )
238         {
239             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
240             {
241                 DXUT_ERR( L"DuplicateSoundBuffer", hr );
242                 goto LFail;
243             }
244         }
245     }
246     else
247     {
248         for( i=1; i<dwNumBuffers; i++ )
249         {
250             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
251             if( FAILED(hr) )
252             {
253                 DXUT_ERR( L"CreateSoundBuffer", hr );
254                 goto LFail;
255             }
256         }
257    }
258 
259     // Create the sound
260     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
261 
262     SAFE_DELETE_ARRAY( apDSBuffer );
263     return hrRet;
264 
265 LFail:
266     // Cleanup
267     SAFE_DELETE( pWaveFile );
268     SAFE_DELETE_ARRAY( apDSBuffer );
269     return hr;
270 }
271 
272 
273 //-----------------------------------------------------------------------------
274 // Name: CSoundManager::CreateFromMemory()
275 // Desc:
276 //-----------------------------------------------------------------------------
CreateFromMemory(CSound ** ppSound,BYTE * pbData,ULONG ulDataSize,LPWAVEFORMATEX pwfx,DWORD dwCreationFlags,GUID guid3DAlgorithm,DWORD dwNumBuffers)277 HRESULT CSoundManager::CreateFromMemory( CSound** ppSound,
278                                         BYTE* pbData,
279                                         ULONG  ulDataSize,
280                                         LPWAVEFORMATEX pwfx,
281                                         DWORD dwCreationFlags,
282                                         GUID guid3DAlgorithm,
283                                         DWORD dwNumBuffers )
284 {
285     HRESULT hr;
286     DWORD   i;
287     LPDIRECTSOUNDBUFFER* apDSBuffer     = NULL;
288     DWORD                dwDSBufferSize = NULL;
289     CWaveFile*           pWaveFile      = NULL;
290 
291     if( m_pDS == NULL )
292         return CO_E_NOTINITIALIZED;
293     if( pbData == NULL || ppSound == NULL || dwNumBuffers < 1 )
294         return E_INVALIDARG;
295 
296     apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
297     if( apDSBuffer == NULL )
298     {
299         hr = E_OUTOFMEMORY;
300         goto LFail;
301     }
302 
303     pWaveFile = new CWaveFile();
304     if( pWaveFile == NULL )
305     {
306         hr = E_OUTOFMEMORY;
307         goto LFail;
308     }
309 
310     pWaveFile->OpenFromMemory( pbData,ulDataSize, pwfx, WAVEFILE_READ );
311 
312 
313     // Make the DirectSound buffer the same size as the wav file
314     dwDSBufferSize = ulDataSize;
315 
316     // Create the direct sound buffer, and only request the flags needed
317     // since each requires some overhead and limits if the buffer can
318     // be hardware accelerated
319     DSBUFFERDESC dsbd;
320     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
321     dsbd.dwSize          = sizeof(DSBUFFERDESC);
322     dsbd.dwFlags         = dwCreationFlags;
323     dsbd.dwBufferBytes   = dwDSBufferSize;
324     dsbd.guid3DAlgorithm = guid3DAlgorithm;
325     dsbd.lpwfxFormat     = pwfx;
326 
327     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) )
328     {
329         DXUT_ERR( L"CreateSoundBuffer", hr );
330         goto LFail;
331     }
332 
333     // Default to use DuplicateSoundBuffer() when created extra buffers since always
334     // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if
335     // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case.
336     if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 )
337     {
338         for( i=1; i<dwNumBuffers; i++ )
339         {
340             if( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) )
341             {
342                 DXUT_ERR( L"DuplicateSoundBuffer", hr );
343                 goto LFail;
344             }
345         }
346     }
347     else
348     {
349         for( i=1; i<dwNumBuffers; i++ )
350         {
351             hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL );
352             if( FAILED(hr) )
353             {
354                 DXUT_ERR( L"CreateSoundBuffer", hr );
355                 goto LFail;
356             }
357         }
358    }
359 
360     // Create the sound
361     *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags );
362 
363     SAFE_DELETE_ARRAY( apDSBuffer );
364     return S_OK;
365 
366 LFail:
367     // Cleanup
368 
369     SAFE_DELETE_ARRAY( apDSBuffer );
370     return hr;
371 }
372 
373 
374 //-----------------------------------------------------------------------------
375 // Name: CSoundManager::CreateStreaming()
376 // Desc:
377 //-----------------------------------------------------------------------------
CreateStreaming(CStreamingSound ** ppStreamingSound,LPWSTR strWaveFileName,DWORD dwCreationFlags,GUID guid3DAlgorithm,DWORD dwNotifyCount,DWORD dwNotifySize,HANDLE hNotifyEvent)378 HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound,
379                                         LPWSTR strWaveFileName,
380                                         DWORD dwCreationFlags,
381                                         GUID guid3DAlgorithm,
382                                         DWORD dwNotifyCount,
383                                         DWORD dwNotifySize,
384                                         HANDLE hNotifyEvent )
385 {
386     HRESULT hr;
387 
388     if( m_pDS == NULL )
389         return CO_E_NOTINITIALIZED;
390     if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL )
391         return E_INVALIDARG;
392 
393     LPDIRECTSOUNDBUFFER pDSBuffer      = NULL;
394     DWORD               dwDSBufferSize = NULL;
395     CWaveFile*          pWaveFile      = NULL;
396     DSBPOSITIONNOTIFY*  aPosNotify     = NULL;
397     LPDIRECTSOUNDNOTIFY pDSNotify      = NULL;
398 
399     pWaveFile = new CWaveFile();
400     if( pWaveFile == NULL )
401         return E_OUTOFMEMORY;
402     pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ );
403 
404     // Figure out how big the DirectSound buffer should be
405     dwDSBufferSize = dwNotifySize * dwNotifyCount;
406 
407     // Set up the direct sound buffer.  Request the NOTIFY flag, so
408     // that we are notified as the sound buffer plays.  Note, that using this flag
409     // may limit the amount of hardware acceleration that can occur.
410     DSBUFFERDESC dsbd;
411     ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
412     dsbd.dwSize          = sizeof(DSBUFFERDESC);
413     dsbd.dwFlags         = dwCreationFlags |
414                            DSBCAPS_CTRLPOSITIONNOTIFY |
415                            DSBCAPS_GETCURRENTPOSITION2;
416     dsbd.dwBufferBytes   = dwDSBufferSize;
417     dsbd.guid3DAlgorithm = guid3DAlgorithm;
418     dsbd.lpwfxFormat     = pWaveFile->m_pwfx;
419 
420     if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) )
421     {
422         // If wave format isn't then it will return
423         // either DSERR_BADFORMAT or E_INVALIDARG
424         if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG )
425             return DXUT_ERR( L"CreateSoundBuffer", hr );
426 
427         return DXUT_ERR( L"CreateSoundBuffer", hr );
428     }
429 
430     // Create the notification events, so that we know when to fill
431     // the buffer as the sound plays.
432     if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify,
433                                                 (VOID**)&pDSNotify ) ) )
434     {
435         SAFE_DELETE_ARRAY( aPosNotify );
436         return DXUT_ERR( L"QueryInterface", hr );
437     }
438 
439     aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ];
440     if( aPosNotify == NULL )
441         return E_OUTOFMEMORY;
442 
443     for( DWORD i = 0; i < dwNotifyCount; i++ )
444     {
445         aPosNotify[i].dwOffset     = (dwNotifySize * i) + dwNotifySize - 1;
446         aPosNotify[i].hEventNotify = hNotifyEvent;
447     }
448 
449     // Tell DirectSound when to notify us. The notification will come in the from
450     // of signaled events that are handled in WinMain()
451     if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount,
452                                                           aPosNotify ) ) )
453     {
454         SAFE_RELEASE( pDSNotify );
455         SAFE_DELETE_ARRAY( aPosNotify );
456         return DXUT_ERR( L"SetNotificationPositions", hr );
457     }
458 
459     SAFE_RELEASE( pDSNotify );
460     SAFE_DELETE_ARRAY( aPosNotify );
461 
462     // Create the sound
463     *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize );
464 
465     return S_OK;
466 }
467 
468 
469 //-----------------------------------------------------------------------------
470 // Name: CSound::CSound()
471 // Desc: Constructs the class
472 //-----------------------------------------------------------------------------
CSound(LPDIRECTSOUNDBUFFER * apDSBuffer,DWORD dwDSBufferSize,DWORD dwNumBuffers,CWaveFile * pWaveFile,DWORD dwCreationFlags)473 CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize,
474                 DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags )
475 {
476     DWORD i;
477 
478     if( dwNumBuffers <= 0 )
479         return;
480 
481     m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers];
482     if( NULL != m_apDSBuffer )
483     {
484         for( i=0; i<dwNumBuffers; i++ )
485             m_apDSBuffer[i] = apDSBuffer[i];
486 
487         m_dwDSBufferSize = dwDSBufferSize;
488         m_dwNumBuffers   = dwNumBuffers;
489         m_pWaveFile      = pWaveFile;
490         m_dwCreationFlags = dwCreationFlags;
491 
492         FillBufferWithSound( m_apDSBuffer[0], FALSE );
493     }
494 }
495 
496 
497 //-----------------------------------------------------------------------------
498 // Name: CSound::~CSound()
499 // Desc: Destroys the class
500 //-----------------------------------------------------------------------------
~CSound()501 CSound::~CSound()
502 {
503     for( DWORD i=0; i<m_dwNumBuffers; i++ )
504     {
505         SAFE_RELEASE( m_apDSBuffer[i] );
506     }
507 
508     SAFE_DELETE_ARRAY( m_apDSBuffer );
509     SAFE_DELETE( m_pWaveFile );
510 }
511 
512 
513 //-----------------------------------------------------------------------------
514 // Name: CSound::FillBufferWithSound()
515 // Desc: Fills a DirectSound buffer with a sound file
516 //-----------------------------------------------------------------------------
FillBufferWithSound(LPDIRECTSOUNDBUFFER pDSB,BOOL bRepeatWavIfBufferLarger)517 HRESULT CSound::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, BOOL bRepeatWavIfBufferLarger )
518 {
519     HRESULT hr;
520     VOID*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
521     DWORD   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer
522     DWORD   dwWavDataRead        = 0;    // Amount of data read from the wav file
523 
524     if( pDSB == NULL )
525         return CO_E_NOTINITIALIZED;
526 
527     // Make sure we have focus, and we didn't just switch in from
528     // an app which had a DirectSound device
529     if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) )
530         return DXUT_ERR( L"RestoreBuffer", hr );
531 
532     // Lock the buffer down
533     if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize,
534                                  &pDSLockedBuffer, &dwDSLockedBufferSize,
535                                  NULL, NULL, 0L ) ) )
536         return DXUT_ERR( L"Lock", hr );
537 
538     // Reset the wave file to the beginning
539     m_pWaveFile->ResetFile();
540 
541     if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer,
542                                         dwDSLockedBufferSize,
543                                         &dwWavDataRead ) ) )
544         return DXUT_ERR( L"Read", hr );
545 
546     if( dwWavDataRead == 0 )
547     {
548         // Wav is blank, so just fill with silence
549         FillMemory( (BYTE*) pDSLockedBuffer,
550                     dwDSLockedBufferSize,
551                     (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
552     }
553     else if( dwWavDataRead < dwDSLockedBufferSize )
554     {
555         // If the wav file was smaller than the DirectSound buffer,
556         // we need to fill the remainder of the buffer with data
557         if( bRepeatWavIfBufferLarger )
558         {
559             // Reset the file and fill the buffer with wav data
560             DWORD dwReadSoFar = dwWavDataRead;    // From previous call above.
561             while( dwReadSoFar < dwDSLockedBufferSize )
562             {
563                 // This will keep reading in until the buffer is full
564                 // for very short files
565                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
566                     return DXUT_ERR( L"ResetFile", hr );
567 
568                 hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
569                                         dwDSLockedBufferSize - dwReadSoFar,
570                                         &dwWavDataRead );
571                 if( FAILED(hr) )
572                     return DXUT_ERR( L"Read", hr );
573 
574                 dwReadSoFar += dwWavDataRead;
575             }
576         }
577         else
578         {
579             // Don't repeat the wav file, just fill in silence
580             FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead,
581                         dwDSLockedBufferSize - dwWavDataRead,
582                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
583         }
584     }
585 
586     // Unlock the buffer, we don't need it anymore.
587     pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
588 
589     return S_OK;
590 }
591 
592 
593 //-----------------------------------------------------------------------------
594 // Name: CSound::RestoreBuffer()
595 // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was
596 //       restored.  It can also NULL if the information is not needed.
597 //-----------------------------------------------------------------------------
RestoreBuffer(LPDIRECTSOUNDBUFFER pDSB,BOOL * pbWasRestored)598 HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored )
599 {
600     HRESULT hr;
601 
602     if( pDSB == NULL )
603         return CO_E_NOTINITIALIZED;
604     if( pbWasRestored )
605         *pbWasRestored = FALSE;
606 
607     DWORD dwStatus;
608     if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) )
609         return DXUT_ERR( L"GetStatus", hr );
610 
611     if( dwStatus & DSBSTATUS_BUFFERLOST )
612     {
613         // Since the app could have just been activated, then
614         // DirectSound may not be giving us control yet, so
615         // the restoring the buffer may fail.
616         // If it does, sleep until DirectSound gives us control.
617         do
618         {
619             hr = pDSB->Restore();
620             if( hr == DSERR_BUFFERLOST )
621                 Sleep( 10 );
622         }
623         while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST );
624 
625         if( pbWasRestored != NULL )
626             *pbWasRestored = TRUE;
627 
628         return S_OK;
629     }
630     else
631     {
632         return S_FALSE;
633     }
634 }
635 
636 
637 //-----------------------------------------------------------------------------
638 // Name: CSound::GetFreeBuffer()
639 // Desc: Finding the first buffer that is not playing and return a pointer to
640 //       it, or if all are playing return a pointer to a randomly selected buffer.
641 //-----------------------------------------------------------------------------
GetFreeBuffer()642 LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer()
643 {
644     if( m_apDSBuffer == NULL )
645         return FALSE;
646 
647     DWORD i;
648     for( i=0; i<m_dwNumBuffers; i++ )
649     {
650         if( m_apDSBuffer[i] )
651         {
652             DWORD dwStatus = 0;
653             m_apDSBuffer[i]->GetStatus( &dwStatus );
654             if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
655                 break;
656         }
657     }
658 
659     if( i != m_dwNumBuffers )
660         return m_apDSBuffer[ i ];
661     else
662         return m_apDSBuffer[ rand() % m_dwNumBuffers ];
663 }
664 
665 
666 //-----------------------------------------------------------------------------
667 // Name: CSound::GetBuffer()
668 // Desc:
669 //-----------------------------------------------------------------------------
GetBuffer(DWORD dwIndex)670 LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex )
671 {
672     if( m_apDSBuffer == NULL )
673         return NULL;
674     if( dwIndex >= m_dwNumBuffers )
675         return NULL;
676 
677     return m_apDSBuffer[dwIndex];
678 }
679 
680 
681 //-----------------------------------------------------------------------------
682 // Name: CSound::Get3DBufferInterface()
683 // Desc:
684 //-----------------------------------------------------------------------------
Get3DBufferInterface(DWORD dwIndex,LPDIRECTSOUND3DBUFFER * ppDS3DBuffer)685 HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer )
686 {
687     if( m_apDSBuffer == NULL )
688         return CO_E_NOTINITIALIZED;
689     if( dwIndex >= m_dwNumBuffers )
690         return E_INVALIDARG;
691 
692     *ppDS3DBuffer = NULL;
693 
694     return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer,
695                                                   (VOID**)ppDS3DBuffer );
696 }
697 
698 
699 //-----------------------------------------------------------------------------
700 // Name: CSound::Play()
701 // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
702 //       in the dwFlags to loop the sound
703 //-----------------------------------------------------------------------------
Play(DWORD dwPriority,DWORD dwFlags,LONG lVolume,LONG lFrequency,LONG lPan)704 HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan )
705 {
706     HRESULT hr;
707     BOOL    bRestored;
708 
709     if( m_apDSBuffer == NULL )
710         return CO_E_NOTINITIALIZED;
711 
712     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
713 
714     if( pDSB == NULL )
715         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
716 
717     // Restore the buffer if it was lost
718     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
719         return DXUT_ERR( L"RestoreBuffer", hr );
720 
721     if( bRestored )
722     {
723         // The buffer was restored, so we need to fill it with new data
724         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
725             return DXUT_ERR( L"FillBufferWithSound", hr );
726     }
727 
728     if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME )
729     {
730         pDSB->SetVolume( lVolume );
731     }
732 
733     if( lFrequency != -1 &&
734         (m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY) )
735     {
736         pDSB->SetFrequency( lFrequency );
737     }
738 
739     if( m_dwCreationFlags & DSBCAPS_CTRLPAN )
740     {
741         pDSB->SetPan( lPan );
742     }
743 
744     return pDSB->Play( 0, dwPriority, dwFlags );
745 }
746 
747 
748 //-----------------------------------------------------------------------------
749 // Name: CSound::Play3D()
750 // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
751 //       in the dwFlags to loop the sound
752 //-----------------------------------------------------------------------------
Play3D(LPDS3DBUFFER p3DBuffer,DWORD dwPriority,DWORD dwFlags,LONG lFrequency)753 HRESULT CSound::Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority, DWORD dwFlags, LONG lFrequency )
754 {
755     HRESULT hr;
756     BOOL    bRestored;
757     DWORD   dwBaseFrequency;
758 
759     if( m_apDSBuffer == NULL )
760         return CO_E_NOTINITIALIZED;
761 
762     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
763     if( pDSB == NULL )
764         return DXUT_ERR( L"GetFreeBuffer", E_FAIL );
765 
766     // Restore the buffer if it was lost
767     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
768         return DXUT_ERR( L"RestoreBuffer", hr );
769 
770     if( bRestored )
771     {
772         // The buffer was restored, so we need to fill it with new data
773         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
774             return DXUT_ERR( L"FillBufferWithSound", hr );
775     }
776 
777     if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY )
778     {
779         pDSB->GetFrequency( &dwBaseFrequency );
780         pDSB->SetFrequency( dwBaseFrequency + lFrequency );
781     }
782 
783     // QI for the 3D buffer
784     LPDIRECTSOUND3DBUFFER pDS3DBuffer;
785     hr = pDSB->QueryInterface( IID_IDirectSound3DBuffer, (VOID**) &pDS3DBuffer );
786     if( SUCCEEDED( hr ) )
787     {
788         hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE );
789         if( SUCCEEDED( hr ) )
790         {
791             hr = pDSB->Play( 0, dwPriority, dwFlags );
792         }
793 
794         pDS3DBuffer->Release();
795     }
796 
797     return hr;
798 }
799 
800 
801 //-----------------------------------------------------------------------------
802 // Name: CSound::Stop()
803 // Desc: Stops the sound from playing
804 //-----------------------------------------------------------------------------
Stop()805 HRESULT CSound::Stop()
806 {
807     if( m_apDSBuffer == NULL )
808         return CO_E_NOTINITIALIZED;
809 
810     HRESULT hr = 0;
811 
812     for( DWORD i=0; i<m_dwNumBuffers; i++ )
813         hr |= m_apDSBuffer[i]->Stop();
814 
815     return hr;
816 }
817 
818 
819 //-----------------------------------------------------------------------------
820 // Name: CSound::Reset()
821 // Desc: Reset all of the sound buffers
822 //-----------------------------------------------------------------------------
Reset()823 HRESULT CSound::Reset()
824 {
825     if( m_apDSBuffer == NULL )
826         return CO_E_NOTINITIALIZED;
827 
828     HRESULT hr = 0;
829 
830     for( DWORD i=0; i<m_dwNumBuffers; i++ )
831         hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );
832 
833     return hr;
834 }
835 
836 
837 //-----------------------------------------------------------------------------
838 // Name: CSound::IsSoundPlaying()
839 // Desc: Checks to see if a buffer is playing and returns TRUE if it is.
840 //-----------------------------------------------------------------------------
IsSoundPlaying()841 BOOL CSound::IsSoundPlaying()
842 {
843     BOOL bIsPlaying = FALSE;
844 
845     if( m_apDSBuffer == NULL )
846         return FALSE;
847 
848     for( DWORD i=0; i<m_dwNumBuffers; i++ )
849     {
850         if( m_apDSBuffer[i] )
851         {
852             DWORD dwStatus = 0;
853             m_apDSBuffer[i]->GetStatus( &dwStatus );
854             bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
855         }
856     }
857 
858     return bIsPlaying;
859 }
860 
861 
862 //-----------------------------------------------------------------------------
863 // Name: CStreamingSound::CStreamingSound()
864 // Desc: Setups up a buffer so data can be streamed from the wave file into
865 //       a buffer.  This is very useful for large wav files that would take a
866 //       while to load.  The buffer is initially filled with data, then
867 //       as sound is played the notification events are signaled and more data
868 //       is written into the buffer by calling HandleWaveStreamNotification()
869 //-----------------------------------------------------------------------------
CStreamingSound(LPDIRECTSOUNDBUFFER pDSBuffer,DWORD dwDSBufferSize,CWaveFile * pWaveFile,DWORD dwNotifySize)870 CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize,
871                                   CWaveFile* pWaveFile, DWORD dwNotifySize )
872                 : CSound( &pDSBuffer, dwDSBufferSize, 1, pWaveFile, 0 )
873 {
874     m_dwLastPlayPos     = 0;
875     m_dwPlayProgress    = 0;
876     m_dwNotifySize      = dwNotifySize;
877     m_dwNextWriteOffset = 0;
878     m_bFillNextNotificationWithSilence = FALSE;
879 }
880 
881 
882 //-----------------------------------------------------------------------------
883 // Name: CStreamingSound::~CStreamingSound()
884 // Desc: Destroys the class
885 //-----------------------------------------------------------------------------
~CStreamingSound()886 CStreamingSound::~CStreamingSound()
887 {
888 }
889 
890 
891 //-----------------------------------------------------------------------------
892 // Name: CStreamingSound::HandleWaveStreamNotification()
893 // Desc: Handle the notification that tells us to put more wav data in the
894 //       circular buffer
895 //-----------------------------------------------------------------------------
HandleWaveStreamNotification(BOOL bLoopedPlay)896 HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay )
897 {
898     HRESULT hr;
899     DWORD   dwCurrentPlayPos;
900     DWORD   dwPlayDelta;
901     DWORD   dwBytesWrittenToBuffer;
902     VOID*   pDSLockedBuffer = NULL;
903     VOID*   pDSLockedBuffer2 = NULL;
904     DWORD   dwDSLockedBufferSize;
905     DWORD   dwDSLockedBufferSize2;
906 
907     if( m_apDSBuffer == NULL || m_pWaveFile == NULL )
908         return CO_E_NOTINITIALIZED;
909 
910     // Restore the buffer if it was lost
911     BOOL bRestored;
912     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
913         return DXUT_ERR( L"RestoreBuffer", hr );
914 
915     if( bRestored )
916     {
917         // The buffer was restored, so we need to fill it with new data
918         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
919             return DXUT_ERR( L"FillBufferWithSound", hr );
920         return S_OK;
921     }
922 
923     // Lock the DirectSound buffer
924     if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize,
925                                             &pDSLockedBuffer, &dwDSLockedBufferSize,
926                                             &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) )
927         return DXUT_ERR( L"Lock", hr );
928 
929     // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize,
930     // it should the second buffer, so it should never be valid
931     if( pDSLockedBuffer2 != NULL )
932         return E_UNEXPECTED;
933 
934     if( !m_bFillNextNotificationWithSilence )
935     {
936         // Fill the DirectSound buffer with wav data
937         if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer,
938                                                   dwDSLockedBufferSize,
939                                                   &dwBytesWrittenToBuffer ) ) )
940             return DXUT_ERR( L"Read", hr );
941     }
942     else
943     {
944         // Fill the DirectSound buffer with silence
945         FillMemory( pDSLockedBuffer, dwDSLockedBufferSize,
946                     (BYTE)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
947         dwBytesWrittenToBuffer = dwDSLockedBufferSize;
948     }
949 
950     // If the number of bytes written is less than the
951     // amount we requested, we have a short file.
952     if( dwBytesWrittenToBuffer < dwDSLockedBufferSize )
953     {
954         if( !bLoopedPlay )
955         {
956             // Fill in silence for the rest of the buffer.
957             FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer,
958                         dwDSLockedBufferSize - dwBytesWrittenToBuffer,
959                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
960 
961             // Any future notifications should just fill the buffer with silence
962             m_bFillNextNotificationWithSilence = TRUE;
963         }
964         else
965         {
966             // We are looping, so reset the file and fill the buffer with wav data
967             DWORD dwReadSoFar = dwBytesWrittenToBuffer;    // From previous call above.
968             while( dwReadSoFar < dwDSLockedBufferSize )
969             {
970                 // This will keep reading in until the buffer is full (for very short files).
971                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
972                     return DXUT_ERR( L"ResetFile", hr );
973 
974                 if( FAILED( hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
975                                                           dwDSLockedBufferSize - dwReadSoFar,
976                                                           &dwBytesWrittenToBuffer ) ) )
977                     return DXUT_ERR( L"Read", hr );
978 
979                 dwReadSoFar += dwBytesWrittenToBuffer;
980             }
981         }
982     }
983 
984     // Unlock the DirectSound buffer
985     m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
986 
987     // Figure out how much data has been played so far.  When we have played
988     // past the end of the file, we will either need to start filling the
989     // buffer with silence or starting reading from the beginning of the file,
990     // depending if the user wants to loop the sound
991     if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) )
992         return DXUT_ERR( L"GetCurrentPosition", hr );
993 
994     // Check to see if the position counter looped
995     if( dwCurrentPlayPos < m_dwLastPlayPos )
996         dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos;
997     else
998         dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;
999 
1000     m_dwPlayProgress += dwPlayDelta;
1001     m_dwLastPlayPos = dwCurrentPlayPos;
1002 
1003     // If we are now filling the buffer with silence, then we have found the end so
1004     // check to see if the entire sound has played, if it has then stop the buffer.
1005     if( m_bFillNextNotificationWithSilence )
1006     {
1007         // We don't want to cut off the sound before it's done playing.
1008         if( m_dwPlayProgress >= m_pWaveFile->GetSize() )
1009         {
1010             m_apDSBuffer[0]->Stop();
1011         }
1012     }
1013 
1014     // Update where the buffer will lock (for next time)
1015     m_dwNextWriteOffset += dwDSLockedBufferSize;
1016     m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer
1017 
1018     return S_OK;
1019 }
1020 
1021 
1022 //-----------------------------------------------------------------------------
1023 // Name: CStreamingSound::Reset()
1024 // Desc: Resets the sound so it will begin playing at the beginning
1025 //-----------------------------------------------------------------------------
Reset()1026 HRESULT CStreamingSound::Reset()
1027 {
1028     HRESULT hr;
1029 
1030     if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL )
1031         return CO_E_NOTINITIALIZED;
1032 
1033     m_dwLastPlayPos     = 0;
1034     m_dwPlayProgress    = 0;
1035     m_dwNextWriteOffset = 0;
1036     m_bFillNextNotificationWithSilence = FALSE;
1037 
1038     // Restore the buffer if it was lost
1039     BOOL bRestored;
1040     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
1041         return DXUT_ERR( L"RestoreBuffer", hr );
1042 
1043     if( bRestored )
1044     {
1045         // The buffer was restored, so we need to fill it with new data
1046         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
1047             return DXUT_ERR( L"FillBufferWithSound", hr );
1048     }
1049 
1050     m_pWaveFile->ResetFile();
1051 
1052     return m_apDSBuffer[0]->SetCurrentPosition( 0L );
1053 }
1054 
1055 
1056 //-----------------------------------------------------------------------------
1057 // Name: CWaveFile::CWaveFile()
1058 // Desc: Constructs the class.  Call Open() to open a wave file for reading.
1059 //       Then call Read() as needed.  Calling the destructor or Close()
1060 //       will close the file.
1061 //-----------------------------------------------------------------------------
CWaveFile()1062 CWaveFile::CWaveFile()
1063 {
1064     m_pwfx    = NULL;
1065     m_hmmio   = NULL;
1066     m_pResourceBuffer = NULL;
1067     m_dwSize  = 0;
1068     m_bIsReadingFromMemory = FALSE;
1069 }
1070 
1071 
1072 //-----------------------------------------------------------------------------
1073 // Name: CWaveFile::~CWaveFile()
1074 // Desc: Destructs the class
1075 //-----------------------------------------------------------------------------
~CWaveFile()1076 CWaveFile::~CWaveFile()
1077 {
1078     Close();
1079 
1080     if( !m_bIsReadingFromMemory )
1081         SAFE_DELETE_ARRAY( m_pwfx );
1082 }
1083 
1084 
1085 //-----------------------------------------------------------------------------
1086 // Name: CWaveFile::Open()
1087 // Desc: Opens a wave file for reading
1088 //-----------------------------------------------------------------------------
Open(LPWSTR strFileName,WAVEFORMATEX * pwfx,DWORD dwFlags)1089 HRESULT CWaveFile::Open( LPWSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
1090 {
1091     HRESULT hr;
1092 
1093     m_dwFlags = dwFlags;
1094     m_bIsReadingFromMemory = FALSE;
1095 
1096     if( m_dwFlags == WAVEFILE_READ )
1097     {
1098         if( strFileName == NULL )
1099             return E_INVALIDARG;
1100         SAFE_DELETE_ARRAY( m_pwfx );
1101 
1102         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
1103 
1104         if( NULL == m_hmmio )
1105         {
1106             HRSRC   hResInfo;
1107             HGLOBAL hResData;
1108             DWORD   dwSize;
1109             VOID*   pvRes;
1110 
1111             // Loading it as a file failed, so try it as a resource
1112             if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAVE" ) ) )
1113             {
1114                 if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAV" ) ) )
1115                     return DXUT_ERR( L"FindResource", E_FAIL );
1116             }
1117 
1118             if( NULL == ( hResData = LoadResource( GetModuleHandle(NULL), hResInfo ) ) )
1119                 return DXUT_ERR( L"LoadResource", E_FAIL );
1120 
1121             if( 0 == ( dwSize = SizeofResource( GetModuleHandle(NULL), hResInfo ) ) )
1122                 return DXUT_ERR( L"SizeofResource", E_FAIL );
1123 
1124             if( NULL == ( pvRes = LockResource( hResData ) ) )
1125                 return DXUT_ERR( L"LockResource", E_FAIL );
1126 
1127             m_pResourceBuffer = new CHAR[ dwSize ];
1128             if( m_pResourceBuffer == NULL )
1129                 return DXUT_ERR( L"new", E_OUTOFMEMORY );
1130             memcpy( m_pResourceBuffer, pvRes, dwSize );
1131 
1132             MMIOINFO mmioInfo;
1133             ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
1134             mmioInfo.fccIOProc = FOURCC_MEM;
1135             mmioInfo.cchBuffer = dwSize;
1136             mmioInfo.pchBuffer = (CHAR*) m_pResourceBuffer;
1137 
1138             m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
1139         }
1140 
1141         if( FAILED( hr = ReadMMIO() ) )
1142         {
1143             // ReadMMIO will fail if its an not a wave file
1144             mmioClose( m_hmmio, 0 );
1145             return DXUT_ERR( L"ReadMMIO", hr );
1146         }
1147 
1148         if( FAILED( hr = ResetFile() ) )
1149             return DXUT_ERR( L"ResetFile", hr );
1150 
1151         // After the reset, the size of the wav file is m_ck.cksize so store it now
1152         m_dwSize = m_ck.cksize;
1153     }
1154     else
1155     {
1156         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF  |
1157                                                   MMIO_READWRITE |
1158                                                   MMIO_CREATE );
1159         if( NULL == m_hmmio )
1160             return DXUT_ERR( L"mmioOpen", E_FAIL );
1161 
1162         if( FAILED( hr = WriteMMIO( pwfx ) ) )
1163         {
1164             mmioClose( m_hmmio, 0 );
1165             return DXUT_ERR( L"WriteMMIO", hr );
1166         }
1167 
1168         if( FAILED( hr = ResetFile() ) )
1169             return DXUT_ERR( L"ResetFile", hr );
1170     }
1171 
1172     return hr;
1173 }
1174 
1175 
1176 //-----------------------------------------------------------------------------
1177 // Name: CWaveFile::OpenFromMemory()
1178 // Desc: copy data to CWaveFile member variable from memory
1179 //-----------------------------------------------------------------------------
OpenFromMemory(BYTE * pbData,ULONG ulDataSize,WAVEFORMATEX * pwfx,DWORD dwFlags)1180 HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize,
1181                                    WAVEFORMATEX* pwfx, DWORD dwFlags )
1182 {
1183     m_pwfx       = pwfx;
1184     m_ulDataSize = ulDataSize;
1185     m_pbData     = pbData;
1186     m_pbDataCur  = m_pbData;
1187     m_bIsReadingFromMemory = TRUE;
1188 
1189     if( dwFlags != WAVEFILE_READ )
1190         return E_NOTIMPL;
1191 
1192     return S_OK;
1193 }
1194 
1195 
1196 //-----------------------------------------------------------------------------
1197 // Name: CWaveFile::ReadMMIO()
1198 // Desc: Support function for reading from a multimedia I/O stream.
1199 //       m_hmmio must be valid before calling.  This function uses it to
1200 //       update m_ckRiff, and m_pwfx.
1201 //-----------------------------------------------------------------------------
ReadMMIO()1202 HRESULT CWaveFile::ReadMMIO()
1203 {
1204     MMCKINFO        ckIn;           // chunk info. for general use.
1205     PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.
1206 
1207     m_pwfx = NULL;
1208 
1209     if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
1210         return DXUT_ERR( L"mmioDescend", E_FAIL );
1211 
1212     // Check to make sure this is a valid wave file
1213     if( (m_ckRiff.ckid != FOURCC_RIFF) ||
1214         (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
1215         return DXUT_ERR( L"mmioFOURCC", E_FAIL );
1216 
1217     // Search the input file for for the 'fmt ' chunk.
1218     ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
1219     if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
1220         return DXUT_ERR( L"mmioDescend", E_FAIL );
1221 
1222     // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
1223     // if there are extra parameters at the end, we'll ignore them
1224        if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
1225            return DXUT_ERR( L"sizeof(PCMWAVEFORMAT)", E_FAIL );
1226 
1227     // Read the 'fmt ' chunk into <pcmWaveFormat>.
1228     if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat,
1229                   sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
1230         return DXUT_ERR( L"mmioRead", E_FAIL );
1231 
1232     // Allocate the waveformatex, but if its not pcm format, read the next
1233     // word, and thats how many extra bytes to allocate.
1234     if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
1235     {
1236         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
1237         if( NULL == m_pwfx )
1238             return DXUT_ERR( L"m_pwfx", E_FAIL );
1239 
1240         // Copy the bytes from the pcm structure to the waveformatex structure
1241         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
1242         m_pwfx->cbSize = 0;
1243     }
1244     else
1245     {
1246         // Read in length of extra bytes.
1247         WORD cbExtraBytes = 0L;
1248         if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
1249             return DXUT_ERR( L"mmioRead", E_FAIL );
1250 
1251         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
1252         if( NULL == m_pwfx )
1253             return DXUT_ERR( L"new", E_FAIL );
1254 
1255         // Copy the bytes from the pcm structure to the waveformatex structure
1256         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
1257         m_pwfx->cbSize = cbExtraBytes;
1258 
1259         // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
1260         if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)),
1261                       cbExtraBytes ) != cbExtraBytes )
1262         {
1263             SAFE_DELETE( m_pwfx );
1264             return DXUT_ERR( L"mmioRead", E_FAIL );
1265         }
1266     }
1267 
1268     // Ascend the input file out of the 'fmt ' chunk.
1269     if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
1270     {
1271         SAFE_DELETE( m_pwfx );
1272         return DXUT_ERR( L"mmioAscend", E_FAIL );
1273     }
1274 
1275     return S_OK;
1276 }
1277 
1278 
1279 //-----------------------------------------------------------------------------
1280 // Name: CWaveFile::GetSize()
1281 // Desc: Retuns the size of the read access wave file
1282 //-----------------------------------------------------------------------------
GetSize()1283 DWORD CWaveFile::GetSize()
1284 {
1285     return m_dwSize;
1286 }
1287 
1288 
1289 //-----------------------------------------------------------------------------
1290 // Name: CWaveFile::ResetFile()
1291 // Desc: Resets the internal m_ck pointer so reading starts from the
1292 //       beginning of the file again
1293 //-----------------------------------------------------------------------------
ResetFile()1294 HRESULT CWaveFile::ResetFile()
1295 {
1296     if( m_bIsReadingFromMemory )
1297     {
1298         m_pbDataCur = m_pbData;
1299     }
1300     else
1301     {
1302         if( m_hmmio == NULL )
1303             return CO_E_NOTINITIALIZED;
1304 
1305         if( m_dwFlags == WAVEFILE_READ )
1306         {
1307             // Seek to the data
1308             if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
1309                             SEEK_SET ) )
1310                 return DXUT_ERR( L"mmioSeek", E_FAIL );
1311 
1312             // Search the input file for the 'data' chunk.
1313             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
1314             if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
1315               return DXUT_ERR( L"mmioDescend", E_FAIL );
1316         }
1317         else
1318         {
1319             // Create the 'data' chunk that holds the waveform samples.
1320             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
1321             m_ck.cksize = 0;
1322 
1323             if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
1324                 return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
1325 
1326             if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
1327                 return DXUT_ERR( L"mmioGetInfo", E_FAIL );
1328         }
1329     }
1330 
1331     return S_OK;
1332 }
1333 
1334 
1335 //-----------------------------------------------------------------------------
1336 // Name: CWaveFile::Read()
1337 // Desc: Reads section of data from a wave file into pBuffer and returns
1338 //       how much read in pdwSizeRead, reading not more than dwSizeToRead.
1339 //       This uses m_ck to determine where to start reading from.  So
1340 //       subsequent calls will be continue where the last left off unless
1341 //       Reset() is called.
1342 //-----------------------------------------------------------------------------
Read(BYTE * pBuffer,DWORD dwSizeToRead,DWORD * pdwSizeRead)1343 HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
1344 {
1345     if( m_bIsReadingFromMemory )
1346     {
1347         if( m_pbDataCur == NULL )
1348             return CO_E_NOTINITIALIZED;
1349         if( pdwSizeRead != NULL )
1350             *pdwSizeRead = 0;
1351 
1352         if( (BYTE*)(m_pbDataCur + dwSizeToRead) >
1353             (BYTE*)(m_pbData + m_ulDataSize) )
1354         {
1355             dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
1356         }
1357 
1358         CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
1359 
1360         if( pdwSizeRead != NULL )
1361             *pdwSizeRead = dwSizeToRead;
1362 
1363         return S_OK;
1364     }
1365     else
1366     {
1367         MMIOINFO mmioinfoIn; // current status of m_hmmio
1368 
1369         if( m_hmmio == NULL )
1370             return CO_E_NOTINITIALIZED;
1371         if( pBuffer == NULL || pdwSizeRead == NULL )
1372             return E_INVALIDARG;
1373 
1374         if( pdwSizeRead != NULL )
1375             *pdwSizeRead = 0;
1376 
1377         if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
1378             return DXUT_ERR( L"mmioGetInfo", E_FAIL );
1379 
1380         UINT cbDataIn = dwSizeToRead;
1381         if( cbDataIn > m_ck.cksize )
1382             cbDataIn = m_ck.cksize;
1383 
1384         m_ck.cksize -= cbDataIn;
1385 
1386         for( DWORD cT = 0; cT < cbDataIn; cT++ )
1387         {
1388             // Copy the bytes from the io to the buffer.
1389             if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
1390             {
1391                 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
1392                     return DXUT_ERR( L"mmioAdvance", E_FAIL );
1393 
1394                 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
1395                     return DXUT_ERR( L"mmioinfoIn.pchNext", E_FAIL );
1396             }
1397 
1398             // Actual copy.
1399             *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
1400             mmioinfoIn.pchNext++;
1401         }
1402 
1403         if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
1404             return DXUT_ERR( L"mmioSetInfo", E_FAIL );
1405 
1406         if( pdwSizeRead != NULL )
1407             *pdwSizeRead = cbDataIn;
1408 
1409         return S_OK;
1410     }
1411 }
1412 
1413 
1414 //-----------------------------------------------------------------------------
1415 // Name: CWaveFile::Close()
1416 // Desc: Closes the wave file
1417 //-----------------------------------------------------------------------------
Close()1418 HRESULT CWaveFile::Close()
1419 {
1420     if( m_dwFlags == WAVEFILE_READ )
1421     {
1422         mmioClose( m_hmmio, 0 );
1423         m_hmmio = NULL;
1424         SAFE_DELETE_ARRAY( m_pResourceBuffer );
1425     }
1426     else
1427     {
1428         m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
1429 
1430         if( m_hmmio == NULL )
1431             return CO_E_NOTINITIALIZED;
1432 
1433         if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
1434             return DXUT_ERR( L"mmioSetInfo", E_FAIL );
1435 
1436         // Ascend the output file out of the 'data' chunk -- this will cause
1437         // the chunk size of the 'data' chunk to be written.
1438         if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
1439             return DXUT_ERR( L"mmioAscend", E_FAIL );
1440 
1441         // Do this here instead...
1442         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
1443             return DXUT_ERR( L"mmioAscend", E_FAIL );
1444 
1445         mmioSeek( m_hmmio, 0, SEEK_SET );
1446 
1447         if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) )
1448             return DXUT_ERR( L"mmioDescend", E_FAIL );
1449 
1450         m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
1451 
1452         if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
1453         {
1454             DWORD dwSamples = 0;
1455             mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) );
1456             mmioAscend( m_hmmio, &m_ck, 0 );
1457         }
1458 
1459         // Ascend the output file out of the 'RIFF' chunk -- this will cause
1460         // the chunk size of the 'RIFF' chunk to be written.
1461         if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) )
1462             return DXUT_ERR( L"mmioAscend", E_FAIL );
1463 
1464         mmioClose( m_hmmio, 0 );
1465         m_hmmio = NULL;
1466     }
1467 
1468     return S_OK;
1469 }
1470 
1471 
1472 //-----------------------------------------------------------------------------
1473 // Name: CWaveFile::WriteMMIO()
1474 // Desc: Support function for reading from a multimedia I/O stream
1475 //       pwfxDest is the WAVEFORMATEX for this new wave file.
1476 //       m_hmmio must be valid before calling.  This function uses it to
1477 //       update m_ckRiff, and m_ck.
1478 //-----------------------------------------------------------------------------
WriteMMIO(WAVEFORMATEX * pwfxDest)1479 HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest )
1480 {
1481     DWORD    dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile.
1482     MMCKINFO ckOut1;
1483 
1484     dwFactChunk = (DWORD)-1;
1485 
1486     // Create the output file RIFF chunk of form type 'WAVE'.
1487     m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E');
1488     m_ckRiff.cksize = 0;
1489 
1490     if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) )
1491         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
1492 
1493     // We are now descended into the 'RIFF' chunk we just created.
1494     // Now create the 'fmt ' chunk. Since we know the size of this chunk,
1495     // specify it in the MMCKINFO structure so MMIO doesn't have to seek
1496     // back and set the chunk size after ascending from the chunk.
1497     m_ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
1498     m_ck.cksize = sizeof(PCMWAVEFORMAT);
1499 
1500     if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) )
1501         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
1502 
1503     // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type.
1504     if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM )
1505     {
1506         if( mmioWrite( m_hmmio, (HPSTR) pwfxDest,
1507                        sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
1508             return DXUT_ERR( L"mmioWrite", E_FAIL );
1509     }
1510     else
1511     {
1512         // Write the variable length size.
1513         if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest,
1514                              sizeof(*pwfxDest) + pwfxDest->cbSize ) !=
1515                              ( sizeof(*pwfxDest) + pwfxDest->cbSize ) )
1516             return DXUT_ERR( L"mmioWrite", E_FAIL );
1517     }
1518 
1519     // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
1520     if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) )
1521         return DXUT_ERR( L"mmioAscend", E_FAIL );
1522 
1523     // Now create the fact chunk, not required for PCM but nice to have.  This is filled
1524     // in when the close routine is called.
1525     ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't');
1526     ckOut1.cksize = 0;
1527 
1528     if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) )
1529         return DXUT_ERR( L"mmioCreateChunk", E_FAIL );
1530 
1531     if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) !=
1532                     sizeof(dwFactChunk) )
1533          return DXUT_ERR( L"mmioWrite", E_FAIL );
1534 
1535     // Now ascend out of the fact chunk...
1536     if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) )
1537         return DXUT_ERR( L"mmioAscend", E_FAIL );
1538 
1539     return S_OK;
1540 }
1541 
1542 
1543 //-----------------------------------------------------------------------------
1544 // Name: CWaveFile::Write()
1545 // Desc: Writes data to the open wave file
1546 //-----------------------------------------------------------------------------
Write(UINT nSizeToWrite,BYTE * pbSrcData,UINT * pnSizeWrote)1547 HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote )
1548 {
1549     UINT cT;
1550 
1551     if( m_bIsReadingFromMemory )
1552         return E_NOTIMPL;
1553     if( m_hmmio == NULL )
1554         return CO_E_NOTINITIALIZED;
1555     if( pnSizeWrote == NULL || pbSrcData == NULL )
1556         return E_INVALIDARG;
1557 
1558     *pnSizeWrote = 0;
1559 
1560     for( cT = 0; cT < nSizeToWrite; cT++ )
1561     {
1562         if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite )
1563         {
1564             m_mmioinfoOut.dwFlags |= MMIO_DIRTY;
1565             if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) )
1566                 return DXUT_ERR( L"mmioAdvance", E_FAIL );
1567         }
1568 
1569         *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT);
1570         (BYTE*)m_mmioinfoOut.pchNext++;
1571 
1572         (*pnSizeWrote)++;
1573     }
1574 
1575     return S_OK;
1576 }
1577