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