source: GTP/trunk/App/Demos/Illum/StochasticIteration/Common/DXUTsound.cpp @ 1808

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