//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //===========================================================================// #include #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" // NOTE: This has to be the last file included! #include "tier0/memdbgon.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; }