home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / common / src / dsutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  51.4 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. // Copyright (c) 1999-2000 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.     LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
  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.     SetPrimaryBufferFormat( dwPrimaryChannels, dwPrimaryFreq, dwPrimaryBitRate );
  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     = 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 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
  605.         // DirectSound may not be giving us control yet, so 
  606.         // the 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 = pDSB->Restore() );
  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.     BOOL bIsPlaying = FALSE;
  637.  
  638.     if( m_apDSBuffer == NULL )
  639.         return FALSE; 
  640.  
  641.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  642.     {
  643.         if( m_apDSBuffer[i] )
  644.         {  
  645.             DWORD dwStatus = 0;
  646.             m_apDSBuffer[i]->GetStatus( &dwStatus );
  647.             if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
  648.                 break;
  649.         }
  650.     }
  651.  
  652.     if( i != m_dwNumBuffers )
  653.         return m_apDSBuffer[ i ];
  654.     else
  655.         return m_apDSBuffer[ rand() % m_dwNumBuffers ];
  656. }
  657.  
  658.  
  659.  
  660.  
  661. //-----------------------------------------------------------------------------
  662. // Name: CSound::GetBuffer()
  663. // Desc: 
  664. //-----------------------------------------------------------------------------
  665. LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex )
  666. {
  667.     if( m_apDSBuffer == NULL )
  668.         return NULL;
  669.     if( dwIndex >= m_dwNumBuffers )
  670.         return NULL;
  671.  
  672.     return m_apDSBuffer[dwIndex];
  673. }
  674.  
  675.  
  676.  
  677.  
  678. //-----------------------------------------------------------------------------
  679. // Name: CSound::Get3DBufferInterface()
  680. // Desc: 
  681. //-----------------------------------------------------------------------------
  682. HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer )
  683. {
  684.     if( m_apDSBuffer == NULL )
  685.         return CO_E_NOTINITIALIZED;
  686.     if( dwIndex >= m_dwNumBuffers )
  687.         return E_INVALIDARG;
  688.  
  689.     *ppDS3DBuffer = NULL;
  690.  
  691.     return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer, 
  692.                                                   (VOID**)ppDS3DBuffer );
  693. }
  694.  
  695.  
  696. //-----------------------------------------------------------------------------
  697. // Name: CSound::Play()
  698. // Desc: Plays the sound using voice management flags.  Pass in DSBPLAY_LOOPING
  699. //       in the dwFlags to loop the sound
  700. //-----------------------------------------------------------------------------
  701. HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags )
  702. {
  703.     HRESULT hr;
  704.     BOOL    bRestored;
  705.  
  706.     if( m_apDSBuffer == NULL )
  707.         return CO_E_NOTINITIALIZED;
  708.  
  709.     LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer();
  710.  
  711.     if( pDSB == NULL )
  712.         return DXTRACE_ERR( TEXT("GetFreeBuffer"), E_FAIL );
  713.  
  714.     // Restore the buffer if it was lost
  715.     if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
  716.         return DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
  717.  
  718.     if( bRestored )
  719.     {
  720.         // The buffer was restored, so we need to fill it with new data
  721.         if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) )
  722.             return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr );
  723.  
  724.         // Make DirectSound do pre-processing on sound effects
  725.         Reset();
  726.     }
  727.  
  728.     return pDSB->Play( 0, dwPriority, dwFlags );
  729. }
  730.  
  731.  
  732.  
  733.  
  734. //-----------------------------------------------------------------------------
  735. // Name: CSound::Stop()
  736. // Desc: Stops the sound from playing
  737. //-----------------------------------------------------------------------------
  738. HRESULT CSound::Stop()
  739. {
  740.     if( m_apDSBuffer == NULL )
  741.         return CO_E_NOTINITIALIZED;
  742.  
  743.     HRESULT hr = 0;
  744.  
  745.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  746.         hr |= m_apDSBuffer[i]->Stop();
  747.  
  748.     return hr;
  749. }
  750.  
  751.  
  752.  
  753.  
  754. //-----------------------------------------------------------------------------
  755. // Name: CSound::Reset()
  756. // Desc: Reset all of the sound buffers
  757. //-----------------------------------------------------------------------------
  758. HRESULT CSound::Reset()
  759. {
  760.     if( m_apDSBuffer == NULL )
  761.         return CO_E_NOTINITIALIZED;
  762.  
  763.     HRESULT hr = 0;
  764.  
  765.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  766.         hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 );
  767.  
  768.     return hr;
  769. }
  770.  
  771.  
  772.  
  773.  
  774. //-----------------------------------------------------------------------------
  775. // Name: CSound::IsSoundPlaying()
  776. // Desc: Checks to see if a buffer is playing and returns TRUE if it is.
  777. //-----------------------------------------------------------------------------
  778. BOOL CSound::IsSoundPlaying()
  779. {
  780.     BOOL bIsPlaying = FALSE;
  781.  
  782.     if( m_apDSBuffer == NULL )
  783.         return FALSE; 
  784.  
  785.     for( DWORD i=0; i<m_dwNumBuffers; i++ )
  786.     {
  787.         if( m_apDSBuffer[i] )
  788.         {  
  789.             DWORD dwStatus = 0;
  790.             m_apDSBuffer[i]->GetStatus( &dwStatus );
  791.             bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
  792.         }
  793.     }
  794.  
  795.     return bIsPlaying;
  796. }
  797.  
  798.  
  799.  
  800.  
  801. //-----------------------------------------------------------------------------
  802. // Name: CStreamingSound::CStreamingSound()
  803. // Desc: Setups up a buffer so data can be streamed from the wave file into 
  804. //       buffer.  This is very useful for large wav files that would take a 
  805. //       while to load.  The buffer is initially filled with data, then 
  806. //       as sound is played the notification events are signaled and more data
  807. //       is written into the buffer by calling HandleWaveStreamNotification()
  808. //-----------------------------------------------------------------------------
  809. CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize, 
  810.                                   CWaveFile* pWaveFile, DWORD dwNotifySize ) 
  811.                 : CSound( &pDSBuffer, dwDSBufferSize, 1, pWaveFile )           
  812. {
  813.     m_dwLastPlayPos     = 0;
  814.     m_dwPlayProgress    = 0;
  815.     m_dwNotifySize      = dwNotifySize;
  816.     m_dwNextWriteOffset = 0;
  817.     m_bFillNextNotificationWithSilence = FALSE;
  818. }
  819.  
  820.  
  821.  
  822.  
  823. //-----------------------------------------------------------------------------
  824. // Name: CStreamingSound::~CStreamingSound()
  825. // Desc: Destroys the class
  826. //-----------------------------------------------------------------------------
  827. CStreamingSound::~CStreamingSound()
  828. {
  829. }
  830.  
  831.  
  832.  
  833. //-----------------------------------------------------------------------------
  834. // Name: CStreamingSound::HandleWaveStreamNotification()
  835. // Desc: Handle the notification that tell us to put more wav data in the 
  836. //       circular buffer
  837. //-----------------------------------------------------------------------------
  838. HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay )
  839. {
  840.     HRESULT hr;
  841.     DWORD   dwCurrentPlayPos;
  842.     DWORD   dwPlayDelta;
  843.     DWORD   dwBytesWrittenToBuffer;
  844.     VOID*   pDSLockedBuffer = NULL;
  845.     VOID*   pDSLockedBuffer2 = NULL;
  846.     DWORD   dwDSLockedBufferSize;
  847.     DWORD   dwDSLockedBufferSize2;
  848.  
  849.     if( m_apDSBuffer == NULL || m_pWaveFile == NULL )
  850.         return CO_E_NOTINITIALIZED;
  851.  
  852.     // Restore the buffer if it was lost
  853.     BOOL bRestored;
  854.     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
  855.         return DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
  856.  
  857.     if( bRestored )
  858.     {
  859.         // The buffer was restored, so we need to fill it with new data
  860.         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
  861.             return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr );
  862.         return S_OK;
  863.     }
  864.  
  865.     // Lock the DirectSound buffer
  866.     if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize, 
  867.                                             &pDSLockedBuffer, &dwDSLockedBufferSize, 
  868.                                             &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) )
  869.         return DXTRACE_ERR( TEXT("Lock"), hr );
  870.  
  871.     // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize, 
  872.     // it should the second buffer should never be valid
  873.     if( pDSLockedBuffer2 != NULL )
  874.         return E_UNEXPECTED; 
  875.  
  876.     if( !m_bFillNextNotificationWithSilence )
  877.     {
  878.         // Fill the DirectSound buffer with wav data
  879.         if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer, 
  880.                                                   dwDSLockedBufferSize, 
  881.                                                   &dwBytesWrittenToBuffer ) ) )           
  882.             return DXTRACE_ERR( TEXT("Read"), hr );
  883.     }
  884.     else
  885.     {
  886.         // Fill the DirectSound buffer with silence
  887.         FillMemory( pDSLockedBuffer, dwDSLockedBufferSize, 
  888.                     (BYTE)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  889.         dwBytesWrittenToBuffer = dwDSLockedBufferSize;
  890.     }
  891.  
  892.     // If the number of bytes written is less than the 
  893.     // amount we requested, we have a short file.
  894.     if( dwBytesWrittenToBuffer < dwDSLockedBufferSize )
  895.     {
  896.         if( !bLoopedPlay ) 
  897.         {
  898.             // Fill in silence for the rest of the buffer.
  899.             FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer, 
  900.                         dwDSLockedBufferSize - dwBytesWrittenToBuffer, 
  901.                         (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
  902.  
  903.             // Any future notifications should just fill the buffer with silence
  904.             m_bFillNextNotificationWithSilence = TRUE;
  905.         }
  906.         else
  907.         {
  908.             // We are looping, so reset the file and fill the buffer with wav data
  909.             DWORD dwReadSoFar = dwBytesWrittenToBuffer;    // From previous call above.
  910.             while( dwReadSoFar < dwDSLockedBufferSize )
  911.             {  
  912.                 // This will keep reading in until the buffer is full (for very short files).
  913.                 if( FAILED( hr = m_pWaveFile->ResetFile() ) )
  914.                     return DXTRACE_ERR( TEXT("ResetFile"), hr );
  915.  
  916.                 if( FAILED( hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar,
  917.                                                           dwDSLockedBufferSize - dwReadSoFar,
  918.                                                           &dwBytesWrittenToBuffer ) ) )
  919.                     return DXTRACE_ERR( TEXT("Read"), hr );
  920.  
  921.                 dwReadSoFar += dwBytesWrittenToBuffer;
  922.             } 
  923.         } 
  924.     }
  925.  
  926.     // Unlock the DirectSound buffer
  927.     m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
  928.  
  929.     // Figure out how much data has been played so far.  When we have played
  930.     // passed the end of the file, we will either need to start filling the
  931.     // buffer with silence or starting reading from the beginning of the file, 
  932.     // depending if the user wants to loop the sound
  933.     if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) )
  934.         return DXTRACE_ERR( TEXT("GetCurrentPosition"), hr );
  935.  
  936.     // Check to see if the position counter looped
  937.     if( dwCurrentPlayPos < m_dwLastPlayPos )
  938.         dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos;
  939.     else
  940.         dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos;
  941.  
  942.     m_dwPlayProgress += dwPlayDelta;
  943.     m_dwLastPlayPos = dwCurrentPlayPos;
  944.  
  945.     // If we are now filling the buffer with silence, then we have found the end so 
  946.     // check to see if the entire sound has played, if it has then stop the buffer.
  947.     if( m_bFillNextNotificationWithSilence )
  948.     {
  949.         // We don't want to cut off the sound before it's done playing.
  950.         if( m_dwPlayProgress >= m_pWaveFile->GetSize() )
  951.         {
  952.             m_apDSBuffer[0]->Stop();
  953.         }
  954.     }
  955.  
  956.     // Update where the buffer will lock (for next time)
  957.     m_dwNextWriteOffset += dwDSLockedBufferSize; 
  958.     m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer
  959.  
  960.     return S_OK;
  961. }
  962.  
  963.  
  964.  
  965.  
  966. //-----------------------------------------------------------------------------
  967. // Name: CStreamingSound::Reset()
  968. // Desc: Resets the sound so it will begin playing at the beginning
  969. //-----------------------------------------------------------------------------
  970. HRESULT CStreamingSound::Reset()
  971. {
  972.     HRESULT hr;
  973.  
  974.     if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL )
  975.         return CO_E_NOTINITIALIZED;
  976.  
  977.     m_dwLastPlayPos     = 0;
  978.     m_dwPlayProgress    = 0;
  979.     m_dwNextWriteOffset = 0;
  980.     m_bFillNextNotificationWithSilence = FALSE;
  981.  
  982.     // Restore the buffer if it was lost
  983.     BOOL bRestored;
  984.     if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) )
  985.         return DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
  986.  
  987.     if( bRestored )
  988.     {
  989.         // The buffer was restored, so we need to fill it with new data
  990.         if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) )
  991.             return DXTRACE_ERR( TEXT("FillBufferWithSound"), hr );
  992.     }
  993.  
  994.     m_pWaveFile->ResetFile();
  995.  
  996.     return m_apDSBuffer[0]->SetCurrentPosition( 0L );  
  997. }
  998.  
  999.  
  1000.  
  1001.  
  1002. //-----------------------------------------------------------------------------
  1003. // Name: CWaveFile::CWaveFile()
  1004. // Desc: Constructs the class.  Call Open() to open a wave file for reading.  
  1005. //       Then call Read() as needed.  Calling the destructor or Close() 
  1006. //       will close the file.  
  1007. //-----------------------------------------------------------------------------
  1008. CWaveFile::CWaveFile()
  1009. {
  1010.     m_pwfx    = NULL;
  1011.     m_hmmio   = NULL;
  1012.     m_dwSize  = 0;
  1013.     m_bIsReadingFromMemory = FALSE;
  1014. }
  1015.  
  1016.  
  1017.  
  1018.  
  1019. //-----------------------------------------------------------------------------
  1020. // Name: CWaveFile::~CWaveFile()
  1021. // Desc: Destructs the class
  1022. //-----------------------------------------------------------------------------
  1023. CWaveFile::~CWaveFile()
  1024. {
  1025.     Close();
  1026.  
  1027.     if( !m_bIsReadingFromMemory )
  1028.         SAFE_DELETE_ARRAY( m_pwfx );
  1029. }
  1030.  
  1031.  
  1032.  
  1033.  
  1034. //-----------------------------------------------------------------------------
  1035. // Name: CWaveFile::Open()
  1036. // Desc: Opens a wave file for reading
  1037. //-----------------------------------------------------------------------------
  1038. HRESULT CWaveFile::Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags )
  1039. {
  1040.     HRESULT hr;
  1041.  
  1042.     m_dwFlags = dwFlags;
  1043.     m_bIsReadingFromMemory = FALSE;
  1044.  
  1045.     if( m_dwFlags == WAVEFILE_READ )
  1046.     {
  1047.         if( strFileName == NULL )
  1048.             return E_INVALIDARG;
  1049.         SAFE_DELETE_ARRAY( m_pwfx );
  1050.  
  1051.         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ );
  1052.  
  1053.         if( NULL == m_hmmio )
  1054.         {
  1055.             HRSRC   hResInfo;
  1056.             HGLOBAL hResData;
  1057.             DWORD   dwSize;
  1058.             VOID*   pvRes;
  1059.  
  1060.             // Loading it as a file failed, so try it as a resource
  1061.             if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) )
  1062.             {
  1063.                 if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) )
  1064.                     return DXTRACE_ERR_NOMSGBOX( TEXT("FindResource"), E_FAIL );
  1065.             }
  1066.  
  1067.             if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) )
  1068.                 return DXTRACE_ERR( TEXT("LoadResource"), E_FAIL );
  1069.  
  1070.             if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) 
  1071.                 return DXTRACE_ERR( TEXT("SizeofResource"), E_FAIL );
  1072.  
  1073.             if( NULL == ( pvRes = LockResource( hResData ) ) )
  1074.                 return DXTRACE_ERR( TEXT("LockResource"), E_FAIL );
  1075.  
  1076.             CHAR* pData = new CHAR[ dwSize ];
  1077.             memcpy( pData, pvRes, dwSize );
  1078.  
  1079.             MMIOINFO mmioInfo;
  1080.             ZeroMemory( &mmioInfo, sizeof(mmioInfo) );
  1081.             mmioInfo.fccIOProc = FOURCC_MEM;
  1082.             mmioInfo.cchBuffer = dwSize;
  1083.             mmioInfo.pchBuffer = (CHAR*) pData;
  1084.  
  1085.             m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ );
  1086.         }
  1087.  
  1088.         if( FAILED( hr = ReadMMIO() ) )
  1089.         {
  1090.             // ReadMMIO will fail if its an not a wave file
  1091.             mmioClose( m_hmmio, 0 );
  1092.             return DXTRACE_ERR_NOMSGBOX( TEXT("ReadMMIO"), hr );
  1093.         }
  1094.  
  1095.         if( FAILED( hr = ResetFile() ) )
  1096.             return DXTRACE_ERR( TEXT("ResetFile"), hr );
  1097.  
  1098.         // After the reset, the size of the wav file is m_ck.cksize so store it now
  1099.         m_dwSize = m_ck.cksize;
  1100.     }
  1101.     else
  1102.     {
  1103.         m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF  | 
  1104.                                                   MMIO_READWRITE | 
  1105.                                                   MMIO_CREATE );
  1106.         if( NULL == m_hmmio )
  1107.             return DXTRACE_ERR( TEXT("mmioOpen"), E_FAIL );
  1108.  
  1109.         if( FAILED( hr = WriteMMIO( pwfx ) ) )
  1110.         {
  1111.             mmioClose( m_hmmio, 0 );
  1112.             return DXTRACE_ERR( TEXT("WriteMMIO"), hr );
  1113.         }
  1114.                         
  1115.         if( FAILED( hr = ResetFile() ) )
  1116.             return DXTRACE_ERR( TEXT("ResetFile"), hr );
  1117.     }
  1118.  
  1119.     return hr;
  1120. }
  1121.  
  1122.  
  1123.  
  1124.  
  1125. //-----------------------------------------------------------------------------
  1126. // Name: CWaveFile::OpenFromMemory()
  1127. // Desc: copy data to CWaveFile member variable from memory
  1128. //-----------------------------------------------------------------------------
  1129. HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize, 
  1130.                                    WAVEFORMATEX* pwfx, DWORD dwFlags )
  1131. {
  1132.     m_pwfx       = pwfx;
  1133.     m_ulDataSize = ulDataSize;
  1134.     m_pbData     = pbData;
  1135.     m_pbDataCur  = m_pbData;
  1136.     m_bIsReadingFromMemory = TRUE;
  1137.     
  1138.     if( dwFlags != WAVEFILE_READ )
  1139.         return E_NOTIMPL;       
  1140.     
  1141.     return S_OK;
  1142. }
  1143.  
  1144.  
  1145.  
  1146.  
  1147. //-----------------------------------------------------------------------------
  1148. // Name: CWaveFile::ReadMMIO()
  1149. // Desc: Support function for reading from a multimedia I/O stream.
  1150. //       m_hmmio must be valid before calling.  This function uses it to
  1151. //       update m_ckRiff, and m_pwfx. 
  1152. //-----------------------------------------------------------------------------
  1153. HRESULT CWaveFile::ReadMMIO()
  1154. {
  1155.     MMCKINFO        ckIn;           // chunk info. for general use.
  1156.     PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.       
  1157.  
  1158.     m_pwfx = NULL;
  1159.  
  1160.     if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) )
  1161.         return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
  1162.  
  1163.     // Check to make sure this is a valid wave file
  1164.     if( (m_ckRiff.ckid != FOURCC_RIFF) ||
  1165.         (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) )
  1166.         return DXTRACE_ERR_NOMSGBOX( TEXT("mmioFOURCC"), E_FAIL ); 
  1167.  
  1168.     // Search the input file for for the 'fmt ' chunk.
  1169.     ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
  1170.     if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) )
  1171.         return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
  1172.  
  1173.     // Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
  1174.     // if there are extra parameters at the end, we'll ignore them
  1175.        if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) )
  1176.            return DXTRACE_ERR( TEXT("sizeof(PCMWAVEFORMAT)"), E_FAIL );
  1177.  
  1178.     // Read the 'fmt ' chunk into <pcmWaveFormat>.
  1179.     if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, 
  1180.                   sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) )
  1181.         return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
  1182.  
  1183.     // Allocate the waveformatex, but if its not pcm format, read the next
  1184.     // word, and thats how many extra bytes to allocate.
  1185.     if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM )
  1186.     {
  1187.         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
  1188.         if( NULL == m_pwfx )
  1189.             return DXTRACE_ERR( TEXT("m_pwfx"), E_FAIL );
  1190.  
  1191.         // Copy the bytes from the pcm structure to the waveformatex structure
  1192.         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
  1193.         m_pwfx->cbSize = 0;
  1194.     }
  1195.     else
  1196.     {
  1197.         // Read in length of extra bytes.
  1198.         WORD cbExtraBytes = 0L;
  1199.         if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) )
  1200.             return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
  1201.  
  1202.         m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ];
  1203.         if( NULL == m_pwfx )
  1204.             return DXTRACE_ERR( TEXT("new"), E_FAIL );
  1205.  
  1206.         // Copy the bytes from the pcm structure to the waveformatex structure
  1207.         memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) );
  1208.         m_pwfx->cbSize = cbExtraBytes;
  1209.  
  1210.         // Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
  1211.         if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)),
  1212.                       cbExtraBytes ) != cbExtraBytes )
  1213.         {
  1214.             SAFE_DELETE( m_pwfx );
  1215.             return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL );
  1216.         }
  1217.     }
  1218.  
  1219.     // Ascend the input file out of the 'fmt ' chunk.
  1220.     if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) )
  1221.     {
  1222.         SAFE_DELETE( m_pwfx );
  1223.         return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL );
  1224.     }
  1225.  
  1226.     return S_OK;
  1227. }
  1228.  
  1229.  
  1230.  
  1231.  
  1232. //-----------------------------------------------------------------------------
  1233. // Name: CWaveFile::GetSize()
  1234. // Desc: Retuns the size of the read access wave file 
  1235. //-----------------------------------------------------------------------------
  1236. DWORD CWaveFile::GetSize()
  1237. {
  1238.     return m_dwSize;
  1239. }
  1240.  
  1241.  
  1242.  
  1243.  
  1244. //-----------------------------------------------------------------------------
  1245. // Name: CWaveFile::ResetFile()
  1246. // Desc: Resets the internal m_ck pointer so reading starts from the 
  1247. //       beginning of the file again 
  1248. //-----------------------------------------------------------------------------
  1249. HRESULT CWaveFile::ResetFile()
  1250. {
  1251.     if( m_bIsReadingFromMemory )
  1252.     {
  1253.         m_pbDataCur = m_pbData;
  1254.     }
  1255.     else 
  1256.     {
  1257.         if( m_hmmio == NULL )
  1258.             return CO_E_NOTINITIALIZED;
  1259.  
  1260.         if( m_dwFlags == WAVEFILE_READ )
  1261.         {
  1262.             // Seek to the data
  1263.             if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC),
  1264.                             SEEK_SET ) )
  1265.                 return DXTRACE_ERR( TEXT("mmioSeek"), E_FAIL );
  1266.  
  1267.             // Search the input file for the 'data' chunk.
  1268.             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1269.             if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) )
  1270.               return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL );
  1271.         }
  1272.         else
  1273.         {
  1274.             // Create the 'data' chunk that holds the waveform samples.  
  1275.             m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1276.             m_ck.cksize = 0;
  1277.  
  1278.             if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) 
  1279.                 return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL );
  1280.  
  1281.             if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) )
  1282.                 return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
  1283.         }
  1284.     }
  1285.     
  1286.     return S_OK;
  1287. }
  1288.  
  1289.  
  1290.  
  1291.  
  1292. //-----------------------------------------------------------------------------
  1293. // Name: CWaveFile::Read()
  1294. // Desc: Reads section of data from a wave file into pBuffer and returns 
  1295. //       how much read in pdwSizeRead, reading not more than dwSizeToRead.
  1296. //       This uses m_ck to determine where to start reading from.  So 
  1297. //       subsequent calls will be continue where the last left off unless 
  1298. //       Reset() is called.
  1299. //-----------------------------------------------------------------------------
  1300. HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead )
  1301. {
  1302.     if( m_bIsReadingFromMemory )
  1303.     {
  1304.         if( m_pbDataCur == NULL )
  1305.             return CO_E_NOTINITIALIZED;
  1306.         if( pdwSizeRead != NULL )
  1307.             *pdwSizeRead = 0;
  1308.  
  1309.         if( (BYTE*)(m_pbDataCur + dwSizeToRead) > 
  1310.             (BYTE*)(m_pbData + m_ulDataSize) )
  1311.         {
  1312.             dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData);
  1313.         }
  1314.         
  1315.         CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead );
  1316.         
  1317.         if( pdwSizeRead != NULL )
  1318.             *pdwSizeRead = dwSizeToRead;
  1319.  
  1320.         return S_OK;
  1321.     }
  1322.     else 
  1323.     {
  1324.         MMIOINFO mmioinfoIn; // current status of m_hmmio
  1325.  
  1326.         if( m_hmmio == NULL )
  1327.             return CO_E_NOTINITIALIZED;
  1328.         if( pBuffer == NULL || pdwSizeRead == NULL )
  1329.             return E_INVALIDARG;
  1330.  
  1331.         if( pdwSizeRead != NULL )
  1332.             *pdwSizeRead = 0;
  1333.  
  1334.         if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) )
  1335.             return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL );
  1336.                 
  1337.         UINT cbDataIn = dwSizeToRead;
  1338.         if( cbDataIn > m_ck.cksize ) 
  1339.             cbDataIn = m_ck.cksize;       
  1340.  
  1341.         m_ck.cksize -= cbDataIn;
  1342.     
  1343.         for( DWORD cT = 0; cT < cbDataIn; cT++ )
  1344.         {
  1345.             // Copy the bytes from the io to the buffer.
  1346.             if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
  1347.             {
  1348.                 if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) )
  1349.                     return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL );
  1350.  
  1351.                 if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead )
  1352.                     return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL );
  1353.             }
  1354.  
  1355.             // Actual copy.
  1356.             *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext);
  1357.             mmioinfoIn.pchNext++;
  1358.         }
  1359.  
  1360.         if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) )
  1361.             return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL );
  1362.  
  1363.         if( pdwSizeRead != NULL )
  1364.             *pdwSizeRead = cbDataIn;
  1365.  
  1366.         return S_OK;
  1367.     }
  1368. }
  1369.  
  1370.  
  1371.  
  1372.  
  1373. //-----------------------------------------------------------------------------
  1374. // Name: CWaveFile::Close()
  1375. // Desc: Closes the wave file 
  1376. //-----------------------------------------------------------------------------
  1377. HRESULT CWaveFile::Close()
  1378. {
  1379.     if( m_dwFlags == WAVEFILE_READ )
  1380.     {
  1381.         mmioClose( m_hmmio, 0 );
  1382.         m_hmmio = NULL;
  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.