summaryrefslogtreecommitdiff
path: root/soundsystem/snd_dev_wave.cpp
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/snd_dev_wave.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'soundsystem/snd_dev_wave.cpp')
-rw-r--r--soundsystem/snd_dev_wave.cpp899
1 files changed, 899 insertions, 0 deletions
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 );
+}
+