diff options
Diffstat (limited to 'engine/audio/private/snd_wave_mixer.cpp')
| -rw-r--r-- | engine/audio/private/snd_wave_mixer.cpp | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/engine/audio/private/snd_wave_mixer.cpp b/engine/audio/private/snd_wave_mixer.cpp new file mode 100644 index 0000000..484b431 --- /dev/null +++ b/engine/audio/private/snd_wave_mixer.cpp @@ -0,0 +1,788 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "audio_pch.h" +#include "fmtstr.h" +#include "sysexternal.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern bool FUseHighQualityPitch( channel_t *pChannel ); + +//----------------------------------------------------------------------------- +// 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( IWaveData *data ) : CAudioMixerWave( data ) {} + virtual int GetMixSampleSize() { return CalcSampleSize(8, 1); } + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress ) + { + pDevice->Mix8Mono( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 8-bit stereo mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave8Stereo : public CAudioMixerWave +{ +public: + CAudioMixerWave8Stereo( IWaveData *data ) : CAudioMixerWave( data ) {} + virtual int GetMixSampleSize( ) { return CalcSampleSize(8, 2); } + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress ) + { + pDevice->Mix8Stereo( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 16-bit mono mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave16Mono : public CAudioMixerWave +{ +public: + CAudioMixerWave16Mono( IWaveData *data ) : CAudioMixerWave( data ) {} + virtual int GetMixSampleSize() { return CalcSampleSize(16, 1); } + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress ) + { + pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: maps mixing to 16-bit stereo mixer +//----------------------------------------------------------------------------- +class CAudioMixerWave16Stereo : public CAudioMixerWave +{ +public: + CAudioMixerWave16Stereo( IWaveData *data ) : CAudioMixerWave( data ) {} + virtual int GetMixSampleSize() { return CalcSampleSize(16, 2); } + virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress ) + { + pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Create an appropriate 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( IWaveData *data, int format, int nChannels, int bits, int initialStreamPosition ) +{ + CAudioMixer *pMixer = NULL; + + if ( format == WAVE_FORMAT_PCM ) + { + if ( nChannels > 1 ) + { + if ( bits == 8 ) + pMixer = new CAudioMixerWave8Stereo( data ); + else + pMixer = new CAudioMixerWave16Stereo( data ); + } + else + { + if ( bits == 8 ) + pMixer = new CAudioMixerWave8Mono( data ); + else + pMixer = new CAudioMixerWave16Mono( data ); + } + } + else if ( format == WAVE_FORMAT_ADPCM ) + { + return CreateADPCMMixer( data ); + } +#if defined( _X360 ) + else if ( format == WAVE_FORMAT_XMA ) + { + return CreateXMAMixer( data, initialStreamPosition ); + } +#endif + else + { + // unsupported format or wav file missing!!! + return NULL; + } + + if ( pMixer ) + { + Assert( CalcSampleSize(bits, nChannels ) == pMixer->GetMixSampleSize() ); + } + else + { + Assert( 0 ); + } + return pMixer; +} + + +//----------------------------------------------------------------------------- +// Purpose: Init the base WAVE mixer. +// Input : *data - data access object +//----------------------------------------------------------------------------- +CAudioMixerWave::CAudioMixerWave( IWaveData *data ) : m_pData(data) +{ + CAudioSource *pSource = GetSource(); + if ( pSource ) + { + pSource->ReferenceAdd( this ); + } + + m_fsample_index = 0; + m_sample_max_loaded = 0; + m_sample_loaded_index = -1; + m_finished = false; + m_forcedEndSample = 0; + m_delaySamples = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Frees the data access object (we own it after construction) +//----------------------------------------------------------------------------- +CAudioMixerWave::~CAudioMixerWave( void ) +{ + CAudioSource *pSource = GetSource(); + if ( pSource ) + { + pSource->ReferenceRemove( this ); + } + delete m_pData; +} + +bool CAudioMixerWave::IsReadyToMix() +{ + return m_pData->IsReadyToMix(); +} + +//----------------------------------------------------------------------------- +// 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 sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) +{ + int samples_loaded; + // clear this out in case the underlying code leaves it unmodified + *pData = NULL; + samples_loaded = m_pData->ReadSourceData( pData, m_sample_max_loaded, sampleCount, copyBuf ); + + // keep track of total samples loaded + m_sample_max_loaded += samples_loaded; + + // keep track of index of last sample loaded + m_sample_loaded_index += samples_loaded; + + return samples_loaded; +} + + +//----------------------------------------------------------------------------- +// 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 (index of next sample +// to be loaded). +// Output : int (samples from start of wave) +//----------------------------------------------------------------------------- +int CAudioMixerWave::GetSamplePosition( void ) +{ + return m_sample_max_loaded; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : delaySamples - +//----------------------------------------------------------------------------- +void CAudioMixerWave::SetStartupDelaySamples( int delaySamples ) +{ + m_delaySamples = delaySamples; +} + +// Move the current position to newPosition +void CAudioMixerWave::SetSampleStart( int newPosition ) +{ + CAudioSource *pSource = GetSource(); + if ( pSource ) + newPosition = pSource->ZeroCrossingAfter( newPosition ); + + m_fsample_index = newPosition; + + // index of last sample loaded - set to sample at new position + m_sample_loaded_index = newPosition; + m_sample_max_loaded = m_sample_loaded_index + 1; +} + +// End playback at newEndPosition +void CAudioMixerWave::SetSampleEnd( int newEndPosition ) +{ + // forced end of zero means play the whole sample + if ( !newEndPosition ) + newEndPosition = 1; + + CAudioSource *pSource = GetSource(); + if ( pSource ) + newEndPosition = pSource->ZeroCrossingBefore( newEndPosition ); + + // past current position? limit. + if ( newEndPosition < m_fsample_index ) + newEndPosition = m_fsample_index; + + m_forcedEndSample = newEndPosition; +} + +//----------------------------------------------------------------------------- +// Purpose: Skip source data (read but don't mix). The mixer must provide the +// full amount of samples or have silence in its output stream. +//----------------------------------------------------------------------------- +int CAudioMixerWave::SkipSamples( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset ) +{ + float flTempPitch = pChannel->pitch; + pChannel->pitch = 1.0f; + int nRetVal = MixDataToDevice_( NULL, pChannel, sampleCount, outputRate, outputOffset, true ); + pChannel->pitch = flTempPitch; + return nRetVal; +} + +// wrapper routine to append without overflowing the temp buffer +static uint AppendToBuffer( char *pBuffer, const char *pSampleData, size_t nBytes, const char *pBufferEnd ) +{ +#if defined(_WIN32) && !defined(_X360) + // FIXME: Some clients are crashing here. Let's try to detect why. + if ( nBytes > 0 && ( (size_t)pBuffer <= 0xFFF || (size_t)pSampleData <= 0xFFF ) ) + { + Warning( "AppendToBuffer received potentially bad values (%p, %p, %u, %p)\n", pBuffer, pSampleData, (int)nBytes, pBufferEnd ); + } +#endif + + if ( pBufferEnd > pBuffer ) + { + size_t nAvail = pBufferEnd - pBuffer; + size_t nCopy = MIN( nBytes, nAvail ); + Q_memcpy( pBuffer, pSampleData, nCopy ); + return nCopy; + } + else + { + return 0; + } +} + +// Load a static copy buffer (g_temppaintbuffer) with the requested number of samples, +// with the first sample(s) in the buffer always set up as the last sample(s) of the previous load. +// Return a pointer to the head of the copy buffer. +// This ensures that interpolating pitch shifters always have the previous sample to reference. +// pChannel: sound's channel data +// sample_load_request: number of samples to load from source data +// pSamplesLoaded: returns the actual number of samples loaded (should always = sample_load_request) +// copyBuf: req'd by GetOutputData, used by some Mixers +// Returns: NULL ptr to data if no samples available, otherwise always fills remainder of copy buffer with +// 0 to pad remainder. +// NOTE: DO NOT MODIFY THIS ROUTINE (KELLYB) +char *CAudioMixerWave::LoadMixBuffer( channel_t *pChannel, int sample_load_request, int *pSamplesLoaded, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) +{ + int samples_loaded; + char *pSample = NULL; + char *pData = NULL; + int cCopySamps = 0; + + // save index of last sample loaded (updated in GetOutputData) + int sample_loaded_index = m_sample_loaded_index; + + // get data from source (copyBuf is expected to be available for use) + samples_loaded = GetOutputData( (void **)&pData, sample_load_request, copyBuf ); + if ( !samples_loaded && sample_load_request ) + { + // none available, bail out + // 360 might not be able to get samples due to latency of loop seek + // could also be the valid EOF for non-loops (caller keeps polling for data, until no more) + AssertOnce( IsX360() || !m_pData->Source().IsLooped() ); + *pSamplesLoaded = 0; + return NULL; + } + + int samplesize = GetMixSampleSize(); + const int nTempCopyBufferSize = ( TEMP_COPY_BUFFER_SIZE * sizeof( portable_samplepair_t ) ); + char *pCopy = (char *)g_temppaintbuffer; + const char *pCopyBufferEnd = pCopy + nTempCopyBufferSize; + + + + if ( IsX360() || IsDebug() ) + { + // for safety, 360 always validates sample request, due to new xma audio code and possible logic flaws + // PC can expect number of requested samples to be within tolerances due to exisiting aged code + // otherwise buffer overruns cause hard to track random crashes + if ( ( ( sample_load_request + 1 ) * samplesize ) > nTempCopyBufferSize ) + { + // make sure requested samples will fit in temp buffer. + // if this assert fails, then pitch is too high (ie: > 2.0) or the sample counters have diverged. + // NOTE: to prevent this, pitch should always be capped in MixDataToDevice (but isn't nor are the sample counters). + DevWarning( "LoadMixBuffer: sample load request %d exceeds buffer sizes\n", sample_load_request ); + Assert( 0 ); + *pSamplesLoaded = 0; + return NULL; + } + } + + // copy all samples from pData to copy buffer, set 0th sample to saved previous sample - this ensures + // interpolation pitch shift routines always have a previous sample to reference. + + // copy previous sample(s) to head of copy buffer pCopy + // In some cases, we'll need the previous 2 samples. This occurs when + // Rate < 1.0 - in example below, sample 4.86 - 6.48 requires samples 4-7 (previous samples saved are 4 & 5) + + /* + Example: + rate = 0.81, sampleCount = 3 (ie: # of samples to return ) + + _____load 3______ ____load 3_______ __load 2__ + + 0 1 2 3 4 5 6 7 sample_index (whole samples) + + ^ ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | | + 0 0.81 1.68 2.43 3.24 4.05 4.86 5.67 6.48 m_fsample_index (rate*sample) + _______________ ________________ ________________ + ^ ^ ^ ^ + | | | | + m_sample_loaded_index | | m_sample_loaded_index + | | + m_fsample_index---- ----m_fsample_index + + [return 3 samp] [return 3 samp] [return 3 samp] + */ + pSample = &(pChannel->sample_prev[0]); + + // determine how many saved samples we need to copy to head of copy buffer (0,1 or 2) + // so that pitch interpolation will correctly reference samples. + // NOTE: pitch interpolators always reference the sample before and after the indexed sample. + + // cCopySamps = sample_max_loaded - floor(m_fsample_index); + + if ( sample_loaded_index < 0 || (floor(m_fsample_index) > sample_loaded_index)) + { + // no samples previously loaded, or + // next sample index is entirely within the next block of samples to be loaded, + // so we won't need any samples from the previous block. (can occur when rate > 2.0) + cCopySamps = 0; + } + else if ( m_fsample_index < sample_loaded_index ) + { + // next sample index is entirely within the previous block of samples loaded, + // so we'll need the last 2 samples loaded. (can occur when rate < 1.0) + Assert ( ceil(m_fsample_index + 0.00000001) == sample_loaded_index ); + cCopySamps = 2; + } + else + { + // next sample index is between the next block and the previously loaded block, + // so we'll need the last sample loaded. (can occur when 1.0 < rate < 2.0) + Assert( floor(m_fsample_index) == sample_loaded_index ); + cCopySamps = 1; + } + Assert( cCopySamps >= 0 && cCopySamps <= 2 ); + + // point to the sample(s) we are to copy + if ( cCopySamps ) + { + pSample = cCopySamps == 1 ? pSample + samplesize : pSample; + pCopy += AppendToBuffer( pCopy, pSample, samplesize * cCopySamps, pCopyBufferEnd ); + } + + // copy loaded samples from pData into pCopy + // and update pointer to free space in copy buffer + if ( ( samples_loaded * samplesize ) != 0 && !pData ) + { + char const *pWavName = ""; + CSfxTable *source = pChannel->sfx; + if ( source ) + { + pWavName = source->getname(); + } + + Warning( "CAudioMixerWave::LoadMixBuffer: '%s' samples_loaded * samplesize = %i but pData == NULL\n", pWavName, ( samples_loaded * samplesize ) ); + *pSamplesLoaded = 0; + return NULL; + } + + pCopy += AppendToBuffer( pCopy, pData, samples_loaded * samplesize, pCopyBufferEnd ); + + // if we loaded fewer samples than we wanted to, and we're not + // delaying, load more samples or, if we run out of samples from non-looping source, + // pad copy buffer. + if ( samples_loaded < sample_load_request ) + { + // retry loading source data until 0 bytes returned, or we've loaded enough data. + // if we hit 0 bytes, fill remaining space in copy buffer with 0 and exit + int samples_load_extra; + int samples_loaded_retry = -1; + + for ( int k = 0; (k < 10000 && samples_loaded_retry && samples_loaded < sample_load_request); k++ ) + { + // how many more samples do we need to satisfy load request + samples_load_extra = sample_load_request - samples_loaded; + samples_loaded_retry = GetOutputData( (void**)&pData, samples_load_extra, copyBuf ); + + // copy loaded samples from pData into pCopy + if ( samples_loaded_retry ) + { + if ( ( samples_loaded_retry * samplesize ) != 0 && !pData ) + { + Warning( "CAudioMixerWave::LoadMixBuffer: samples_loaded_retry * samplesize = %i but pData == NULL\n", ( samples_loaded_retry * samplesize ) ); + *pSamplesLoaded = 0; + return NULL; + } + + pCopy += AppendToBuffer( pCopy, pData, samples_loaded_retry * samplesize, pCopyBufferEnd ); + samples_loaded += samples_loaded_retry; + } + } + } + + // if we still couldn't load the requested samples, fill rest of copy buffer with 0 + if ( samples_loaded < sample_load_request ) + { + // should always be able to get as many samples as we request from looping sound sources + AssertOnce ( IsX360() || !m_pData->Source().IsLooped() ); + + // these samples are filled with 0, not loaded. + // non-looping source hit end of data, fill rest of g_temppaintbuffer with 0 + int samples_zero_fill = sample_load_request - samples_loaded; + + int nAvail = pCopyBufferEnd - pCopy; + int nFill = samples_zero_fill * samplesize; + nFill = MIN( nAvail, nFill ); + Q_memset( pCopy, 0, nFill ); + pCopy += nFill; + samples_loaded += samples_zero_fill; + } + + if ( samples_loaded >= 2 ) + { + // always save last 2 samples from copy buffer to channel + // (we'll need 0,1 or 2 samples as start of next buffer for interpolation) + Assert( sizeof( pChannel->sample_prev ) >= samplesize*2 ); + pSample = pCopy - samplesize*2; + Q_memcpy( &(pChannel->sample_prev[0]), pSample, samplesize*2 ); + } + + // this routine must always return as many samples loaded (or zeros) as requested. + Assert( samples_loaded == sample_load_request ); + + *pSamplesLoaded = samples_loaded; + + return (char *)g_temppaintbuffer; +} + +// Helper routine to round (rate * samples) down to fixed point precision + +double RoundToFixedPoint( double rate, int samples, bool bInterpolated_pitch ) +{ + fixedint fixp_rate; + int64 d64_newSamps; // need to use double precision int to avoid overflow + + double newSamps; + + // get rate, in fixed point, determine new samples at rate + + if ( bInterpolated_pitch ) + fixp_rate = FIX_FLOAT14(rate); // 14 bit iterator + else + fixp_rate = FIX_FLOAT(rate); // 28 bit iterator + + // get number of new samples, convert back to float + + d64_newSamps = (int64)fixp_rate * (int64)samples; + + if ( bInterpolated_pitch ) + newSamps = FIX_14TODOUBLE(d64_newSamps); + else + newSamps = FIX_TODOUBLE(d64_newSamps); + + return newSamps; +} + +extern double MIX_GetMaxRate( double rate, int sampleCount ); + +// Helper routine for MixDataToDevice: +// Compute number of new samples to load at 'rate' so we can +// output 'sampleCount' samples, from m_fsample_index to fsample_index_end (inclusive) +// rate: sample rate +// sampleCountOut: number of samples calling routine needs to output +// bInterpolated_pitch: true if mixers use interpolating pitch shifters +int CAudioMixerWave::GetSampleLoadRequest( double rate, int sampleCountOut, bool bInterpolated_pitch ) +{ + double fsample_index_end; // index of last sample we'll need + int sample_index_high; // rounded up last sample index + int sample_load_request; // number of samples to load + + // NOTE: we must use fixed point math here, identical to math in mixers, to make sure + // we predict iteration results exactly. + // get floating point sample index of last sample we'll need + fsample_index_end = m_fsample_index + RoundToFixedPoint( rate, sampleCountOut-1, bInterpolated_pitch ); + + // always round up to ensure we'll have that n+1 sample for interpolation + sample_index_high = (int)( ceil( fsample_index_end ) ); + + // make sure we always round the floating point index up by at least 1 sample, + // ie: make sure integer sample_index_high is greater than floating point sample index + if ( (double)sample_index_high <= fsample_index_end ) + { + sample_index_high++; + } + Assert ( sample_index_high > fsample_index_end ); + + // attempt to load enough samples so we can reach sample_index_high sample. + sample_load_request = sample_index_high - m_sample_loaded_index; + Assert( sample_index_high >= m_sample_loaded_index ); + + // NOTE: we can actually return 0 samples to load if rate < 1.0 + // and sampleCountOut == 1. In this case, the output sample + // is computed from the previously saved buffer data. + return sample_load_request; +} + +int CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int sampleCount, int outputRate, int outputOffset ) +{ + return MixDataToDevice_(pDevice, pChannel, sampleCount, outputRate, outputOffset, false ); +} + +//----------------------------------------------------------------------------- +// Purpose: The device calls this to request data. The mixer must provide the +// full amount of samples or have silence in its output stream. +// Mix channel to all active paintbuffers. +// NOTE: cannot be called consecutively to mix into multiple paintbuffers! +// Input : *pDevice - requesting device +// sampleCount - number of samples at the output rate - should never be more than size of paintbuffer. +// outputRate - sampling rate of the request +// outputOffset - starting offset to mix to in paintbuffer +// bskipallmixing - true if we just want to skip ahead in source data + +// Output : Returns true to keep mixing, false to delete this mixer + +// NOTE: DO NOT MODIFY THIS ROUTINE (KELLYB) + +//----------------------------------------------------------------------------- +int CAudioMixerWave::MixDataToDevice_( IAudioDevice *pDevice, channel_t *pChannel, int sampleCount, int outputRate, int outputOffset, bool bSkipAllMixing ) +{ + // shouldn't be playing this if finished, but return if we are + if ( m_finished ) + return 0; + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + // save this to compute total output + int startingOffset = outputOffset; + + double inputRate = (pChannel->pitch * m_pData->Source().SampleRate()); + double rate_max = inputRate / outputRate; + + // If we are terminating this wave prematurely, then make sure we detect the limit + if ( m_forcedEndSample ) + { + // How many total input samples will we need? + int samplesRequired = (int)(sampleCount * rate_max); + // will this hit the end? + if ( m_fsample_index + samplesRequired >= m_forcedEndSample ) + { + // yes, mark finished and truncate the sample request + m_finished = true; + sampleCount = (int)( (m_forcedEndSample - m_fsample_index) / rate_max ); + } + } + + /* + Example: + rate = 1.2, sampleCount = 3 (ie: # of samples to return ) + + ______load 4 samples_____ ________load 4 samples____ ___load 3 samples__ + + 0 1 2 3 4 5 6 7 8 9 10 sample_index (whole samples) + + ^ ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | | + 0 1.2 2.4 3.6 4.8 6.0 7.2 8.4 9.6 m_fsample_index (rate*sample) + _______return 3_______ _______return 3_______ _______return 3__________ + ^ ^ + | | + m_sample_loaded_index----- | (after first load 4 samples, this is where pointers are) + m_fsample_index--------- + */ + while ( sampleCount > 0 ) + { + bool advanceSample = true; + int samples_loaded, outputSampleCount; + char *pData = NULL; + double fsample_index_prev = m_fsample_index; // save so we can modify in LoadMixBuffer + bool bInterpolated_pitch = FUseHighQualityPitch( pChannel ); + double rate; + + VPROF_( bInterpolated_pitch ? "CAudioMixerWave::MixData innerloop interpolated" : "CAudioMixerWave::MixData innerloop not interpolated", 2, VPROF_BUDGETGROUP_OTHER_SOUND, false, BUDGETFLAG_OTHER ); + + // process samples in paintbuffer-sized batches + int sampleCountOut = min( sampleCount, PAINTBUFFER_SIZE ); + + // cap rate so that we never overflow the input copy buffer. + rate = MIX_GetMaxRate( rate_max, sampleCountOut ); + + if ( m_delaySamples > 0 ) + { + // If we are preceding sample playback with a delay, + // just fill data buffer with 0 value samples. + // Because there is no pitch shift applied, outputSampleCount == sampleCountOut. + int num_zero_samples = min( m_delaySamples, sampleCountOut ); + + // Decrement delay counter + m_delaySamples -= num_zero_samples; + + int sampleSize = GetMixSampleSize(); + int readBytes = sampleSize * num_zero_samples; + + // make sure we don't overflow temp copy buffer (g_temppaintbuffer) + Assert ( (TEMP_COPY_BUFFER_SIZE * sizeof(portable_samplepair_t)) > readBytes ); + pData = (char *)g_temppaintbuffer; + + // Now copy in some zeroes + memset( pData, 0, readBytes ); + + // we don't pitch shift these samples, so outputSampleCount == samples_loaded + samples_loaded = num_zero_samples; + outputSampleCount = num_zero_samples; + + advanceSample = false; + + // the zero samples are at the output rate, so set the input/output ratio to 1.0 + rate = 1.0f; + } + else + { + // ask the source for the data... + // temp buffer req'd by some data loaders + char copyBuf[AUDIOSOURCE_COPYBUF_SIZE]; + + // compute number of new samples to load at 'rate' so we can + // output 'sampleCount' samples, from m_fsample_index to fsample_index_end (inclusive) + int sample_load_request = GetSampleLoadRequest( rate, sampleCountOut, bInterpolated_pitch ); + + // return pointer to a new copy buffer (g_temppaintbuffer) loaded with sample_load_request samples + + // first sample(s), which are always the last sample(s) from the previous load. + // Always returns sample_load_request samples. Updates m_sample_max_loaded, m_sample_loaded_index. + pData = LoadMixBuffer( pChannel, sample_load_request, &samples_loaded, copyBuf ); + + // LoadMixBuffer should always return requested samples. + Assert ( !pData || ( samples_loaded == sample_load_request ) ); + + outputSampleCount = sampleCountOut; + } + + // no samples available + if ( !pData ) + { + break; + } + + // get sample fraction from 0th sample in copy buffer + double sampleFraction = m_fsample_index - floor( m_fsample_index ); + + // if just skipping samples in source, don't mix, just keep reading + if ( !bSkipAllMixing ) + { + // mix this data to all active paintbuffers + // Verify that we won't get a buffer overrun. + Assert( floor( sampleFraction + RoundToFixedPoint(rate, (outputSampleCount-1), bInterpolated_pitch) ) <= samples_loaded ); + + int saveIndex = MIX_GetCurrentPaintbufferIndex(); + for ( int i = 0 ; i < g_paintBuffers.Count(); i++ ) + { + if ( g_paintBuffers[i].factive ) + { + // mix channel into all active paintbuffers + MIX_SetCurrentPaintbuffer( i ); + + Mix( + pDevice, // Device. + pChannel, // Channel. + pData, // Input buffer. + outputOffset, // Output position. + FIX_FLOAT( sampleFraction ), // Iterators. + FIX_FLOAT( rate ), + outputSampleCount, + 0 ); + } + } + MIX_SetCurrentPaintbuffer( saveIndex ); + } + + if ( advanceSample ) + { + // update sample index to point to the next sample to output + // if we're not delaying + // Use fixed point math to make sure we exactly match results of mix + // iterators. + m_fsample_index = fsample_index_prev + RoundToFixedPoint( rate, outputSampleCount, bInterpolated_pitch ); + } + + outputOffset += outputSampleCount; + sampleCount -= outputSampleCount; + } + + // Did we run out of samples? if so, mark finished + if ( sampleCount > 0 ) + { + m_finished = true; + } + + // total number of samples mixed !!! at the output clock rate !!! + return outputOffset - startingOffset; +} + + +bool CAudioMixerWave::ShouldContinueMixing( void ) +{ + return !m_finished; +} + +float CAudioMixerWave::ModifyPitch( float pitch ) +{ + return pitch; +} + +float CAudioMixerWave::GetVolumeScale( void ) +{ + return 1.0f; +} + |