Files
Webcam/include/engine/core/mixer_thread_controller.h
2026-06-22 11:49:35 +02:00

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__