home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the 3D Game Programming Gurus / gurus.iso / DirectX / dx9sdkcp.exe / SDK (C++) / Bin / DXUtils / Visual Studio 6.0 Wizards / Source Code / Template / dsutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2002-11-12  |  54.9 KB  |  1,647 lines

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