Run SFX in a separate thread (for #1511).

This commit is contained in:
hiker 2014-09-22 17:04:14 +10:00
parent 2b86705dc0
commit 99fd49bdc0
4 changed files with 98 additions and 19 deletions

View File

@ -24,6 +24,7 @@
#include "io/file_manager.hpp"
#include "race/race_manager.hpp"
#include <pthread.h>
#include <stdexcept>
#include <algorithm>
#include <map>
@ -68,6 +69,7 @@ void SFXManager::destroy()
*/
SFXManager::SFXManager()
{
// The sound manager initialises OpenAL
m_initialized = music_manager->initialized();
m_master_gain = UserConfigParams::m_sfx_volume;
@ -75,11 +77,39 @@ SFXManager::SFXManager()
m_position = Vec3(0,0,0);
loadSfx();
pthread_cond_init(&m_cond_request, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Should be the default, but just in case:
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
m_thread_id.setAtomic(new pthread_t());
// The thread is created even if there atm sfx are disabled
// (since the user might enable it later).
int error = pthread_create(m_thread_id.getData(), &attr,
&SFXManager::mainLoop, this);
if (error)
{
m_thread_id.lock();
delete m_thread_id.getData();
m_thread_id.unlock();
m_thread_id.setAtomic(0);
Log::error("HTTP Manager", "Could not create thread, error=%d.",
errno);
}
pthread_attr_destroy(&attr);
if (!sfxAllowed()) return;
setMasterSFXVolume( UserConfigParams::m_sfx_volume );
m_sfx_to_play.lock();
m_sfx_to_play.getData().clear();
m_sfx_to_play.unlock();
} // SoundManager
//-----------------------------------------------------------------------------
@ -87,6 +117,12 @@ SFXManager::SFXManager()
*/
SFXManager::~SFXManager()
{
m_thread_id.lock();
pthread_join(*m_thread_id.getData(), NULL);
delete m_thread_id.getData();
m_thread_id.unlock();
pthread_cond_destroy(&m_cond_request);
// ---- clear m_all_sfx
const int sfx_amount = m_all_sfx.size();
for (int n=0; n<sfx_amount; n++)
@ -131,29 +167,68 @@ void SFXManager::queue(SFXBase *sfx)
{
// Don't add sfx that are either not working correctly (e.g. because sfx
// are disabled);
if(sfx->getStatus()==SFX_UNKNOWN ) return;
if(sfx && sfx->getStatus()==SFX_UNKNOWN ) return;
m_sfx_to_play.lock();
m_sfx_to_play.getData().push_back(sfx);
m_sfx_to_play.unlock();
} // playSFX
// Wake up the sfx thread
pthread_cond_signal(&m_cond_request);
} // queue
//----------------------------------------------------------------------------
/** Starts all sfx that are queues to be started. Called once per frame.
/** Puts a NULL request into the queue, which will trigger the thread to
* exit.
*/
void SFXManager::update()
void SFXManager::stopThread()
{
m_sfx_to_play.lock();
while(!m_sfx_to_play.getData().empty())
queue(NULL);
} // stopThread
//----------------------------------------------------------------------------
/** This loops runs in a different threads, and starts sfx to be played.
* This can sometimes take up to 5 ms, so it needs to be handled in a thread
* in order to avoid rendering delays.
* \param obj A pointer to the SFX singleton.
*/
void* SFXManager::mainLoop(void *obj)
{
SFXManager *me = (SFXManager*)obj;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
me->m_sfx_to_play.lock();
// Wait till we have an empty sfx in the queue
while (me->m_sfx_to_play.getData().empty() ||
me->m_sfx_to_play.getData().front()!=NULL )
{
SFXBase *sfx = m_sfx_to_play.getData().front();
m_sfx_to_play.getData().erase(m_sfx_to_play.getData().begin());
m_sfx_to_play.unlock();
sfx->reallyPlayNow();
m_sfx_to_play.lock();
} // while !empty
m_sfx_to_play.unlock();
} // update
bool empty = me->m_sfx_to_play.getData().empty();
// Wait in cond_wait for a request to arrive. The 'while' is necessary
// since "spurious wakeups from the pthread_cond_wait ... may occur"
// (pthread_cond_wait man page)!
while (empty)
{
pthread_cond_wait(&me->m_cond_request, me->m_sfx_to_play.getMutex());
empty = me->m_sfx_to_play.getData().empty();
}
SFXBase *current = me->m_sfx_to_play.getData().front();
me->m_sfx_to_play.getData().erase(me->m_sfx_to_play.getData().begin());
if (!current) // empty sfx indicates to abort the sfx manager
break;
me->m_sfx_to_play.unlock();
current->reallyPlayNow();
me->m_sfx_to_play.lock();
} // while
return NULL;
} // mainLoop
//----------------------------------------------------------------------------
/** Called then sound is globally switched on or off. It either pauses or

View File

@ -110,15 +110,21 @@ private:
/** Master gain value, taken from the user config value. */
float m_master_gain;
/** Thread id of the thread running in this object. */
Synchronised<pthread_t *> m_thread_id;
/** A conditional variable to wake up the main loop. */
pthread_cond_t m_cond_request;
void loadSfx();
SFXManager();
virtual ~SFXManager();
static void* mainLoop(void *obj);
public:
static void create();
static void destroy();
void queue(SFXBase *sfx);
void update();
// ------------------------------------------------------------------------
/** Static function to get the singleton sfx manager. */
static SFXManager *get()
@ -128,6 +134,7 @@ public:
} // get
// ------------------------------------------------------------------------
void stopThread();
bool sfxAllowed();
SFXBuffer* loadSingleSfx(const XMLNode* node,
const std::string &path=std::string(""),

View File

@ -1413,8 +1413,6 @@ int main(int argc, char *argv[] )
}
#endif
return 0 ;
} // main
@ -1439,6 +1437,7 @@ static void cleanSuperTuxKart()
if(Online::RequestManager::isRunning())
Online::RequestManager::get()->stopNetworkThread();
SFXManager::get()->stopThread();
irr_driver->updateConfigIfRelevant();
AchievementsManager::destroy();
Referee::cleanup();

View File

@ -22,7 +22,6 @@
#include <assert.h>
#include "audio/music_manager.hpp"
#include "audio/sfx_manager.hpp"
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/material_manager.hpp"
@ -140,7 +139,6 @@ void MainLoop::run()
{
PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00);
music_manager->update(dt);
SFXManager::get()->update();
input_manager->update(dt);
#ifdef ENABLE_WIIUSE