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:
parent
2f45266c6f
commit
19f9afb67f
@ -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) {}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user