summaryrefslogtreecommitdiff
path: root/soundsystem
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /soundsystem
downloadarchived-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.cpp58
-rw-r--r--soundsystem/snd_dev_wave.cpp899
-rw-r--r--soundsystem/snd_dev_wave.h27
-rw-r--r--soundsystem/snd_io.cpp93
-rw-r--r--soundsystem/snd_wave_mixer.cpp527
-rw-r--r--soundsystem/snd_wave_mixer.h31
-rw-r--r--soundsystem/snd_wave_mixer_adpcm.cpp516
-rw-r--r--soundsystem/snd_wave_mixer_adpcm.h24
-rw-r--r--soundsystem/snd_wave_mixer_private.h103
-rw-r--r--soundsystem/snd_wave_source.cpp602
-rw-r--r--soundsystem/snd_wave_source.h76
-rw-r--r--soundsystem/soundsystem.cpp279
-rw-r--r--soundsystem/soundsystem.h35
-rw-r--r--soundsystem/soundsystem.vpc64
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"
+ }
+}