1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-12-24 13:45:47 +00:00

Do all sound decoding in *one* background thread, instead of one thread per decoder.

This commit is contained in:
fgenesis 2012-05-25 17:38:59 +02:00
parent 6ffb668668
commit f953f2b5ab
4 changed files with 333 additions and 73 deletions

View file

@ -42,6 +42,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ogg/ogg.h"
#include "vorbis/vorbisfile.h"
#include "MT.h"
#ifndef _DEBUG
//#define _DEBUG 1
#endif
@ -79,7 +81,13 @@ public:
static int mem_seek(void *datasource, ogg_int64_t offset, int whence);
static long mem_tell(void *datasource);
static void startDecoderThread();
static void stopDecoderThread();
private:
void _stop();
// Decoding loop, run in a separate thread (if threads are available).
static void decode_loop(OggDecoder *this_);
@ -107,19 +115,27 @@ private:
ALenum format;
int freq;
#ifdef BBGE_BUILD_SDL
SDL_Thread *thread;
#else
#warning Threads not supported, music may cut out on area changes!
// ... because the stream runs out of decoded data while the area is
// still loading, so OpenAL aborts playback.
#endif
volatile bool stop_thread;
bool thread; // true if played by background thread
bool playing;
bool loop;
bool eof; // End of file _or_ unrecoverable error encountered
bool stopped; // true if enqueued deletion
unsigned int samples_done; // Number of samples played and dequeued
#ifdef BBGE_BUILD_SDL
static SDL_Thread *decoderThread;
static LockedQueue<OggDecoder*> decoderQ;
static volatile bool stop_thread;
static std::list<OggDecoder*> decoderList; // used by decoder thread only
#else
#warning Threads not supported, music may cut out on area changes!
// ... because the stream runs out of decoded data while the area is
// still loading, so OpenAL aborts playback.
#endif
static void detachDecoder(OggDecoder *);
};
// File I/O callback set (OV_CALLBACKS_NOCLOSE from libvorbis 1.2.0).
@ -155,6 +171,88 @@ static const ov_callbacks ogg_memory_callbacks = {
OggDecoder::mem_tell
};
#ifdef BBGE_BUILD_SDL
SDL_Thread *OggDecoder::decoderThread = NULL;
LockedQueue<OggDecoder*> OggDecoder::decoderQ;
volatile bool OggDecoder::stop_thread;
std::list<OggDecoder*> OggDecoder::decoderList;
#endif
void OggDecoder::startDecoderThread()
{
#ifdef BBGE_BUILD_SDL
stop_thread = false;
decoderThread = SDL_CreateThread((int (*)(void *))decode_loop, NULL);
if (!decoderThread)
{
debugLog("Failed to create Ogg Vorbis decode thread: "
+ std::string(SDL_GetError()));
}
#endif
}
void OggDecoder::stopDecoderThread()
{
#ifdef BBGE_BUILD_SDL
if (decoderThread)
{
stop_thread = true;
debugLog("Waiting for decoder thread to exit...");
SDL_WaitThread(decoderThread, NULL);
decoderThread = NULL;
}
#endif
}
void OggDecoder::detachDecoder(OggDecoder *ogg)
{
#ifdef BBGE_BUILD_SDL
if(decoderThread)
{
ogg->thread = true;
decoderQ.push(ogg);
}
#endif
}
void OggDecoder::decode_loop(OggDecoder *this_)
{
while (!this_->stop_thread)
{
#ifdef BBGE_BUILD_SDL
SDL_Delay(10);
#endif
// Transfer decoder to this background thread
OggDecoder *ogg;
while(decoderQ.pop(ogg))
decoderList.push_back(ogg);
for(std::list<OggDecoder*>::iterator it = decoderList.begin(); it != decoderList.end(); )
{
ogg = *it;
if (ogg->playing)
{
int processed = 0;
alGetSourcei(ogg->source, AL_BUFFERS_PROCESSED, &processed);
for (int i = 0; i < processed; i++)
{
ogg->samples_done += BUFFER_LENGTH;
ALuint buffer = 0;
alSourceUnqueueBuffers(ogg->source, 1, &buffer);
if (buffer)
ogg->queue(buffer);
}
++it;
}
else
{
delete ogg;
decoderList.erase(it++);
}
}
}
}
OggDecoder::OggDecoder(FILE *fp)
{
@ -167,14 +265,12 @@ OggDecoder::OggDecoder(FILE *fp)
this->data = NULL;
this->data_size = 0;
this->data_pos = 0;
#ifdef BBGE_BUILD_SDL
this->thread = NULL;
#endif
this->stop_thread = true;
this->thread = false;
this->playing = false;
this->loop = false;
this->eof = false;
this->samples_done = 0;
this->stopped = false;
}
OggDecoder::OggDecoder(const void *data, long data_size)
@ -188,20 +284,17 @@ OggDecoder::OggDecoder(const void *data, long data_size)
this->data = (const char *)data;
this->data_size = data_size;
this->data_pos = 0;
#ifdef BBGE_BUILD_SDL
this->thread = NULL;
#endif
this->stop_thread = true;
this->thread = false;
this->playing = false;
this->loop = false;
this->eof = false;
this->samples_done = 0;
this->stopped = false;
}
OggDecoder::~OggDecoder()
{
if (playing)
stop();
_stop();
for (int i = 0; i < NUM_BUFFERS; i++)
{
@ -280,27 +373,15 @@ bool OggDecoder::start(ALuint source, bool loop)
for (int i = 0; i < NUM_BUFFERS; i++)
queue(buffers[i]);
#ifdef BBGE_BUILD_SDL
stop_thread = false;
thread = SDL_CreateThread((int (*)(void *))decode_loop, this);
if (!thread)
{
debugLog("Failed to create Ogg Vorbis decode thread: "
+ std::string(SDL_GetError()));
}
#endif
detachDecoder(this);
return true;
}
void OggDecoder::update()
{
if (!playing)
if (!playing || thread)
return;
#ifdef BBGE_BUILD_SDL
if (thread)
return;
#endif
int processed = 0;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
@ -315,17 +396,18 @@ void OggDecoder::update()
void OggDecoder::stop()
{
if (!playing)
return;
#ifdef BBGE_BUILD_SDL
if (thread)
{
stop_thread = true;
SDL_WaitThread(thread, NULL);
thread = NULL;
}
#endif
playing = false; // The background thread will take care of deletion then.
else
delete this;
}
void OggDecoder::_stop()
{
playing = false;
if (stopped)
return;
ov_clear(&vf);
@ -342,6 +424,7 @@ void OggDecoder::stop()
alDeleteBuffers(1, &buffers[i]);
buffers[i] = 0;
}
stopped = true;
}
double OggDecoder::position()
@ -352,27 +435,6 @@ double OggDecoder::position()
return (double)samples_played / (double)freq;
}
void OggDecoder::decode_loop(OggDecoder *this_)
{
while (!this_->stop_thread)
{
#ifdef BBGE_BUILD_SDL
SDL_Delay(10);
#endif
int processed = 0;
alGetSourcei(this_->source, AL_BUFFERS_PROCESSED, &processed);
for (int i = 0; i < processed; i++)
{
this_->samples_done += BUFFER_LENGTH;
ALuint buffer = 0;
alSourceUnqueueBuffers(this_->source, 1, &buffer);
if (buffer)
this_->queue(buffer);
}
}
}
#if (defined(BBGE_BUILD_SDL) && (SDL_BYTEORDER == SDL_BIG_ENDIAN))
#define BBGE_BIGENDIAN 1
#else
@ -675,16 +737,16 @@ void OpenALChannel::setGroupVolume(const float _volume)
bool OpenALChannel::start(OpenALSound *sound)
{
if (decoder)
delete decoder;
delete decoder;
if (sound->getFile())
decoder = new OggDecoder(sound->getFile());
decoder = new OggDecoder(sound->getFile());
else
decoder = new OggDecoder(sound->getData(), sound->getSize());
decoder = new OggDecoder(sound->getData(), sound->getSize());
if (!decoder->start(sid, sound->isLooping()))
{
delete decoder;
decoder = NULL;
return false;
delete decoder;
decoder = NULL;
return false;
}
return true;
}
@ -693,8 +755,8 @@ void OpenALChannel::update()
{
if (inuse)
{
if (decoder)
decoder->update();
if (decoder)
decoder->update();
ALint state = 0;
alGetSourceiv(sid, AL_SOURCE_STATE, &state);
SANITY_CHECK_OPENAL_CALL();
@ -967,8 +1029,8 @@ FMOD_RESULT OpenALChannel::stop()
{
if (decoder)
{
delete decoder;
decoder = NULL;
decoder->stop();
decoder = NULL;
}
alSourceStop(sid);
SANITY_CHECK_OPENAL_CALL();
@ -1248,6 +1310,9 @@ FMOD_RESULT OpenALSystem::init(int maxchannels, const FMOD_INITFLAGS flags, cons
std::stringstream ss;
ss << "Using " << num_channels << " sound channels.";
debugLog(ss.str());
OggDecoder::startDecoderThread();
return FMOD_OK;
}
@ -1301,6 +1366,8 @@ FMOD_RESULT OpenALSystem::playSound(FMOD_CHANNELINDEX channelid, Sound *_sound,
ALBRIDGE(System,release,(),())
FMOD_RESULT OpenALSystem::release()
{
OggDecoder::stopDecoderThread();
ALCcontext *ctx = alcGetCurrentContext();
if (ctx)
{

75
BBGE/MT.cpp Normal file
View file

@ -0,0 +1,75 @@
#include "MT.h"
#include "Base.h"
#ifdef BBGE_BUILD_SDL
// --------- Lockable ----------
Lockable::Lockable()
: _mtx(NULL)
{
#ifdef BBGE_BUILD_SDL
_mtx = SDL_CreateMutex();
#endif
}
Lockable::~Lockable()
{
#ifdef BBGE_BUILD_SDL
SDL_DestroyMutex((SDL_mutex*)_mtx);
#endif
}
void Lockable::lock()
{
#ifdef BBGE_BUILD_SDL
SDL_LockMutex((SDL_mutex*)_mtx);
#endif
}
void Lockable::unlock()
{
#ifdef BBGE_BUILD_SDL
SDL_UnlockMutex((SDL_mutex*)_mtx);
#endif
}
// --------- Waitable ----------
Waitable::Waitable()
: _cond(NULL)
{
#ifdef BBGE_BUILD_SDL
_cond = SDL_CreateCond();
#endif
}
Waitable::~Waitable()
{
#ifdef BBGE_BUILD_SDL
SDL_DestroyCond((SDL_cond*)_cond);
#endif
}
void Waitable::wait()
{
#ifdef BBGE_BUILD_SDL
SDL_CondWait((SDL_cond*)_cond, (SDL_mutex*)mutex());
#endif
}
void Waitable::signal()
{
#ifdef BBGE_BUILD_SDL
SDL_CondSignal((SDL_cond*)_cond);
#endif
}
void Waitable::broadcast()
{
#ifdef BBGE_BUILD_SDL
SDL_CondBroadcast((SDL_cond*)_cond);
#endif
}
#endif // BBGE_BUILD_SDL

117
BBGE/MT.h Normal file
View file

@ -0,0 +1,117 @@
#ifndef BBGE_MT_H
#define BBGE_MT_H
#include <cstddef>
#include <queue>
#include <map>
class Lockable
{
public:
Lockable();
virtual ~Lockable();
void lock();
void unlock();
protected:
inline void *mutex() { return _mtx; }
private:
void *_mtx;
};
class Waitable : public Lockable
{
public:
Waitable();
virtual ~Waitable();
void wait(); // releases the associated lock while waiting
void signal(); // signal a single waiting thread
void broadcast(); // signal all waiting threads
private:
void *_cond;
};
class MTGuard
{
public:
MTGuard(Lockable& x) : _obj(&x) { _obj->lock(); }
MTGuard(Lockable* x) : _obj(x) { _obj->lock(); }
~MTGuard() { _obj->unlock(); }
Lockable *_obj;
};
template <typename T> class BlockingQueue : public Waitable
{
public:
void push(const T& e)
{
lock();
_q.push(e);
unlock();
signal();
}
bool pop(T& e) // blocks if empty
{
lock();
while(_q.empty())
wait();
e = _q.front();
_q.pop();
unlock();
return true;
}
private:
std::queue<T> _q;
};
template <typename T> class LockedQueue : public Lockable
{
public:
void push(const T& e)
{
lock();
_q.push(e);
unlock();
}
bool pop(T& e) // continues if empty
{
lock();
if(_q.empty())
{
unlock();
return false;
}
e = _q.front();
_q.pop();
unlock();
return true;
}
bool empty()
{
lock();
bool e = _q.empty();
unlock();
return e;
}
bool popIfPossible(T& e)
{
lock();
if(!_q.empty())
{
e = _q.front();
_q.pop();
unlock();
return true;
}
unlock();
return false;
}
private:
std::queue<T> _q;
};
#endif

View file

@ -382,6 +382,7 @@ SET(BBGE_SRCS
${BBGEDIR}/Joystick.cpp
${BBGEDIR}/LensFlare.cpp
${BBGEDIR}/Math.cpp
${BBGEDIR}/MT.cpp
${BBGEDIR}/ParticleEffect.cpp
${BBGEDIR}/ParticleManager.cpp
${BBGEDIR}/Particles.cpp