249 lines
7.2 KiB
C++
249 lines
7.2 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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__
|