first commit
This commit is contained in:
248
include/engine/core/mixer_thread_controller.h
Normal file
248
include/engine/core/mixer_thread_controller.h
Normal file
@ -0,0 +1,248 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
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 <FutureData> 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 <MixerVariant> 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 <MixerVariant *> *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 _Thread> class ThreadedMixer : public IMixer
|
||||
{
|
||||
AutoPtr <_Thread> thread;
|
||||
|
||||
bool PutCommand(MixerCommand *cmd, MixerVariant **result = 0)
|
||||
{
|
||||
if (thread.IsNull() || !thread->device)
|
||||
return false;
|
||||
|
||||
ASync::Future <MixerVariant *> 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 <MixerVariant> 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 <MixerVariant> 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 <MixerVariant> 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 <MixerVariant> 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 <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetPitch, channel)));
|
||||
return v.IsValid() ? v->f_value : 1;
|
||||
}
|
||||
virtual float GetChannelVolume(int channel)
|
||||
{
|
||||
AutoPtr <MixerVariant> 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 <MixerVariant> 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 <MixerVariant> 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 <MixerVariant> v(uri ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::LoadSound, uri)) : 0);
|
||||
return v.IsValid() ? v->future_data.Detach() : 0;
|
||||
}
|
||||
virtual void UnloadSound(FutureData *data)
|
||||
{
|
||||
AutoPtr <MixerVariant> v(data ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::UnloadSound, data->Get())) : 0);
|
||||
}
|
||||
|
||||
~ThreadedMixer()
|
||||
{ Close(); }
|
||||
};
|
||||
|
||||
} // Audio
|
||||
} // GS
|
||||
|
||||
|
||||
#endif // __NMIXER_THREAD_CONTROLLER__
|
||||
Reference in New Issue
Block a user