/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "../../idLib.h" #include "../../idAudio.h" #include "../../../mssdk/include/dxerr8.h" #include "../../../mssdk/include/dsound.h" #include "eax.h" #undef DEFINE_GUID #define DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8 ) \ EXTERN_C const GUID name \ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } DEFINE_GUID( CLSID_EAXDirectSound8, 0xca503b60, 0xb176, 0x11d4, 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc ); //----------------------------------------------------------------------------- // Name: idAudioHardware::idAudioHardware() // Desc: Constructs the class //----------------------------------------------------------------------------- idAudioHardware::idAudioHardware() { CoInitialize( 0 ); m_pDS = NULL; pDSBPrimary = NULL; pEAXListener = NULL; eax = false; } //----------------------------------------------------------------------------- // Name: idAudioHardware::~idAudioHardware() // Desc: Destroys the class //----------------------------------------------------------------------------- idAudioHardware::~idAudioHardware() { if ( pEAXListener ) { IKsPropertySet_Release( pEAXListener ); } SAFE_RELEASE( pDSBPrimary ); SAFE_RELEASE( m_pDS ); CoUninitialize(); } //----------------------------------------------------------------------------- // Name: idAudioHardware::Initialize() // Desc: Initializes the IDirectSound object and also sets the primary buffer // format. This function must be called before any others. //----------------------------------------------------------------------------- int idAudioHardware::Initialize( dword dwCoopLevel, dword dwPrimaryChannels, dword dwPrimaryFreq, dword dwPrimaryBitRate ) { int hr; SAFE_RELEASE( pDSBPrimary ); SAFE_RELEASE( m_pDS ); hr = CoCreateInstance( CLSID_EAXDirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (void**)&m_pDS ); if ( FAILED( hr ) ) { // Failed to initialize eax.dll, if this happens then we can fallback to use Direct Sound if ( FAILED( hr = CoCreateInstance( CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (void**)&m_pDS ) ) ) { Com_Error( ERR_DROP, TEXT( "DirectSoundCreate" ) ); } } // Need to call the initialize function on Direct Sound when creating through CoCreateInstance instead of // using the DirectSoundCreate call. if ( m_pDS->Initialize( NULL ) != DS_OK ) { Com_Error( ERR_DROP, TEXT( "Initialize" ), hr ); } // Set DirectSound coop level if ( FAILED( hr = m_pDS->SetCooperativeLevel( g_wv.hWnd, dwCoopLevel ) ) ) { Com_Error( ERR_DROP, TEXT( "SetCooperativeLevel" ), hr ); } // Obtain primary buffer, asking it for 3D control // Get the primary buffer DSBUFFERDESC dsbdesc; memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) ); dsbdesc.dwSize = sizeof( DSBUFFERDESC ); dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER; if ( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) ) { Com_Error( ERR_DROP, TEXT( "Initialize" ), hr ); } // Set primary buffer format SetPrimaryBufferFormat( dwPrimaryChannels, dwPrimaryFreq, dwPrimaryBitRate ); return S_OK; } //----------------------------------------------------------------------------- // Name: idAudioHardware::SetPrimaryBufferFormat() // Desc: Set primary buffer to a specified format // For example, to set the primary buffer format to 22kHz stereo, 16-bit // then: dwPrimaryChannels = 2 // dwPrimaryFreq = 22050, // dwPrimaryBitRate = 16 //----------------------------------------------------------------------------- int idAudioHardware::SetPrimaryBufferFormat( dword dwPrimaryChannels, dword dwPrimaryFreq, dword dwPrimaryBitRate ) { int hr; if ( m_pDS == NULL ) { return CO_E_NOTINITIALIZED; } WAVEFORMATEX wfx; memset( &wfx, 0, sizeof( wfx ) ); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = (WORD) dwPrimaryChannels; wfx.nSamplesPerSec = dwPrimaryFreq; wfx.wBitsPerSample = (WORD) dwPrimaryBitRate; wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if ( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) ) { Com_Error( ERR_DROP, TEXT( "SetFormat" ), hr ); } return S_OK; } //----------------------------------------------------------------------------- // Name: idAudioHardware::Get3DListenerInterface() // Desc: Returns the 3D listener interface associated with primary buffer. //----------------------------------------------------------------------------- int idAudioHardware::Get3DListenerInterface( LPDIRECTSOUND3DLISTENER8* ppDSListener ) { int hr; if ( ppDSListener == NULL ) { return E_INVALIDARG; } if ( m_pDS == NULL ) { return CO_E_NOTINITIALIZED; } *ppDSListener = NULL; if ( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener8, (void**)ppDSListener ) ) ) { Com_Error( ERR_DROP, TEXT( "QueryInterface" ), hr ); } return S_OK; } //----------------------------------------------------------------------------- // Name: idAudioHardware::Get3DListenerInterface() // Desc: Returns the 3D listener interface associated with primary buffer. //----------------------------------------------------------------------------- bool idAudioHardware::GetEAX( LPDIRECTSOUND3DBUFFER genBuf ) { static bool tried = false; if ( !eax && !tried ) { ULONG support = 0; tried = true; genBuf->QueryInterface( IID_IKsPropertySet, (void**)&pEAXListener ); if ( FAILED( pEAXListener->QuerySupport( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &support ) ) ) { return false; } if ( ( support & ( KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET ) ) != ( KSPROPERTY_SUPPORT_GET | KSPROPERTY_SUPPORT_SET ) ) { return false; } eax = true; } return eax; } //----------------------------------------------------------------------------- // Name: idAudioHardware::Create() // Desc: //----------------------------------------------------------------------------- int idAudioHardware::Create( idAudioBuffer** ppSound, const char* strWaveFileName, dword dwCreationFlags, GUID guid3DAlgorithm, dword dwNumBuffers, dword dwMaxNumBuffers ) { int hr; int hrRet = S_OK; dword i; LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; dword dwDSBufferSize = NULL; idWavefile* 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 idWavefile(); 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; memset( &dsbd, 0, sizeof( DSBUFFERDESC ) ); dsbd.dwSize = sizeof( DSBUFFERDESC ); dsbd.dwFlags = dwCreationFlags; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = (WAVEFORMATEX*)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 (100ms) and the buffer is created // with DSBCAPS_CTRLFX. if ( hr != DSERR_BUFFERTOOSMALL ) { Com_Error( ERR_DROP, TEXT( "CreateSoundBuffer" ), hr ); } goto LFail; } for ( i = 1; i < dwNumBuffers; i++ ) { if ( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) { Com_Error( ERR_DROP, TEXT( "DuplicateSoundBuffer" ), hr ); goto LFail; } } // Create the sound *ppSound = new idAudioBuffer( apDSBuffer, dwDSBufferSize, dwNumBuffers, dwMaxNumBuffers, pWaveFile ); pWaveFile->Close(); SAFE_DELETE( apDSBuffer ); return hrRet; LFail: // Cleanup SAFE_DELETE( pWaveFile ); SAFE_DELETE( apDSBuffer ); return hr; } //----------------------------------------------------------------------------- // Name: idAudioHardware::CreateFromMemory() // Desc: //----------------------------------------------------------------------------- int idAudioHardware::CreateFromMemory( idAudioBuffer** ppSound, byte* pbData, ulong ulDataSize, waveformatex* pwfx, dword dwCreationFlags, GUID guid3DAlgorithm, dword dwNumBuffers ) { int hr; dword i; LPDIRECTSOUNDBUFFER* apDSBuffer = NULL; dword dwDSBufferSize = NULL; idWavefile* 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 idWavefile(); 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; memset( &dsbd, 0, sizeof( DSBUFFERDESC ) ); dsbd.dwSize = sizeof( DSBUFFERDESC ); dsbd.dwFlags = dwCreationFlags; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = (WAVEFORMATEX *)pwfx; if ( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer[0], NULL ) ) ) { Com_Error( ERR_DROP, TEXT( "CreateSoundBuffer" ), hr ); goto LFail; } for ( i = 1; i < dwNumBuffers; i++ ) { if ( FAILED( hr = m_pDS->DuplicateSoundBuffer( apDSBuffer[0], &apDSBuffer[i] ) ) ) { Com_Error( ERR_DROP, TEXT( "DuplicateSoundBuffer" ), hr ); goto LFail; } } // Create the sound *ppSound = new idAudioBuffer( apDSBuffer, dwDSBufferSize, dwNumBuffers, dwNumBuffers, pWaveFile ); SAFE_DELETE( apDSBuffer ); return S_OK; LFail: // Cleanup SAFE_DELETE( apDSBuffer ); return hr; } //----------------------------------------------------------------------------- // Name: idAudioHardware::CreateStreaming() // Desc: //----------------------------------------------------------------------------- int idAudioHardware::CreateStreaming( idStreamingBuffer** ppStreamingSound, const char* strIntroWaveFileName, const char* strWaveFileName, dword dwCreationFlags, GUID guid3DAlgorithm, bool useNotification, dword dwNotifyCount, dword dwNotifySize, HANDLE hNotifyEvent ) { int hr; if ( m_pDS == NULL ) { return CO_E_NOTINITIALIZED; } if ( strIntroWaveFileName == NULL || ppStreamingSound == NULL || ( useNotification && ( hNotifyEvent == NULL ) ) ) { return E_INVALIDARG; } LPDIRECTSOUNDBUFFER pDSBuffer = NULL; dword dwDSBufferSize = NULL; idWavefile* pWaveFile = NULL; idWavefile* qWaveFile = NULL; DSBPOSITIONNOTIFY* aPosNotify = NULL; LPDIRECTSOUNDNOTIFY pDSNotify = NULL; if ( strIntroWaveFileName ) { pWaveFile = new idWavefile(); pWaveFile->Open( strIntroWaveFileName, NULL, WAVEFILE_READ ); } if ( strWaveFileName && strcmp( strIntroWaveFileName, strWaveFileName ) ) { qWaveFile = new idWavefile(); qWaveFile->Open( strWaveFileName, NULL, WAVEFILE_READ ); } // Figure out how big the DSound 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; memset( &dsbd, 0, sizeof( DSBUFFERDESC ) ); dsbd.dwSize = sizeof( DSBUFFERDESC ); dsbd.dwFlags = dwCreationFlags | DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes = dwDSBufferSize; dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = (WAVEFORMATEX *)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 Com_Error( ERR_DROP, TEXT( "CreateSoundBuffer" ), hr ); } if ( useNotification ) { // 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( aPosNotify ); Com_Error( ERR_DROP, TEXT( "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( aPosNotify ); Com_Error( ERR_DROP, TEXT( "SetNotificationPositions" ), hr ); } SAFE_RELEASE( pDSNotify ); SAFE_DELETE( aPosNotify ); } // Create the sound *ppStreamingSound = new idStreamingBuffer( pDSBuffer, dwDSBufferSize, pWaveFile, qWaveFile, useNotification, dwNotifySize ); return S_OK; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::idAudioBuffer() // Desc: Constructs the class //----------------------------------------------------------------------------- idAudioBuffer::idAudioBuffer( LPDIRECTSOUNDBUFFER* apDSBuffer, dword dwDSBufferSize, dword dwNumBuffers, dword dwMaxNumBuffers, idWavefile* pWaveFile ) { dword i; m_pDS3DBuffer = NULL; name = NULL; m_apDSBuffer = new LPDIRECTSOUNDBUFFER[dwMaxNumBuffers]; for ( i = 0; i < dwNumBuffers; i++ ) { m_apDSBuffer[i] = apDSBuffer[i]; } m_dwDSBufferSize = dwDSBufferSize; m_dwNumBuffers = dwNumBuffers; m_dwMaxNumBuffers = dwMaxNumBuffers; m_pWaveFile = pWaveFile; FillBufferWithSound( m_apDSBuffer[0], false ); // Make DirectSound do pre-processing on sound effects for ( i = 0; i < dwNumBuffers; i++ ) { m_apDSBuffer[i]->SetCurrentPosition( 0 ); } for ( i = 0; i < MAX_CHANNELS; i++ ) { entnum[i] = 0; entchannel[i] = 0; flags[i] = 0; } } //----------------------------------------------------------------------------- // Name: idAudioBuffer::~idAudioBuffer() // Desc: Destroys the class //----------------------------------------------------------------------------- idAudioBuffer::~idAudioBuffer() { for ( dword i = 0; i < m_dwNumBuffers; i++ ) { SAFE_RELEASE( m_apDSBuffer[i] ); if ( m_pDS3DBuffer ) { SAFE_RELEASE( m_pDS3DBuffer[i] ); } } SAFE_DELETE_ARRAY( m_apDSBuffer ); SAFE_DELETE_ARRAY( m_pDS3DBuffer ); SAFE_DELETE( m_pWaveFile ); SAFE_DELETE( name ); } //----------------------------------------------------------------------------- // Name: idAudioBuffer::FillBufferWithSound() // Desc: Fills a DirectSound buffer with a sound file //----------------------------------------------------------------------------- int idAudioBuffer::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, bool bRepeatWavIfBufferLarger ) { int hr; void* pDSLockedBuffer = NULL; // Pointer to locked buffer memory dword dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer dword dwWavDataRead = 0; // Amount of data read from the wav file if ( pDSB == NULL ) { return CO_E_NOTINITIALIZED; } // Make sure we have focus, and we didn't just switch in from // an app which had a DirectSound device if ( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) { Com_Error( ERR_DROP, TEXT( "RestoreBuffer" ), hr ); } // Lock the buffer down if ( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) ) { Com_Error( ERR_DROP, TEXT( "Lock" ), hr ); } // Reset the wave file to the beginning m_pWaveFile->ResetFile(); if ( FAILED( hr = m_pWaveFile->Read( (byte*) pDSLockedBuffer, dwDSLockedBufferSize, &dwWavDataRead ) ) ) { Com_Error( ERR_DROP, TEXT( "Read" ), hr ); } if ( dwWavDataRead == 0 ) { // Wav is blank, so just fill with silence memset( pDSLockedBuffer, (byte)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize ); } 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() ) ) { Com_Error( ERR_DROP, TEXT( "ResetFile" ), hr ); } hr = m_pWaveFile->Read( (byte*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwWavDataRead ); if ( FAILED( hr ) ) { Com_Error( ERR_DROP, TEXT( "Read" ), hr ); } dwReadSoFar += dwWavDataRead; } } else { // Don't repeat the wav file, just fill in silence memset( (byte*) pDSLockedBuffer + dwWavDataRead, (byte)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize - dwWavDataRead ); } } // Unlock the buffer, we don't need it anymore. pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); return S_OK; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::RestoreBuffer() // Desc: Restores the lost buffer. *pbWasRestored returns qtrue if the buffer was // restored. It can also NULL if the information is not needed. //----------------------------------------------------------------------------- int idAudioBuffer::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, bool* pbWasRestored ) { int hr; if ( pDSB == NULL ) { return CO_E_NOTINITIALIZED; } if ( pbWasRestored ) { *pbWasRestored = false; } dword dwStatus; if ( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) { Com_Error( ERR_DROP, TEXT( "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() ); if ( pbWasRestored != NULL ) { *pbWasRestored = qtrue; } return S_OK; } else { return S_FALSE; } } //----------------------------------------------------------------------------- // Name: idAudioBuffer::GetFreeBuffer() // Desc: Checks to see if a buffer is playing and returns qtrue if it is. //----------------------------------------------------------------------------- LPDIRECTSOUNDBUFFER idAudioBuffer::GetFreeBuffer() { bool bIsPlaying = false; if ( m_apDSBuffer == NULL ) { return NULL; } for ( dword i = 0; i < m_dwNumBuffers; i++ ) { if ( m_apDSBuffer[i] ) { dword dwStatus = 0; m_apDSBuffer[i]->GetStatus( &dwStatus ); if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) { break; } } } if ( i != m_dwNumBuffers ) { return m_apDSBuffer[ i ]; } else { return m_apDSBuffer[ rand() % m_dwNumBuffers ]; } } //----------------------------------------------------------------------------- // Name: idAudioBuffer::GetBuffer() // Desc: //----------------------------------------------------------------------------- LPDIRECTSOUNDBUFFER idAudioBuffer::GetBuffer( dword dwIndex ) { if ( m_apDSBuffer == NULL ) { return NULL; } if ( dwIndex >= m_dwNumBuffers ) { return NULL; } return m_apDSBuffer[dwIndex]; } LPDIRECTSOUND3DBUFFER idAudioBuffer::Get3DBuffer( dword dwIndex ) { if ( m_apDSBuffer == NULL ) { return NULL; } if ( dwIndex >= m_dwNumBuffers ) { return NULL; } return m_pDS3DBuffer[dwIndex]; } void idAudioBuffer::SetPosition( int index, float x, float y, float z ) { if ( m_pDS3DBuffer[index] ) { m_pDS3DBuffer[index]->SetPosition( x, y, z, DS3D_DEFERRED ); } } void idAudioBuffer::SetRange( int index, float min, float max ) { if ( m_pDS3DBuffer[index] ) { m_pDS3DBuffer[index]->SetMinDistance( min, DS3D_DEFERRED ); m_pDS3DBuffer[index]->SetMaxDistance( max, DS3D_DEFERRED ); } } void idAudioBuffer::SetVelocity( int index, float x, float y, float z ) { if ( m_pDS3DBuffer[index] ) { m_pDS3DBuffer[index]->SetVelocity( x, y, z, DS3D_DEFERRED ); } } void idAudioBuffer::SetVolume( int index, float x ) { if ( m_apDSBuffer[index] ) { if ( x < 0 ) { x = 0; } if ( x > 1 ) { x = 1; } x = ( 1.0 - x ) * DSBVOLUME_MIN; m_apDSBuffer[index]->SetVolume( x ); } } int idAudioBuffer::GetFreeIndex( void ) { bool bIsPlaying = false; if ( m_apDSBuffer == NULL ) { return -1; } for ( dword i = 0; i < m_dwNumBuffers; i++ ) { if ( m_apDSBuffer[i] ) { dword dwStatus = 0; m_apDSBuffer[i]->GetStatus( &dwStatus ); if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) { break; } } } if ( i != m_dwNumBuffers ) { return i; } else { int hr; if ( m_dwNumBuffers < m_dwMaxNumBuffers ) { DS3DBUFFER g_dsBufferParams; // 3D buffer properties LPDIRECTSOUND m_pDS = idSpeak->hw->GetDirectSound(); i = m_dwNumBuffers; if ( FAILED( hr = m_pDS->DuplicateSoundBuffer( m_apDSBuffer[0], &m_apDSBuffer[i] ) ) ) { Com_Error( ERR_DROP, TEXT( "DuplicateSoundBuffer" ), hr ); } m_apDSBuffer[i]->QueryInterface( IID_IDirectSound3DBuffer8, (void**)&m_pDS3DBuffer[i] ); // Get the 3D buffer parameters g_dsBufferParams.dwSize = sizeof( DS3DBUFFER ); m_pDS3DBuffer[0]->GetAllParameters( &g_dsBufferParams ); m_pDS3DBuffer[i]->SetAllParameters( &g_dsBufferParams, DS3D_DEFERRED ); m_dwNumBuffers++; return i; } } return -1; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::Get3DBufferInterfaces() // Desc: //----------------------------------------------------------------------------- void idAudioBuffer::Get3DBufferInterfaces( float minDistance, float maxDistance ) { int i; DS3DBUFFER g_dsBufferParams; // 3D buffer properties if ( m_apDSBuffer == NULL ) { return; } m_pDS3DBuffer = new LPDIRECTSOUND3DBUFFER[m_dwMaxNumBuffers]; for ( i = 0; i < m_dwNumBuffers; i++ ) { if ( m_apDSBuffer[i] ) { m_apDSBuffer[i]->QueryInterface( IID_IDirectSound3DBuffer8, (void**)&m_pDS3DBuffer[i] ); // Get the 3D buffer parameters g_dsBufferParams.dwSize = sizeof( DS3DBUFFER ); m_pDS3DBuffer[i]->GetAllParameters( &g_dsBufferParams ); // Set new 3D buffer parameters g_dsBufferParams.dwMode = DS3DMODE_NORMAL; g_dsBufferParams.flMinDistance = minDistance; g_dsBufferParams.flMaxDistance = maxDistance; m_pDS3DBuffer[i]->SetAllParameters( &g_dsBufferParams, DS3D_DEFERRED ); } } } void idAudioBuffer::Make2D( dword index ) { if ( m_apDSBuffer == NULL ) { return ; } if ( index >= m_dwNumBuffers ) { return; } if ( m_pDS3DBuffer[index] ) { m_pDS3DBuffer[index]->SetMode( DS3DMODE_DISABLE, DS3D_DEFERRED ); } } //----------------------------------------------------------------------------- // Name: idAudioBuffer::Play() // Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING // in the dwFlags to loop the sound //----------------------------------------------------------------------------- int idAudioBuffer::Play( dword index, dword dwPriority, dword dwFlags ) { int hr; bool bRestored; if ( m_apDSBuffer == NULL ) { return CO_E_NOTINITIALIZED; } if ( index >= m_dwNumBuffers ) { return 0; } LPDIRECTSOUNDBUFFER pDSB = GetBuffer( index ); if ( pDSB == NULL ) { Com_Error( ERR_DROP, TEXT( "GetFreeBuffer" ), E_FAIL ); } // Restore the buffer if it was lost if ( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) ) { Com_Error( ERR_DROP, TEXT( "RestoreBuffer" ), hr ); } if ( bRestored ) { // The buffer was restored, so we need to fill it with new data if ( FAILED( hr = FillBufferWithSound( pDSB, false ) ) ) { Com_Error( ERR_DROP, TEXT( "FillBufferWithSound" ), hr ); } // Make DirectSound do pre-processing on sound effects Reset(); } return pDSB->Play( 0, dwPriority, dwFlags ); } //----------------------------------------------------------------------------- // Name: idAudioBuffer::Stop() // Desc: Stops the sound from playing //----------------------------------------------------------------------------- int idAudioBuffer::Stop( dword index ) { if ( this == NULL || m_apDSBuffer == NULL ) { return CO_E_NOTINITIALIZED; } int hr = 0; if ( index < m_dwNumBuffers ) { hr = m_apDSBuffer[index]->Stop(); } return hr; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::Reset() // Desc: Reset all of the sound buffers //----------------------------------------------------------------------------- int idAudioBuffer::Reset() { if ( m_apDSBuffer == NULL ) { return CO_E_NOTINITIALIZED; } int hr = 0; for ( dword i = 0; i < m_dwNumBuffers; i++ ) hr |= m_apDSBuffer[i]->SetCurrentPosition( 0 ); return hr; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::IsSoundPlaying() // Desc: Checks to see if a buffer is playing and returns qtrue if it is. //----------------------------------------------------------------------------- bool idAudioBuffer::IsSoundPlaying() { bool bIsPlaying = false; if ( m_apDSBuffer == NULL ) { return false; } for ( dword i = 0; i < m_dwNumBuffers; i++ ) { if ( m_apDSBuffer[i] ) { dword dwStatus = 0; m_apDSBuffer[i]->GetStatus( &dwStatus ); if ( dwStatus & DSBSTATUS_PLAYING ) { bIsPlaying = true; break; } } } return bIsPlaying; } //----------------------------------------------------------------------------- // Name: idAudioBuffer::IsSoundPlaying() // Desc: Checks to see if a buffer is playing and returns qtrue if it is. //----------------------------------------------------------------------------- bool idAudioBuffer::AllChannelsPlaying() { if ( m_apDSBuffer == NULL ) { return false; } bool bIsPlaying = true; for ( dword i = 0; i < m_dwNumBuffers; i++ ) { if ( m_apDSBuffer[i] ) { dword dwStatus = 0; m_apDSBuffer[i]->GetStatus( &dwStatus ); if ( !( dwStatus & DSBSTATUS_PLAYING ) ) { bIsPlaying = false; break; } } } return bIsPlaying; } //----------------------------------------------------------------------------- // Name: idStreamingBuffer::idStreamingBuffer() // Desc: Setups up a buffer so data can be streamed from the wave file into // 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() //----------------------------------------------------------------------------- idStreamingBuffer::idStreamingBuffer( LPDIRECTSOUNDBUFFER pDSBuffer, dword dwDSBufferSize, idWavefile* pWaveFile, idWavefile* qWaveFile, bool useNotification, dword dwNotifySize ) : idAudioBuffer( &pDSBuffer, dwDSBufferSize, 1, 1, pWaveFile ) { m_pWaveFile = pWaveFile; m_qWaveFile = qWaveFile; m_dwLastPlayPos = 0; m_dwPlayProgress = 0; m_dwNotifySize = dwNotifySize; m_dwNextWriteOffset = 0; m_dwNextEvent = Com_Milliseconds() + ( m_dwNotifySize * 1000 ) / ( m_pWaveFile->m_pwfx->nAvgBytesPerSec ); m_bFillNextNotificationWithSilence = false; m_bUsingNotification = useNotification; } //----------------------------------------------------------------------------- // Name: idStreamingBuffer::~idStreamingBuffer() // Desc: Destroys the class //----------------------------------------------------------------------------- idStreamingBuffer::~idStreamingBuffer() { SAFE_DELETE( m_pWaveFile ); SAFE_DELETE( m_qWaveFile ); } //----------------------------------------------------------------------------- // Name: idStreamingBuffer::HandleWaveStreamNotification() // Desc: Handle the notification that tell us to put more wav data in the // circular buffer //----------------------------------------------------------------------------- int idStreamingBuffer::HandleWaveStreamNotification() { if ( !m_bUsingNotification ) { int dwRead = Com_Milliseconds(); if ( dwRead < m_dwNextEvent ) { return S_OK; } m_dwNextEvent = dwRead + ( m_dwNotifySize * 1000 ) / ( m_pWaveFile->m_pwfx->nAvgBytesPerSec ); } int 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 ) ) ) { Com_Error( ERR_DROP, TEXT( "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 ) ) ) { Com_Error( ERR_DROP, TEXT( "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 ) ) ) { Com_Error( ERR_DROP, TEXT( "Lock" ), hr ); } // m_dwDSBufferSize and m_dwNextWriteOffset are both multiples of m_dwNotifySize, // it should the second buffer 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 ) ) ) { Com_Error( ERR_DROP, TEXT( "Read" ), hr ); } } else { // Fill the DirectSound buffer with silence memset( pDSLockedBuffer, (byte)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize ); dwBytesWrittenToBuffer = dwDSLockedBufferSize; } // If the number of bytes written is less than the // amount we requested, we have a short file. if ( dwBytesWrittenToBuffer < dwDSLockedBufferSize ) { if ( !looping ) { // Fill in silence for the rest of the buffer. memset( (byte*) pDSLockedBuffer + dwBytesWrittenToBuffer, (byte)( m_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize - dwBytesWrittenToBuffer ); // Any future notifications should just fill the buffer with silence m_bFillNextNotificationWithSilence = qtrue; } 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 ) { if ( m_qWaveFile != NULL ) { m_pWaveFile->Close(); m_pWaveFile = m_qWaveFile; m_qWaveFile = NULL; } // This will keep reading in until the buffer is full (for very short files). if ( FAILED( hr = m_pWaveFile->ResetFile() ) ) { Com_Error( ERR_DROP, TEXT( "ResetFile" ), hr ); } if ( FAILED( hr = m_pWaveFile->Read( (byte*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwBytesWrittenToBuffer ) ) ) { Com_Error( ERR_DROP, TEXT( "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 // passed 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 ) ) ) { Com_Error( ERR_DROP, TEXT( "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: idStreamingBuffer::Reset() // Desc: Resets the sound so it will begin playing at the beginning //----------------------------------------------------------------------------- int idStreamingBuffer::Reset() { int 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 ) ) ) { Com_Error( ERR_DROP, TEXT( "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 ) ) ) { Com_Error( ERR_DROP, TEXT( "FillBufferWithSound" ), hr ); } } m_pWaveFile->ResetFile(); return m_apDSBuffer[0]->SetCurrentPosition( 0L ); } //----------------------------------------------------------------------------- // Name: idWavefile::idWavefile() // 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. //----------------------------------------------------------------------------- idWavefile::idWavefile() { m_pwfx = NULL; m_hmmio = -1; m_dwSize = 0; m_bIsReadingFromMemory = false; } //----------------------------------------------------------------------------- // Name: idWavefile::~idWavefile() // Desc: Destructs the class //----------------------------------------------------------------------------- idWavefile::~idWavefile() { Close(); if ( !m_bIsReadingFromMemory ) { SAFE_DELETE_ARRAY( m_pwfx ); } } //----------------------------------------------------------------------------- // Name: idWavefile::Open() // Desc: Opens a wave file for reading //----------------------------------------------------------------------------- int idWavefile::Open( const char* strFileName, waveformatex* pwfx, dword dwFlags ) { int hr; m_dwFlags = dwFlags; m_bIsReadingFromMemory = false; if ( m_dwFlags == WAVEFILE_READ ) { if ( strFileName == NULL ) { return E_INVALIDARG; } SAFE_DELETE_ARRAY( m_pwfx ); m_dwSize = FS_FOpenFileRead( strFileName, &m_hmmio, qtrue ); if ( m_dwSize == 0xffffffff ) { m_dwSize = 0; return E_FAIL; } if ( FAILED( hr = ReadMMIO() ) ) { // ReadMMIO will fail if its an not a wave file FS_FCloseFile( m_hmmio ); Com_Error( ERR_DROP, TEXT( "ReadMMIO" ), hr ); } if ( FAILED( hr = ResetFile() ) ) { Com_Error( ERR_DROP, TEXT( "ResetFile" ), hr ); } // After the reset, the size of the wav file is m_ck.cksize so store it now m_dwSize = m_ck.cksize; } if ( m_dwSize != -1 ) { return S_OK; } return E_FAIL; } //----------------------------------------------------------------------------- // Name: idWavefile::OpenFromMemory() // Desc: copy data to idWavefile member variable from memory //----------------------------------------------------------------------------- int idWavefile::OpenFromMemory( byte* pbData, ulong ulDataSize, waveformatex* pwfx, dword dwFlags ) { m_pwfx = pwfx; m_ulDataSize = ulDataSize; m_pbData = pbData; m_pbDataCur = m_pbData; m_bIsReadingFromMemory = qtrue; if ( dwFlags != WAVEFILE_READ ) { return E_NOTIMPL; } return S_OK; } //----------------------------------------------------------------------------- // Name: idWavefile::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. //----------------------------------------------------------------------------- int idWavefile::ReadMMIO() { mminfo ckIn; // chunk info. for general use. PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in. m_pwfx = NULL; FS_Read( &m_ckRiff, 12, m_hmmio ); m_ckRiff.dwDataOffset = 12; // Check to make sure this is a valid wave file if ( ( m_ckRiff.ckid != FOURCC_RIFF ) || ( m_ckRiff.fccType != mmioFOURCC( 'W', 'A', 'V', 'E' ) ) ) { Com_Error( ERR_DROP, TEXT( "mmioFOURCC" ), E_FAIL ); } // Search the input file for for the 'fmt ' chunk. ckIn.dwDataOffset = 12; do { if ( 8 != FS_Read( &ckIn, 8, m_hmmio ) ) { Com_Error( ERR_DROP, TEXT( "mmioDescend" ), E_FAIL ); } ckIn.dwDataOffset += ckIn.cksize - 8; } while ( ckIn.ckid != mmioFOURCC( 'f', 'm', 't', ' ' ) ); // 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 ) ) { Com_Error( ERR_DROP, TEXT( "sizeof(PCMWAVEFORMAT)" ), E_FAIL ); } // Read the 'fmt ' chunk into . if ( FS_Read( (HPSTR) &pcmWaveFormat, sizeof( pcmWaveFormat ), m_hmmio ) != sizeof( pcmWaveFormat ) ) { Com_Error( ERR_DROP, TEXT( "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 ) { Com_Error( ERR_DROP, TEXT( "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 ( FS_Read( (CHAR*)&cbExtraBytes, sizeof( WORD ), m_hmmio ) != sizeof( WORD ) ) { Com_Error( ERR_DROP, TEXT( "mmioRead" ), E_FAIL ); } m_pwfx = (waveformatex*)new CHAR[ sizeof( waveformatex ) + cbExtraBytes ]; if ( NULL == m_pwfx ) { Com_Error( ERR_DROP, TEXT( "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 ( FS_Read( ( CHAR* )( ( (byte*)&( m_pwfx->cbSize ) ) + sizeof( WORD ) ), cbExtraBytes, m_hmmio ) != cbExtraBytes ) { SAFE_DELETE( m_pwfx ); Com_Error( ERR_DROP, TEXT( "mmioRead" ), E_FAIL ); } } return S_OK; } //----------------------------------------------------------------------------- // Name: idWavefile::GetSize() // Desc: Retuns the size of the read access wave file //----------------------------------------------------------------------------- dword idWavefile::GetSize() { return m_dwSize; } //----------------------------------------------------------------------------- // Name: idWavefile::ResetFile() // Desc: Resets the internal m_ck pointer so reading starts from the // beginning of the file again //----------------------------------------------------------------------------- int idWavefile::ResetFile() { if ( m_bIsReadingFromMemory ) { m_pbDataCur = m_pbData; } else { if ( m_hmmio == -1 ) { return CO_E_NOTINITIALIZED; } if ( m_dwFlags == WAVEFILE_READ ) { // Seek to the data if ( -1 == FS_Seek( m_hmmio, m_ckRiff.dwDataOffset + sizeof( FOURCC ), FS_SEEK_SET ) ) { Com_Error( ERR_DROP, TEXT( "mmioSeek" ), E_FAIL ); } // Search the input file for for the 'fmt ' chunk. do { if ( 4 != FS_Read( &m_ck, 4, m_hmmio ) ) { Com_Error( ERR_DROP, TEXT( "mmioDescend" ), E_FAIL ); } } while ( m_ck.ckid != mmioFOURCC( 'd', 'a', 't', 'a' ) ); FS_Read( &m_ck.cksize, 4, m_hmmio ); } } return S_OK; } //----------------------------------------------------------------------------- // Name: idWavefile::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. //----------------------------------------------------------------------------- int idWavefile::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 { if ( m_hmmio == -1 ) { return CO_E_NOTINITIALIZED; } if ( pBuffer == NULL || pdwSizeRead == NULL ) { return E_INVALIDARG; } if ( pdwSizeRead != NULL ) { *pdwSizeRead = 0; } int cbDataIn = dwSizeToRead; if ( cbDataIn > m_ck.cksize ) { cbDataIn = m_ck.cksize; } m_ck.cksize -= cbDataIn; *pdwSizeRead = FS_Read( pBuffer, cbDataIn, m_hmmio ); return S_OK; } } //----------------------------------------------------------------------------- // Name: idWavefile::Close() // Desc: Closes the wave file //----------------------------------------------------------------------------- int idWavefile::Close() { if ( m_dwFlags == WAVEFILE_READ && m_hmmio != -1 ) { FS_FCloseFile( m_hmmio ); m_hmmio = -1; } return S_OK; }