unsigned RageSoundReader_Chain::ActivateSound( const sound &s )
{
	SoundReader *pSound = m_apLoadedSounds[s.sPath];

	ActiveSound add;
	add.fPan = s.fPan;
	add.pSound = pSound->Copy();

	m_apActiveSounds.push_back( add );
	return m_apActiveSounds.size() - 1;
}
bool test_file( const TestFile &tf, int filters )
{
	const char *fn = tf.fn;
			
	LOG->Trace("Testing: %s", fn );
	CString error;
	SoundReader *s = SoundReader_FileReader::OpenFile( fn, error );
	s = ApplyFilters( s, filters );
	
	if( s == NULL )
	{
		LOG->Trace( "File '%s' failed to open: %s", fn, error.c_str() );
		return false;
	}
	//SoundReader *snd = s;
	SoundReader *snd = s->Copy();
	delete s;

	bool ret = RunTests( snd, tf );

	delete snd;

	/* Check SetPosition_Accurate consistency:
	 * 
	 * Reopen the file from scratch, seek to 100ms, read some data, do some
	 * operations that would result in the internal TOC being filled (seek
	 * to the end), then re-read the data at 100ms and make sure it's the same. */
	
	snd = SoundReader_FileReader::OpenFile( fn, error );
	snd = ApplyFilters( snd, filters );
	
	if( snd == NULL )
	{
		LOG->Trace( "File '%s' failed to open: %s", fn, error.c_str() );
		return false;
	}

	if( !CheckSetPositionAccurate( snd ) )
		ret = false;

	delete snd;
	return ret;
}
int RageSoundReader_Chain::SetPosition_Accurate( int ms )
{
	/* Clear m_apActiveSounds. */
	while( !m_apActiveSounds.empty() )
		ReleaseSound( 0 );

	m_iCurrentFrame = int( int64_t(ms) * m_iActualSampleRate / 1000 );

	/* Run through all sounds in the chain, and activate all sounds which have data
	 * at ms. */
	for( unsigned i = 0; i < m_Sounds.size(); ++i )
	{
		sound &sound = m_Sounds[i];

		/* If this sound is in the future, skip it. */
		if( sound.iOffsetMS > ms )
			continue;

		/* Find the SoundReader. */
		int n = ActivateSound( sound );
		SoundReader *pSound = m_apActiveSounds[n].pSound;

		int iOffsetMS = ms - sound.iOffsetMS;
		if( pSound->SetPosition_Accurate(iOffsetMS) == 0 )
		{
			/* We're past the end of this sound. */
			ReleaseSound( n );
			continue;
		}
	}

	m_iNextSound = GetNextSoundIndex();

	/* If no sounds were started, and we have no sounds ahead of us, we've seeked
	 * past EOF. */
	if( m_apActiveSounds.empty() && m_iNextSound == m_Sounds.size() )
		return 0;

	return ms;
}
int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
{
	/* How many samples should we read before we need to start up a sound? */
	int iFramesToRead = INT_MAX;
	if( m_iNextSound < m_Sounds.size() )
	{
		int iStartFrame = m_iCurrentFrame;
		int iOffsetFrame = m_Sounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate);
		ASSERT_M( iOffsetFrame >= iStartFrame, ssprintf("%i %i", iOffsetFrame, iStartFrame) );
		iFramesToRead = iOffsetFrame - iStartFrame;
	}

	iFramesToRead = min( iFramesToRead, iFrames );

	if( iFramesToRead > 0 && m_apActiveSounds.size() == 1 &&
		m_apActiveSounds.front().fPan == 0 &&
		m_apActiveSounds.front().pSound->GetNumChannels() == m_iChannels &&
		m_apActiveSounds.front().pSound->GetSampleRate() == m_iActualSampleRate )
	{
		/* We have only one source, and it matches our target.  Don't mix; read
		 * directly from the source into the destination.  This is to optimize
		 * the common case of having one BGM track and no autoplay sounds. */
		int iBytes = m_apActiveSounds.front().pSound->Read( (char *) pBuffer, iFramesToRead * sizeof(int16_t) * m_iChannels );
		if( iBytes == 0 )
			ReleaseSound( 0 );
		return iBytes / (sizeof(int16_t) * m_iChannels);
	}

	if( iFramesToRead > 0 && !m_apActiveSounds.empty() )
	{
		RageSoundMixBuffer mix;
		/* Read iFramesToRead from each sound. */
		int16_t Buffer[2048];
		iFramesToRead = min( iFramesToRead, 1024 );
		int iMaxFramesRead = 0;
		for( unsigned i = 0; i < m_apActiveSounds.size(); )
		{
			ActiveSound &s = m_apActiveSounds[i];
			SoundReader *pSound = s.pSound;
			int iSamples = min( iFramesToRead * pSound->GetNumChannels(), ARRAYLEN(Buffer) );
			int iBytesRead = pSound->Read( (char *) Buffer, iSamples*sizeof(int16_t) );
			if( iBytesRead == -1 || iBytesRead == 0 )
			{
				/* The sound is at EOF.  Release it. */
				ReleaseSound( i );
				continue;
			}

			int iSamplesRead = iBytesRead / sizeof(int16_t);
			int iFramesRead = iSamplesRead / pSound->GetNumChannels();

			iMaxFramesRead = max( iMaxFramesRead, iFramesRead );

			if( m_iChannels == 2 && pSound->GetNumChannels() == 1 )
			{
				RageSoundUtil::ConvertMonoToStereoInPlace( Buffer, iSamplesRead );
				iSamplesRead *= 2;
			}

			if( fabsf(s.fPan) > 0.0001f )
				RageSoundUtil::Pan( Buffer, iFramesRead, s.fPan );

			mix.write( Buffer, iSamplesRead );
			++i;
		}

		/* Read mixed frames into the output buffer. */
		mix.read( (int16_t *) pBuffer );
		return iMaxFramesRead;
	}

	/* If we have more sounds ahead of us, pretend we read the entire block, since
	 * there's silence in between.  Otherwise, we're at EOF. */
	if( iFramesToRead > 0 )
	{
		memset( pBuffer, 0, iFramesToRead * m_iChannels * sizeof(int16_t) );
		return iFramesToRead;
	}

	return 0;
}