void RageSoundDriver::StartMixing( RageSoundBase *pSound )
{
	/* Lock available m_Sounds[], and reserve a slot. */
	m_SoundListMutex.Lock();

	unsigned i;
	for( i = 0; i < ARRAYLEN(m_Sounds); ++i )
		if( m_Sounds[i].m_State == Sound::AVAILABLE )
			break;
	if( i == ARRAYLEN(m_Sounds) )
	{
		m_SoundListMutex.Unlock();
		return;
	}

	Sound &s = m_Sounds[i];
	s.m_State = Sound::BUFFERING;

	/* We've reserved our slot; we can safely unlock now.  Don't hold onto it longer
	 * than needed, since prebuffering might take some time. */
	m_SoundListMutex.Unlock();

	s.m_pSound = pSound;
	s.m_StartTime = pSound->GetStartTime();
	s.m_Buffer.clear();

	/* Initialize the sound buffer. */
	int BufferSize = frames_to_buffer;

	s.Allocate( BufferSize );

//	LOG->Trace("StartMixing(%s) (%p)", s.m_pSound->GetLoadedFilePath().c_str(), s.m_pSound );

	/* Prebuffer some frames before changing the sound to PLAYING. */
	while( s.m_Buffer.num_writable() )
	{
//		LOG->Trace("StartMixing: (#%i) buffering %i (%i writable) (%p)", i, (int) frames_to_buffer, s.buffer.num_writable(), s.m_pSound );
		int iWrote = GetDataForSound( s );
		if( iWrote < 0 )
			break;
	}

	s.m_State = Sound::PLAYING;

//	LOG->Trace("StartMixing: (#%i) finished prebuffering(%s) (%p)", i, s.m_pSound->GetLoadedFilePath().c_str(), s.m_pSound );
}
void RageSoundDriver::DecodeThread()
{
	SetupDecodingThread();

	while( !m_bShutdownDecodeThread )
	{
		/* Fill each playing sound, round-robin. */
		{
			int iSampleRate = GetSampleRate();
			ASSERT_M( iSampleRate > 0, ssprintf("%i", iSampleRate) );
			int iUsecs = 1000000*chunksize() / iSampleRate;
			usleep( iUsecs );
		}

		LockMut( m_Mutex );
//		LOG->Trace("begin mix");

		for( unsigned i = 0; i < ARRAYLEN(m_Sounds); ++i )
		{
			if( m_Sounds[i].m_State != Sound::PLAYING )
				continue;

			Sound *pSound = &m_Sounds[i];

			CHECKPOINT_M("Processing the sound while buffers are available.");
			while( pSound->m_Buffer.num_writable() )
			{
				int iWrote = GetDataForSound( *pSound );
				if( iWrote == RageSoundReader::WOULD_BLOCK )
					break;
				if( iWrote < 0 )
				{
					/* This sound is finishing. */
					pSound->m_State = Sound::STOPPING;
					break;
//					LOG->Trace("mixer: (#%i) eof (%p)", i, pSound->m_pSound );
				}
			}
		}
//		LOG->Trace("end mix");
	}
}
void RageSound_Generic_Software::StartMixing( RageSoundBase *snd )
{
	/* Lock available sounds[], and reserve a slot. */
	m_SoundListMutex.Lock();

	unsigned i;
	for( i = 0; i < ARRAYSIZE(sounds); ++i )
		if( sounds[i].available )
			break;
	if( i == ARRAYSIZE(sounds) )
	{
		m_SoundListMutex.Unlock();
		return;
	}

	sound &s = sounds[i];
	s.available = false;

	/* We've reserved our slot; we can safely unlock now.  Don't hold onto it longer
	 * than needed, since prebuffering might take some time. */
	m_SoundListMutex.Unlock();

	s.snd = snd;
	s.start_time = snd->GetStartTime();
	s.sound_id = snd->GetID();
	s.volume = snd->GetVolume();
	s.buffer.clear();

	/* Initialize the sound buffer. */
	int BufferSize = frames_to_buffer;

	/* If a sound is streaming from disk, use a bigger buffer, so we don't underrun
	 * the BGM if a drive seek takes too long.  Note that in this case, we'll still
	 * underrun any other sounds that are playing, even if they're preloaded.  This
	 * is less of a problem, since the music position isn't based on those.  It could
	 * be fixed by having two decoding threads; one for streaming sounds and one for
	 * non-streaming sounds. */
	if( s.snd->IsStreamingFromDisk() )
		BufferSize = max( BufferSize, min_streaming_buffer_size );
	s.Allocate( BufferSize );

//	LOG->Trace("StartMixing(%s) (%p)", s.snd->GetLoadedFilePath().c_str(), s.snd );

	/* Prebuffer some frames before changing the sound to PLAYING. */
	bool ReachedEOF = false;
	int frames_filled = 0;
	while( !ReachedEOF && frames_filled < min_fill_frames && frames_filled < BufferSize )
	{
//		LOG->Trace("StartMixing: (#%i) buffering %i (%i writable) (%p)", i, (int) frames_to_buffer, s.buffer.num_writable(), s.snd );
		int wrote = GetDataForSound( s );
		frames_filled += wrote;
		if( !wrote )
		{
//		LOG->Trace("StartMixing: XXX hit EOF (%p)", s.snd );
			ReachedEOF = true;
		}
	}

	/* If we hit EOF already, while prebuffering, then go right to STOPPING. */
	s.state = ReachedEOF? sound::STOPPING: sound::PLAYING;

//	LOG->Trace("StartMixing: (#%i) finished prebuffering(%s) (%p)", i, s.snd->GetLoadedFilePath().c_str(), s.snd );
}
void RageSound_Generic_Software::DecodeThread()
{
	/* SOUNDMAN will be set once RageSoundManager's ctor returns and
	 * assigns it; we might get here before that happens, though. */
	while( !SOUNDMAN && !shutdown_decode_thread )
		usleep( 10000 );

	SetupDecodingThread();

	while( !shutdown_decode_thread )
	{
		/* Fill each playing sound, round-robin. */
		usleep( 1000000*chunksize() / GetSampleRate(0) );

		LockMut( m_Mutex );
//		LOG->Trace("begin mix");

		for( unsigned i = 0; i < ARRAYSIZE(sounds); ++i )
		{
			/* The volume can change while the sound is playing; update it. */
			if( sounds[i].state == sound::PLAYING || sounds[i].state == sound::STOPPING )
				sounds[i].volume = sounds[i].snd->GetVolume();
		}

		/*
		 * If a buffer is low on data, keep filling until it has a reasonable amount.
		 * However, once beyond a certain threshold, clamp the rate at which we fill
		 * it.  For example, if the threshold is 4k frames, and we have a 32k frame
		 * buffer, fill the buffer as fast as we can until it reaches 4k frames; but
		 * beyond that point, only fill it at a rate relative to realtime (for example,
		 * at 2x realtime).
		 *
		 * This allows a stream to have a large buffer, for higher reliability, without
		 * causing major CPU bursts when the stream starts or underruns.  (Filling 32k
		 * takes more CPU than filling 4k frames, and may cause a gameplay skip.)
		 */
		for( unsigned i = 0; i < ARRAYSIZE(sounds); ++i )
		{
			if( sounds[i].state != sound::PLAYING )
				continue;

			sound *pSound = &sounds[i];

			CHECKPOINT;
			int frames_filled = 0;
			while( pSound->buffer.num_writable() )
			{
				/* If there are more than min_fill_frames available, check for
				 * rate clamping. */
				if( pSound->buffer.num_readable()*samples_per_block >= unsigned(min_fill_frames) )
				{
					/* Don't write more than two chunks worth of data in one
					 * iteration.  Since we delay for one chunk period per loop,
					 * this means we'll fill at no more than 2x realtime. */
					if( frames_filled >= chunksize()*2 )
						break;
				}

				int wrote = GetDataForSound( *pSound );
				if( !wrote )
				{
					/* This sound is finishing. */
					pSound->state = sound::STOPPING;
					break;
//					LOG->Trace("mixer: (#%i) eof (%p)", i, pSound->snd );
				}

				frames_filled += wrote;
			}
		}
//		LOG->Trace("end mix");
	}
}