diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /soundsystem | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'soundsystem')
| -rw-r--r-- | soundsystem/snd_audio_source.cpp | 58 | ||||
| -rw-r--r-- | soundsystem/snd_dev_wave.cpp | 899 | ||||
| -rw-r--r-- | soundsystem/snd_dev_wave.h | 27 | ||||
| -rw-r--r-- | soundsystem/snd_io.cpp | 93 | ||||
| -rw-r--r-- | soundsystem/snd_wave_mixer.cpp | 527 | ||||
| -rw-r--r-- | soundsystem/snd_wave_mixer.h | 31 | ||||
| -rw-r--r-- | soundsystem/snd_wave_mixer_adpcm.cpp | 516 | ||||
| -rw-r--r-- | soundsystem/snd_wave_mixer_adpcm.h | 24 | ||||
| -rw-r--r-- | soundsystem/snd_wave_mixer_private.h | 103 | ||||
| -rw-r--r-- | soundsystem/snd_wave_source.cpp | 602 | ||||
| -rw-r--r-- | soundsystem/snd_wave_source.h | 76 | ||||
| -rw-r--r-- | soundsystem/soundsystem.cpp | 279 | ||||
| -rw-r--r-- | soundsystem/soundsystem.h | 35 | ||||
| -rw-r--r-- | soundsystem/soundsystem.vpc | 64 |
14 files changed, 3334 insertions, 0 deletions
diff --git a/soundsystem/snd_audio_source.cpp b/soundsystem/snd_audio_source.cpp new file mode 100644 index 0000000..fb4fe2b --- /dev/null +++ b/soundsystem/snd_audio_source.cpp @@ -0,0 +1,58 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include <stdio.h> +#include "soundsystem/snd_audio_source.h" +#include "soundsystem/isoundsystem.h" +#include "soundsystem.h" + + + +extern CAudioSource *Audio_CreateMemoryWave( const char *pName ); + +//----------------------------------------------------------------------------- +// Purpose: Simple wrapper to crack naming convention and create the proper wave source +// Input : *pName - WAVE filename +// Output : CAudioSource +//----------------------------------------------------------------------------- +CAudioSource *AudioSource_Create( const char *pName ) +{ + if ( !pName ) + return NULL; + +// if ( pName[0] == '!' ) // sentence + ; + + // Names that begin with "*" are streaming. + // Skip over the * and create a streamed source + if ( pName[0] == '*' ) + { + + return NULL; + } + + // These are loaded into memory directly + return Audio_CreateMemoryWave( pName ); +} + +CAudioSource::~CAudioSource( void ) +{ + CAudioMixer *mixer; + + while ( 1 ) + { + mixer = g_pSoundSystem->FindMixer( this ); + if ( !mixer ) + break; + + g_pSoundSystem->StopSound( mixer ); + } +} + +CAudioSource::CAudioSource( void ) +{ +} diff --git a/soundsystem/snd_dev_wave.cpp b/soundsystem/snd_dev_wave.cpp new file mode 100644 index 0000000..478f417 --- /dev/null +++ b/soundsystem/snd_dev_wave.cpp @@ -0,0 +1,899 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "snd_dev_wave.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#pragma warning( disable: 4201 ) +#include <mmsystem.h> +#pragma warning( default: 4201 ) +#include <stdio.h> +#include <math.h> +#include "soundsystem/snd_audio_source.h" +#include "soundsystem.h" +#include "soundsystem/snd_device.h" +#include "tier1/utlvector.h" +#include "filesystem.h" +#include "sentence.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CAudioMixer; + + +//----------------------------------------------------------------------------- +// Important constants +//----------------------------------------------------------------------------- + +// 64K is > 1 second at 16-bit, 22050 Hz +// 44k: UNDONE - need to double buffers now that we're playing back at 44100? +#define OUTPUT_CHANNEL_COUNT 2 +#define BYTES_PER_SAMPLE 2 +#define OUTPUT_SAMPLE_RATE SOUND_DMA_SPEED +#define OUTPUT_BUFFER_COUNT 64 +#define OUTPUT_BUFFER_MASK 0x3F +#define OUTPUT_BUFFER_SAMPLE_COUNT (OUTPUT_BUFFER_SIZE_BYTES / BYTES_PER_SAMPLE) +#define OUTPUT_BUFFER_SIZE_BYTES 1024 +#define PAINTBUFFER_SIZE 1024 +#define MAX_CHANNELS 16 + + +//----------------------------------------------------------------------------- +// Implementation of IAudioDevice for WAV files +//----------------------------------------------------------------------------- +class CAudioDeviceWave : public IAudioDevice +{ +public: + // Inherited from IAudioDevice + virtual bool Init( void ); + virtual void Shutdown( void ); + virtual const char *DeviceName( void ) const; + virtual int DeviceChannels( void ) const; + virtual int DeviceSampleBits( void ) const; + virtual int DeviceSampleBytes( void ) const; + virtual int DeviceSampleRate( void ) const; + virtual int DeviceSampleCount( void ) const; + virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ); + virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ); + virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ); + virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ); + virtual int PaintBufferSampleCount( void ) const; + virtual void MixBegin( void ); + + // mix a buffer up to time (time is absolute) + void Update( float time ); + void Flush( void ); + void TransferBufferStereo16( short *pOutput, int sampleCount ); + int GetOutputPosition( void ); + float GetAmountofTimeAhead( void ); + int GetNumberofSamplesAhead( void ); + void AddSource( CAudioMixer *pSource ); + void StopSounds( void ); + int FindSourceIndex( CAudioMixer *pSource ); + CAudioMixer *GetMixerForSource( CAudioSource *source ); + +private: + class CAudioMixerState + { + public: + CAudioMixer *mixer; + }; + + class CAudioBuffer + { + public: + WAVEHDR *hdr; + bool submitted; + int submit_sample_count; + + CUtlVector< CAudioMixerState > m_Referenced; + }; + + struct portable_samplepair_t + { + int left; + int right; + }; + + void OpenWaveOut( void ); + void CloseWaveOut( void ); + void AllocateOutputBuffers(); + void FreeOutputBuffers(); + void* AllocOutputMemory( int nSize, HGLOBAL &hMemory ); + void FreeOutputMemory( HGLOBAL &hMemory ); + + bool ValidWaveOut( void ) const; + CAudioBuffer *GetEmptyBuffer( void ); + void SilenceBuffer( short *pSamples, int sampleCount ); + + void SetChannel( int channelIndex, CAudioMixer *pSource ); + void FreeChannel( int channelIndex ); + + void RemoveMixerChannelReferences( CAudioMixer *mixer ); + void AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ); + void RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ); + bool IsSourceReferencedByActiveBuffer( CAudioMixer *mixer ); + bool IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ); + + // Compute how many samples we've mixed since most recent buffer submission + void ComputeSampleAheadAmount( void ); + + // This is a single allocation for all wave headers (there are OUTPUT_BUFFER_COUNT of them) + HGLOBAL m_hWaveHdr; + + // This is a single allocation for all wave data (there are OUTPUT_BUFFER_COUNT of them) + HANDLE m_hWaveData; + + HWAVEOUT m_waveOutHandle; + float m_mixTime; + float m_baseTime; + int m_sampleIndex; + CAudioBuffer m_buffers[ OUTPUT_BUFFER_COUNT ]; + CAudioMixer *m_sourceList[MAX_CHANNELS]; + int m_nEstimatedSamplesAhead; + + portable_samplepair_t m_paintbuffer[ PAINTBUFFER_SIZE ]; +}; + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +IAudioDevice *Audio_CreateWaveDevice( void ) +{ + return new CAudioDeviceWave; +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +bool CAudioDeviceWave::Init( void ) +{ + m_hWaveData = NULL; + m_hWaveHdr = NULL; + m_waveOutHandle = NULL; + + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + CAudioBuffer *buffer = &m_buffers[ i ]; + Assert( buffer ); + buffer->hdr = NULL; + buffer->submitted = false; + buffer->submit_sample_count = false; + } + + OpenWaveOut(); + + m_mixTime = m_baseTime = -1; + m_sampleIndex = 0; + memset( m_sourceList, 0, sizeof(m_sourceList) ); + + m_nEstimatedSamplesAhead = (int)( ( float ) OUTPUT_SAMPLE_RATE / 10.0f ); + + return true; +} + +void CAudioDeviceWave::Shutdown( void ) +{ + CloseWaveOut(); +} + + +//----------------------------------------------------------------------------- +// WAV out device +//----------------------------------------------------------------------------- +inline bool CAudioDeviceWave::ValidWaveOut( void ) const +{ + return m_waveOutHandle != 0; +} + + +//----------------------------------------------------------------------------- +// Opens the windows wave out device +//----------------------------------------------------------------------------- +void CAudioDeviceWave::OpenWaveOut( void ) +{ + WAVEFORMATEX waveFormat; + memset( &waveFormat, 0, sizeof(waveFormat) ); + + // Select a PCM, 16-bit stereo playback device + waveFormat.cbSize = sizeof(waveFormat); + waveFormat.wFormatTag = WAVE_FORMAT_PCM; + waveFormat.nChannels = DeviceChannels(); + waveFormat.wBitsPerSample = DeviceSampleBits(); + waveFormat.nSamplesPerSec = DeviceSampleRate(); + waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; + waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; + + MMRESULT errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL ); + while ( errorCode != MMSYSERR_NOERROR ) + { + if ( errorCode != MMSYSERR_ALLOCATED ) + { + DWarning( "soundsystem", 1, "waveOutOpen failed\n" ); + m_waveOutHandle = 0; + return; + } + + int nRetVal = MessageBox( NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION); + + if ( nRetVal != IDRETRY ) + { + DWarning( "soundsystem", 1, "waveOutOpen failure--hardware already in use\n" ); + m_waveOutHandle = 0; + return; + } + + errorCode = waveOutOpen( &m_waveOutHandle, WAVE_MAPPER, &waveFormat, 0, 0L, CALLBACK_NULL ); + } + + AllocateOutputBuffers(); +} + + +//----------------------------------------------------------------------------- +// Closes the windows wave out device +//----------------------------------------------------------------------------- +void CAudioDeviceWave::CloseWaveOut( void ) +{ + if ( ValidWaveOut() ) + { + waveOutReset( m_waveOutHandle ); + FreeOutputBuffers(); + waveOutClose( m_waveOutHandle ); + m_waveOutHandle = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Alloc output memory +//----------------------------------------------------------------------------- +void* CAudioDeviceWave::AllocOutputMemory( int nSize, HGLOBAL &hMemory ) +{ + // Output memory for waveform data+hdrs must be + // globally allocated with GMEM_MOVEABLE and GMEM_SHARE flags. + hMemory = GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, nSize ); + if ( !hMemory ) + { + DWarning( "soundsystem", 1, "Sound: Out of memory.\n"); + CloseWaveOut(); + return NULL; + } + + HPSTR lpData = (char *)GlobalLock( hMemory ); + if ( !lpData ) + { + DWarning( "soundsystem", 1, "Sound: Failed to lock.\n"); + GlobalFree( hMemory ); + hMemory = NULL; + CloseWaveOut(); + return NULL; + } + memset( lpData, 0, nSize ); + return lpData; +} + + +//----------------------------------------------------------------------------- +// Free output memory +//----------------------------------------------------------------------------- +void CAudioDeviceWave::FreeOutputMemory( HGLOBAL &hMemory ) +{ + if ( hMemory ) + { + GlobalUnlock( hMemory ); + GlobalFree( hMemory ); + hMemory = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Allocate, free output buffers +//----------------------------------------------------------------------------- +void CAudioDeviceWave::AllocateOutputBuffers() +{ + // Allocate and lock memory for the waveform data. + int nBufferSize = OUTPUT_BUFFER_SIZE_BYTES * OUTPUT_BUFFER_COUNT; + HPSTR lpData = (char *)AllocOutputMemory( nBufferSize, m_hWaveData ); + if ( !lpData ) + return; + + // Allocate and lock memory for the waveform header + int nHdrSize = sizeof( WAVEHDR ) * OUTPUT_BUFFER_COUNT; + LPWAVEHDR lpWaveHdr = (LPWAVEHDR)AllocOutputMemory( nHdrSize, m_hWaveHdr ); + if ( !lpWaveHdr ) + return; + + // After allocation, set up and prepare headers. + for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ ) + { + LPWAVEHDR lpHdr = lpWaveHdr + i; + lpHdr->dwBufferLength = OUTPUT_BUFFER_SIZE_BYTES; + lpHdr->lpData = lpData + (i * OUTPUT_BUFFER_SIZE_BYTES); + + MMRESULT nResult = waveOutPrepareHeader( m_waveOutHandle, lpHdr, sizeof(WAVEHDR) ); + if ( nResult != MMSYSERR_NOERROR ) + { + DWarning( "soundsystem", 1, "Sound: failed to prepare wave headers\n" ); + CloseWaveOut(); + return; + } + + m_buffers[i].hdr = lpHdr; + } +} + + +void CAudioDeviceWave::FreeOutputBuffers() +{ + // Unprepare headers. + for ( int i=0 ; i < OUTPUT_BUFFER_COUNT; i++ ) + { + if ( m_buffers[i].hdr ) + { + waveOutUnprepareHeader( m_waveOutHandle, m_buffers[i].hdr, sizeof(WAVEHDR) ); + m_buffers[i].hdr = NULL; + } + + m_buffers[i].submitted = false; + m_buffers[i].submit_sample_count = 0; + m_buffers[i].m_Referenced.Purge(); + } + + FreeOutputMemory( m_hWaveData ); + FreeOutputMemory( m_hWaveHdr ); +} + + +//----------------------------------------------------------------------------- +// Device parameters +//----------------------------------------------------------------------------- +const char *CAudioDeviceWave::DeviceName( void ) const +{ + return "Windows WAVE"; +} + +int CAudioDeviceWave::DeviceChannels( void ) const +{ + return 2; +} + +int CAudioDeviceWave::DeviceSampleBits( void ) const +{ + return (BYTES_PER_SAMPLE * 8); +} + +int CAudioDeviceWave::DeviceSampleBytes( void ) const +{ + return BYTES_PER_SAMPLE; +} + +int CAudioDeviceWave::DeviceSampleRate( void ) const +{ + return OUTPUT_SAMPLE_RATE; +} + +int CAudioDeviceWave::DeviceSampleCount( void ) const +{ + return OUTPUT_BUFFER_SAMPLE_COUNT; +} + +int CAudioDeviceWave::PaintBufferSampleCount( void ) const +{ + return PAINTBUFFER_SIZE; +} + + +//----------------------------------------------------------------------------- +// Mixing routines +//----------------------------------------------------------------------------- +void CAudioDeviceWave::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex]; + m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex]; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac); + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceWave::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex]; + m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex+1]; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac)<<1; + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceWave::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8; + m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex])>>8; + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac); + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceWave::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward ) +{ + int sampleIndex = 0; + fixedint sampleFrac = inputOffset; + + int fixup = 0; + int fixupstep = 1; + + if ( !forward ) + { + fixup = outCount - 1; + fixupstep = -1; + } + + for ( int i = 0; i < outCount; i++, fixup += fixupstep ) + { + int dest = max( outputOffset + fixup, 0 ); + + m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8; + m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex+1])>>8; + + sampleFrac += rateScaleFix; + sampleIndex += FIX_INTPART(sampleFrac)<<1; + sampleFrac = FIX_FRACPART(sampleFrac); + } +} + + +void CAudioDeviceWave::MixBegin( void ) +{ + memset( m_paintbuffer, 0, sizeof(m_paintbuffer) ); +} + +void CAudioDeviceWave::TransferBufferStereo16( short *pOutput, int sampleCount ) +{ + for ( int i = 0; i < sampleCount; i++ ) + { + if ( m_paintbuffer[i].left > 32767 ) + m_paintbuffer[i].left = 32767; + else if ( m_paintbuffer[i].left < -32768 ) + m_paintbuffer[i].left = -32768; + + if ( m_paintbuffer[i].right > 32767 ) + m_paintbuffer[i].right = 32767; + else if ( m_paintbuffer[i].right < -32768 ) + m_paintbuffer[i].right = -32768; + + *pOutput++ = (short)m_paintbuffer[i].left; + *pOutput++ = (short)m_paintbuffer[i].right; + } +} + +void CAudioDeviceWave::RemoveMixerChannelReferences( CAudioMixer *mixer ) +{ + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + RemoveFromReferencedList( mixer, &m_buffers[ i ] ); + } +} + +void CAudioDeviceWave::AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + // Already in list + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + return; + } + + // Just remove it + int idx = buffer->m_Referenced.AddToTail(); + + CAudioMixerState *state = &buffer->m_Referenced[ idx ]; + state->mixer = mixer; +} + +void CAudioDeviceWave::RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + { + buffer->m_Referenced.Remove( i ); + break; + } + } +} + +bool CAudioDeviceWave::IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer ) +{ + for ( int i = 0; i < buffer->m_Referenced.Size(); i++ ) + { + if ( buffer->m_Referenced[ i ].mixer == mixer ) + { + return true; + } + } + return false; +} + +bool CAudioDeviceWave::IsSourceReferencedByActiveBuffer( CAudioMixer *mixer ) +{ + if ( !ValidWaveOut() ) + return false; + + CAudioBuffer *buffer; + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + buffer = &m_buffers[ i ]; + if ( !buffer->submitted ) + continue; + + if ( buffer->hdr->dwFlags & WHDR_DONE ) + continue; + + // See if it's referenced + if ( IsSoundInReferencedList( mixer, buffer ) ) + return true; + } + + return false; +} + +CAudioDeviceWave::CAudioBuffer *CAudioDeviceWave::GetEmptyBuffer( void ) +{ + CAudioBuffer *pOutput = NULL; + if ( ValidWaveOut() ) + { + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + if ( !(m_buffers[ i ].submitted ) || + m_buffers[i].hdr->dwFlags & WHDR_DONE ) + { + pOutput = &m_buffers[i]; + pOutput->submitted = true; + pOutput->m_Referenced.Purge(); + break; + } + } + } + + return pOutput; +} + +void CAudioDeviceWave::SilenceBuffer( short *pSamples, int sampleCount ) +{ + int i; + + for ( i = 0; i < sampleCount; i++ ) + { + // left + *pSamples++ = 0; + // right + *pSamples++ = 0; + } +} + +void CAudioDeviceWave::Flush( void ) +{ + waveOutReset( m_waveOutHandle ); +} + +// mix a buffer up to time (time is absolute) +void CAudioDeviceWave::Update( float time ) +{ + if ( !ValidWaveOut() ) + return; + + // reset the system + if ( m_mixTime < 0 || time < m_baseTime ) + { + m_baseTime = time; + m_mixTime = 0; + } + + // put time in our coordinate frame + time -= m_baseTime; + + if ( time > m_mixTime ) + { + CAudioBuffer *pBuffer = GetEmptyBuffer(); + + // no free buffers, mixing is ahead of the playback! + if ( !pBuffer || !pBuffer->hdr ) + { + //Con_Printf( "out of buffers\n" ); + return; + } + + // UNDONE: These numbers are constants + // calc number of samples (2 channels * 2 bytes per sample) + int sampleCount = pBuffer->hdr->dwBufferLength >> 2; + m_mixTime += sampleCount * (1.0f / OUTPUT_SAMPLE_RATE); + + short *pSamples = reinterpret_cast<short *>(pBuffer->hdr->lpData); + + SilenceBuffer( pSamples, sampleCount ); + + int tempCount = sampleCount; + + while ( tempCount > 0 ) + { + if ( tempCount > PaintBufferSampleCount() ) + { + sampleCount = PaintBufferSampleCount(); + } + else + { + sampleCount = tempCount; + } + + MixBegin(); + for ( int i = 0; i < MAX_CHANNELS; i++ ) + { + CAudioMixer *pSource = m_sourceList[i]; + if ( !pSource ) + continue; + + int currentsample = pSource->GetSamplePosition(); + bool forward = pSource->GetDirection(); + + if ( pSource->GetActive() ) + { + if ( !pSource->MixDataToDevice( this, pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward ) ) + { + // Source becomes inactive when last submitted sample is finally + // submitted. But it lingers until it's no longer referenced + pSource->SetActive( false ); + } + else + { + AddToReferencedList( pSource, pBuffer ); + } + } + else + { + if ( !IsSourceReferencedByActiveBuffer( pSource ) ) + { + if ( !pSource->GetAutoDelete() ) + { + FreeChannel( i ); + } + } + else + { + pSource->IncrementSamples( pSource->GetChannel(), currentsample, sampleCount, DeviceSampleRate(), forward ); + } + } + + } + + TransferBufferStereo16( pSamples, sampleCount ); + + m_sampleIndex += sampleCount; + tempCount -= sampleCount; + pSamples += sampleCount * 2; + } + // if the buffers aren't aligned on sample boundaries, this will hard-lock the machine! + + pBuffer->submit_sample_count = GetOutputPosition(); + + waveOutWrite( m_waveOutHandle, pBuffer->hdr, sizeof(*(pBuffer->hdr)) ); + } +} + +/* +int CAudioDeviceWave::GetNumberofSamplesAhead( void ) +{ + ComputeSampleAheadAmount(); + return m_nEstimatedSamplesAhead; +} + +float CAudioDeviceWave::GetAmountofTimeAhead( void ) +{ + ComputeSampleAheadAmount(); + return ( (float)m_nEstimatedSamplesAhead / (float)OUTPUT_SAMPLE_RATE ); +} + +// Find the most recent submitted sample that isn't flagged as whdr_done +void CAudioDeviceWave::ComputeSampleAheadAmount( void ) +{ + m_nEstimatedSamplesAhead = 0; + + int newest_sample_index = -1; + int newest_sample_count = 0; + + CAudioBuffer *buffer; + + if ( ValidDevice() ) + { + + for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ ) + { + buffer = &m_buffers[ i ]; + if ( !buffer->submitted ) + continue; + + if ( buffer->hdr->dwFlags & WHDR_DONE ) + continue; + + if ( buffer->submit_sample_count > newest_sample_count ) + { + newest_sample_index = i; + newest_sample_count = buffer->submit_sample_count; + } + } + } + + if ( newest_sample_index == -1 ) + return; + + + buffer = &m_buffers[ newest_sample_index ]; + int currentPos = GetOutputPosition() ; + m_nEstimatedSamplesAhead = currentPos - buffer->submit_sample_count; +} +*/ + +int CAudioDeviceWave::FindSourceIndex( CAudioMixer *pSource ) +{ + for ( int i = 0; i < MAX_CHANNELS; i++ ) + { + if ( pSource == m_sourceList[i] ) + { + return i; + } + } + return -1; +} + +CAudioMixer *CAudioDeviceWave::GetMixerForSource( CAudioSource *source ) +{ + for ( int i = 0; i < MAX_CHANNELS; i++ ) + { + if ( !m_sourceList[i] ) + continue; + + if ( source == m_sourceList[i]->GetSource() ) + { + return m_sourceList[i]; + } + } + return NULL; +} + +void CAudioDeviceWave::AddSource( CAudioMixer *pSource ) +{ + int slot = 0; + for ( int i = 0; i < MAX_CHANNELS; i++ ) + { + if ( !m_sourceList[i] ) + { + slot = i; + break; + } + } + + if ( m_sourceList[slot] ) + { + FreeChannel( slot ); + } + SetChannel( slot, pSource ); + + pSource->SetActive( true ); +} + + +void CAudioDeviceWave::StopSounds( void ) +{ + for ( int i = 0; i < MAX_CHANNELS; i++ ) + { + if ( m_sourceList[i] ) + { + FreeChannel( i ); + } + } +} + + +void CAudioDeviceWave::SetChannel( int channelIndex, CAudioMixer *pSource ) +{ + if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS ) + return; + + m_sourceList[channelIndex] = pSource; +} + +void CAudioDeviceWave::FreeChannel( int channelIndex ) +{ + if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS ) + return; + + if ( m_sourceList[channelIndex] ) + { + RemoveMixerChannelReferences( m_sourceList[channelIndex] ); + + delete m_sourceList[channelIndex]; + m_sourceList[channelIndex] = NULL; + } +} + +int CAudioDeviceWave::GetOutputPosition( void ) +{ + if ( !m_waveOutHandle ) + return 0; + + MMTIME mmtime; + mmtime.wType = TIME_SAMPLES; + waveOutGetPosition( m_waveOutHandle, &mmtime, sizeof( MMTIME ) ); + + // Convert time to sample count + return ( mmtime.u.sample ); +} + diff --git a/soundsystem/snd_dev_wave.h b/soundsystem/snd_dev_wave.h new file mode 100644 index 0000000..8935c9a --- /dev/null +++ b/soundsystem/snd_dev_wave.h @@ -0,0 +1,27 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SND_DEV_WAVE_H +#define SND_DEV_WAVE_H +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IAudioDevice; + + +//----------------------------------------------------------------------------- +// Creates a device that mixes WAVs using windows +//----------------------------------------------------------------------------- +IAudioDevice *Audio_CreateWaveDevice( void ); + + +#endif // SND_DEV_WAVE_H diff --git a/soundsystem/snd_io.cpp b/soundsystem/snd_io.cpp new file mode 100644 index 0000000..887e1a8 --- /dev/null +++ b/soundsystem/snd_io.cpp @@ -0,0 +1,93 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "soundsystem.h" +#include "tier2/riff.h" +#include "filesystem.h" +#include "tier1/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Implements Audio IO on the engine's COMMON filesystem +//----------------------------------------------------------------------------- +class COM_IOReadBinary : public IFileReadBinary +{ +public: + int open( const char *pFileName ); + int read( void *pOutput, int size, int file ); + void seek( int file, int pos ); + unsigned int tell( int file ); + unsigned int size( int file ); + void close( int file ); +}; + + +// prepend sound/ to the filename -- all sounds are loaded from the sound/ directory +int COM_IOReadBinary::open( const char *pFileName ) +{ + char namebuffer[512]; + FileHandle_t hFile; + + Q_strncpy(namebuffer, "sound", sizeof( namebuffer ) ); + + //HACK HACK HACK the server is sending back sound names with slashes in front... + if (pFileName[0]!='/') + { + Q_strncat(namebuffer,"/", sizeof( namebuffer ), COPY_ALL_CHARACTERS ); + } + + Q_strncat( namebuffer, pFileName, sizeof( namebuffer ), COPY_ALL_CHARACTERS ); + + hFile = g_pFullFileSystem->Open( namebuffer, "rb", "GAME" ); + + return (int)hFile; +} + +int COM_IOReadBinary::read( void *pOutput, int size, int file ) +{ + if ( !file ) + return 0; + + return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file ); +} + +void COM_IOReadBinary::seek( int file, int pos ) +{ + if ( !file ) + return; + + g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); +} + +unsigned int COM_IOReadBinary::tell( int file ) +{ + if ( !file ) + return 0; + return g_pFullFileSystem->Tell( (FileHandle_t)file ); +} + +unsigned int COM_IOReadBinary::size( int file ) +{ + if (!file) + return 0; + return g_pFullFileSystem->Size( (FileHandle_t)file ); +} + +void COM_IOReadBinary::close( int file ) +{ + if (!file) + return; + + g_pFullFileSystem->Close( (FileHandle_t)file ); +} + +static COM_IOReadBinary io; +IFileReadBinary *g_pSndIO = &io; + diff --git a/soundsystem/snd_wave_mixer.cpp b/soundsystem/snd_wave_mixer.cpp new file mode 100644 index 0000000..556e65c --- /dev/null +++ b/soundsystem/snd_wave_mixer.cpp @@ -0,0 +1,527 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// +#include <stdio.h> + +#include "snd_dev_wave.h" +#include "snd_wave_source.h" +#include "soundsystem/snd_audio_source.h" +#include "snd_wave_mixer_private.h" +#include "snd_wave_mixer_adpcm.h" +#include "tier2/riff.h" + +//----------------------------------------------------------------------------- +// These mixers provide an abstraction layer between the audio device and +// mixing/decoding code. They allow data to be decoded and mixed using +// optimized, format sensitive code by calling back into the device that +// controls them. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 8-bit mono mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave8Mono : public CAudioMixerWave +{ +public: + CAudioMixerWave8Mono( CWaveData *data ) : CAudioMixerWave( data ) {} + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true ) + { + pDevice->Mix8Mono( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 8-bit stereo mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave8Stereo : public CAudioMixerWave +{ +public: + CAudioMixerWave8Stereo( CWaveData *data ) : CAudioMixerWave( data ) {} + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true ) + { + pDevice->Mix8Stereo( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 16-bit mono mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave16Mono : public CAudioMixerWave +{ +public: + CAudioMixerWave16Mono( CWaveData *data ) : CAudioMixerWave( data ) {} + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true ) + { + pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 16-bit stereo mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave16Stereo : public CAudioMixerWave +{ +public: + CAudioMixerWave16Stereo( CWaveData *data ) : CAudioMixerWave( data ) {} + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true ) + { + pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); + } +}; + + + +//----------------------------------------------------------------------------- +// Purpose: Create an approprite mixer type given the data format +// Input : *data - data access abstraction +// format - pcm or adpcm (1 or 2 -- RIFF format) +// channels - number of audio channels (1 = mono, 2 = stereo) +// bits - bits per sample +// Output : CAudioMixer * abstract mixer type that maps mixing to appropriate code +//----------------------------------------------------------------------------- +CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits ) +{ + if ( format == WAVE_FORMAT_PCM ) + { + if ( channels > 1 ) + { + if ( bits == 8 ) + return new CAudioMixerWave8Stereo( data ); + else + return new CAudioMixerWave16Stereo( data ); + } + else + { + if ( bits == 8 ) + return new CAudioMixerWave8Mono( data ); + else + return new CAudioMixerWave16Mono( data ); + } + } + else if ( format == WAVE_FORMAT_ADPCM ) + { + return CreateADPCMMixer( data ); + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Init the base WAVE mixer. +// Input : *data - data access object +//----------------------------------------------------------------------------- +CAudioMixerWave::CAudioMixerWave( CWaveData *data ) : m_pData(data), m_pChannel(NULL) +{ + m_loop = 0; + m_sample = 0; + m_absoluteSample = 0; + m_scubSample = -1; + m_fracOffset = 0; + m_bActive = false; + m_nModelIndex = -1; + m_bForward = true; + m_bAutoDelete = true; + m_pChannel = new channel_t; + m_pChannel->leftvol = 127; + m_pChannel->rightvol = 127; + m_pChannel->pitch = 1.0; +} + +//----------------------------------------------------------------------------- +// Purpose: Frees the data access object (we own it after construction) +//----------------------------------------------------------------------------- +CAudioMixerWave::~CAudioMixerWave( void ) +{ + delete m_pData; + delete m_pChannel; +} + + +//----------------------------------------------------------------------------- +// Purpose: Decode and read the data +// by default we just pass the request on to the data access object +// other mixers may need to buffer or decode the data for some reason +// +// Input : **pData - dest pointer +// sampleCount - number of samples needed +// Output : number of samples available in this batch +//----------------------------------------------------------------------------- +int CAudioMixerWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ ) +{ + if ( samplePosition != m_sample ) + { + // Seek + m_sample = samplePosition; + m_absoluteSample = samplePosition; + } + + return m_pData->ReadSourceData( pData, m_sample, sampleCount, forward ); +} + + +//----------------------------------------------------------------------------- +// Purpose: calls through the wavedata to get the audio source +// Output : CAudioSource +//----------------------------------------------------------------------------- +CAudioSource *CAudioMixerWave::GetSource( void ) +{ + if ( m_pData ) + return &m_pData->Source(); + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the current sample location in playback +// Output : int (samples from start of wave) +//----------------------------------------------------------------------------- +int CAudioMixerWave::GetSamplePosition( void ) +{ + return m_sample; +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the current sample location in playback +// Output : int (samples from start of wave) +//----------------------------------------------------------------------------- +int CAudioMixerWave::GetScubPosition( void ) +{ + if (m_scubSample != -1) + { + return m_scubSample; + } + return m_sample; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : position - +//----------------------------------------------------------------------------- +bool CAudioMixerWave::SetSamplePosition( int position, bool scrubbing ) +{ + position = max( 0, position ); + + m_sample = position; + m_absoluteSample = position; + m_startpos = m_sample; + if (scrubbing) + { + m_scubSample = position; + } + else + { + m_scubSample = -1; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : position - +//----------------------------------------------------------------------------- +void CAudioMixerWave::SetLoopPosition( int position ) +{ + m_loop = position; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CAudioMixerWave::GetStartPosition( void ) +{ + return m_startpos; +} + +bool CAudioMixerWave::GetActive( void ) +{ + return m_bActive; +} + +void CAudioMixerWave::SetActive( bool active ) +{ + m_bActive = active; +} + +void CAudioMixerWave::SetModelIndex( int index ) +{ + m_nModelIndex = index; +} + +int CAudioMixerWave::GetModelIndex( void ) const +{ + return m_nModelIndex; +} + +void CAudioMixerWave::SetDirection( bool forward ) +{ + m_bForward = forward; +} + +bool CAudioMixerWave::GetDirection( void ) const +{ + return m_bForward; +} + +void CAudioMixerWave::SetAutoDelete( bool autodelete ) +{ + m_bAutoDelete = autodelete; +} + +bool CAudioMixerWave::GetAutoDelete( void ) const +{ + return m_bAutoDelete; +} + +void CAudioMixerWave::SetVolume( float volume ) +{ + int ivolume = (int)( clamp( volume, 0.0f, 1.0f ) * 127.0f ); + + m_pChannel->leftvol = ivolume; + m_pChannel->rightvol = ivolume; +} + +channel_t *CAudioMixerWave::GetChannel() +{ + Assert( m_pChannel ); + return m_pChannel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pChannel - +// sampleCount - +// outputRate - +//----------------------------------------------------------------------------- +void CAudioMixerWave::IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward /*= true*/ ) +{ + int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate()); + float rate = (float)inputSampleRate / outputRate; + + int startpos = startSample; + + if ( !forward ) + { + int requestedstart = startSample - (int)( sampleCount * rate ); + if ( requestedstart < 0 ) + return; + + startpos = max( 0, requestedstart ); + SetSamplePosition( startpos ); + } + + while ( sampleCount > 0 ) + { + int inputSampleCount; + int outputSampleCount = sampleCount; + + if ( outputRate != inputSampleRate ) + { + inputSampleCount = (int)(sampleCount * rate); + } + else + { + inputSampleCount = sampleCount; + } + + sampleCount -= outputSampleCount; + if ( forward ) + { + m_sample += inputSampleCount; + m_absoluteSample += inputSampleCount; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: The device calls this to request data. The mixer must provide the +// full amount of samples or have silence in its output stream. +// Input : *pDevice - requesting device +// sampleCount - number of samples at the output rate +// outputRate - sampling rate of the request +// Output : Returns true to keep mixing, false to delete this mixer +//----------------------------------------------------------------------------- +bool CAudioMixerWave::SkipSamples( IAudioDevice *pDevice, channel_t *pChannel, + int startSample, int sampleCount, int outputRate, bool forward /*= true*/ ) +{ + int offset = 0; + + int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate()); + float rate = (float)inputSampleRate / outputRate; + + sampleCount = min( sampleCount, pDevice->PaintBufferSampleCount() ); + + int startpos = startSample; + + if ( !forward ) + { + int requestedstart = startSample - (int)( sampleCount * rate ); + if ( requestedstart < 0 ) + return false; + + startpos = max( 0, requestedstart ); + SetSamplePosition( startpos ); + } + + while ( sampleCount > 0 ) + { + int availableSamples; + int inputSampleCount; + char *pData = NULL; + int outputSampleCount = sampleCount; + + if ( outputRate != inputSampleRate ) + { + inputSampleCount = (int)(sampleCount * rate); + if ( !forward ) + { + startSample = max( 0, startSample - inputSampleCount ); + } + int availableSamples = GetOutputData( (void **)&pData, startSample, inputSampleCount, forward ); + if ( !availableSamples ) + break; + + if ( availableSamples < inputSampleCount ) + { + outputSampleCount = (int)(availableSamples / rate); + inputSampleCount = availableSamples; + } + + // compute new fraction part of sample index + float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount); + offset = offset - (float)((int)offset); + m_fracOffset = FIX_FLOAT(offset); + } + else + { + if ( !forward ) + { + startSample = max( 0, startSample - sampleCount ); + } + availableSamples = GetOutputData( (void **)&pData, startSample, sampleCount, forward ); + if ( !availableSamples ) + break; + outputSampleCount = availableSamples; + inputSampleCount = availableSamples; + + } + offset += outputSampleCount; + sampleCount -= outputSampleCount; + if ( forward ) + { + m_sample += inputSampleCount; + m_absoluteSample += inputSampleCount; + } + + if ( m_loop != 0 && m_sample >= m_loop ) + { + SetSamplePosition( m_startpos ); + } + + } + + if ( sampleCount > 0 ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: The device calls this to request data. The mixer must provide the +// full amount of samples or have silence in its output stream. +// Input : *pDevice - requesting device +// sampleCount - number of samples at the output rate +// outputRate - sampling rate of the request +// Output : Returns true to keep mixing, false to delete this mixer +//----------------------------------------------------------------------------- +bool CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ ) +{ + int offset = 0; + + int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate()); + float rate = (float)inputSampleRate / outputRate; + fixedint fracstep = FIX_FLOAT( rate ); + + sampleCount = min( sampleCount, pDevice->PaintBufferSampleCount() ); + + int startpos = startSample; + + if ( !forward ) + { + int requestedstart = startSample - (int)( sampleCount * rate ); + if ( requestedstart < 0 ) + return false; + + startpos = max( 0, requestedstart ); + SetSamplePosition( startpos ); + } + + while ( sampleCount > 0 ) + { + int availableSamples; + int inputSampleCount; + char *pData = NULL; + int outputSampleCount = sampleCount; + + + if ( outputRate != inputSampleRate ) + { + inputSampleCount = (int)(sampleCount * rate); + + int availableSamples = GetOutputData( (void **)&pData, startpos, inputSampleCount, forward ); + if ( !availableSamples ) + break; + + if ( availableSamples < inputSampleCount ) + { + outputSampleCount = (int)(availableSamples / rate); + inputSampleCount = availableSamples; + } + + Mix( pDevice, pChannel, pData, offset, m_fracOffset, fracstep, outputSampleCount, 0, forward ); + + // compute new fraction part of sample index + float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount); + offset = offset - (float)((int)offset); + m_fracOffset = FIX_FLOAT(offset); + } + else + { + availableSamples = GetOutputData( (void **)&pData, startpos, sampleCount, forward ); + if ( !availableSamples ) + break; + + outputSampleCount = availableSamples; + inputSampleCount = availableSamples; + + Mix( pDevice, pChannel, pData, offset, m_fracOffset, FIX(1), outputSampleCount, 0, forward ); + } + offset += outputSampleCount; + sampleCount -= outputSampleCount; + + if ( forward ) + { + m_sample += inputSampleCount; + m_absoluteSample += inputSampleCount; + } + + if ( m_loop != 0 && m_sample >= m_loop ) + { + SetSamplePosition( m_startpos ); + } + + } + + if ( sampleCount > 0 ) + return false; + + return true; +} diff --git a/soundsystem/snd_wave_mixer.h b/soundsystem/snd_wave_mixer.h new file mode 100644 index 0000000..5ce2c7e --- /dev/null +++ b/soundsystem/snd_wave_mixer.h @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef SND_WAVE_MIXER_H +#define SND_WAVE_MIXER_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CWaveData; +class CAudioMixer; + + +//----------------------------------------------------------------------------- +// Wave mixer +//----------------------------------------------------------------------------- +CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits ); + + +#endif // SND_WAVE_MIXER_H diff --git a/soundsystem/snd_wave_mixer_adpcm.cpp b/soundsystem/snd_wave_mixer_adpcm.cpp new file mode 100644 index 0000000..e016aa2 --- /dev/null +++ b/soundsystem/snd_wave_mixer_adpcm.cpp @@ -0,0 +1,516 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#pragma warning( disable: 4201 ) +#include <mmsystem.h> +#pragma warning( default: 4201 ) + +#include <mmreg.h> +#include "snd_wave_source.h" +#include "snd_wave_mixer_adpcm.h" +#include "snd_wave_mixer_private.h" +#include "soundsystem.h" + +// max size of ADPCM block in bytes +#define MAX_BLOCK_SIZE 4096 + + +//----------------------------------------------------------------------------- +// Purpose: Mixer for ADPCM encoded audio +//----------------------------------------------------------------------------- +class CAudioMixerWaveADPCM : public CAudioMixerWave +{ +public: + CAudioMixerWaveADPCM( CWaveData *data ); + ~CAudioMixerWaveADPCM( void ); + + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true ); + virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true ); + + virtual bool SetSamplePosition( int position, bool scrubbing = false ); + +private: + bool DecodeBlock( void ); + int NumChannels( void ); + void DecompressBlockMono( short *pOut, const char *pIn, int count ); + void DecompressBlockStereo( short *pOut, const char *pIn, int count ); + + void SetCurrentBlock( int block ); + int GetCurrentBlock( void ) const; + int GetBlockNumberForSample( int samplePosition ); + bool IsSampleInCurrentBlock( int samplePosition ); + int GetFirstSampleForBlock( int blocknum ) const; + + const ADPCMWAVEFORMAT *m_pFormat; + const ADPCMCOEFSET *m_pCoefficients; + + short *m_pSamples; + int m_sampleCount; + int m_samplePosition; + + int m_blockSize; + int m_offset; + + int m_currentBlock; +}; + + +CAudioMixerWaveADPCM::CAudioMixerWaveADPCM( CWaveData *data ) : CAudioMixerWave( data ) +{ + m_currentBlock = -1; + m_pSamples = NULL; + m_sampleCount = 0; + m_samplePosition = 0; + m_offset = 0; + + m_pFormat = (const ADPCMWAVEFORMAT *)m_pData->Source().GetHeader(); + if ( m_pFormat ) + { + m_pCoefficients = (ADPCMCOEFSET *)((char *)m_pFormat + sizeof(WAVEFORMATEX) + 4); + + // create the decode buffer + m_pSamples = new short[m_pFormat->wSamplesPerBlock * m_pFormat->wfx.nChannels]; + + // number of bytes for samples + m_blockSize = ((m_pFormat->wSamplesPerBlock - 2) * m_pFormat->wfx.nChannels ) / 2; + // size of channel header + m_blockSize += 7 * m_pFormat->wfx.nChannels; +// Assert(m_blockSize < MAX_BLOCK_SIZE); + } +} + + +CAudioMixerWaveADPCM::~CAudioMixerWaveADPCM( void ) +{ + delete[] m_pSamples; +} + + +int CAudioMixerWaveADPCM::NumChannels( void ) +{ + if ( m_pFormat ) + { + return m_pFormat->wfx.nChannels; + } + return 0; +} + +void CAudioMixerWaveADPCM::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward /*= true*/ ) +{ + if ( NumChannels() == 1 ) + pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); + else + pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward ); +} + + +static int error_sign_lut[] = { 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }; +static int error_coefficients_lut[] = { 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 }; + +//----------------------------------------------------------------------------- +// Purpose: ADPCM decompress a single block of 1-channel audio +// Input : *pOut - output buffer 16-bit +// *pIn - input block +// count - number of samples to decode (to support partial blocks) +//----------------------------------------------------------------------------- +void CAudioMixerWaveADPCM::DecompressBlockMono( short *pOut, const char *pIn, int count ) +{ + + int pred = *pIn++; + int co1 = m_pCoefficients[pred].iCoef1; + int co2 = m_pCoefficients[pred].iCoef2; + + // read initial delta + int delta = *((short *)pIn); + pIn += 2; + + // read initial samples for prediction + int samp1 = *((short *)pIn); + pIn += 2; + + int samp2 = *((short *)pIn); + pIn += 2; + + // write out the initial samples (stored in reverse order) + *pOut++ = (short)samp2; + *pOut++ = (short)samp1; + + // subtract the 2 samples in the header + count -= 2; + + // this is a toggle to read nibbles, first nibble is high + int high = 1; + + int error = 0, sample = 0; + + // now process the block + while ( count ) + { + // read the error nibble from the input stream + if ( high ) + { + sample = (unsigned char) (*pIn++); + // high nibble + error = sample >> 4; + // cache low nibble for next read + sample = sample & 0xf; + // Next read is from cache, not stream + high = 0; + } + else + { + // stored in previous read (low nibble) + error = sample; + // next read is from stream + high = 1; + } + // convert to signed with LUT + int errorSign = error_sign_lut[error]; + + // interpolate the new sample + int predSample = (samp1 * co1) + (samp2 * co2); + // coefficients are fixed point 8-bit, so shift back to 16-bit integer + predSample >>= 8; + + // Add in current error estimate + predSample += (errorSign * delta); + + // Correct error estimate + delta = (delta * error_coefficients_lut[error]) >> 8; + // Clamp error estimate + if ( delta < 16 ) + delta = 16; + + // clamp + if ( predSample > 32767L ) + predSample = 32767L; + else if ( predSample < -32768L ) + predSample = -32768L; + + // output + *pOut++ = (short)predSample; + // move samples over + samp2 = samp1; + samp1 = predSample; + + count--; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Decode a single block of stereo ADPCM audio +// Input : *pOut - 16-bit output buffer +// *pIn - ADPCM encoded block data +// count - number of sample pairs to decode +//----------------------------------------------------------------------------- +void CAudioMixerWaveADPCM::DecompressBlockStereo( short *pOut, const char *pIn, int count ) +{ + int pred[2], co1[2], co2[2]; + int i; + + for ( i = 0; i < 2; i++ ) + { + pred[i] = *pIn++; + co1[i] = m_pCoefficients[pred[i]].iCoef1; + co2[i] = m_pCoefficients[pred[i]].iCoef2; + } + + int delta[2], samp1[2], samp2[2]; + + for ( i = 0; i < 2; i++, pIn += 2 ) + { + // read initial delta + delta[i] = *((short *)pIn); + } + + // read initial samples for prediction + for ( i = 0; i < 2; i++, pIn += 2 ) + { + samp1[i] = *((short *)pIn); + } + for ( i = 0; i < 2; i++, pIn += 2 ) + { + samp2[i] = *((short *)pIn); + } + + // write out the initial samples (stored in reverse order) + *pOut++ = (short)samp2[0]; // left + *pOut++ = (short)samp2[1]; // right + *pOut++ = (short)samp1[0]; // left + *pOut++ = (short)samp1[1]; // right + + // subtract the 2 samples in the header + count -= 2; + + // this is a toggle to read nibbles, first nibble is high + int high = 1; + + int error, sample = 0; + + // now process the block + while ( count ) + { + for ( i = 0; i < 2; i++ ) + { + // read the error nibble from the input stream + if ( high ) + { + sample = (unsigned char) (*pIn++); + // high nibble + error = sample >> 4; + // cache low nibble for next read + sample = sample & 0xf; + // Next read is from cache, not stream + high = 0; + } + else + { + // stored in previous read (low nibble) + error = sample; + // next read is from stream + high = 1; + } + // convert to signed with LUT + int errorSign = error_sign_lut[error]; + + // interpolate the new sample + int predSample = (samp1[i] * co1[i]) + (samp2[i] * co2[i]); + // coefficients are fixed point 8-bit, so shift back to 16-bit integer + predSample >>= 8; + + // Add in current error estimate + predSample += (errorSign * delta[i]); + + // Correct error estimate + delta[i] = (delta[i] * error_coefficients_lut[error]) >> 8; + // Clamp error estimate + if ( delta[i] < 16 ) + delta[i] = 16; + + // clamp + if ( predSample > 32767L ) + predSample = 32767L; + else if ( predSample < -32768L ) + predSample = -32768L; + + // output + *pOut++ = (short)predSample; + // move samples over + samp2[i] = samp1[i]; + samp1[i] = predSample; + } + count--; + } +} + + +bool CAudioMixerWaveADPCM::DecodeBlock( void ) +{ + char tmpBlock[MAX_BLOCK_SIZE]; + char *pData; + + int available = m_pData->ReadSourceData( (void **) (&pData), m_offset, m_blockSize ); + if ( available < m_blockSize ) + { + int total = 0; + while ( available && total < m_blockSize ) + { + memcpy( tmpBlock + total, pData, available ); + total += available; + available = m_pData->ReadSourceData( (void **) (&pData), m_offset + total, m_blockSize - total ); + } + pData = tmpBlock; + available = total; + } + + Assert( m_blockSize > 0 ); + + // Current block number is based on starting offset + int blockNumber = m_offset / m_blockSize; + SetCurrentBlock( blockNumber ); + + if ( !available ) + { + return false; + } + + // advance the file pointer + m_offset += available; + + int channelCount = NumChannels(); + + // this is sample pairs for stereo, samples for mono + m_sampleCount = m_pFormat->wSamplesPerBlock; + + // short block?, fixup sample count (2 samples per byte, divided by number of channels per sample set) + m_sampleCount -= ((m_blockSize - available) * 2) / channelCount; + + // new block, start at the first sample + m_samplePosition = 0; + + // no need to subclass for different channel counts... + if ( channelCount == 1 ) + { + DecompressBlockMono( m_pSamples, pData, m_sampleCount ); + } + else + { + DecompressBlockStereo( m_pSamples, pData, m_sampleCount ); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : block - +//----------------------------------------------------------------------------- +void CAudioMixerWaveADPCM::SetCurrentBlock( int block ) +{ + m_currentBlock = block; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CAudioMixerWaveADPCM::GetCurrentBlock( void ) const +{ + return m_currentBlock; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : samplePosition - +// Output : int +//----------------------------------------------------------------------------- +int CAudioMixerWaveADPCM::GetBlockNumberForSample( int samplePosition ) +{ + int blockNum = samplePosition / m_pFormat->wSamplesPerBlock; + return blockNum; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : samplePosition - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAudioMixerWaveADPCM::IsSampleInCurrentBlock( int samplePosition ) +{ + int currentBlock = GetCurrentBlock(); + + int startSample = currentBlock * m_pFormat->wSamplesPerBlock; + int endSample = startSample + m_pFormat->wSamplesPerBlock - 1; + + if ( samplePosition >= startSample && + samplePosition <= endSample ) + { + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : blocknum - +// Output : int +//----------------------------------------------------------------------------- +int CAudioMixerWaveADPCM::GetFirstSampleForBlock( int blocknum ) const +{ + return m_pFormat->wSamplesPerBlock * blocknum; +} + +//----------------------------------------------------------------------------- +// Purpose: Read existing buffer or decompress a new block when necessary +// Input : **pData - output data pointer +// sampleCount - number of samples (or pairs) +// Output : int - available samples (zero to stop decoding) +//----------------------------------------------------------------------------- +int CAudioMixerWaveADPCM::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ ) +{ + int requestedBlock = GetBlockNumberForSample( samplePosition ); + if ( requestedBlock != GetCurrentBlock() ) + { + // Ran out of data!!! + if ( !SetSamplePosition( samplePosition ) ) + return 0; + } + + Assert( requestedBlock == GetCurrentBlock() ); + + if ( m_samplePosition >= m_sampleCount ) + { + if ( !DecodeBlock() ) + return 0; + } + + if ( m_samplePosition < m_sampleCount ) + { + *pData = (void *)(m_pSamples + m_samplePosition * NumChannels()); + int available = m_sampleCount - m_samplePosition; + if ( available > sampleCount ) + available = sampleCount; + + m_samplePosition += available; + return available; + } + + return 0; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : position - +//----------------------------------------------------------------------------- +bool CAudioMixerWaveADPCM::SetSamplePosition( int position, bool scrubbing ) +{ + position = max( 0, position ); + + CAudioMixerWave::SetSamplePosition( position, scrubbing ); + + int requestedBlock = GetBlockNumberForSample( position ); + int firstSample = GetFirstSampleForBlock( requestedBlock ); + + if ( firstSample >= m_pData->Source().SampleCount() ) + { + // Read past end of file!!! + return false; + } + + int currentSample = ( position - firstSample ); + + if ( requestedBlock != GetCurrentBlock() ) + { + // Rewind file to beginning of block + m_offset = requestedBlock * m_blockSize; + if ( !DecodeBlock() ) + { + return false; + } + } + + m_samplePosition = currentSample; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Abstract factory function for ADPCM mixers +// Input : *data - wave data access object +// channels - +// Output : CAudioMixer +//----------------------------------------------------------------------------- +CAudioMixer *CreateADPCMMixer( CWaveData *data ) +{ + return new CAudioMixerWaveADPCM( data ); +} diff --git a/soundsystem/snd_wave_mixer_adpcm.h b/soundsystem/snd_wave_mixer_adpcm.h new file mode 100644 index 0000000..467ab5f --- /dev/null +++ b/soundsystem/snd_wave_mixer_adpcm.h @@ -0,0 +1,24 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SND_WAVE_MIXER_ADPCM_H +#define SND_WAVE_MIXER_ADPCM_H +#pragma once + + +class CAudioMixer; +class CWaveData; + +CAudioMixer *CreateADPCMMixer( CWaveData *data ); + +#endif // SND_WAVE_MIXER_ADPCM_H diff --git a/soundsystem/snd_wave_mixer_private.h b/soundsystem/snd_wave_mixer_private.h new file mode 100644 index 0000000..9d3dd02 --- /dev/null +++ b/soundsystem/snd_wave_mixer_private.h @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef SND_WAVE_MIXER_PRIVATE_H +#define SND_WAVE_MIXER_PRIVATE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "soundsystem/snd_audio_source.h" +#include "soundsystem/snd_device.h" +#include "snd_wave_mixer.h" + + + +//----------------------------------------------------------------------------- +// Purpose: Linear iterator over source data. +// Keeps track of position in source, and maintains necessary buffers +//----------------------------------------------------------------------------- +class CWaveData +{ +public: + virtual ~CWaveData( void ) {} + virtual CAudioSourceWave &Source( void ) = 0; + virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward = true ) = 0; +}; + +class CAudioMixerWave : public CAudioMixer +{ +public: + CAudioMixerWave( CWaveData *data ); + virtual ~CAudioMixerWave( void ); + + virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true ); + virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true ); + virtual bool SkipSamples( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true ); + virtual void Mix( IAudioDevice *pDevice, + channel_t *pChannel, + void *pData, + int outputOffset, + int inputOffset, + fixedint fracRate, + int outCount, + int timecompress, + bool forward = true ) = 0; + + virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true ); + + virtual CAudioSource *GetSource( void ); + + virtual int GetSamplePosition( void ); + virtual int GetScubPosition( void ); + + virtual bool SetSamplePosition( int position, bool scrubbing = false ); + virtual void SetLoopPosition( int position ); + virtual int GetStartPosition( void ); + + virtual bool GetActive( void ); + virtual void SetActive( bool active ); + + virtual void SetModelIndex( int index ); + virtual int GetModelIndex( void ) const; + + virtual void SetDirection( bool forward ); + virtual bool GetDirection( void ) const; + + virtual void SetAutoDelete( bool autodelete ); + virtual bool GetAutoDelete( void ) const; + + virtual void SetVolume( float volume ); + virtual channel_t *GetChannel(); + +protected: + int m_sample; + int m_absoluteSample; + int m_scubSample; + int m_startpos; + int m_loop; + int m_fracOffset; + CWaveData *m_pData; + + int m_absoluteStartPos; + + bool m_bActive; + // Associated playback model in faceposer + int m_nModelIndex; + + bool m_bForward; + + bool m_bAutoDelete; + + channel_t *m_pChannel; +}; + + +#endif // SND_WAVE_MIXER_PRIVATE_H diff --git a/soundsystem/snd_wave_source.cpp b/soundsystem/snd_wave_source.cpp new file mode 100644 index 0000000..765a2b3 --- /dev/null +++ b/soundsystem/snd_wave_source.cpp @@ -0,0 +1,602 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <windows.h> +#include "tier2/riff.h" +#include "snd_wave_source.h" +#include "snd_wave_mixer_private.h" +#include "soundsystem/snd_audio_source.h" +#include <mmsystem.h> // wave format +#include <mmreg.h> // adpcm format +#include "soundsystem.h" +#include "filesystem.h" +#include "tier1/utlbuffer.h" + + +//----------------------------------------------------------------------------- +// Purpose: Implements the RIFF i/o interface on stdio +//----------------------------------------------------------------------------- +class StdIOReadBinary : public IFileReadBinary +{ +public: + int open( const char *pFileName ) + { + return (int)g_pFullFileSystem->Open( pFileName, "rb", "GAME" ); + } + + int read( void *pOutput, int size, int file ) + { + if ( !file ) + return 0; + + return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file ); + } + + void seek( int file, int pos ) + { + if ( !file ) + return; + + g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); + } + + unsigned int tell( int file ) + { + if ( !file ) + return 0; + + return g_pFullFileSystem->Tell( (FileHandle_t)file ); + } + + unsigned int size( int file ) + { + if ( !file ) + return 0; + + return g_pFullFileSystem->Size( (FileHandle_t)file ); + } + + void close( int file ) + { + if ( !file ) + return; + + g_pFullFileSystem->Close( (FileHandle_t)file ); + } +}; + +static StdIOReadBinary io; + +#define RIFF_WAVE MAKEID('W','A','V','E') +#define WAVE_FMT MAKEID('f','m','t',' ') +#define WAVE_DATA MAKEID('d','a','t','a') +#define WAVE_FACT MAKEID('f','a','c','t') +#define WAVE_CUE MAKEID('c','u','e',' ') + +void ChunkError( unsigned int id ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Init to empty wave +//----------------------------------------------------------------------------- +CAudioSourceWave::CAudioSourceWave( void ) +{ + m_bits = 0; + m_rate = 0; + m_channels = 0; + m_format = 0; + m_pHeader = NULL; + // no looping + m_loopStart = -1; + m_sampleSize = 1; + m_sampleCount = 0; +} + + +CAudioSourceWave::~CAudioSourceWave( void ) +{ + // for non-standard waves, we store a copy of the header in RAM + delete[] m_pHeader; + // m_pWords points into m_pWordBuffer, no need to delete +} + +//----------------------------------------------------------------------------- +// Purpose: Init the wave data. +// Input : *pHeaderBuffer - the RIFF fmt chunk +// headerSize - size of that chunk +//----------------------------------------------------------------------------- +void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize ) +{ + const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer; + + // copy the relevant header data + m_format = pHeader->wFormatTag; + m_bits = pHeader->wBitsPerSample; + m_rate = pHeader->nSamplesPerSec; + m_channels = pHeader->nChannels; + + m_sampleSize = (m_bits * m_channels) / 8; + + // this can never be zero -- other functions divide by this. + // This should never happen, but avoid crashing + if ( m_sampleSize <= 0 ) + m_sampleSize = 1; + + // For non-standard waves (like ADPCM) store the header, it has some useful data + if ( m_format != WAVE_FORMAT_PCM ) + { + m_pHeader = new char[headerSize]; + memcpy( m_pHeader, pHeader, headerSize ); + if ( m_format == WAVE_FORMAT_ADPCM ) + { + // treat ADPCM sources as a file of bytes. They are decoded by the mixer + m_sampleSize = 1; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CAudioSourceWave::TrueSampleSize( void ) +{ + if ( m_format == WAVE_FORMAT_ADPCM ) + { + return 0.5f; + } + return (float)m_sampleSize; +} + +//----------------------------------------------------------------------------- +// Purpose: Total number of samples in this source +// Output : int +//----------------------------------------------------------------------------- +int CAudioSourceWave::SampleCount( void ) +{ + if ( m_format == WAVE_FORMAT_ADPCM ) + { + ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)m_pHeader; + int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2; + blockSize += 7 * pFormat->wfx.nChannels; + + int blockCount = m_sampleCount / blockSize; + int blockRem = m_sampleCount % blockSize; + + // total samples in complete blocks + int sampleCount = blockCount * pFormat->wSamplesPerBlock; + + // add remaining in a short block + if ( blockRem ) + { + sampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / m_channels); + } + return sampleCount; + } + return m_sampleCount; +} + +//----------------------------------------------------------------------------- +// Purpose: Do any sample conversion +// For 8 bit PCM, convert to signed because the mixing routine assumes this +// Input : *pData - pointer to sample data +// sampleCount - number of samples +//----------------------------------------------------------------------------- +void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount ) +{ + if ( m_format == WAVE_FORMAT_PCM ) + { + if ( m_bits == 8 ) + { + for ( int i = 0; i < sampleCount; i++ ) + { + for ( int j = 0; j < m_channels; j++ ) + { + *pData = (unsigned char)((int)((unsigned)*pData) - 128); + pData++; + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &walk - +//----------------------------------------------------------------------------- +void CAudioSourceWave::ParseSentence( IterateRIFF &walk ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + buf.EnsureCapacity( walk.ChunkSize() ); + walk.ChunkRead( buf.Base() ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() ); + + m_Sentence.InitFromDataChunk( buf.Base(), buf.TellPut() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Parse base chunks +// Input : &walk - riff file to parse +// : chunkName - name of the chunk to parse +//----------------------------------------------------------------------------- +// UNDONE: Move parsing loop here and drop each chunk into a virtual function +// instead of this being virtual. +void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName ) +{ + switch( chunkName ) + { + case WAVE_CUE: + { + m_loopStart = ParseCueChunk( walk ); + } + break; + case WAVE_VALVEDATA: + { + ParseSentence( walk ); + } + break; + // unknown/don't care + default: + { + ChunkError( walk.ChunkName() ); + } + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CSentence +//----------------------------------------------------------------------------- +CSentence *CAudioSourceWave::GetSentence( void ) +{ + return &m_Sentence; +} + +//----------------------------------------------------------------------------- +// Purpose: Bastardized construction routine. This is just to avoid complex +// constructor functions so code can be shared more easily by sub-classes +// Input : *pFormatBuffer - RIFF header +// formatSize - header size +// &walk - RIFF file +//----------------------------------------------------------------------------- +void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk ) +{ + Init( pFormatBuffer, formatSize ); + + while ( walk.ChunkAvailable() ) + { + ParseChunk( walk, walk.ChunkName() ); + walk.ChunkNext(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Wave file that is completely in memory +// UNDONE: Implement Lock/Unlock and caching +//----------------------------------------------------------------------------- +class CAudioSourceMemWave : public CAudioSourceWave +{ +public: + CAudioSourceMemWave( void ); + ~CAudioSourceMemWave( void ); + + // Create an instance (mixer) of this audio source + virtual CAudioMixer *CreateMixer( void ); + + virtual void ParseChunk( IterateRIFF &walk, int chunkName ); + void ParseDataChunk( IterateRIFF &walk ); + + virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true ); + virtual float GetRunningLength( void ) { return CAudioSourceWave::GetRunningLength(); }; + + virtual int GetNumChannels(); + +private: + char *m_pData; // wave data +}; + + +//----------------------------------------------------------------------------- +// Purpose: Iterator for wave data (this is to abstract streaming/buffering) +//----------------------------------------------------------------------------- +class CWaveDataMemory : public CWaveData +{ +public: + CWaveDataMemory( CAudioSourceWave &source ) : m_source(source) {} + ~CWaveDataMemory( void ) {} + CAudioSourceWave &Source( void ) { return m_source; } + + // this file is in memory, simply pass along the data request to the source + virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward /*= true*/ ) + { + return m_source.GetOutputData( pData, sampleIndex, sampleCount, forward ); + } +private: + CAudioSourceWave &m_source; // pointer to source +}; + + +//----------------------------------------------------------------------------- +// Purpose: NULL the wave data pointer (we haven't loaded yet) +//----------------------------------------------------------------------------- +CAudioSourceMemWave::CAudioSourceMemWave( void ) +{ + m_pData = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Free any wave data we've allocated +//----------------------------------------------------------------------------- +CAudioSourceMemWave::~CAudioSourceMemWave( void ) +{ + delete[] m_pData; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a mixer and initializes it with an appropriate mixer +//----------------------------------------------------------------------------- +CAudioMixer *CAudioSourceMemWave::CreateMixer( void ) +{ + return CreateWaveMixer( new CWaveDataMemory(*this), m_format, m_channels, m_bits ); +} + +//----------------------------------------------------------------------------- +// Purpose: parse chunks with unique processing to in-memory waves +// Input : &walk - RIFF file +//----------------------------------------------------------------------------- +void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName ) +{ + switch( chunkName ) + { + // this is the audio data + case WAVE_DATA: + { + ParseDataChunk( walk ); + } + return; + } + + CAudioSourceWave::ParseChunk( walk, chunkName ); +} + + +//----------------------------------------------------------------------------- +// Purpose: reads the actual sample data and parses it +// Input : &walk - RIFF file +//----------------------------------------------------------------------------- +void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk ) +{ + int size = walk.ChunkSize(); + + // create a buffer for the samples + m_pData = new char[size]; + + // load them into memory + walk.ChunkRead( m_pData ); + + if ( m_format == WAVE_FORMAT_PCM ) + { + // number of samples loaded + m_sampleCount = size / m_sampleSize; + + // some samples need to be converted + ConvertSamples( m_pData, m_sampleCount ); + } + else if ( m_format == WAVE_FORMAT_ADPCM ) + { + // The ADPCM mixers treat the wave source as a flat file of bytes. + m_sampleSize = 1; + // Since each "sample" is a byte (this is a flat file), the number of samples is the file size + m_sampleCount = size; + + // file says 4, output is 16 + m_bits = 16; + } +} + +int CAudioSourceMemWave::GetNumChannels() +{ + return m_channels; +} + + + +//----------------------------------------------------------------------------- +// Purpose: parses loop information from a cue chunk +// Input : &walk - RIFF iterator +// Output : int loop start position +//----------------------------------------------------------------------------- +int CAudioSourceWave::ParseCueChunk( IterateRIFF &walk ) +{ + // Cue chunk as specified by RIFF format + // see $/research/jay/sound/riffnew.htm + struct + { + unsigned int dwName; + unsigned int dwPosition; + unsigned int fccChunk; + unsigned int dwChunkStart; + unsigned int dwBlockStart; + unsigned int dwSampleOffset; + } cue_chunk; + + int cueCount; + + // assume that the cue chunk stored in the wave is the start of the loop + // assume only one cue chunk, UNDONE: Test this assumption here? + cueCount = walk.ChunkReadInt(); + + walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) ); + return cue_chunk.dwSampleOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: get the wave header +//----------------------------------------------------------------------------- +void *CAudioSourceWave::GetHeader( void ) +{ + return m_pHeader; +} + + +//----------------------------------------------------------------------------- +// Purpose: wrap the position wrt looping +// Input : samplePosition - absolute position +// Output : int - looped position +//----------------------------------------------------------------------------- +int CAudioSourceWave::ConvertLoopedPosition( int samplePosition ) +{ + // if the wave is looping and we're past the end of the sample + // convert to a position within the loop + // At the end of the loop, we return a short buffer, and subsequent call + // will loop back and get the rest of the buffer + if ( m_loopStart >= 0 ) + { + if ( samplePosition >= m_sampleCount ) + { + // size of loop + int loopSize = m_sampleCount - m_loopStart; + // subtract off starting bit of the wave + samplePosition -= m_loopStart; + + if ( loopSize ) + { + // "real" position in memory (mod off extra loops) + samplePosition = m_loopStart + (samplePosition % loopSize); + } + // ERROR? if no loopSize + } + } + + return samplePosition; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **pData - output pointer to samples +// samplePosition - position (in samples not bytes) +// sampleCount - number of samples (not bytes) +// Output : int - number of samples available +//----------------------------------------------------------------------------- +int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ ) +{ + // handle position looping + samplePosition = ConvertLoopedPosition( samplePosition ); + + // how many samples are available (linearly not counting looping) + int availableSampleCount = m_sampleCount - samplePosition; + if ( !forward ) + { + if ( samplePosition >= m_sampleCount ) + { + availableSampleCount = 0; + } + else + { + availableSampleCount = samplePosition; + } + } + + // may be asking for a sample out of range, clip at zero + if ( availableSampleCount < 0 ) + availableSampleCount = 0; + + // clip max output samples to max available + if ( sampleCount > availableSampleCount ) + sampleCount = availableSampleCount; + + // byte offset in sample database + samplePosition *= m_sampleSize; + + // if we are returning some samples, store the pointer + if ( sampleCount ) + { + *pData = m_pData + samplePosition; + } + + return sampleCount; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a wave audio source (streaming or in memory) +// Input : *pName - file name +// streaming - if true, don't load, stream each instance +// Output : CAudioSource * - a new source +//----------------------------------------------------------------------------- +// UNDONE : Pool these and check for duplicates? +CAudioSource *CreateWave( const char *pName ) +{ + char formatBuffer[1024]; + InFileRIFF riff( pName, io ); + + if ( riff.RIFFName() != RIFF_WAVE ) + { + Warning("Bad RIFF file type %s\n", pName ); + return NULL; + } + + // set up the iterator for the whole file (root RIFF is a chunk) + IterateRIFF walk( riff, riff.RIFFSize() ); + + int format = 0; + int formatSize = 0; + + // This chunk must be first as it contains the wave's format + // break out when we've parsed it + while ( walk.ChunkAvailable() && format == 0 ) + { + switch( walk.ChunkName() ) + { + case WAVE_FMT: + { + if ( walk.ChunkSize() <= 1024 ) + { + walk.ChunkRead( formatBuffer ); + formatSize = walk.ChunkSize(); + format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag; + } + } + break; + default: + { + ChunkError( walk.ChunkName() ); + } + break; + } + walk.ChunkNext(); + } + + // Not really a WAVE file or no format chunk, bail + if ( !format ) + return NULL; + + CAudioSourceWave *pWave; + + // create the source from this file + pWave = new CAudioSourceMemWave(); + + // init the wave source + pWave->Setup( formatBuffer, formatSize, walk ); + + return pWave; +} + +//----------------------------------------------------------------------------- +// Purpose: Wrapper for CreateWave() +//----------------------------------------------------------------------------- +CAudioSource *Audio_CreateMemoryWave( const char *pName ) +{ + return CreateWave( pName ); +} diff --git a/soundsystem/snd_wave_source.h b/soundsystem/snd_wave_source.h new file mode 100644 index 0000000..a335507 --- /dev/null +++ b/soundsystem/snd_wave_source.h @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef SND_WAVE_SOURCE_H +#define SND_WAVE_SOURCE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "soundsystem/snd_audio_source.h" +#include "sentence.h" + +class IterateRIFF; + +class CAudioSourceWave : public CAudioSource +{ +public: + CAudioSourceWave( void ); + ~CAudioSourceWave( void ); + + void Setup( const char *pFormat, int formatSize, IterateRIFF &walk ); + + virtual int SampleRate( void ) { return m_rate; } + inline int SampleSize( void ) { return m_sampleSize; } + virtual float TrueSampleSize( void ); + + void *GetHeader( void ); + + // Legacy + virtual void ParseChunk( IterateRIFF &walk, int chunkName ); + + virtual void ParseSentence( IterateRIFF &walk ); + + void ConvertSamples( char *pData, int sampleCount ); + bool IsLooped( void ) { return (m_loopStart >= 0) ? true : false; } + bool IsStreaming( void ) { return false; } + int ConvertLoopedPosition( int samplePosition ); + + int SampleCount( void ); + + virtual float GetRunningLength( void ) + { + if ( m_rate > 0.0 ) + { + return (float)SampleCount() / m_rate; + } + return 0.0f; } + + CSentence *GetSentence( void ); + +protected: + // returns the loop start from a cue chunk + int ParseCueChunk( IterateRIFF &walk ); + void Init( const char *pHeaderBuffer, int headerSize ); + + int m_bits; + int m_rate; + int m_channels; + int m_format; + int m_sampleSize; + int m_loopStart; + int m_sampleCount; + +private: + char *m_pHeader; + CSentence m_Sentence; +}; + +#endif // SND_WAVE_SOURCE_H diff --git a/soundsystem/soundsystem.cpp b/soundsystem/soundsystem.cpp new file mode 100644 index 0000000..d48893a --- /dev/null +++ b/soundsystem/soundsystem.cpp @@ -0,0 +1,279 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: DLL interface for low-level sound utilities +// +//===========================================================================// + +#include "soundsystem/isoundsystem.h" +#include "filesystem.h" +#include "tier1/strtools.h" +#include "tier1/convar.h" +#include "mathlib/mathlib.h" +#include "soundsystem/snd_device.h" +#include "datacache/idatacache.h" +#include "soundchars.h" +#include "tier1/utldict.h" +#include "snd_wave_source.h" +#include "snd_dev_wave.h" +#include "tier2/tier2.h" + + +//----------------------------------------------------------------------------- +// External interfaces +//----------------------------------------------------------------------------- +IAudioDevice *g_pAudioDevice = NULL; +ISoundSystem *g_pSoundSystem = NULL; +IDataCache *g_pDataCache = NULL; + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +int g_nSoundFrameCount = 0; + + +//----------------------------------------------------------------------------- +// Purpose: DLL interface for low-level sound utilities +//----------------------------------------------------------------------------- +class CSoundSystem : public CTier2AppSystem< ISoundSystem > +{ + typedef CTier2AppSystem< ISoundSystem > BaseClass; + +public: + // Inherited from IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual void *QueryInterface( const char *pInterfaceName ); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + void Update( float dt ); + void Flush( void ); + + CAudioSource *FindOrAddSound( const char *filename ); + CAudioSource *LoadSound( const char *wavfile ); + void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ); + + bool IsSoundPlaying( CAudioMixer *pMixer ); + CAudioMixer *FindMixer( CAudioSource *source ); + + void StopAll( void ); + void StopSound( CAudioMixer *mixer ); + +private: + struct CSoundFile + { + char filename[ 512 ]; + CAudioSource *source; + long filetime; + }; + + IAudioDevice *m_pAudioDevice; + float m_flElapsedTime; + CUtlVector < CSoundFile > m_ActiveSounds; +}; + + +//----------------------------------------------------------------------------- +// Singleton interface +//----------------------------------------------------------------------------- +static CSoundSystem s_SoundSystem; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundSystem, ISoundSystem, SOUNDSYSTEM_INTERFACE_VERSION, s_SoundSystem ); + + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CSoundSystem::Connect( CreateInterfaceFn factory ) +{ + if ( !BaseClass::Connect( factory ) ) + return false; + + g_pDataCache = (IDataCache*)factory( DATACACHE_INTERFACE_VERSION, NULL ); + g_pSoundSystem = this; + return (g_pFullFileSystem != NULL) && (g_pDataCache != NULL); +} + +void CSoundSystem::Disconnect() +{ + g_pSoundSystem = NULL; + g_pDataCache = NULL; + BaseClass::Disconnect(); +} + + +//----------------------------------------------------------------------------- +// Query interface +//----------------------------------------------------------------------------- +void *CSoundSystem::QueryInterface( const char *pInterfaceName ) +{ + if (!Q_strncmp( pInterfaceName, SOUNDSYSTEM_INTERFACE_VERSION, Q_strlen(SOUNDSYSTEM_INTERFACE_VERSION) + 1)) + return (ISoundSystem*)this; + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +InitReturnVal_t CSoundSystem::Init() +{ + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); + m_flElapsedTime = 0.0f; + m_pAudioDevice = Audio_CreateWaveDevice(); + if ( !m_pAudioDevice->Init() ) + return INIT_FAILED; + + return INIT_OK; +} + +void CSoundSystem::Shutdown() +{ + Msg( "Removing %i sounds\n", m_ActiveSounds.Size() ); + for ( int i = 0 ; i < m_ActiveSounds.Size(); i++ ) + { + CSoundFile *p = &m_ActiveSounds[ i ]; + Msg( "Removing sound: %s\n", p->filename ); + delete p->source; + } + + m_ActiveSounds.RemoveAll(); + + if ( m_pAudioDevice ) + { + m_pAudioDevice->Shutdown(); + delete m_pAudioDevice; + } + + BaseClass::Shutdown(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAudioSource *CSoundSystem::FindOrAddSound( const char *filename ) +{ + CSoundFile *s; + + int i; + for ( i = 0; i < m_ActiveSounds.Size(); i++ ) + { + s = &m_ActiveSounds[ i ]; + Assert( s ); + if ( !stricmp( s->filename, filename ) ) + { + long filetime = g_pFullFileSystem->GetFileTime( filename ); + if ( filetime != s->filetime ) + { + Msg( "Reloading sound %s\n", filename ); + delete s->source; + s->source = LoadSound( filename ); + s->filetime = filetime; + } + return s->source; + } + } + + i = m_ActiveSounds.AddToTail(); + s = &m_ActiveSounds[ i ]; + strcpy( s->filename, filename ); + s->source = LoadSound( filename ); + s->filetime = g_pFullFileSystem->GetFileTime( filename ); + + return s->source; +} + +CAudioSource *CSoundSystem::LoadSound( const char *wavfile ) +{ + if ( !m_pAudioDevice ) + return NULL; + + CAudioSource *wave = AudioSource_Create( wavfile ); + return wave; +} + +void CSoundSystem::PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ) +{ + if ( ppMixer ) + { + *ppMixer = NULL; + } + + if ( m_pAudioDevice ) + { + CAudioMixer *mixer = source->CreateMixer(); + if ( ppMixer ) + { + *ppMixer = mixer; + } + mixer->SetVolume( volume ); + m_pAudioDevice->AddSource( mixer ); + } +} + +void CSoundSystem::Update( float dt ) +{ +// closecaptionmanager->PreProcess( g_nSoundFrameCount ); + + if ( m_pAudioDevice ) + { + m_pAudioDevice->Update( m_flElapsedTime ); + } + +// closecaptionmanager->PostProcess( g_nSoundFrameCount, dt ); + + m_flElapsedTime += dt; + g_nSoundFrameCount++; +} + +void CSoundSystem::Flush( void ) +{ + if ( m_pAudioDevice ) + { + m_pAudioDevice->Flush(); + } +} + +void CSoundSystem::StopAll( void ) +{ + if ( m_pAudioDevice ) + { + m_pAudioDevice->StopSounds(); + } +} + +void CSoundSystem::StopSound( CAudioMixer *mixer ) +{ + int idx = m_pAudioDevice->FindSourceIndex( mixer ); + if ( idx != -1 ) + { + m_pAudioDevice->FreeChannel( idx ); + } +} + +bool CSoundSystem::IsSoundPlaying( CAudioMixer *pMixer ) +{ + if ( !m_pAudioDevice || !pMixer ) + return false; + + // + int index = m_pAudioDevice->FindSourceIndex( pMixer ); + if ( index != -1 ) + return true; + + return false; +} + +CAudioMixer *CSoundSystem::FindMixer( CAudioSource *source ) +{ + if ( !m_pAudioDevice ) + return NULL; + + return m_pAudioDevice->GetMixerForSource( source ); +}
\ No newline at end of file diff --git a/soundsystem/soundsystem.h b/soundsystem/soundsystem.h new file mode 100644 index 0000000..4eac24b --- /dev/null +++ b/soundsystem/soundsystem.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: DLL interface for low-level sound utilities +// +//===========================================================================// + +#ifndef SOUNDSYSTEM_H +#define SOUNDSYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier2/tier2.h" +#include "tier3/tier3.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class ISoundSystem; +class ISoundSystemServices; +class IAudioDevice; + + +//----------------------------------------------------------------------------- +// Singleton interface +//----------------------------------------------------------------------------- +extern ISoundSystem *g_pSoundSystem; +extern ISoundSystemServices *g_pSoundServices; +extern IAudioDevice *g_pAudioDevice; + + +#endif // SOUNDSYSTEM_H diff --git a/soundsystem/soundsystem.vpc b/soundsystem/soundsystem.vpc new file mode 100644 index 0000000..d502d87 --- /dev/null +++ b/soundsystem/soundsystem.vpc @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// SOUNDSYSTEM.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + $PreprocessorDefinitions "$BASE;SOUNDSYSTEM_EXPORTS" + } + + $Linker + { + $AdditionalDependencies "$BASE winmm.lib" + } +} + +$Project "Soundsystem" +{ + $Folder "Source Files" + { + $File "$SRCDIR\public\sentence.cpp" + $File "snd_audio_source.cpp" + $File "snd_dev_wave.cpp" + $File "snd_io.cpp" + $File "snd_wave_mixer.cpp" + $File "snd_wave_mixer_adpcm.cpp" + $File "snd_wave_source.cpp" + $File "soundsystem.cpp" + } + + $Folder "Header Files" + { + $File "snd_dev_wave.h" + $File "snd_wave_mixer.h" + $File "snd_wave_mixer_adpcm.h" + $File "snd_wave_mixer_private.h" + $File "snd_wave_source.h" + $File "soundsystem.h" + } + + $Folder "Interface" + { + $File "$SRCDIR\public\soundsystem\isoundsystem.h" + $File "$SRCDIR\public\soundsystem\snd_audio_source.h" + $File "$SRCDIR\public\soundsystem\snd_device.h" + } + + $Folder "Link Libraries" + { + $Lib mathlib + $Lib tier2 + $File "$SRCDIR\dx9sdk\lib\dsound.lib" + $File "$SRCDIR\dx9sdk\lib\dxguid.lib" + $File "$SRCDIR\lib\common\mss32.lib" + } +} |