DXUTsound.cpp
上传用户:junlon
上传日期:2022-01-05
资源大小:39075k
文件大小:54k
源码类别:

DirextX编程

开发平台:

Visual C++

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