diff --git a/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.cpp b/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.cpp index aa57a62bda..ea94cef5ec 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.cpp @@ -24,8 +24,7 @@ static int underruns = 0, logged_underruns = 0; RageSound_Generic_Software::sound::sound() { snd = NULL; - state = STOPPED; - available = true; + state = AVAILABLE; paused = false; } @@ -71,10 +70,8 @@ void RageSound_Generic_Software::Mix( int16_t *buf, int frames, int64_t frameno, if( s.state == sound::HALTING ) { /* This indicates that this stream can be reused. */ - s.Deallocate(); - s.state = sound::STOPPED; - s.available = true; /* do this last */ s.paused = false; + s.state = sound::STOPPED; // LOG->Trace("set %p from HALTING to STOPPED", sounds[i].snd); continue; @@ -268,8 +265,17 @@ void RageSound_Generic_Software::Update(float delta) * this is the only place it'll be changed (to STOPPED). */ for( unsigned i = 0; i < ARRAYSIZE(sounds); ++i ) { - if( sounds[i].state != sound::STOPPING ) + switch( sounds[i].state ) + { + case sound::STOPPED: + sounds[i].Deallocate(); + sounds[i].state = sound::AVAILABLE; continue; + case sound::STOPPING: + break; + default: + continue; + } if( sounds[i].buffer.num_readable() != 0 ) continue; @@ -311,7 +317,7 @@ void RageSound_Generic_Software::StartMixing( RageSoundBase *snd ) unsigned i; for( i = 0; i < ARRAYSIZE(sounds); ++i ) - if( sounds[i].available ) + if( sounds[i].state == sound::AVAILABLE ) break; if( i == ARRAYSIZE(sounds) ) { @@ -320,7 +326,7 @@ void RageSound_Generic_Software::StartMixing( RageSoundBase *snd ) } sound &s = sounds[i]; - s.available = false; + s.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. */ @@ -376,7 +382,7 @@ void RageSound_Generic_Software::StopMixing( RageSoundBase *snd ) /* Find the sound. */ unsigned i; for( i = 0; i < ARRAYSIZE(sounds); ++i ) - if( !sounds[i].available && sounds[i].snd == snd ) + if( sounds[i].state != sound::AVAILABLE && sounds[i].snd == snd ) break; if( i == ARRAYSIZE(sounds) ) { @@ -411,7 +417,7 @@ bool RageSound_Generic_Software::PauseMixing( RageSoundBase *snd, bool bStop ) /* Find the sound. */ unsigned i; for( i = 0; i < ARRAYSIZE(sounds); ++i ) - if( !sounds[i].available && sounds[i].snd == snd ) + if( sounds[i].state != sound::AVAILABLE && sounds[i].snd == snd ) break; /* A sound can be paused in PLAYING or STOPPING. (STOPPING means the sound diff --git a/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.h b/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.h index cfb95d8978..83f51cf45d 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_Generic_Software.h @@ -61,19 +61,27 @@ private: /* * Thread safety and state transitions: * - * STOPPED: The sound is idle, and can be used to play a new sound. The decoding and - * mixing threads will not touch a sound in this state. + * AVAILABLE: The sound is available to play a new sound. The decoding and mixing threads + * will not touch a sound in this state. + * + * BUFFERING: The sound is stopped but StartMixing() is prebuffering. No other threads + * will touch a sound that is BUFFERING. This isn't necessary if only the main thread + * can call StartMixing(). + * + * STOPPED: The sound is idle, but memory is still allocated for its buffer. Update() + * will deallocate memory and the sound will be changed to AVAILABLE. * * PLAYING: The sound is being decoded by the decoding thread, and played by the mixing * thread. If the decoding thread hits EOF, the decoding thread will changed the state * to STOPPING. * * STOPPING: The sound is being played by the mixing thread. No new data will be decoded. - * Once the data buffer is empty (all sound has been played), Update() will call StopMixing, - * and the sound will be changed to STOPPED. + * Once the data buffer is empty (all sound has been played), Update() will change the + * sound to HALTING. * - * HALTING: The main thread has called StopMixing. The mixing thread will flush the buffered - * data without playing it, and then move the sound to STOPPED. + * HALTING: The main thread has called StopMixing or the data buffer is empty. The mixing + * thread will flush any remaining buffered data without playing it, and then move the + * sound to STOPPED. * * The mixing thread operates without any locks. This can lead to a little overlap. For * example, if StopMixing() is called, moving the sound from PLAYING to HALTING, the mixing @@ -87,6 +95,9 @@ private: * * The only state change made by the mixing thread is from HALTING to STOPPED. * This is done with no locks; no other thread can take a sound out of the HALTING state. + * + * Do not allocate or deallocate memory in the mixing thread since allocating memory + * involves taking a lock. Instead, push the deallocation to the main thread. */ struct sound_block { @@ -106,12 +117,13 @@ private: CircBuf buffer; /* If true, this sound is in STOPPED and available for use. */ - bool available; bool paused; enum { + AVAILABLE, + BUFFERING, STOPPED, /* idle */ /* This state is set by the decoder thread, indicating that the sound has just