//----------------------------------------------------------------------------- // File: DXUTsound.cpp // // Desc: DirectSound framework classes for reading and writing wav files and // playing them in DirectSound buffers. Feel free to use this class // as a starting point for adding extra functionality. // // Copyright (c) Microsoft Corp. All rights reserved. //----------------------------------------------------------------------------- #define STRICT #include "dxstdafx.h" #include #include #include "DXUTsound.h" #undef min // use __min instead #undef max // use __max instead //----------------------------------------------------------------------------- // Name: CSoundManager::CSoundManager() // Desc: Constructs the class //----------------------------------------------------------------------------- CSoundManager::CSoundManager() { m_pDS = NULL; } //----------------------------------------------------------------------------- // Name: CSoundManager::~CSoundManager() // Desc: Destroys the class //----------------------------------------------------------------------------- CSoundManager::~CSoundManager() { SAFE_RELEASE( m_pDS ); } //----------------------------------------------------------------------------- // Name: CSoundManager::Initialize() // Desc: Initializes the IDirectSound object and also sets the primary buffer // format. This function must be called before any others. //----------------------------------------------------------------------------- HRESULT CSoundManager::Initialize( HWND hWnd, DWORD dwCoopLevel ) { HRESULT hr; SAFE_RELEASE( m_pDS ); // Create IDirectSound using the primary sound device if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) ) return DXUT_ERR( L"DirectSoundCreate8", hr ); // Set DirectSound coop level if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, dwCoopLevel ) ) ) return DXUT_ERR( L"SetCooperativeLevel", hr ); return S_OK; } //----------------------------------------------------------------------------- // Name: CSoundManager::SetPrimaryBufferFormat() // Desc: Set primary buffer to a specified format // !WARNING! - Setting the primary buffer format and then using this // same DirectSound object for DirectMusic messes up // DirectMusic! // For example, to set the primary buffer format to 22kHz stereo, 16-bit // then: dwPrimaryChannels = 2 // dwPrimaryFreq = 22050, // dwPrimaryBitRate = 16 //----------------------------------------------------------------------------- HRESULT CSoundManager::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, DWORD dwPrimaryFreq, DWORD dwPrimaryBitRate ) { HRESULT hr; LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; // Get the primary buffer DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) return DXUT_ERR( L"CreateSoundBuffer", hr ); WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM; wfx.nChannels = (WORD) dwPrimaryChannels; wfx.nSamplesPerSec = (DWORD) dwPrimaryFreq; wfx.wBitsPerSample = (WORD) dwPrimaryBitRate; wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels); wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign); if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) return DXUT_ERR( L"SetFormat", hr ); SAFE_RELEASE( pDSBPrimary ); return S_OK; } //----------------------------------------------------------------------------- // Name: CSoundManager::Get3DListenerInterface() // Desc: Returns the 3D listener interface associated with primary buffer. //----------------------------------------------------------------------------- HRESULT CSoundManager::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener ) { HRESULT hr; DSBUFFERDESC dsbdesc; LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; if( ppDSListener == NULL ) return E_INVALIDARG; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; *ppDSListener = NULL; // Obtain primary buffer, asking it for 3D control ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) ) return DXUT_ERR( L"CreateSoundBuffer", hr ); if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener, (VOID**)ppDSListener ) ) ) { SAFE_RELEASE( pDSBPrimary ); return DXUT_ERR( L"QueryInterface", hr ); } // Release the primary buffer, since it is not need anymore SAFE_RELEASE( pDSBPrimary ); return S_OK; } //----------------------------------------------------------------------------- // Name: CSoundManager::Create() // Desc: //----------------------------------------------------------------------------- HRESULT CSoundManager::Create( CSound** ppSound, LPWSTR strWaveFileName, DWORD dwCreationFlags, GUID guid3DAlgorithm, DWORD dwNumBuffers ) { HRESULT hr; HRESULT hrRet = S_OK; DWORD i; LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; DWORD dwDSBufferSize = NULL; CWaveFile* pWaveFile = NULL; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; if( strWaveFileName == NULL || ppSound == NULL || dwNumBuffers < 1 ) return E_INVALIDARG; apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; if( apDSBuffer == NULL ) { hr = E_OUTOFMEMORY; goto LFail; } pWaveFile = new CWaveFile(); if( pWaveFile == NULL ) { hr = E_OUTOFMEMORY; goto LFail; } pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); if( pWaveFile->GetSize() == 0 ) { // Wave is blank, so don't create it. hr = E_FAIL; goto LFail; } // Make the DirectSound buffer the same size as the wav file dwDSBufferSize = pWaveFile->GetSize(); // Create the direct sound buffer, and only request the flags needed // since each requires some overhead and limits if the buffer can // be hardware accelerated DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = dwCreationFlags; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = pWaveFile->m_pwfx; // DirectSound is only guarenteed to play PCM data. Other // formats may or may not work depending the sound card driver. hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ); // Be sure to return this error code if it occurs so the // callers knows this happened. if( hr == DS_NO_VIRTUALIZATION ) hrRet = DS_NO_VIRTUALIZATION; if( FAILED(hr) ) { // DSERR_BUFFERTOOSMALL will be returned if the buffer is // less than DSBSIZE_FX_MIN and the buffer is created // with DSBCAPS_CTRLFX. // It might also fail if hardware buffer mixing was requested // on a device that doesn't support it. DXUT_ERR( L"CreateSoundBuffer", hr ); goto LFail; } // Default to use DuplicateSoundBuffer() when created extra buffers since always // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case. if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 ) { for( i=1; iDuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) { DXUT_ERR( L"DuplicateSoundBuffer", hr ); goto LFail; } } } else { for( i=1; iCreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL ); if( FAILED(hr) ) { DXUT_ERR( L"CreateSoundBuffer", hr ); goto LFail; } } } // Create the sound *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags ); SAFE_DELETE_ARRAY( apDSBuffer ); return hrRet; LFail: // Cleanup SAFE_DELETE( pWaveFile ); SAFE_DELETE_ARRAY( apDSBuffer ); return hr; } //----------------------------------------------------------------------------- // Name: CSoundManager::CreateFromMemory() // Desc: //----------------------------------------------------------------------------- HRESULT CSoundManager::CreateFromMemory( CSound** ppSound, BYTE* pbData, ULONG ulDataSize, LPWAVEFORMATEX pwfx, DWORD dwCreationFlags, GUID guid3DAlgorithm, DWORD dwNumBuffers ) { HRESULT hr; DWORD i; LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; DWORD dwDSBufferSize = NULL; CWaveFile* pWaveFile = NULL; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; if( pbData == NULL || ppSound == NULL || dwNumBuffers < 1 ) return E_INVALIDARG; apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; if( apDSBuffer == NULL ) { hr = E_OUTOFMEMORY; goto LFail; } pWaveFile = new CWaveFile(); if( pWaveFile == NULL ) { hr = E_OUTOFMEMORY; goto LFail; } pWaveFile->OpenFromMemory( pbData,ulDataSize, pwfx, WAVEFILE_READ ); // Make the DirectSound buffer the same size as the wav file dwDSBufferSize = ulDataSize; // Create the direct sound buffer, and only request the flags needed // since each requires some overhead and limits if the buffer can // be hardware accelerated DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = dwCreationFlags; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = pwfx; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) ) { DXUT_ERR( L"CreateSoundBuffer", hr ); goto LFail; } // Default to use DuplicateSoundBuffer() when created extra buffers since always // create a buffer that uses the same memory however DuplicateSoundBuffer() will fail if // DSBCAPS_CTRLFX is used, so use CreateSoundBuffer() instead in this case. if( (dwCreationFlags & DSBCAPS_CTRLFX) == 0 ) { for( i=1; iDuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) { DXUT_ERR( L"DuplicateSoundBuffer", hr ); goto LFail; } } } else { for( i=1; iCreateSoundBuffer( &dsbd, &apDSBuffer[i], NULL ); if( FAILED(hr) ) { DXUT_ERR( L"CreateSoundBuffer", hr ); goto LFail; } } } // Create the sound *ppSound = new CSound( apDSBuffer, dwDSBufferSize, dwNumBuffers, pWaveFile, dwCreationFlags ); SAFE_DELETE_ARRAY( apDSBuffer ); return S_OK; LFail: // Cleanup SAFE_DELETE_ARRAY( apDSBuffer ); return hr; } //----------------------------------------------------------------------------- // Name: CSoundManager::CreateStreaming() // Desc: //----------------------------------------------------------------------------- HRESULT CSoundManager::CreateStreaming( CStreamingSound** ppStreamingSound, LPWSTR strWaveFileName, DWORD dwCreationFlags, GUID guid3DAlgorithm, DWORD dwNotifyCount, DWORD dwNotifySize, HANDLE hNotifyEvent ) { HRESULT hr; if( m_pDS == NULL ) return CO_E_NOTINITIALIZED; if( strWaveFileName == NULL || ppStreamingSound == NULL || hNotifyEvent == NULL ) return E_INVALIDARG; LPDIRECTSOUNDBUFFER pDSBuffer = NULL; DWORD dwDSBufferSize = NULL; CWaveFile* pWaveFile = NULL; DSBPOSITIONNOTIFY* aPosNotify = NULL; LPDIRECTSOUNDNOTIFY pDSNotify = NULL; pWaveFile = new CWaveFile(); if( pWaveFile == NULL ) return E_OUTOFMEMORY; pWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); // Figure out how big the DirectSound buffer should be dwDSBufferSize = dwNotifySize * dwNotifyCount; // Set up the direct sound buffer. Request the NOTIFY flag, so // that we are notified as the sound buffer plays. Note, that using this flag // may limit the amount of hardware acceleration that can occur. DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = dwCreationFlags | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = pWaveFile->m_pwfx; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBuffer, NULL ) ) ) { // If wave format isn't then it will return // either DSERR_BADFORMAT or E_INVALIDARG if( hr == DSERR_BADFORMAT || hr == E_INVALIDARG ) return DXUT_ERR( L"CreateSoundBuffer", hr ); return DXUT_ERR( L"CreateSoundBuffer", hr ); } // Create the notification events, so that we know when to fill // the buffer as the sound plays. if( FAILED( hr = pDSBuffer->QueryInterface( IID_IDirectSoundNotify, (VOID**)&pDSNotify ) ) ) { SAFE_DELETE_ARRAY( aPosNotify ); return DXUT_ERR( L"QueryInterface", hr ); } aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ]; if( aPosNotify == NULL ) return E_OUTOFMEMORY; for( DWORD i = 0; i < dwNotifyCount; i++ ) { aPosNotify[i].dwOffset = (dwNotifySize * i) + dwNotifySize - 1; aPosNotify[i].hEventNotify = hNotifyEvent; } // Tell DirectSound when to notify us. The notification will come in the from // of signaled events that are handled in WinMain() if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, aPosNotify ) ) ) { SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); return DXUT_ERR( L"SetNotificationPositions", hr ); } SAFE_RELEASE( pDSNotify ); SAFE_DELETE_ARRAY( aPosNotify ); // Create the sound *ppStreamingSound = new CStreamingSound( pDSBuffer, dwDSBufferSize, pWaveFile, dwNotifySize ); return S_OK; } //----------------------------------------------------------------------------- // Name: CSound::CSound() // Desc: Constructs the class //----------------------------------------------------------------------------- CSound::CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize, DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags ) { DWORD i; if( dwNumBuffers <= 0 ) return; m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwNumBuffers]; if( NULL != m_apDSBuffer ) { for( i=0; iLock( 0, m_dwDSBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) ) return DXUT_ERR( L"Lock", hr ); // Reset the wave file to the beginning m_pWaveFile->ResetFile(); if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer, dwDSLockedBufferSize, &dwWavDataRead ) ) ) return DXUT_ERR( L"Read", hr ); if( dwWavDataRead == 0 ) { // Wav is blank, so just fill with silence FillMemory( (BYTE*) pDSLockedBuffer, dwDSLockedBufferSize, (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); } else if( dwWavDataRead < dwDSLockedBufferSize ) { // If the wav file was smaller than the DirectSound buffer, // we need to fill the remainder of the buffer with data if( bRepeatWavIfBufferLarger ) { // Reset the file and fill the buffer with wav data DWORD dwReadSoFar = dwWavDataRead; // From previous call above. while( dwReadSoFar < dwDSLockedBufferSize ) { // This will keep reading in until the buffer is full // for very short files if( FAILED( hr = m_pWaveFile->ResetFile() ) ) return DXUT_ERR( L"ResetFile", hr ); hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwWavDataRead ); if( FAILED(hr) ) return DXUT_ERR( L"Read", hr ); dwReadSoFar += dwWavDataRead; } } else { // Don't repeat the wav file, just fill in silence FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead, dwDSLockedBufferSize - dwWavDataRead, (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); } } // Unlock the buffer, we don't need it anymore. pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); return S_OK; } //----------------------------------------------------------------------------- // Name: CSound::RestoreBuffer() // Desc: Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was // restored. It can also NULL if the information is not needed. //----------------------------------------------------------------------------- HRESULT CSound::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored ) { HRESULT hr; if( pDSB == NULL ) return CO_E_NOTINITIALIZED; if( pbWasRestored ) *pbWasRestored = FALSE; DWORD dwStatus; if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) return DXUT_ERR( L"GetStatus", hr ); if( dwStatus & DSBSTATUS_BUFFERLOST ) { // Since the app could have just been activated, then // DirectSound may not be giving us control yet, so // the restoring the buffer may fail. // If it does, sleep until DirectSound gives us control. do { hr = pDSB->Restore(); if( hr == DSERR_BUFFERLOST ) Sleep( 10 ); } while( ( hr = pDSB->Restore() ) == DSERR_BUFFERLOST ); if( pbWasRestored != NULL ) *pbWasRestored = TRUE; return S_OK; } else { return S_FALSE; } } //----------------------------------------------------------------------------- // Name: CSound::GetFreeBuffer() // Desc: Finding the first buffer that is not playing and return a pointer to // it, or if all are playing return a pointer to a randomly selected buffer. //----------------------------------------------------------------------------- LPDIRECTSOUNDBUFFER CSound::GetFreeBuffer() { if( m_apDSBuffer == NULL ) return FALSE; DWORD i; for( i=0; iGetStatus( &dwStatus ); if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) break; } } if( i != m_dwNumBuffers ) return m_apDSBuffer[ i ]; else return m_apDSBuffer[ rand() % m_dwNumBuffers ]; } //----------------------------------------------------------------------------- // Name: CSound::GetBuffer() // Desc: //----------------------------------------------------------------------------- LPDIRECTSOUNDBUFFER CSound::GetBuffer( DWORD dwIndex ) { if( m_apDSBuffer == NULL ) return NULL; if( dwIndex >= m_dwNumBuffers ) return NULL; return m_apDSBuffer[dwIndex]; } //----------------------------------------------------------------------------- // Name: CSound::Get3DBufferInterface() // Desc: //----------------------------------------------------------------------------- HRESULT CSound::Get3DBufferInterface( DWORD dwIndex, LPDIRECTSOUND3DBUFFER* ppDS3DBuffer ) { if( m_apDSBuffer == NULL ) return CO_E_NOTINITIALIZED; if( dwIndex >= m_dwNumBuffers ) return E_INVALIDARG; *ppDS3DBuffer = NULL; return m_apDSBuffer[dwIndex]->QueryInterface( IID_IDirectSound3DBuffer, (VOID**)ppDS3DBuffer ); } //----------------------------------------------------------------------------- // Name: CSound::Play() // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING // in the dwFlags to loop the sound //----------------------------------------------------------------------------- HRESULT CSound::Play( DWORD dwPriority, DWORD dwFlags, LONG lVolume, LONG lFrequency, LONG lPan ) { HRESULT hr; BOOL bRestored; if( m_apDSBuffer == NULL ) return CO_E_NOTINITIALIZED; LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer(); if( pDSB == NULL ) return DXUT_ERR( L"GetFreeBuffer", E_FAIL ); // Restore the buffer if it was lost if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) return DXUT_ERR( L"RestoreBuffer", hr ); if( bRestored ) { // The buffer was restored, so we need to fill it with new data if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) ) return DXUT_ERR( L"FillBufferWithSound", hr ); } if( m_dwCreationFlags & DSBCAPS_CTRLVOLUME ) { pDSB->SetVolume( lVolume ); } if( lFrequency != -1 && (m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY) ) { pDSB->SetFrequency( lFrequency ); } if( m_dwCreationFlags & DSBCAPS_CTRLPAN ) { pDSB->SetPan( lPan ); } return pDSB->Play( 0, dwPriority, dwFlags ); } //----------------------------------------------------------------------------- // Name: CSound::Play3D() // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING // in the dwFlags to loop the sound //----------------------------------------------------------------------------- HRESULT CSound::Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority, DWORD dwFlags, LONG lFrequency ) { HRESULT hr; BOOL bRestored; DWORD dwBaseFrequency; if( m_apDSBuffer == NULL ) return CO_E_NOTINITIALIZED; LPDIRECTSOUNDBUFFER pDSB = GetFreeBuffer(); if( pDSB == NULL ) return DXUT_ERR( L"GetFreeBuffer", E_FAIL ); // Restore the buffer if it was lost if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) return DXUT_ERR( L"RestoreBuffer", hr ); if( bRestored ) { // The buffer was restored, so we need to fill it with new data if( FAILED( hr = FillBufferWithSound( pDSB, FALSE ) ) ) return DXUT_ERR( L"FillBufferWithSound", hr ); } if( m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY ) { pDSB->GetFrequency( &dwBaseFrequency ); pDSB->SetFrequency( dwBaseFrequency + lFrequency ); } // QI for the 3D buffer LPDIRECTSOUND3DBUFFER pDS3DBuffer; hr = pDSB->QueryInterface( IID_IDirectSound3DBuffer, (VOID**) &pDS3DBuffer ); if( SUCCEEDED( hr ) ) { hr = pDS3DBuffer->SetAllParameters( p3DBuffer, DS3D_IMMEDIATE ); if( SUCCEEDED( hr ) ) { hr = pDSB->Play( 0, dwPriority, dwFlags ); } pDS3DBuffer->Release(); } return hr; } //----------------------------------------------------------------------------- // Name: CSound::Stop() // Desc: Stops the sound from playing //----------------------------------------------------------------------------- HRESULT CSound::Stop() { if( m_apDSBuffer == NULL ) return CO_E_NOTINITIALIZED; HRESULT hr = 0; for( DWORD i=0; iStop(); return hr; } //----------------------------------------------------------------------------- // Name: CSound::Reset() // Desc: Reset all of the sound buffers //----------------------------------------------------------------------------- HRESULT CSound::Reset() { if( m_apDSBuffer == NULL ) return CO_E_NOTINITIALIZED; HRESULT hr = 0; for( DWORD i=0; iSetCurrentPosition( 0 ); return hr; } //----------------------------------------------------------------------------- // Name: CSound::IsSoundPlaying() // Desc: Checks to see if a buffer is playing and returns TRUE if it is. //----------------------------------------------------------------------------- BOOL CSound::IsSoundPlaying() { BOOL bIsPlaying = FALSE; if( m_apDSBuffer == NULL ) return FALSE; for( DWORD i=0; iGetStatus( &dwStatus ); bIsPlaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 ); } } return bIsPlaying; } //----------------------------------------------------------------------------- // Name: CStreamingSound::CStreamingSound() // Desc: Setups up a buffer so data can be streamed from the wave file into // a buffer. This is very useful for large wav files that would take a // while to load. The buffer is initially filled with data, then // as sound is played the notification events are signaled and more data // is written into the buffer by calling HandleWaveStreamNotification() //----------------------------------------------------------------------------- CStreamingSound::CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize, CWaveFile* pWaveFile, DWORD dwNotifySize ) : CSound( &pDSBuffer, dwDSBufferSize, 1, pWaveFile, 0 ) { m_dwLastPlayPos = 0; m_dwPlayProgress = 0; m_dwNotifySize = dwNotifySize; m_dwNextWriteOffset = 0; m_bFillNextNotificationWithSilence = FALSE; } //----------------------------------------------------------------------------- // Name: CStreamingSound::~CStreamingSound() // Desc: Destroys the class //----------------------------------------------------------------------------- CStreamingSound::~CStreamingSound() { } //----------------------------------------------------------------------------- // Name: CStreamingSound::HandleWaveStreamNotification() // Desc: Handle the notification that tells us to put more wav data in the // circular buffer //----------------------------------------------------------------------------- HRESULT CStreamingSound::HandleWaveStreamNotification( BOOL bLoopedPlay ) { HRESULT hr; DWORD dwCurrentPlayPos; DWORD dwPlayDelta; DWORD dwBytesWrittenToBuffer; VOID* pDSLockedBuffer = NULL; VOID* pDSLockedBuffer2 = NULL; DWORD dwDSLockedBufferSize; DWORD dwDSLockedBufferSize2; if( m_apDSBuffer == NULL || m_pWaveFile == NULL ) return CO_E_NOTINITIALIZED; // Restore the buffer if it was lost BOOL bRestored; if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) ) return DXUT_ERR( L"RestoreBuffer", hr ); if( bRestored ) { // The buffer was restored, so we need to fill it with new data if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) ) return DXUT_ERR( L"FillBufferWithSound", hr ); return S_OK; } // Lock the DirectSound buffer if( FAILED( hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) ) return DXUT_ERR( L"Lock", hr ); // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize, // it should the second buffer, so it should never be valid if( pDSLockedBuffer2 != NULL ) return E_UNEXPECTED; if( !m_bFillNextNotificationWithSilence ) { // Fill the DirectSound buffer with wav data if( FAILED( hr = m_pWaveFile->Read( (BYTE*) pDSLockedBuffer, dwDSLockedBufferSize, &dwBytesWrittenToBuffer ) ) ) return DXUT_ERR( L"Read", hr ); } else { // Fill the DirectSound buffer with silence FillMemory( pDSLockedBuffer, dwDSLockedBufferSize, (BYTE)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); dwBytesWrittenToBuffer = dwDSLockedBufferSize; } // If the number of bytes written is less than the // amount we requested, we have a short file. if( dwBytesWrittenToBuffer < dwDSLockedBufferSize ) { if( !bLoopedPlay ) { // Fill in silence for the rest of the buffer. FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer, dwDSLockedBufferSize - dwBytesWrittenToBuffer, (BYTE)(m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ); // Any future notifications should just fill the buffer with silence m_bFillNextNotificationWithSilence = TRUE; } else { // We are looping, so reset the file and fill the buffer with wav data DWORD dwReadSoFar = dwBytesWrittenToBuffer; // From previous call above. while( dwReadSoFar < dwDSLockedBufferSize ) { // This will keep reading in until the buffer is full (for very short files). if( FAILED( hr = m_pWaveFile->ResetFile() ) ) return DXUT_ERR( L"ResetFile", hr ); if( FAILED( hr = m_pWaveFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwBytesWrittenToBuffer ) ) ) return DXUT_ERR( L"Read", hr ); dwReadSoFar += dwBytesWrittenToBuffer; } } } // Unlock the DirectSound buffer m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); // Figure out how much data has been played so far. When we have played // past the end of the file, we will either need to start filling the // buffer with silence or starting reading from the beginning of the file, // depending if the user wants to loop the sound if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) ) return DXUT_ERR( L"GetCurrentPosition", hr ); // Check to see if the position counter looped if( dwCurrentPlayPos < m_dwLastPlayPos ) dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos; else dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos; m_dwPlayProgress += dwPlayDelta; m_dwLastPlayPos = dwCurrentPlayPos; // If we are now filling the buffer with silence, then we have found the end so // check to see if the entire sound has played, if it has then stop the buffer. if( m_bFillNextNotificationWithSilence ) { // We don't want to cut off the sound before it's done playing. if( m_dwPlayProgress >= m_pWaveFile->GetSize() ) { m_apDSBuffer[0]->Stop(); } } // Update where the buffer will lock (for next time) m_dwNextWriteOffset += dwDSLockedBufferSize; m_dwNextWriteOffset %= m_dwDSBufferSize; // Circular buffer return S_OK; } //----------------------------------------------------------------------------- // Name: CStreamingSound::Reset() // Desc: Resets the sound so it will begin playing at the beginning //----------------------------------------------------------------------------- HRESULT CStreamingSound::Reset() { HRESULT hr; if( m_apDSBuffer[0] == NULL || m_pWaveFile == NULL ) return CO_E_NOTINITIALIZED; m_dwLastPlayPos = 0; m_dwPlayProgress = 0; m_dwNextWriteOffset = 0; m_bFillNextNotificationWithSilence = FALSE; // Restore the buffer if it was lost BOOL bRestored; if( FAILED( hr = RestoreBuffer( m_apDSBuffer[0], &bRestored ) ) ) return DXUT_ERR( L"RestoreBuffer", hr ); if( bRestored ) { // The buffer was restored, so we need to fill it with new data if( FAILED( hr = FillBufferWithSound( m_apDSBuffer[0], FALSE ) ) ) return DXUT_ERR( L"FillBufferWithSound", hr ); } m_pWaveFile->ResetFile(); return m_apDSBuffer[0]->SetCurrentPosition( 0L ); } //----------------------------------------------------------------------------- // Name: CWaveFile::CWaveFile() // Desc: Constructs the class. Call Open() to open a wave file for reading. // Then call Read() as needed. Calling the destructor or Close() // will close the file. //----------------------------------------------------------------------------- CWaveFile::CWaveFile() { m_pwfx = NULL; m_hmmio = NULL; m_pResourceBuffer = NULL; m_dwSize = 0; m_bIsReadingFromMemory = FALSE; } //----------------------------------------------------------------------------- // Name: CWaveFile::~CWaveFile() // Desc: Destructs the class //----------------------------------------------------------------------------- CWaveFile::~CWaveFile() { Close(); if( !m_bIsReadingFromMemory ) SAFE_DELETE_ARRAY( m_pwfx ); } //----------------------------------------------------------------------------- // Name: CWaveFile::Open() // Desc: Opens a wave file for reading //----------------------------------------------------------------------------- HRESULT CWaveFile::Open( LPWSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ) { HRESULT hr; m_dwFlags = dwFlags; m_bIsReadingFromMemory = FALSE; if( m_dwFlags == WAVEFILE_READ ) { if( strFileName == NULL ) return E_INVALIDARG; SAFE_DELETE_ARRAY( m_pwfx ); m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ ); if( NULL == m_hmmio ) { HRSRC hResInfo; HGLOBAL hResData; DWORD dwSize; VOID* pvRes; // Loading it as a file failed, so try it as a resource if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAVE" ) ) ) { if( NULL == ( hResInfo = FindResource( NULL, strFileName, L"WAV" ) ) ) return DXUT_ERR( L"FindResource", E_FAIL ); } if( NULL == ( hResData = LoadResource( GetModuleHandle(NULL), hResInfo ) ) ) return DXUT_ERR( L"LoadResource", E_FAIL ); if( 0 == ( dwSize = SizeofResource( GetModuleHandle(NULL), hResInfo ) ) ) return DXUT_ERR( L"SizeofResource", E_FAIL ); if( NULL == ( pvRes = LockResource( hResData ) ) ) return DXUT_ERR( L"LockResource", E_FAIL ); m_pResourceBuffer = new CHAR[ dwSize ]; if( m_pResourceBuffer == NULL ) return DXUT_ERR( L"new", E_OUTOFMEMORY ); memcpy( m_pResourceBuffer, pvRes, dwSize ); MMIOINFO mmioInfo; ZeroMemory( &mmioInfo, sizeof(mmioInfo) ); mmioInfo.fccIOProc = FOURCC_MEM; mmioInfo.cchBuffer = dwSize; mmioInfo.pchBuffer = (CHAR*) m_pResourceBuffer; m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ ); } if( FAILED( hr = ReadMMIO() ) ) { // ReadMMIO will fail if its an not a wave file mmioClose( m_hmmio, 0 ); return DXUT_ERR( L"ReadMMIO", hr ); } if( FAILED( hr = ResetFile() ) ) return DXUT_ERR( L"ResetFile", hr ); // After the reset, the size of the wav file is m_ck.cksize so store it now m_dwSize = m_ck.cksize; } else { m_hmmio = mmioOpen( strFileName, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE ); if( NULL == m_hmmio ) return DXUT_ERR( L"mmioOpen", E_FAIL ); if( FAILED( hr = WriteMMIO( pwfx ) ) ) { mmioClose( m_hmmio, 0 ); return DXUT_ERR( L"WriteMMIO", hr ); } if( FAILED( hr = ResetFile() ) ) return DXUT_ERR( L"ResetFile", hr ); } return hr; } //----------------------------------------------------------------------------- // Name: CWaveFile::OpenFromMemory() // Desc: copy data to CWaveFile member variable from memory //----------------------------------------------------------------------------- HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX* pwfx, DWORD dwFlags ) { m_pwfx = pwfx; m_ulDataSize = ulDataSize; m_pbData = pbData; m_pbDataCur = m_pbData; m_bIsReadingFromMemory = TRUE; if( dwFlags != WAVEFILE_READ ) return E_NOTIMPL; return S_OK; } //----------------------------------------------------------------------------- // Name: CWaveFile::ReadMMIO() // Desc: Support function for reading from a multimedia I/O stream. // m_hmmio must be valid before calling. This function uses it to // update m_ckRiff, and m_pwfx. //----------------------------------------------------------------------------- HRESULT CWaveFile::ReadMMIO() { MMCKINFO ckIn; // chunk info. for general use. PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in. m_pwfx = NULL; if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) ) return DXUT_ERR( L"mmioDescend", E_FAIL ); // Check to make sure this is a valid wave file if( (m_ckRiff.ckid != FOURCC_RIFF) || (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) ) return DXUT_ERR( L"mmioFOURCC", E_FAIL ); // Search the input file for for the 'fmt ' chunk. ckIn.ckid = mmioFOURCC('f', 'm', 't', ' '); if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) ) return DXUT_ERR( L"mmioDescend", E_FAIL ); // Expect the 'fmt' chunk to be at least as large as ; // if there are extra parameters at the end, we'll ignore them if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) ) return DXUT_ERR( L"sizeof(PCMWAVEFORMAT)", E_FAIL ); // Read the 'fmt ' chunk into . if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) ) return DXUT_ERR( L"mmioRead", E_FAIL ); // Allocate the waveformatex, but if its not pcm format, read the next // word, and thats how many extra bytes to allocate. if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM ) { m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ]; if( NULL == m_pwfx ) return DXUT_ERR( L"m_pwfx", E_FAIL ); // Copy the bytes from the pcm structure to the waveformatex structure memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); m_pwfx->cbSize = 0; } else { // Read in length of extra bytes. WORD cbExtraBytes = 0L; if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) ) return DXUT_ERR( L"mmioRead", E_FAIL ); m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ]; if( NULL == m_pwfx ) return DXUT_ERR( L"new", E_FAIL ); // Copy the bytes from the pcm structure to the waveformatex structure memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); m_pwfx->cbSize = cbExtraBytes; // Now, read those extra bytes into the structure, if cbExtraAlloc != 0. if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)), cbExtraBytes ) != cbExtraBytes ) { SAFE_DELETE( m_pwfx ); return DXUT_ERR( L"mmioRead", E_FAIL ); } } // Ascend the input file out of the 'fmt ' chunk. if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ) { SAFE_DELETE( m_pwfx ); return DXUT_ERR( L"mmioAscend", E_FAIL ); } return S_OK; } //----------------------------------------------------------------------------- // Name: CWaveFile::GetSize() // Desc: Retuns the size of the read access wave file //----------------------------------------------------------------------------- DWORD CWaveFile::GetSize() { return m_dwSize; } //----------------------------------------------------------------------------- // Name: CWaveFile::ResetFile() // Desc: Resets the internal m_ck pointer so reading starts from the // beginning of the file again //----------------------------------------------------------------------------- HRESULT CWaveFile::ResetFile() { if( m_bIsReadingFromMemory ) { m_pbDataCur = m_pbData; } else { if( m_hmmio == NULL ) return CO_E_NOTINITIALIZED; if( m_dwFlags == WAVEFILE_READ ) { // Seek to the data if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC), SEEK_SET ) ) return DXUT_ERR( L"mmioSeek", E_FAIL ); // Search the input file for the 'data' chunk. m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) return DXUT_ERR( L"mmioDescend", E_FAIL ); } else { // Create the 'data' chunk that holds the waveform samples. m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); m_ck.cksize = 0; if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) return DXUT_ERR( L"mmioCreateChunk", E_FAIL ); if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) return DXUT_ERR( L"mmioGetInfo", E_FAIL ); } } return S_OK; } //----------------------------------------------------------------------------- // Name: CWaveFile::Read() // Desc: Reads section of data from a wave file into pBuffer and returns // how much read in pdwSizeRead, reading not more than dwSizeToRead. // This uses m_ck to determine where to start reading from. So // subsequent calls will be continue where the last left off unless // Reset() is called. //----------------------------------------------------------------------------- HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ) { if( m_bIsReadingFromMemory ) { if( m_pbDataCur == NULL ) return CO_E_NOTINITIALIZED; if( pdwSizeRead != NULL ) *pdwSizeRead = 0; if( (BYTE*)(m_pbDataCur + dwSizeToRead) > (BYTE*)(m_pbData + m_ulDataSize) ) { dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData); } CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead ); if( pdwSizeRead != NULL ) *pdwSizeRead = dwSizeToRead; return S_OK; } else { MMIOINFO mmioinfoIn; // current status of m_hmmio if( m_hmmio == NULL ) return CO_E_NOTINITIALIZED; if( pBuffer == NULL || pdwSizeRead == NULL ) return E_INVALIDARG; if( pdwSizeRead != NULL ) *pdwSizeRead = 0; if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) ) return DXUT_ERR( L"mmioGetInfo", E_FAIL ); UINT cbDataIn = dwSizeToRead; if( cbDataIn > m_ck.cksize ) cbDataIn = m_ck.cksize; m_ck.cksize -= cbDataIn; for( DWORD cT = 0; cT < cbDataIn; cT++ ) { // Copy the bytes from the io to the buffer. if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) { if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) return DXUT_ERR( L"mmioAdvance", E_FAIL ); if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) return DXUT_ERR( L"mmioinfoIn.pchNext", E_FAIL ); } // Actual copy. *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext); mmioinfoIn.pchNext++; } if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ) return DXUT_ERR( L"mmioSetInfo", E_FAIL ); if( pdwSizeRead != NULL ) *pdwSizeRead = cbDataIn; return S_OK; } } //----------------------------------------------------------------------------- // Name: CWaveFile::Close() // Desc: Closes the wave file //----------------------------------------------------------------------------- HRESULT CWaveFile::Close() { if( m_dwFlags == WAVEFILE_READ ) { mmioClose( m_hmmio, 0 ); m_hmmio = NULL; SAFE_DELETE_ARRAY( m_pResourceBuffer ); } else { m_mmioinfoOut.dwFlags |= MMIO_DIRTY; if( m_hmmio == NULL ) return CO_E_NOTINITIALIZED; if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) return DXUT_ERR( L"mmioSetInfo", E_FAIL ); // Ascend the output file out of the 'data' chunk -- this will cause // the chunk size of the 'data' chunk to be written. if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) return DXUT_ERR( L"mmioAscend", E_FAIL ); // Do this here instead... if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) return DXUT_ERR( L"mmioAscend", E_FAIL ); mmioSeek( m_hmmio, 0, SEEK_SET ); if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) return DXUT_ERR( L"mmioDescend", E_FAIL ); m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't'); if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) { DWORD dwSamples = 0; mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) ); mmioAscend( m_hmmio, &m_ck, 0 ); } // Ascend the output file out of the 'RIFF' chunk -- this will cause // the chunk size of the 'RIFF' chunk to be written. if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) return DXUT_ERR( L"mmioAscend", E_FAIL ); mmioClose( m_hmmio, 0 ); m_hmmio = NULL; } return S_OK; } //----------------------------------------------------------------------------- // Name: CWaveFile::WriteMMIO() // Desc: Support function for reading from a multimedia I/O stream // pwfxDest is the WAVEFORMATEX for this new wave file. // m_hmmio must be valid before calling. This function uses it to // update m_ckRiff, and m_ck. //----------------------------------------------------------------------------- HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest ) { DWORD dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile. MMCKINFO ckOut1; dwFactChunk = (DWORD)-1; // Create the output file RIFF chunk of form type 'WAVE'. m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E'); m_ckRiff.cksize = 0; if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) ) return DXUT_ERR( L"mmioCreateChunk", E_FAIL ); // We are now descended into the 'RIFF' chunk we just created. // Now create the 'fmt ' chunk. Since we know the size of this chunk, // specify it in the MMCKINFO structure so MMIO doesn't have to seek // back and set the chunk size after ascending from the chunk. m_ck.ckid = mmioFOURCC('f', 'm', 't', ' '); m_ck.cksize = sizeof(PCMWAVEFORMAT); if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) return DXUT_ERR( L"mmioCreateChunk", E_FAIL ); // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type. if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM ) { if( mmioWrite( m_hmmio, (HPSTR) pwfxDest, sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT)) return DXUT_ERR( L"mmioWrite", E_FAIL ); } else { // Write the variable length size. if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest, sizeof(*pwfxDest) + pwfxDest->cbSize ) != ( sizeof(*pwfxDest) + pwfxDest->cbSize ) ) return DXUT_ERR( L"mmioWrite", E_FAIL ); } // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk. if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) return DXUT_ERR( L"mmioAscend", E_FAIL ); // Now create the fact chunk, not required for PCM but nice to have. This is filled // in when the close routine is called. ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't'); ckOut1.cksize = 0; if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) ) return DXUT_ERR( L"mmioCreateChunk", E_FAIL ); if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) != sizeof(dwFactChunk) ) return DXUT_ERR( L"mmioWrite", E_FAIL ); // Now ascend out of the fact chunk... if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) ) return DXUT_ERR( L"mmioAscend", E_FAIL ); return S_OK; } //----------------------------------------------------------------------------- // Name: CWaveFile::Write() // Desc: Writes data to the open wave file //----------------------------------------------------------------------------- HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote ) { UINT cT; if( m_bIsReadingFromMemory ) return E_NOTIMPL; if( m_hmmio == NULL ) return CO_E_NOTINITIALIZED; if( pnSizeWrote == NULL || pbSrcData == NULL ) return E_INVALIDARG; *pnSizeWrote = 0; for( cT = 0; cT < nSizeToWrite; cT++ ) { if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite ) { m_mmioinfoOut.dwFlags |= MMIO_DIRTY; if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) ) return DXUT_ERR( L"mmioAdvance", E_FAIL ); } *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT); (BYTE*)m_mmioinfoOut.pchNext++; (*pnSizeWrote)++; } return S_OK; }