source: GTP/trunk/App/Demos/Illum/pathmap/Common/DXUTsound.cpp @ 2197

Revision 2197, 54.0 KB checked in by szirmay, 17 years ago (diff)
Line 
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//-----------------------------------------------------------------------------
23CSoundManager::CSoundManager()
24{
25    m_pDS = NULL;
26}
27
28
29//-----------------------------------------------------------------------------
30// Name: CSoundManager::~CSoundManager()
31// Desc: Destroys the class
32//-----------------------------------------------------------------------------
33CSoundManager::~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//-----------------------------------------------------------------------------
44HRESULT 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//-----------------------------------------------------------------------------
74HRESULT 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//-----------------------------------------------------------------------------
117HRESULT 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//-----------------------------------------------------------------------------
155HRESULT 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
265LFail:
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//-----------------------------------------------------------------------------
277HRESULT 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
366LFail:
367    // Cleanup
368
369    SAFE_DELETE_ARRAY( apDSBuffer );
370    return hr;
371}
372
373
374//-----------------------------------------------------------------------------
375// Name: CSoundManager::CreateStreaming()
376// Desc:
377//-----------------------------------------------------------------------------
378HRESULT 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//-----------------------------------------------------------------------------
473CSound::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//-----------------------------------------------------------------------------
501CSound::~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//-----------------------------------------------------------------------------
517HRESULT 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//-----------------------------------------------------------------------------
598HRESULT 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//-----------------------------------------------------------------------------
642LPDIRECTSOUNDBUFFER 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//-----------------------------------------------------------------------------
670LPDIRECTSOUNDBUFFER 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//-----------------------------------------------------------------------------
685HRESULT 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//-----------------------------------------------------------------------------
704HRESULT 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//-----------------------------------------------------------------------------
753HRESULT 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//-----------------------------------------------------------------------------
805HRESULT 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//-----------------------------------------------------------------------------
823HRESULT 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//-----------------------------------------------------------------------------
841BOOL 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//-----------------------------------------------------------------------------
870CStreamingSound::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//-----------------------------------------------------------------------------
886CStreamingSound::~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//-----------------------------------------------------------------------------
896HRESULT 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//-----------------------------------------------------------------------------
1026HRESULT 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//-----------------------------------------------------------------------------
1062CWaveFile::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//-----------------------------------------------------------------------------
1076CWaveFile::~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//-----------------------------------------------------------------------------
1089HRESULT 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//-----------------------------------------------------------------------------
1180HRESULT 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//-----------------------------------------------------------------------------
1202HRESULT 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//-----------------------------------------------------------------------------
1283DWORD 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//-----------------------------------------------------------------------------
1294HRESULT 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//-----------------------------------------------------------------------------
1343HRESULT 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//-----------------------------------------------------------------------------
1418HRESULT 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//-----------------------------------------------------------------------------
1479HRESULT 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//-----------------------------------------------------------------------------
1547HRESULT 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}
Note: See TracBrowser for help on using the repository browser.