Commit b6df4680 authored by Max Kellermann's avatar Max Kellermann

MixerPlugin: convert function pointers to Mixer virtual methods

parent e04090b4
......@@ -46,7 +46,7 @@ mixer_free(Mixer *mixer)
this point (see mixer_auto_close()) */
mixer_close(mixer);
mixer->plugin.finish(mixer);
delete mixer;
}
bool
......@@ -58,12 +58,7 @@ mixer_open(Mixer *mixer, Error &error)
const ScopeLock protect(mixer->mutex);
if (mixer->open)
success = true;
else if (mixer->plugin.open == nullptr)
success = mixer->open = true;
else
success = mixer->open = mixer->plugin.open(mixer, error);
success = mixer->open || (mixer->open = mixer->Open(error));
mixer->failed = !success;
......@@ -76,9 +71,7 @@ mixer_close_internal(Mixer *mixer)
assert(mixer != nullptr);
assert(mixer->open);
if (mixer->plugin.close != nullptr)
mixer->plugin.close(mixer);
mixer->Close();
mixer->open = false;
}
......@@ -128,7 +121,7 @@ mixer_get_volume(Mixer *mixer, Error &error)
const ScopeLock protect(mixer->mutex);
if (mixer->open) {
volume = mixer->plugin.get_volume(mixer, error);
volume = mixer->GetVolume(error);
if (volume < 0 && error.IsDefined())
mixer_failed(mixer);
} else
......@@ -149,6 +142,5 @@ mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
const ScopeLock protect(mixer->mutex);
return mixer->open &&
mixer->plugin.set_volume(mixer, volume, error);
return mixer->open && mixer->SetVolume(volume, error);
}
......@@ -23,6 +23,7 @@
#include "MixerPlugin.hxx"
#include "MixerList.hxx"
#include "thread/Mutex.hxx"
#include "Compiler.h"
class Mixer {
public:
......@@ -53,9 +54,40 @@ public:
Mixer(const Mixer &) = delete;
virtual ~Mixer() {}
bool IsPlugin(const MixerPlugin &other) const {
return &plugin == &other;
}
/**
* Open mixer device
*
* @return true on success, false on error
*/
virtual bool Open(Error &error) = 0;
/**
* Close mixer device
*/
virtual void Close() = 0;
/**
* Reads the current volume.
*
* @return the current volume (0..100 including) or -1 if
* unavailable or on error (error set, mixer will be closed)
*/
gcc_pure
virtual int GetVolume(Error &error) = 0;
/**
* Sets the volume.
*
* @param volume the new volume (0..100 including) @return
* true on success, false on error
*/
virtual bool SetVolume(unsigned volume, Error &error) = 0;
};
#endif
......@@ -47,46 +47,6 @@ struct MixerPlugin {
Error &error);
/**
* Finish and free mixer data
*/
void (*finish)(Mixer *data);
/**
* Open mixer device
*
* @param error_r location to store the error occurring, or
* nullptr to ignore errors
* @return true on success, false on error
*/
bool (*open)(Mixer *data, Error &error);
/**
* Close mixer device
*/
void (*close)(Mixer *data);
/**
* Reads the current volume.
*
* @param error_r location to store the error occurring, or
* nullptr to ignore errors
* @return the current volume (0..100 including) or -1 if
* unavailable or on error (error set, mixer will be closed)
*/
int (*get_volume)(Mixer *mixer, Error &error);
/**
* Sets the volume.
*
* @param error_r location to store the error occurring, or
* nullptr to ignore errors
* @param volume the new volume (0..100 including)
* @return true on success, false on error
*/
bool (*set_volume)(Mixer *mixer, unsigned volume,
Error &error);
/**
* If true, then the mixer is automatically opened, even if
* its audio output is not open. If false, then the mixer is
* disabled as long as its audio output is closed.
......
......@@ -79,13 +79,16 @@ public:
AlsaMixer(EventLoop &_event_loop)
:Mixer(alsa_mixer_plugin), event_loop(_event_loop) {}
virtual ~AlsaMixer();
void Configure(const config_param &param);
bool Setup(Error &error);
bool Open(Error &error);
void Close();
int GetVolume(Error &error);
bool SetVolume(unsigned volume, Error &error);
/* virtual methods from class Mixer */
virtual bool Open(Error &error) override;
virtual void Close() override;
virtual int GetVolume(Error &error) override;
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static constexpr Domain alsa_mixer_domain("alsa_mixer");
......@@ -174,13 +177,8 @@ alsa_mixer_init(EventLoop &event_loop, gcc_unused void *ao,
return am;
}
static void
alsa_mixer_finish(Mixer *data)
AlsaMixer::~AlsaMixer()
{
AlsaMixer *am = (AlsaMixer *)data;
delete am;
/* free libasound's config cache */
snd_config_update_free_global();
}
......@@ -267,14 +265,6 @@ AlsaMixer::Open(Error &error)
return true;
}
static bool
alsa_mixer_open(Mixer *data, Error &error)
{
AlsaMixer *am = (AlsaMixer *)data;
return am->Open(error);
}
inline void
AlsaMixer::Close()
{
......@@ -286,13 +276,6 @@ AlsaMixer::Close()
snd_mixer_close(handle);
}
static void
alsa_mixer_close(Mixer *data)
{
AlsaMixer *am = (AlsaMixer *)data;
am->Close();
}
inline int
AlsaMixer::GetVolume(Error &error)
{
......@@ -332,13 +315,6 @@ AlsaMixer::GetVolume(Error &error)
return ret;
}
static int
alsa_mixer_get_volume(Mixer *mixer, Error &error)
{
AlsaMixer *am = (AlsaMixer *)mixer;
return am->GetVolume(error);
}
inline bool
AlsaMixer::SetVolume(unsigned volume, Error &error)
{
......@@ -367,19 +343,7 @@ AlsaMixer::SetVolume(unsigned volume, Error &error)
return true;
}
static bool
alsa_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
{
AlsaMixer *am = (AlsaMixer *)mixer;
return am->SetVolume(volume, error);
}
const MixerPlugin alsa_mixer_plugin = {
alsa_mixer_init,
alsa_mixer_finish,
alsa_mixer_open,
alsa_mixer_close,
alsa_mixer_get_volume,
alsa_mixer_set_volume,
true,
};
......@@ -41,7 +41,7 @@
#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer"
class OssMixer : public Mixer {
class OssMixer final : public Mixer {
const char *device;
const char *control;
......@@ -52,11 +52,12 @@ public:
OssMixer():Mixer(oss_mixer_plugin) {}
bool Configure(const config_param &param, Error &error);
bool Open(Error &error);
void Close();
int GetVolume(Error &error);
bool SetVolume(unsigned volume, Error &error);
/* virtual methods from class Mixer */
virtual bool Open(Error &error) override;
virtual void Close() override;
virtual int GetVolume(Error &error) override;
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static constexpr Domain oss_mixer_domain("oss_mixer");
......@@ -110,14 +111,6 @@ oss_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused void *ao,
return om;
}
static void
oss_mixer_finish(Mixer *data)
{
OssMixer *om = (OssMixer *) data;
delete om;
}
void
OssMixer::Close()
{
......@@ -126,14 +119,7 @@ OssMixer::Close()
close(device_fd);
}
static void
oss_mixer_close(Mixer *data)
{
OssMixer *om = (OssMixer *) data;
om->Close();
}
inline bool
bool
OssMixer::Open(Error &error)
{
device_fd = open_cloexec(device, O_RDONLY, 0);
......@@ -163,15 +149,7 @@ OssMixer::Open(Error &error)
return true;
}
static bool
oss_mixer_open(Mixer *data, Error &error)
{
OssMixer *om = (OssMixer *) data;
return om->Open(error);
}
inline int
int
OssMixer::GetVolume(Error &error)
{
int left, right, level;
......@@ -197,14 +175,7 @@ OssMixer::GetVolume(Error &error)
return left;
}
static int
oss_mixer_get_volume(Mixer *mixer, Error &error)
{
OssMixer *om = (OssMixer *)mixer;
return om->GetVolume(error);
}
inline bool
bool
OssMixer::SetVolume(unsigned volume, Error &error)
{
int level;
......@@ -224,19 +195,7 @@ OssMixer::SetVolume(unsigned volume, Error &error)
return true;
}
static bool
oss_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
{
OssMixer *om = (OssMixer *)mixer;
return om->SetVolume(volume, error);
}
const MixerPlugin oss_mixer_plugin = {
oss_mixer_init,
oss_mixer_finish,
oss_mixer_open,
oss_mixer_close,
oss_mixer_get_volume,
oss_mixer_set_volume,
true,
};
......@@ -34,74 +34,95 @@
#include <assert.h>
struct PulseMixer final : public Mixer {
class PulseMixer final : public Mixer {
PulseOutput *output;
bool online;
struct pa_cvolume volume;
public:
PulseMixer(PulseOutput *_output)
:Mixer(pulse_mixer_plugin),
output(_output), online(false)
{
}
virtual ~PulseMixer();
void Offline();
void VolumeCallback(const pa_sink_input_info *i, int eol);
void Update(pa_context *context, pa_stream *stream);
/* virtual methods from class Mixer */
virtual bool Open(gcc_unused Error &error) override {
return true;
}
virtual void Close() override {
}
virtual int GetVolume(Error &error) override;
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static constexpr Domain pulse_mixer_domain("pulse_mixer");
static void
pulse_mixer_offline(PulseMixer *pm)
void
PulseMixer::Offline()
{
if (!pm->online)
if (!online)
return;
pm->online = false;
online = false;
GlobalEvents::Emit(GlobalEvents::MIXER);
}
/**
* Callback invoked by pulse_mixer_update(). Receives the new mixer
* value.
*/
static void
pulse_mixer_volume_cb(gcc_unused pa_context *context, const pa_sink_input_info *i,
int eol, void *userdata)
inline void
PulseMixer::VolumeCallback(const pa_sink_input_info *i, int eol)
{
PulseMixer *pm = (PulseMixer *)userdata;
if (eol)
return;
if (i == nullptr) {
pulse_mixer_offline(pm);
Offline();
return;
}
pm->online = true;
pm->volume = i->volume;
online = true;
volume = i->volume;
GlobalEvents::Emit(GlobalEvents::MIXER);
}
/**
* Callback invoked by pulse_mixer_update(). Receives the new mixer
* value.
*/
static void
pulse_mixer_update(PulseMixer *pm,
struct pa_context *context, struct pa_stream *stream)
pulse_mixer_volume_cb(gcc_unused pa_context *context, const pa_sink_input_info *i,
int eol, void *userdata)
{
pa_operation *o;
PulseMixer *pm = (PulseMixer *)userdata;
pm->VolumeCallback(i, eol);
}
inline void
PulseMixer::Update(pa_context *context, pa_stream *stream)
{
assert(context != nullptr);
assert(stream != nullptr);
assert(pa_stream_get_state(stream) == PA_STREAM_READY);
o = pa_context_get_sink_input_info(context,
pa_stream_get_index(stream),
pulse_mixer_volume_cb, pm);
pa_operation *o =
pa_context_get_sink_input_info(context,
pa_stream_get_index(stream),
pulse_mixer_volume_cb, this);
if (o == nullptr) {
FormatError(pulse_mixer_domain,
"pa_context_get_sink_input_info() failed: %s",
pa_strerror(pa_context_errno(context)));
pulse_mixer_offline(pm);
Offline();
return;
}
......@@ -132,14 +153,14 @@ pulse_mixer_on_connect(gcc_unused PulseMixer *pm,
void
pulse_mixer_on_disconnect(PulseMixer *pm)
{
pulse_mixer_offline(pm);
pm->Offline();
}
void
pulse_mixer_on_change(PulseMixer *pm,
struct pa_context *context, struct pa_stream *stream)
{
pulse_mixer_update(pm, context, stream);
pm->Update(context, stream);
}
static Mixer *
......@@ -162,65 +183,48 @@ pulse_mixer_init(gcc_unused EventLoop &event_loop, void *ao,
return pm;
}
static void
pulse_mixer_finish(Mixer *data)
PulseMixer::~PulseMixer()
{
PulseMixer *pm = (PulseMixer *) data;
pulse_output_clear_mixer(pm->output, pm);
delete pm;
pulse_output_clear_mixer(output, this);
}
static int
pulse_mixer_get_volume(Mixer *mixer, gcc_unused Error &error)
int
PulseMixer::GetVolume(gcc_unused Error &error)
{
PulseMixer *pm = (PulseMixer *) mixer;
int ret;
pulse_output_lock(pm->output);
pulse_output_lock(output);
ret = pm->online
? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
int result = online
? (int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM)
: -1;
pulse_output_unlock(pm->output);
pulse_output_unlock(output);
return ret;
return result;
}
static bool
pulse_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
bool
PulseMixer::SetVolume(unsigned new_volume, Error &error)
{
PulseMixer *pm = (PulseMixer *) mixer;
struct pa_cvolume cvolume;
bool success;
pulse_output_lock(pm->output);
pulse_output_lock(output);
if (!pm->online) {
pulse_output_unlock(pm->output);
if (!online) {
pulse_output_unlock(output);
error.Set(pulse_mixer_domain, "disconnected");
return false;
}
pa_cvolume_set(&cvolume, pm->volume.channels,
(pa_volume_t)volume * PA_VOLUME_NORM / 100 + 0.5);
success = pulse_output_set_volume(pm->output, &cvolume, error);
struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels,
(pa_volume_t)new_volume * PA_VOLUME_NORM / 100 + 0.5);
bool success = pulse_output_set_volume(output, &cvolume, error);
if (success)
pm->volume = cvolume;
pulse_output_unlock(pm->output);
volume = cvolume;
pulse_output_unlock(output);
return success;
}
const MixerPlugin pulse_mixer_plugin = {
pulse_mixer_init,
pulse_mixer_finish,
nullptr,
nullptr,
pulse_mixer_get_volume,
pulse_mixer_set_volume,
false,
};
......@@ -20,7 +20,7 @@
#ifndef MPD_PULSE_MIXER_PLUGIN_HXX
#define MPD_PULSE_MIXER_PLUGIN_HXX
struct PulseMixer;
class PulseMixer;
struct pa_context;
struct pa_stream;
......
......@@ -24,13 +24,25 @@
#include "output/plugins/RoarOutputPlugin.hxx"
#include "Compiler.h"
struct RoarMixer final : public Mixer {
class RoarMixer final : public Mixer {
/** the base mixer class */
RoarOutput *self;
public:
RoarMixer(RoarOutput *_output)
:Mixer(roar_mixer_plugin),
self(_output) {}
/* virtual methods from class Mixer */
virtual bool Open(gcc_unused Error &error) override {
return true;
}
virtual void Close() override {
}
virtual int GetVolume(Error &error) override;
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static Mixer *
......@@ -41,35 +53,19 @@ roar_mixer_init(gcc_unused EventLoop &event_loop, void *ao,
return new RoarMixer((RoarOutput *)ao);
}
static void
roar_mixer_finish(Mixer *data)
{
RoarMixer *self = (RoarMixer *) data;
delete self;
}
static int
roar_mixer_get_volume(Mixer *mixer, gcc_unused Error &error)
int
RoarMixer::GetVolume(gcc_unused Error &error)
{
RoarMixer *self = (RoarMixer *)mixer;
return roar_output_get_volume(self->self);
return roar_output_get_volume(self);
}
static bool
roar_mixer_set_volume(Mixer *mixer, unsigned volume,
gcc_unused Error &error)
bool
RoarMixer::SetVolume(unsigned volume, gcc_unused Error &error)
{
RoarMixer *self = (RoarMixer *)mixer;
return roar_output_set_volume(self->self, volume);
return roar_output_set_volume(self, volume);
}
const MixerPlugin roar_mixer_plugin = {
roar_mixer_init,
roar_mixer_finish,
nullptr,
nullptr,
roar_mixer_get_volume,
roar_mixer_set_volume,
false,
};
......@@ -38,7 +38,7 @@ CreateVolumeFilter()
IgnoreError());
}
struct SoftwareMixer final : public Mixer {
class SoftwareMixer final : public Mixer {
Filter *filter;
/**
......@@ -51,6 +51,7 @@ struct SoftwareMixer final : public Mixer {
unsigned volume;
public:
SoftwareMixer()
:Mixer(software_mixer_plugin),
filter(CreateVolumeFilter()),
......@@ -60,10 +61,26 @@ struct SoftwareMixer final : public Mixer {
assert(filter != nullptr);
}
~SoftwareMixer() {
virtual ~SoftwareMixer() {
if (owns_filter)
delete filter;
}
Filter *GetFilter();
/* virtual methods from class Mixer */
virtual bool Open(gcc_unused Error &error) override {
return true;
}
virtual void Close() override {
}
virtual int GetVolume(gcc_unused Error &error) override {
return volume;
}
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static Mixer *
......@@ -74,59 +91,40 @@ software_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused void *ao,
return new SoftwareMixer();
}
static void
software_mixer_finish(Mixer *data)
bool
SoftwareMixer::SetVolume(unsigned new_volume, gcc_unused Error &error)
{
SoftwareMixer *sm = (SoftwareMixer *)data;
delete sm;
}
assert(new_volume <= 100);
static int
software_mixer_get_volume(Mixer *mixer, gcc_unused Error &error)
{
SoftwareMixer *sm = (SoftwareMixer *)mixer;
if (new_volume >= 100)
new_volume = PCM_VOLUME_1;
else if (new_volume > 0)
new_volume = pcm_float_to_volume((exp(new_volume / 25.0) - 1) /
(54.5981500331F - 1));
return sm->volume;
}
static bool
software_mixer_set_volume(Mixer *mixer, unsigned volume,
gcc_unused Error &error)
{
SoftwareMixer *sm = (SoftwareMixer *)mixer;
assert(volume <= 100);
sm->volume = volume;
if (volume >= 100)
volume = PCM_VOLUME_1;
else if (volume > 0)
volume = pcm_float_to_volume((exp(volume / 25.0) - 1) /
(54.5981500331F - 1));
volume_filter_set(sm->filter, volume);
volume = new_volume;
volume_filter_set(filter, new_volume);
return true;
}
const MixerPlugin software_mixer_plugin = {
software_mixer_init,
software_mixer_finish,
nullptr,
nullptr,
software_mixer_get_volume,
software_mixer_set_volume,
true,
};
inline Filter *
SoftwareMixer::GetFilter()
{
assert(owns_filter);
owns_filter = false;
return filter;
}
Filter *
software_mixer_get_filter(Mixer *mixer)
{
SoftwareMixer *sm = (SoftwareMixer *)mixer;
assert(sm->IsPlugin(software_mixer_plugin));
assert(sm->owns_filter);
sm->owns_filter = false;
return sm->filter;
return sm->GetFilter();
}
......@@ -30,13 +30,25 @@
#include <math.h>
#include <windows.h>
struct WinmmMixer final : public Mixer {
class WinmmMixer final : public Mixer {
WinmmOutput *output;
public:
WinmmMixer(WinmmOutput *_output)
:Mixer(winmm_mixer_plugin),
output(_output) {
}
/* virtual methods from class Mixer */
virtual bool Open(gcc_unused Error &error) override {
return true;
}
virtual void Close() override {
}
virtual int GetVolume(Error &error) override;
virtual bool SetVolume(unsigned volume, Error &error) override;
};
static constexpr Domain winmm_mixer_domain("winmm_mixer");
......@@ -64,20 +76,11 @@ winmm_mixer_init(gcc_unused EventLoop &event_loop, void *ao,
return new WinmmMixer((WinmmOutput *)ao);
}
static void
winmm_mixer_finish(Mixer *data)
{
WinmmMixer *wm = (WinmmMixer *)data;
delete wm;
}
static int
winmm_mixer_get_volume(Mixer *mixer, Error &error)
int
WinmmMixer::GetVolume(Error &error)
{
WinmmMixer *wm = (WinmmMixer *) mixer;
DWORD volume;
HWAVEOUT handle = winmm_output_get_handle(wm->output);
HWAVEOUT handle = winmm_output_get_handle(output);
MMRESULT result = waveOutGetVolume(handle, &volume);
if (result != MMSYSERR_NOERROR) {
......@@ -88,12 +91,11 @@ winmm_mixer_get_volume(Mixer *mixer, Error &error)
return winmm_volume_decode(volume);
}
static bool
winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
bool
WinmmMixer::SetVolume(unsigned volume, Error &error)
{
WinmmMixer *wm = (WinmmMixer *) mixer;
DWORD value = winmm_volume_encode(volume);
HWAVEOUT handle = winmm_output_get_handle(wm->output);
HWAVEOUT handle = winmm_output_get_handle(output);
MMRESULT result = waveOutSetVolume(handle, value);
if (result != MMSYSERR_NOERROR) {
......@@ -106,10 +108,5 @@ winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
const MixerPlugin winmm_mixer_plugin = {
winmm_mixer_init,
winmm_mixer_finish,
nullptr,
nullptr,
winmm_mixer_get_volume,
winmm_mixer_set_volume,
false,
};
......@@ -21,7 +21,7 @@
#define MPD_PULSE_OUTPUT_PLUGIN_HXX
struct PulseOutput;
struct PulseMixer;
class PulseMixer;
struct pa_cvolume;
class Error;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment