Manually keep track of the duration each sfx has been played (instead

of relying on the world clock, which can run backwards, e.g. in
FTL mode). Fixes #1624.
This commit is contained in:
hiker 2014-10-18 11:46:22 +11:00
parent 2f45266c6f
commit 19f9afb67f
6 changed files with 69 additions and 57 deletions

View File

@ -35,7 +35,7 @@ public:
/** Late creation, if SFX was initially disabled */
virtual bool init() { return true; }
virtual void updatePlayingSFX(float dt) {}
virtual void setLoop(bool status) {}
virtual void reallySetLoop(bool status) {}
virtual void setPosition(const Vec3 &p) {}

View File

@ -52,6 +52,7 @@ public:
/** Late creation, if SFX was initially disabled */
virtual bool init() = 0;
virtual void updatePlayingSFX(float dt) = 0;
virtual void setPosition(const Vec3 &p) = 0;
virtual void reallySetPosition(const Vec3 &p) = 0;
virtual void setLoop(bool status) = 0;

View File

@ -232,18 +232,6 @@ void SFXManager::queueCommand(SFXCommand *command)
m_sfx_commands.unlock();
} // queueCommand
//----------------------------------------------------------------------------
/** Make sures that the sfx thread is started at least one per frame. It also
* adds an update command for the music manager.
* \param dt Time step size.
*/
void SFXManager::update(float dt)
{
queue(SFX_UPDATE_MUSIC, NULL, dt);
// Wake up the sfx thread to handle all queued up audio commands.
pthread_cond_signal(&m_cond_request);
} // update
//----------------------------------------------------------------------------
/** Puts a NULL request into the queue, which will trigger the thread to
* exit.
@ -309,8 +297,7 @@ void* SFXManager::mainLoop(void *obj)
case SFX_PAUSE_ALL: me->reallyPauseAllNow(); break;
case SFX_RESUME_ALL: me->reallyResumeAllNow(); break;
case SFX_LISTENER: me->reallyPositionListenerNow(); break;
case SFX_UPDATE_MUSIC: music_manager->update(
current->m_parameter.getX()); break;
case SFX_UPDATE: me->reallyUpdateNow(current); break;
default: assert("Not yet supported.");
}
delete current;
@ -593,6 +580,38 @@ void SFXManager::deleteSFXMapping(const std::string &name)
} // deleteSFXMapping
//----------------------------------------------------------------------------
/** Make sures that the sfx thread is started at least one per frame. It also
* adds an update command for the music manager.
* \param dt Time step size.
*/
void SFXManager::update(float dt)
{
queue(SFX_UPDATE, NULL, dt);
// Wake up the sfx thread to handle all queued up audio commands.
pthread_cond_signal(&m_cond_request);
} // update
//----------------------------------------------------------------------------
/** Updates the status of all playing sfx (to test if they are finished).
* This function is executed once per thread (triggered by the
*/
void SFXManager::reallyUpdateNow(SFXCommand *current)
{
assert(current->m_command==SFX_UPDATE);
float dt = current->m_parameter.getX();
music_manager->update(dt);
m_all_sfx.lock();
for (std::vector<SFXBase*>::iterator i = m_all_sfx.getData().begin();
i != m_all_sfx.getData().end(); i++)
{
if((*i)->getStatus()==SFXBase::SFX_PLAYING)
(*i)->updatePlayingSFX(dt);
} // for i in m_all_sfx
m_all_sfx.unlock();
} // reallyUpdateNow
//----------------------------------------------------------------------------
/** Delete a sound effect object, and removes it from the internal list of
* all SFXs. This call deletes the object, and removes it from the list of

View File

@ -74,7 +74,7 @@ public:
SFX_VOLUME,
SFX_LOOP,
SFX_LISTENER,
SFX_UPDATE_MUSIC,
SFX_UPDATE,
SFX_EXIT,
}; // SFXCommands
@ -220,6 +220,7 @@ public:
void resumeAll();
void reallyResumeAllNow();
void update(float dt);
void reallyUpdateNow(SFXCommand *current);
bool soundExist(const std::string &name);
void setMasterSFXVolume(float gain);
float getMasterSFXVolume() const { return m_master_gain; }

View File

@ -51,7 +51,7 @@ SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float gain,
m_gain = -1.0f;
m_master_gain = 1.0f;
m_owns_buffer = owns_buffer;
m_end_time = -1.0f;
m_play_time = 0.0f;
// Don't initialise anything else if the sfx manager was not correctly
// initialised. First of all the initialisation will not work, and it
@ -123,19 +123,24 @@ bool SFXOpenAL::init()
return true;
} // init
// ------------------------------------------------------------------------
/** Updates the status of a playing sfx. If the sound has been played long
* enough, mark it to be finished. This avoid (a potentially costly)
* call to openal.
* \param dt Time step size.
*/
void SFXOpenAL::updatePlayingSFX(float dt)
{
assert(m_status==SFX_PLAYING);
m_play_time += dt;
if(!m_loop && m_play_time > m_sound_buffer->getDuration())
m_status = SFX_STOPPED;
} // updatePlayingSFX
// ------------------------------------------------------------------------
/** Returns the status of this sfx. */
SFXBase::SFXStatus SFXOpenAL::getStatus()
{
if(m_status==SFX_PLAYING)
{
if(m_loop) return SFX_PLAYING;
if(World::getWorld() && World::getWorld()->getTime() > m_end_time)
{
m_status = SFX_STOPPED;
return m_status;
}
}
return m_status;
} // getStatus;
@ -295,24 +300,17 @@ void SFXOpenAL::reallyResumeNow()
}
} // reallyResumeNow
//-----------------------------------------------------------------------------
/** Queues up a delete request for this object. This is necessary to avoid
* a crash if the sfx manager thread might be delayed and access this object
* after it was deleted.
*/
void SFXOpenAL::deleteSFX()
{
SFXManager::get()->queue(SFXManager::SFX_DELETE, this);
} // deleteSFX
//-----------------------------------------------------------------------------
/** This actually queues up the sfx in the sfx manager. It will be started
* from a separate thread later (in this frame).
*/
void SFXOpenAL::play()
{
if(m_status==SFX_STOPPED)
m_play_time = 0.0f;
// Technically the sfx is only playing after the sfx thread starts it,
// but this is important to set this here since stk might decide the
// but it is important to set this here since stk might decide to
// delete a sfx if it has finished playing (i.e. is in stopped state)
// - which can happen if the sfx thread had no time to actually start
// it yet.
@ -337,23 +335,6 @@ void SFXOpenAL::reallyPlayNow()
alSourcePlay(m_sound_source);
SFXManager::checkError("playing");
// At non-race time the end time is not important
if(World::getWorld())
{
float t= World::getWorld()->getTime();
// A special case: the track intro music starts at world clock = 0,
// and has a duration of 3.7 seconds. So if the game is paused in the
// first 3.7 seconds, the sfx wil be considered to be not finished
// (since the world clock stays at 0 before the race start), and
// therefore resumed if the game is resumed, which means it is
// played again. To avoid this, any sound starting at t=0 is set
// to have an end time of 0 - which means the track intro music is
// not resumed in this case.
m_end_time = t>0 ? t+m_sound_buffer->getDuration() : 0;
}
else
m_end_time = 1.0f;
} // reallyPlayNow
//-----------------------------------------------------------------------------
@ -410,6 +391,16 @@ void SFXOpenAL::reallySetPosition(const Vec3 &position)
SFXManager::checkError("positioning");
} // reallySetPosition
//-----------------------------------------------------------------------------
/** Queues up a delete request for this object. This is necessary to avoid
* a crash if the sfx manager thread might be delayed and access this object
* after it was deleted.
*/
void SFXOpenAL::deleteSFX()
{
SFXManager::get()->queue(SFXManager::SFX_DELETE, this);
} // deleteSFX
//-----------------------------------------------------------------------------
void SFXOpenAL::onSoundEnabledBack()

View File

@ -72,15 +72,15 @@ private:
/** If this sfx should also free the sound buffer. */
bool m_owns_buffer;
/** Time at which a sfx ends playing. Used to avoid frequently getting
* the openl status (which can slow down stk). */
float m_end_time;
/** How long the sfx has been playing. */
float m_play_time;
public:
SFXOpenAL(SFXBuffer* buffer, bool positional, float gain,
bool owns_buffer = false);
virtual ~SFXOpenAL();
virtual void updatePlayingSFX(float dt);
virtual bool init();
virtual void play();
virtual void reallyPlayNow();