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;
}
//-----------------------------------------------------------------------------
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;

	// 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 < CPAINTBUFFERS; 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;
}
//-----------------------------------------------------------------------------
// 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, PAINTBUFFER_SIZE );

	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;
}