/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #ifndef __NMIXER_THREAD_CONTROLLER__ #define __NMIXER_THREAD_CONTROLLER__ #include "core/mixer.h" #include "audio/sample_interface.h" #include "nstring/nstring.h" #include "thread/mutex.h" namespace GS { namespace Audio { // struct MixerVariant : public SharedObject { union { int i_value; float f_value; }; AutoPtr future_data; MixerVariant() : i_value(0) {} MixerVariant(int v) : i_value(v) {} MixerVariant(float v) : f_value(v) {} MixerVariant(FutureData *f) : future_data(f) {} }; typedef SharedPtr sMixerVariant; // struct MixerCommand { enum Code { Nop = 0, LoadSound, UnloadSound, Start, Stream, GetState, Pause, Stop, Resume, Lock, Unlock, Close, SetMaster, SetVolume, SetPitch, SetPanning, SetLoopMode, SetLoopPosition, GetMaster, GetVolume, GetPitch, GetPanning, GetLoopMode, GetLoopPosition }; Code code; int channel; String uri; union { bool b_value; float f_value; int i_value; }; sData data; ASync::Future *result; MixerCommand() {} MixerCommand(Code c) : code(c) {} MixerCommand(Code c, float v) : code(c), f_value(v) {} MixerCommand(Code c, const char *name) : code(c), uri(name) {} MixerCommand(Code c, Data *_data) : code(c), data(_data) {} MixerCommand(Code c, int n) : code(c), channel(n) {} MixerCommand(Code c, int n, Data *d) : code(c), channel(n), data(d) {} MixerCommand(Code c, int n, bool v) : code(c), channel(n), b_value(v) {} MixerCommand(Code c, int n, float v) : code(c), channel(n), f_value(v) {} MixerCommand(Code c, int n, int v) : code(c), channel(n), i_value(v) {} MixerCommand(Code c, int n, const char *name) : code(c), channel(n), uri(name) {} }; /*! @short Threaded mixer controller. @author Emmanuel Julien (ejulien@gsworks.fr) */ template class ThreadedMixer : public IMixer { AutoPtr <_Thread> thread; bool PutCommand(MixerCommand *cmd, MixerVariant **result = 0) { if (thread.IsNull() || !thread->device) return false; ASync::Future future_result; forever { Threading::MutexLock lock(&thread->cmdbuffer_mutex); if (thread->cmdbuffer.GetFree() > 0) if (MixerCommand **put = &thread->cmdbuffer.CurrentPut()) { *put = cmd; cmd->result = result ? &future_result : NULL; // Future is to be Set() by the worker thread. thread->cmdbuffer.Produce(); thread->cmdbuffer_event.Trigger(); break; } } if (result) *result = future_result.Get(); return true; } MixerVariant *PutCommandAndWaitResult(MixerCommand *cmd) { MixerVariant *result; return PutCommand(cmd, &result) ? result : 0; } public: virtual bool Open() { if (thread.IsValid()) return true; thread = new _Thread; return thread->Start(); } virtual void Close() { PutCommand(new MixerCommand(MixerCommand::Close)); if (thread.IsValid()) thread->Join(); thread = 0; } virtual void SuspendWorkerThread() { thread->SuspendMixer(); } virtual void ResumeWorkerThread() { thread->ResumeMixer(); } virtual void SetMasterVolume(float volume = 1.0) { PutCommand(new MixerCommand(MixerCommand::SetMaster, volume)); } virtual float GetMasterVolume() { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetMaster))); return v.IsValid() ? v->f_value : 1; } virtual bool StartFast(int channel, FutureData *data) { return data ? PutCommand(new MixerCommand(MixerCommand::Start, channel, data->Get())) : false; } virtual int Start(int channel, FutureData *data) { AutoPtr v(data ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::Start, channel, data->Get())) : 0); return v.IsValid() ? v->i_value : -1; } virtual bool StreamFast(int channel, const char *uri) { return PutCommand(new MixerCommand(MixerCommand::Stream, channel, uri)); } virtual int Stream(int channel, const char *uri) { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::Stream, channel, uri))); return v.IsValid() ? v->i_value : -1; } virtual void Stop(int channel) { PutCommand(new MixerCommand(MixerCommand::Stop, channel)); } virtual void Pause(int channel) { PutCommand(new MixerCommand(MixerCommand::Pause, channel)); } virtual void Resume(int channel) { PutCommand(new MixerCommand(MixerCommand::Resume, channel)); } virtual int LockChannel() { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::Lock))); return v.IsValid() ? v->i_value : -1; } virtual void UnlockChannel(int channel) { PutCommand(new MixerCommand(MixerCommand::Unlock, channel)); } virtual void UnlockAllChannels() { for (int n = 0; n < 64; ++n) PutCommand(new MixerCommand(MixerCommand::Unlock, n)); } virtual float GetChannelPitch(int channel) { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetPitch, channel))); return v.IsValid() ? v->f_value : 1; } virtual float GetChannelVolume(int channel) { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetVolume, channel))); return v.IsValid() ? v->f_value : 1; } virtual float GetChannelPanning(int channel) { return -1; } virtual Loop GetChannelLoopMode(int channel) { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetLoopMode, channel))); return v.IsValid() ? (v->i_value == AL_TRUE ? Repeat : None) : None; } virtual int GetChannelLoopPosition(int channel) { return 0; } virtual void SetChannelPitch(int channel, float pitch) { PutCommand(new MixerCommand(MixerCommand::SetPitch, channel, pitch)); } virtual void SetChannelVolume(int channel, float volume) { PutCommand(new MixerCommand(MixerCommand::SetVolume, channel, volume)); } virtual void SetChannelPanning(int channel, float panning) { PutCommand(new MixerCommand(MixerCommand::SetPanning, channel, panning)); } virtual void SetChannelLoopMode(int channel, Loop loop = None) { PutCommand(new MixerCommand(MixerCommand::SetLoopMode, channel, loop == IMixer::Repeat ? AL_TRUE : AL_FALSE)); } virtual void SetChannelLoopPosition(int, int ms) {} virtual State GetState(int channel) { AutoPtr v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetState, channel))); if (v.IsValid()) switch (v->i_value) { case AL_INITIAL: case AL_STOPPED: return Stopped; case AL_PAUSED: return Paused; case AL_PLAYING: return Playing; } return Invalid; } virtual FutureData *LoadSound(const char *uri) { AutoPtr v(uri ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::LoadSound, uri)) : 0); return v.IsValid() ? v->future_data.Detach() : 0; } virtual void UnloadSound(FutureData *data) { AutoPtr v(data ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::UnloadSound, data->Get())) : 0); } ~ThreadedMixer() { Close(); } }; } // Audio } // GS #endif // __NMIXER_THREAD_CONTROLLER__