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