dsutil.cpp
上传用户:luhy168
上传日期:2022-01-10
资源大小:240k
文件大小:52k
源码类别:

模拟服务器

开发平台:

Visual C++

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