home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / Common / src / dsutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  51.5 KB  |  1,542 lines

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