Do not allocate or deallocate memory in the mixing thread. Doing so can block that thread which is possibly a realtime thread. Distinguish between STOPPED and AVAILABLE. In the STOPPED state, the memory for the sound is still around. It will be deallocated in Update() at which time the state will change to AVAILABLE.
The BUFFERING state is to keep all other threads from accessing the sound while StartMixing() is prebuffering. It corresponds to the old state STOPPED with available set to false.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<sound_block> 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
|
||||
|
||||
Reference in New Issue
Block a user