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