diff options
Diffstat (limited to 'src/mixer')
-rw-r--r-- | src/mixer/AlsaMixerPlugin.cxx | 370 | ||||
-rw-r--r-- | src/mixer/OssMixerPlugin.cxx (renamed from src/mixer/oss_mixer_plugin.c) | 162 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.cxx (renamed from src/mixer/pulse_mixer_plugin.c) | 81 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.hxx (renamed from src/mixer/pulse_mixer_plugin.h) | 22 | ||||
-rw-r--r-- | src/mixer/RoarMixerPlugin.cxx | 74 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.cxx | 113 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.hxx (renamed from src/mixer/software_mixer_plugin.h) | 14 | ||||
-rw-r--r-- | src/mixer/WinmmMixerPlugin.cxx (renamed from src/mixer/winmm_mixer_plugin.c) | 57 | ||||
-rw-r--r-- | src/mixer/alsa_mixer_plugin.c | 431 | ||||
-rw-r--r-- | src/mixer/roar_mixer_plugin.c | 104 | ||||
-rw-r--r-- | src/mixer/software_mixer_plugin.c | 109 |
11 files changed, 752 insertions, 785 deletions
diff --git a/src/mixer/AlsaMixerPlugin.cxx b/src/mixer/AlsaMixerPlugin.cxx new file mode 100644 index 000000000..31e9997e3 --- /dev/null +++ b/src/mixer/AlsaMixerPlugin.cxx @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "MixerInternal.hxx" +#include "OutputAPI.hxx" +#include "GlobalEvents.hxx" +#include "Main.hxx" +#include "event/MultiSocketMonitor.hxx" + +#include <algorithm> + +#include <glib.h> +#include <alsa/asoundlib.h> + +#define VOLUME_MIXER_ALSA_DEFAULT "default" +#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" +static constexpr unsigned VOLUME_MIXER_ALSA_INDEX_DEFAULT = 0; + +class AlsaMixerMonitor final : private MultiSocketMonitor { + snd_mixer_t *const mixer; + +public: + AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer) + :MultiSocketMonitor(_loop), mixer(_mixer) {} + +private: + virtual void PrepareSockets(gcc_unused gint *timeout_r) override; + virtual void DispatchSockets() override; +}; + +class AlsaMixer final : public Mixer { + const char *device; + const char *control; + unsigned int index; + + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + long volume_min; + long volume_max; + int volume_set; + + AlsaMixerMonitor *monitor; + +public: + AlsaMixer():Mixer(alsa_mixer_plugin) {} + + void Configure(const config_param ¶m); + bool Setup(GError **error_r); + bool Open(GError **error_r); + void Close(); + + int GetVolume(GError **error_r); + bool SetVolume(unsigned volume, GError **error_r); +}; + +/** + * The quark used for GError.domain. + */ +static inline GQuark +alsa_mixer_quark(void) +{ + return g_quark_from_static_string("alsa_mixer"); +} + +void +AlsaMixerMonitor::PrepareSockets(gcc_unused gint *timeout_r) +{ + int count = snd_mixer_poll_descriptors_count(mixer); + if (count < 0) + count = 0; + + struct pollfd *pfds = g_new(struct pollfd, count); + count = snd_mixer_poll_descriptors(mixer, pfds, count); + if (count < 0) + count = 0; + + struct pollfd *end = pfds + count; + + UpdateSocketList([pfds, end](int fd) -> unsigned { + auto i = std::find_if(pfds, end, [fd](const struct pollfd &pfd){ + return pfd.fd == fd; + }); + if (i == end) + return 0; + + auto events = i->events; + i->events = 0; + return events; + }); + + for (auto i = pfds; i != end; ++i) + if (i->events != 0) + AddSocket(i->fd, i->events); + + g_free(pfds); +} + +void +AlsaMixerMonitor::DispatchSockets() +{ + snd_mixer_handle_events(mixer); +} + +/* + * libasound callbacks + * + */ + +static int +alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask) +{ + if (mask & SND_CTL_EVENT_MASK_VALUE) + GlobalEvents::Emit(GlobalEvents::MIXER); + + return 0; +} + +/* + * mixer_plugin methods + * + */ + +inline void +AlsaMixer::Configure(const config_param ¶m) +{ + device = param.GetBlockValue("mixer_device", + VOLUME_MIXER_ALSA_DEFAULT); + control = param.GetBlockValue("mixer_control", + VOLUME_MIXER_ALSA_CONTROL_DEFAULT); + index = param.GetBlockValue("mixer_index", + VOLUME_MIXER_ALSA_INDEX_DEFAULT); +} + +static Mixer * +alsa_mixer_init(G_GNUC_UNUSED void *ao, const config_param ¶m, + G_GNUC_UNUSED GError **error_r) +{ + AlsaMixer *am = new AlsaMixer(); + am->Configure(param); + + return am; +} + +static void +alsa_mixer_finish(Mixer *data) +{ + AlsaMixer *am = (AlsaMixer *)data; + + delete am; + + /* free libasound's config cache */ + snd_config_update_free_global(); +} + +G_GNUC_PURE +static snd_mixer_elem_t * +alsa_mixer_lookup_elem(snd_mixer_t *handle, const char *name, unsigned idx) +{ + for (snd_mixer_elem_t *elem = snd_mixer_first_elem(handle); + elem != NULL; elem = snd_mixer_elem_next(elem)) { + if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE && + g_ascii_strcasecmp(snd_mixer_selem_get_name(elem), + name) == 0 && + snd_mixer_selem_get_index(elem) == idx) + return elem; + } + + return NULL; +} + +inline bool +AlsaMixer::Setup(GError **error_r) +{ + int err; + + if ((err = snd_mixer_attach(handle, device)) < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "failed to attach to %s: %s", + device, snd_strerror(err)); + return false; + } + + if ((err = snd_mixer_selem_register(handle, NULL, + NULL)) < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "snd_mixer_selem_register() failed: %s", + snd_strerror(err)); + return false; + } + + if ((err = snd_mixer_load(handle)) < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "snd_mixer_load() failed: %s\n", + snd_strerror(err)); + return false; + } + + elem = alsa_mixer_lookup_elem(handle, control, index); + if (elem == NULL) { + g_set_error(error_r, alsa_mixer_quark(), 0, + "no such mixer control: %s", control); + return false; + } + + snd_mixer_selem_get_playback_volume_range(elem, &volume_min, + &volume_max); + + snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback); + + monitor = new AlsaMixerMonitor(*main_loop, handle); + + return true; +} + +inline bool +AlsaMixer::Open(GError **error_r) +{ + int err; + + volume_set = -1; + + err = snd_mixer_open(&handle, 0); + if (err < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "snd_mixer_open() failed: %s", snd_strerror(err)); + return false; + } + + if (!Setup(error_r)) { + snd_mixer_close(handle); + return false; + } + + return true; +} + +static bool +alsa_mixer_open(Mixer *data, GError **error_r) +{ + AlsaMixer *am = (AlsaMixer *)data; + + return am->Open(error_r); +} + +inline void +AlsaMixer::Close() +{ + assert(handle != NULL); + + delete monitor; + + snd_mixer_elem_set_callback(elem, NULL); + snd_mixer_close(handle); +} + +static void +alsa_mixer_close(Mixer *data) +{ + AlsaMixer *am = (AlsaMixer *)data; + am->Close(); +} + +inline int +AlsaMixer::GetVolume(GError **error_r) +{ + int err; + int ret; + long level; + + assert(handle != NULL); + + err = snd_mixer_handle_events(handle); + if (err < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "snd_mixer_handle_events() failed: %s", + snd_strerror(err)); + return false; + } + + err = snd_mixer_selem_get_playback_volume(elem, + SND_MIXER_SCHN_FRONT_LEFT, + &level); + if (err < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "failed to read ALSA volume: %s", + snd_strerror(err)); + return false; + } + + ret = ((volume_set / 100.0) * (volume_max - volume_min) + + volume_min) + 0.5; + if (volume_set > 0 && ret == level) { + ret = volume_set; + } else { + ret = (int)(100 * (((float)(level - volume_min)) / + (volume_max - volume_min)) + 0.5); + } + + return ret; +} + +static int +alsa_mixer_get_volume(Mixer *mixer, GError **error_r) +{ + AlsaMixer *am = (AlsaMixer *)mixer; + return am->GetVolume(error_r); +} + +inline bool +AlsaMixer::SetVolume(unsigned volume, GError **error_r) +{ + float vol; + long level; + int err; + + assert(handle != NULL); + + vol = volume; + + volume_set = vol + 0.5; + + level = (long)(((vol / 100.0) * (volume_max - volume_min) + + volume_min) + 0.5); + level = level > volume_max ? volume_max : level; + level = level < volume_min ? volume_min : level; + + err = snd_mixer_selem_set_playback_volume_all(elem, level); + if (err < 0) { + g_set_error(error_r, alsa_mixer_quark(), err, + "failed to set ALSA volume: %s", + snd_strerror(err)); + return false; + } + + return true; +} + +static bool +alsa_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) +{ + AlsaMixer *am = (AlsaMixer *)mixer; + return am->SetVolume(volume, error_r); +} + +const struct mixer_plugin alsa_mixer_plugin = { + alsa_mixer_init, + alsa_mixer_finish, + alsa_mixer_open, + alsa_mixer_close, + alsa_mixer_get_volume, + alsa_mixer_set_volume, + true, +}; diff --git a/src/mixer/oss_mixer_plugin.c b/src/mixer/OssMixerPlugin.cxx index 608f1f9b8..bbb5b6c88 100644 --- a/src/mixer/oss_mixer_plugin.c +++ b/src/mixer/OssMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,13 +18,14 @@ */ #include "config.h" -#include "mixer_api.h" -#include "output_api.h" +#include "MixerInternal.hxx" +#include "OutputAPI.hxx" #include "fd_util.h" #include <glib.h> #include <assert.h> +#include <string.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> @@ -40,15 +41,22 @@ #define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" -struct oss_mixer { - /** the base mixer class */ - struct mixer base; - +class OssMixer : public Mixer { const char *device; const char *control; int device_fd; int volume_control; + +public: + OssMixer():Mixer(oss_mixer_plugin) {} + + bool Configure(const config_param ¶m, GError **error_r); + bool Open(GError **error_r); + void Close(); + + int GetVolume(GError **error_r); + bool SetVolume(unsigned volume, GError **error_r); }; /** @@ -75,95 +83,114 @@ oss_find_mixer(const char *name) return -1; } -static struct mixer * -oss_mixer_init(G_GNUC_UNUSED void *ao, const struct config_param *param, - GError **error_r) +inline bool +OssMixer::Configure(const config_param ¶m, GError **error_r) { - struct oss_mixer *om = g_new(struct oss_mixer, 1); - - mixer_init(&om->base, &oss_mixer_plugin); - - om->device = config_get_block_string(param, "mixer_device", + device = param.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT); - om->control = config_get_block_string(param, "mixer_control", NULL); + control = param.GetBlockValue("mixer_control"); - if (om->control != NULL) { - om->volume_control = oss_find_mixer(om->control); - if (om->volume_control < 0) { - g_free(om); + if (control != NULL) { + volume_control = oss_find_mixer(control); + if (volume_control < 0) { g_set_error(error_r, oss_mixer_quark(), 0, - "no such mixer control: %s", om->control); - return NULL; + "no such mixer control: %s", control); + return false; } } else - om->volume_control = SOUND_MIXER_PCM; + volume_control = SOUND_MIXER_PCM; - return &om->base; + return true; } -static void -oss_mixer_finish(struct mixer *data) +static Mixer * +oss_mixer_init(G_GNUC_UNUSED void *ao, const config_param ¶m, + GError **error_r) { - struct oss_mixer *om = (struct oss_mixer *) data; + OssMixer *om = new OssMixer(); - g_free(om); + if (!om->Configure(param, error_r)) { + delete om; + return nullptr; + } + + return om; } static void -oss_mixer_close(struct mixer *data) +oss_mixer_finish(Mixer *data) { - struct oss_mixer *om = (struct oss_mixer *) data; + OssMixer *om = (OssMixer *) data; - assert(om->device_fd >= 0); + delete om; +} - close(om->device_fd); +void +OssMixer::Close() +{ + assert(device_fd >= 0); + + close(device_fd); } -static bool -oss_mixer_open(struct mixer *data, GError **error_r) +static void +oss_mixer_close(Mixer *data) { - struct oss_mixer *om = (struct oss_mixer *) data; + OssMixer *om = (OssMixer *) data; + om->Close(); +} - om->device_fd = open_cloexec(om->device, O_RDONLY, 0); - if (om->device_fd < 0) { +inline bool +OssMixer::Open(GError **error_r) +{ + device_fd = open_cloexec(device, O_RDONLY, 0); + if (device_fd < 0) { g_set_error(error_r, oss_mixer_quark(), errno, "failed to open %s: %s", - om->device, g_strerror(errno)); + device, g_strerror(errno)); return false; } - if (om->control) { + if (control) { int devmask = 0; - if (ioctl(om->device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { + if (ioctl(device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { g_set_error(error_r, oss_mixer_quark(), errno, "READ_DEVMASK failed: %s", g_strerror(errno)); - oss_mixer_close(data); + Close(); return false; } - if (((1 << om->volume_control) & devmask) == 0) { + if (((1 << volume_control) & devmask) == 0) { g_set_error(error_r, oss_mixer_quark(), 0, "mixer control \"%s\" not usable", - om->control); - oss_mixer_close(data); + control); + Close(); return false; } } + return true; } -static int -oss_mixer_get_volume(struct mixer *mixer, GError **error_r) +static bool +oss_mixer_open(Mixer *data, GError **error_r) +{ + OssMixer *om = (OssMixer *) data; + + return om->Open(error_r); +} + +inline int +OssMixer::GetVolume(GError **error_r) { - struct oss_mixer *om = (struct oss_mixer *)mixer; int left, right, level; int ret; - assert(om->device_fd >= 0); + assert(device_fd >= 0); - ret = ioctl(om->device_fd, MIXER_READ(om->volume_control), &level); + ret = ioctl(device_fd, MIXER_READ(volume_control), &level); if (ret < 0) { g_set_error(error_r, oss_mixer_quark(), errno, "failed to read OSS volume: %s", @@ -182,19 +209,25 @@ oss_mixer_get_volume(struct mixer *mixer, GError **error_r) return left; } -static bool -oss_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) +static int +oss_mixer_get_volume(Mixer *mixer, GError **error_r) +{ + OssMixer *om = (OssMixer *)mixer; + return om->GetVolume(error_r); +} + +inline bool +OssMixer::SetVolume(unsigned volume, GError **error_r) { - struct oss_mixer *om = (struct oss_mixer *)mixer; int level; int ret; - assert(om->device_fd >= 0); + assert(device_fd >= 0); assert(volume <= 100); level = (volume << 8) + volume; - ret = ioctl(om->device_fd, MIXER_WRITE(om->volume_control), &level); + ret = ioctl(device_fd, MIXER_WRITE(volume_control), &level); if (ret < 0) { g_set_error(error_r, oss_mixer_quark(), errno, "failed to set OSS volume: %s", @@ -205,12 +238,19 @@ oss_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) return true; } +static bool +oss_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) +{ + OssMixer *om = (OssMixer *)mixer; + return om->SetVolume(volume, error_r); +} + const struct mixer_plugin oss_mixer_plugin = { - .init = oss_mixer_init, - .finish = oss_mixer_finish, - .open = oss_mixer_open, - .close = oss_mixer_close, - .get_volume = oss_mixer_get_volume, - .set_volume = oss_mixer_set_volume, - .global = true, + oss_mixer_init, + oss_mixer_finish, + oss_mixer_open, + oss_mixer_close, + oss_mixer_get_volume, + oss_mixer_set_volume, + true, }; diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/PulseMixerPlugin.cxx index a82c032b3..9cfd2dcf8 100644 --- a/src/mixer/pulse_mixer_plugin.c +++ b/src/mixer/PulseMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,11 +18,11 @@ */ #include "config.h" -#include "pulse_mixer_plugin.h" -#include "mixer_api.h" -#include "output/pulse_output_plugin.h" +#include "PulseMixerPlugin.hxx" +#include "MixerInternal.hxx" +#include "output/PulseOutputPlugin.hxx" #include "conf.h" -#include "event_pipe.h" +#include "GlobalEvents.hxx" #include <glib.h> @@ -39,14 +39,17 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "pulse_mixer" -struct pulse_mixer { - struct mixer base; - - struct pulse_output *output; +struct PulseMixer final : public Mixer { + PulseOutput *output; bool online; struct pa_cvolume volume; + PulseMixer(PulseOutput *_output) + :Mixer(pulse_mixer_plugin), + output(_output), online(false) + { + } }; /** @@ -59,14 +62,14 @@ pulse_mixer_quark(void) } static void -pulse_mixer_offline(struct pulse_mixer *pm) +pulse_mixer_offline(PulseMixer *pm) { if (!pm->online) return; pm->online = false; - event_pipe_emit(PIPE_EVENT_MIXER); + GlobalEvents::Emit(GlobalEvents::MIXER); } /** @@ -77,7 +80,7 @@ static void pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i, int eol, void *userdata) { - struct pulse_mixer *pm = userdata; + PulseMixer *pm = (PulseMixer *)userdata; if (eol) return; @@ -90,11 +93,11 @@ pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_inf pm->online = true; pm->volume = i->volume; - event_pipe_emit(PIPE_EVENT_MIXER); + GlobalEvents::Emit(GlobalEvents::MIXER); } static void -pulse_mixer_update(struct pulse_mixer *pm, +pulse_mixer_update(PulseMixer *pm, struct pa_context *context, struct pa_stream *stream) { pa_operation *o; @@ -117,7 +120,7 @@ pulse_mixer_update(struct pulse_mixer *pm, } void -pulse_mixer_on_connect(G_GNUC_UNUSED struct pulse_mixer *pm, +pulse_mixer_on_connect(G_GNUC_UNUSED PulseMixer *pm, struct pa_context *context) { pa_operation *o; @@ -137,58 +140,51 @@ pulse_mixer_on_connect(G_GNUC_UNUSED struct pulse_mixer *pm, } void -pulse_mixer_on_disconnect(struct pulse_mixer *pm) +pulse_mixer_on_disconnect(PulseMixer *pm) { pulse_mixer_offline(pm); } void -pulse_mixer_on_change(struct pulse_mixer *pm, +pulse_mixer_on_change(PulseMixer *pm, struct pa_context *context, struct pa_stream *stream) { pulse_mixer_update(pm, context, stream); } -static struct mixer * -pulse_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param, +static Mixer * +pulse_mixer_init(void *ao, gcc_unused const config_param ¶m, GError **error_r) { - struct pulse_mixer *pm; - struct pulse_output *po = ao; + PulseOutput *po = (PulseOutput *)ao; if (ao == NULL) { g_set_error(error_r, pulse_mixer_quark(), 0, "The pulse mixer cannot work without the audio output"); - return false; + return nullptr; } - pm = g_new(struct pulse_mixer,1); - mixer_init(&pm->base, &pulse_mixer_plugin); - - pm->online = false; - pm->output = po; + PulseMixer *pm = new PulseMixer(po); pulse_output_set_mixer(po, pm); - return &pm->base; + return pm; } static void -pulse_mixer_finish(struct mixer *data) +pulse_mixer_finish(Mixer *data) { - struct pulse_mixer *pm = (struct pulse_mixer *) data; + PulseMixer *pm = (PulseMixer *) data; pulse_output_clear_mixer(pm->output, pm); - /* free resources */ - - g_free(pm); + delete pm; } static int -pulse_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r) +pulse_mixer_get_volume(Mixer *mixer, G_GNUC_UNUSED GError **error_r) { - struct pulse_mixer *pm = (struct pulse_mixer *) mixer; + PulseMixer *pm = (PulseMixer *) mixer; int ret; pulse_output_lock(pm->output); @@ -203,9 +199,9 @@ pulse_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r) } static bool -pulse_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) +pulse_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) { - struct pulse_mixer *pm = (struct pulse_mixer *) mixer; + PulseMixer *pm = (PulseMixer *) mixer; struct pa_cvolume cvolume; bool success; @@ -229,8 +225,11 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) } const struct mixer_plugin pulse_mixer_plugin = { - .init = pulse_mixer_init, - .finish = pulse_mixer_finish, - .get_volume = pulse_mixer_get_volume, - .set_volume = pulse_mixer_set_volume, + pulse_mixer_init, + pulse_mixer_finish, + nullptr, + nullptr, + pulse_mixer_get_volume, + pulse_mixer_set_volume, + false, }; diff --git a/src/mixer/pulse_mixer_plugin.h b/src/mixer/PulseMixerPlugin.hxx index 461633d37..debc2cf3c 100644 --- a/src/mixer/pulse_mixer_plugin.h +++ b/src/mixer/PulseMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,23 +17,31 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_PULSE_MIXER_PLUGIN_H -#define MPD_PULSE_MIXER_PLUGIN_H +#ifndef MPD_PULSE_MIXER_PLUGIN_HXX +#define MPD_PULSE_MIXER_PLUGIN_HXX #include <pulse/def.h> -struct pulse_mixer; +struct PulseMixer; struct pa_context; struct pa_stream; +#ifdef __cplusplus +extern "C" { +#endif + void -pulse_mixer_on_connect(struct pulse_mixer *pm, struct pa_context *context); +pulse_mixer_on_connect(PulseMixer *pm, struct pa_context *context); void -pulse_mixer_on_disconnect(struct pulse_mixer *pm); +pulse_mixer_on_disconnect(PulseMixer *pm); void -pulse_mixer_on_change(struct pulse_mixer *pm, +pulse_mixer_on_change(PulseMixer *pm, struct pa_context *context, struct pa_stream *stream); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/mixer/RoarMixerPlugin.cxx b/src/mixer/RoarMixerPlugin.cxx new file mode 100644 index 000000000..90d54ddaa --- /dev/null +++ b/src/mixer/RoarMixerPlugin.cxx @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft + * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include "config.h" +#include "MixerInternal.hxx" +#include "OutputAPI.hxx" +#include "output/RoarOutputPlugin.hxx" + +struct RoarMixer final : public Mixer { + /** the base mixer class */ + RoarOutput *self; + + RoarMixer(RoarOutput *_output) + :Mixer(roar_mixer_plugin), + self(_output) {} +}; + +static Mixer * +roar_mixer_init(void *ao, gcc_unused const config_param ¶m, + gcc_unused GError **error_r) +{ + 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 GError **error_r) +{ + RoarMixer *self = (RoarMixer *)mixer; + return roar_output_get_volume(self->self); +} + +static bool +roar_mixer_set_volume(Mixer *mixer, unsigned volume, + gcc_unused GError **error_r) +{ + RoarMixer *self = (RoarMixer *)mixer; + return roar_output_set_volume(self->self, volume); +} + +const struct mixer_plugin roar_mixer_plugin = { + roar_mixer_init, + roar_mixer_finish, + nullptr, + nullptr, + roar_mixer_get_volume, + roar_mixer_set_volume, + false, +}; diff --git a/src/mixer/SoftwareMixerPlugin.cxx b/src/mixer/SoftwareMixerPlugin.cxx new file mode 100644 index 000000000..8a268aaf1 --- /dev/null +++ b/src/mixer/SoftwareMixerPlugin.cxx @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "SoftwareMixerPlugin.hxx" +#include "MixerInternal.hxx" +#include "FilterPlugin.hxx" +#include "FilterRegistry.hxx" +#include "FilterInternal.hxx" +#include "filter/VolumeFilterPlugin.hxx" +#include "pcm/PcmVolume.hxx" +#include "ConfigData.hxx" + +#include <assert.h> +#include <math.h> + +struct SoftwareMixer final : public Mixer { + Filter *filter; + + unsigned volume; + + SoftwareMixer() + :Mixer(software_mixer_plugin), + filter(filter_new(&volume_filter_plugin, config_param(), + nullptr)), + volume(100) + { + assert(filter != nullptr); + } + + ~SoftwareMixer() { + delete filter; + } +}; + +static Mixer * +software_mixer_init(gcc_unused void *ao, + gcc_unused const config_param ¶m, + gcc_unused GError **error_r) +{ + return new SoftwareMixer(); +} + +static void +software_mixer_finish(Mixer *data) +{ + SoftwareMixer *sm = (SoftwareMixer *)data; + + delete sm; +} + +static int +software_mixer_get_volume(Mixer *mixer, gcc_unused GError **error_r) +{ + SoftwareMixer *sm = (SoftwareMixer *)mixer; + + return sm->volume; +} + +static bool +software_mixer_set_volume(Mixer *mixer, unsigned volume, + gcc_unused GError **error_r) +{ + 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); + return true; +} + +const struct mixer_plugin software_mixer_plugin = { + software_mixer_init, + software_mixer_finish, + nullptr, + nullptr, + software_mixer_get_volume, + software_mixer_set_volume, + true, +}; + +Filter * +software_mixer_get_filter(Mixer *mixer) +{ + SoftwareMixer *sm = (SoftwareMixer *)mixer; + assert(sm->IsPlugin(software_mixer_plugin)); + + return sm->filter; +} diff --git a/src/mixer/software_mixer_plugin.h b/src/mixer/SoftwareMixerPlugin.hxx index ee2b2023c..be59c08db 100644 --- a/src/mixer/software_mixer_plugin.h +++ b/src/mixer/SoftwareMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,17 +17,17 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef SOFTWARE_MIXER_PLUGIN_H -#define SOFTWARE_MIXER_PLUGIN_H +#ifndef MPD_SOFTWARE_MIXER_PLUGIN_HXX +#define MPD_SOFTWARE_MIXER_PLUGIN_HXX -struct mixer; -struct filter; +class Mixer; +class Filter; /** * Returns the (volume) filter associated with this mixer. All users * of this mixer plugin should install this filter. */ -struct filter * -software_mixer_get_filter(struct mixer *mixer); +Filter * +software_mixer_get_filter(Mixer *mixer); #endif diff --git a/src/mixer/winmm_mixer_plugin.c b/src/mixer/WinmmMixerPlugin.cxx index ceddf6afd..139cb1399 100644 --- a/src/mixer/winmm_mixer_plugin.c +++ b/src/mixer/WinmmMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,9 +18,11 @@ */ #include "config.h" -#include "mixer_api.h" -#include "output_api.h" -#include "output/winmm_output_plugin.h" +#include "MixerInternal.hxx" +#include "OutputAPI.hxx" +#include "output/WinmmOutputPlugin.hxx" + +#include <mmsystem.h> #include <assert.h> #include <math.h> @@ -29,9 +31,13 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "winmm_mixer" -struct winmm_mixer { - struct mixer base; - struct winmm_output *output; +struct WinmmMixer final : public Mixer { + WinmmOutput *output; + + WinmmMixer(WinmmOutput *_output) + :Mixer(winmm_mixer_plugin), + output(_output) { + } }; static inline GQuark @@ -53,29 +59,27 @@ winmm_volume_encode(int volume) return MAKELONG(value, value); } -static struct mixer * -winmm_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param, +static Mixer * +winmm_mixer_init(void *ao, gcc_unused const config_param ¶m, G_GNUC_UNUSED GError **error_r) { - assert(ao != NULL); + assert(ao != nullptr); - struct winmm_mixer *wm = g_new(struct winmm_mixer, 1); - mixer_init(&wm->base, &winmm_mixer_plugin); - wm->output = (struct winmm_output *) ao; - - return &wm->base; + return new WinmmMixer((WinmmOutput *)ao); } static void -winmm_mixer_finish(struct mixer *data) +winmm_mixer_finish(Mixer *data) { - g_free(data); + WinmmMixer *wm = (WinmmMixer *)data; + + delete wm; } static int -winmm_mixer_get_volume(struct mixer *mixer, GError **error_r) +winmm_mixer_get_volume(Mixer *mixer, GError **error_r) { - struct winmm_mixer *wm = (struct winmm_mixer *) mixer; + WinmmMixer *wm = (WinmmMixer *) mixer; DWORD volume; HWAVEOUT handle = winmm_output_get_handle(wm->output); MMRESULT result = waveOutGetVolume(handle, &volume); @@ -90,9 +94,9 @@ winmm_mixer_get_volume(struct mixer *mixer, GError **error_r) } static bool -winmm_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) +winmm_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) { - struct winmm_mixer *wm = (struct winmm_mixer *) mixer; + WinmmMixer *wm = (WinmmMixer *) mixer; DWORD value = winmm_volume_encode(volume); HWAVEOUT handle = winmm_output_get_handle(wm->output); MMRESULT result = waveOutSetVolume(handle, value); @@ -107,8 +111,11 @@ winmm_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) } const struct mixer_plugin winmm_mixer_plugin = { - .init = winmm_mixer_init, - .finish = winmm_mixer_finish, - .get_volume = winmm_mixer_get_volume, - .set_volume = winmm_mixer_set_volume, + winmm_mixer_init, + winmm_mixer_finish, + nullptr, + nullptr, + winmm_mixer_get_volume, + winmm_mixer_set_volume, + false, }; diff --git a/src/mixer/alsa_mixer_plugin.c b/src/mixer/alsa_mixer_plugin.c deleted file mode 100644 index 22e4e22bd..000000000 --- a/src/mixer/alsa_mixer_plugin.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "mixer_api.h" -#include "output_api.h" -#include "event_pipe.h" - -#include <glib.h> -#include <alsa/asoundlib.h> - -#define VOLUME_MIXER_ALSA_DEFAULT "default" -#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" -#define VOLUME_MIXER_ALSA_INDEX_DEFAULT 0 - -struct alsa_mixer_source { - GSource source; - - snd_mixer_t *mixer; - - /** a linked list of all registered GPollFD objects */ - GSList *fds; -}; - -struct alsa_mixer { - /** the base mixer class */ - struct mixer base; - - const char *device; - const char *control; - unsigned int index; - - snd_mixer_t *handle; - snd_mixer_elem_t *elem; - long volume_min; - long volume_max; - int volume_set; - - struct alsa_mixer_source *source; -}; - -/** - * The quark used for GError.domain. - */ -static inline GQuark -alsa_mixer_quark(void) -{ - return g_quark_from_static_string("alsa_mixer"); -} - -/* - * GSource helper functions - * - */ - -static GSList ** -find_fd(GSList **list_r, int fd) -{ - while (true) { - GSList *list = *list_r; - if (list == NULL) - return NULL; - - GPollFD *p = list->data; - if (p->fd == fd) - return list_r; - - list_r = &list->next; - } -} - -static void -alsa_mixer_update_fd(struct alsa_mixer_source *source, const struct pollfd *p, - GSList **old_r) -{ - GSList **found_r = find_fd(old_r, p->fd); - if (found_r == NULL) { - /* new fd */ - GPollFD *q = g_new(GPollFD, 1); - q->fd = p->fd; - q->events = p->events; - g_source_add_poll(&source->source, q); - source->fds = g_slist_prepend(source->fds, q); - return; - } - - GSList *found = *found_r; - *found_r = found->next; - - GPollFD *q = found->data; - if (q->events != p->events) { - /* refresh events */ - g_source_remove_poll(&source->source, q); - q->events = p->events; - g_source_add_poll(&source->source, q); - } - - found->next = source->fds; - source->fds = found; -} - -static void -alsa_mixer_update_fds(struct alsa_mixer_source *source) -{ - int count = snd_mixer_poll_descriptors_count(source->mixer); - if (count < 0) - count = 0; - - struct pollfd *pfds = g_new(struct pollfd, count); - count = snd_mixer_poll_descriptors(source->mixer, pfds, count); - if (count < 0) - count = 0; - - GSList *old = source->fds; - source->fds = NULL; - - for (int i = 0; i < count; ++i) - alsa_mixer_update_fd(source, &pfds[i], &old); - g_free(pfds); - - for (; old != NULL; old = old->next) { - GPollFD *q = old->data; - g_source_remove_poll(&source->source, q); - g_free(q); - } - - g_slist_free(old); -} - -/* - * GSource methods - * - */ - -static gboolean -alsa_mixer_source_prepare(GSource *_source, G_GNUC_UNUSED gint *timeout_r) -{ - struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source; - alsa_mixer_update_fds(source); - - return false; -} - -static gboolean -alsa_mixer_source_check(GSource *_source) -{ - struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source; - - for (const GSList *i = source->fds; i != NULL; i = i->next) { - const GPollFD *poll_fd = i->data; - if (poll_fd->revents != 0) - return true; - } - - return false; -} - -static gboolean -alsa_mixer_source_dispatch(GSource *_source, - G_GNUC_UNUSED GSourceFunc callback, - G_GNUC_UNUSED gpointer user_data) -{ - struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source; - - snd_mixer_handle_events(source->mixer); - return true; -} - -static void -alsa_mixer_source_finalize(GSource *_source) -{ - struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source; - - for (GSList *i = source->fds; i != NULL; i = i->next) - g_free(i->data); - - g_slist_free(source->fds); -} - -static GSourceFuncs alsa_mixer_source_funcs = { - .prepare = alsa_mixer_source_prepare, - .check = alsa_mixer_source_check, - .dispatch = alsa_mixer_source_dispatch, - .finalize = alsa_mixer_source_finalize, -}; - -/* - * libasound callbacks - * - */ - -static int -alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask) -{ - if (mask & SND_CTL_EVENT_MASK_VALUE) - event_pipe_emit(PIPE_EVENT_MIXER); - - return 0; -} - -/* - * mixer_plugin methods - * - */ - -static struct mixer * -alsa_mixer_init(G_GNUC_UNUSED void *ao, const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct alsa_mixer *am = g_new(struct alsa_mixer, 1); - - mixer_init(&am->base, &alsa_mixer_plugin); - - am->device = config_get_block_string(param, "mixer_device", - VOLUME_MIXER_ALSA_DEFAULT); - am->control = config_get_block_string(param, "mixer_control", - VOLUME_MIXER_ALSA_CONTROL_DEFAULT); - am->index = config_get_block_unsigned(param, "mixer_index", - VOLUME_MIXER_ALSA_INDEX_DEFAULT); - - return &am->base; -} - -static void -alsa_mixer_finish(struct mixer *data) -{ - struct alsa_mixer *am = (struct alsa_mixer *)data; - - g_free(am); - - /* free libasound's config cache */ - snd_config_update_free_global(); -} - -G_GNUC_PURE -static snd_mixer_elem_t * -alsa_mixer_lookup_elem(snd_mixer_t *handle, const char *name, unsigned idx) -{ - for (snd_mixer_elem_t *elem = snd_mixer_first_elem(handle); - elem != NULL; elem = snd_mixer_elem_next(elem)) { - if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE && - g_ascii_strcasecmp(snd_mixer_selem_get_name(elem), - name) == 0 && - snd_mixer_selem_get_index(elem) == idx) - return elem; - } - - return NULL; -} - -static bool -alsa_mixer_setup(struct alsa_mixer *am, GError **error_r) -{ - int err; - - if ((err = snd_mixer_attach(am->handle, am->device)) < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "failed to attach to %s: %s", - am->device, snd_strerror(err)); - return false; - } - - if ((err = snd_mixer_selem_register(am->handle, NULL, - NULL)) < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "snd_mixer_selem_register() failed: %s", - snd_strerror(err)); - return false; - } - - if ((err = snd_mixer_load(am->handle)) < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "snd_mixer_load() failed: %s\n", - snd_strerror(err)); - return false; - } - - am->elem = alsa_mixer_lookup_elem(am->handle, am->control, am->index); - if (am->elem == NULL) { - g_set_error(error_r, alsa_mixer_quark(), 0, - "no such mixer control: %s", am->control); - return false; - } - - snd_mixer_selem_get_playback_volume_range(am->elem, - &am->volume_min, - &am->volume_max); - - snd_mixer_elem_set_callback(am->elem, alsa_mixer_elem_callback); - - am->source = (struct alsa_mixer_source *) - g_source_new(&alsa_mixer_source_funcs, sizeof(*am->source)); - am->source->mixer = am->handle; - am->source->fds = NULL; - g_source_attach(&am->source->source, g_main_context_default()); - - return true; -} - -static bool -alsa_mixer_open(struct mixer *data, GError **error_r) -{ - struct alsa_mixer *am = (struct alsa_mixer *)data; - int err; - - am->volume_set = -1; - - err = snd_mixer_open(&am->handle, 0); - if (err < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "snd_mixer_open() failed: %s", snd_strerror(err)); - return false; - } - - if (!alsa_mixer_setup(am, error_r)) { - snd_mixer_close(am->handle); - return false; - } - - return true; -} - -static void -alsa_mixer_close(struct mixer *data) -{ - struct alsa_mixer *am = (struct alsa_mixer *)data; - - assert(am->handle != NULL); - - g_source_destroy(&am->source->source); - g_source_unref(&am->source->source); - - snd_mixer_elem_set_callback(am->elem, NULL); - snd_mixer_close(am->handle); -} - -static int -alsa_mixer_get_volume(struct mixer *mixer, GError **error_r) -{ - struct alsa_mixer *am = (struct alsa_mixer *)mixer; - int err; - int ret; - long level; - - assert(am->handle != NULL); - - err = snd_mixer_handle_events(am->handle); - if (err < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "snd_mixer_handle_events() failed: %s", - snd_strerror(err)); - return false; - } - - err = snd_mixer_selem_get_playback_volume(am->elem, - SND_MIXER_SCHN_FRONT_LEFT, - &level); - if (err < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "failed to read ALSA volume: %s", - snd_strerror(err)); - return false; - } - - ret = ((am->volume_set / 100.0) * (am->volume_max - am->volume_min) - + am->volume_min) + 0.5; - if (am->volume_set > 0 && ret == level) { - ret = am->volume_set; - } else { - ret = (int)(100 * (((float)(level - am->volume_min)) / - (am->volume_max - am->volume_min)) + 0.5); - } - - return ret; -} - -static bool -alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) -{ - struct alsa_mixer *am = (struct alsa_mixer *)mixer; - float vol; - long level; - int err; - - assert(am->handle != NULL); - - vol = volume; - - am->volume_set = vol + 0.5; - - level = (long)(((vol / 100.0) * (am->volume_max - am->volume_min) + - am->volume_min) + 0.5); - level = level > am->volume_max ? am->volume_max : level; - level = level < am->volume_min ? am->volume_min : level; - - err = snd_mixer_selem_set_playback_volume_all(am->elem, level); - if (err < 0) { - g_set_error(error_r, alsa_mixer_quark(), err, - "failed to set ALSA volume: %s", - snd_strerror(err)); - return false; - } - - return true; -} - -const struct mixer_plugin alsa_mixer_plugin = { - .init = alsa_mixer_init, - .finish = alsa_mixer_finish, - .open = alsa_mixer_open, - .close = alsa_mixer_close, - .get_volume = alsa_mixer_get_volume, - .set_volume = alsa_mixer_set_volume, - .global = true, -}; diff --git a/src/mixer/roar_mixer_plugin.c b/src/mixer/roar_mixer_plugin.c deleted file mode 100644 index 47d3c17f9..000000000 --- a/src/mixer/roar_mixer_plugin.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2003-2010 The Music Player Daemon Project - * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft - * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - - -#include "config.h" -#include "mixer_api.h" -#include "output_api.h" -#include "output/roar_output_plugin.h" - -#include <glib.h> - -#include <assert.h> -#include <stdlib.h> -#include <unistd.h> - -typedef struct roar_mpd_mixer -{ - /** the base mixer class */ - struct mixer base; - struct roar *self; -} roar_mixer_t; - -/** - * The quark used for GError.domain. - */ -static inline GQuark -roar_mixer_quark(void) -{ - return g_quark_from_static_string("roar_mixer"); -} - -static struct mixer * -roar_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - roar_mixer_t *self = g_new(roar_mixer_t, 1); - self->self = ao; - - mixer_init(&self->base, &roar_mixer_plugin); - - return &self->base; -} - -static void -roar_mixer_finish(struct mixer *data) -{ - roar_mixer_t *self = (roar_mixer_t *) data; - - g_free(self); -} - -static void -roar_mixer_close(G_GNUC_UNUSED struct mixer *data) -{ -} - -static bool -roar_mixer_open(G_GNUC_UNUSED struct mixer *data, - G_GNUC_UNUSED GError **error_r) -{ - return true; -} - -static int -roar_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r) -{ - roar_mixer_t *self = (roar_mixer_t *)mixer; - return roar_output_get_volume(self->self); -} - -static bool -roar_mixer_set_volume(struct mixer *mixer, unsigned volume, - G_GNUC_UNUSED GError **error_r) -{ - roar_mixer_t *self = (roar_mixer_t *)mixer; - return roar_output_set_volume(self->self, volume); -} - -const struct mixer_plugin roar_mixer_plugin = { - .init = roar_mixer_init, - .finish = roar_mixer_finish, - .open = roar_mixer_open, - .close = roar_mixer_close, - .get_volume = roar_mixer_get_volume, - .set_volume = roar_mixer_set_volume, - .global = false, -}; diff --git a/src/mixer/software_mixer_plugin.c b/src/mixer/software_mixer_plugin.c deleted file mode 100644 index 0206c3b99..000000000 --- a/src/mixer/software_mixer_plugin.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "software_mixer_plugin.h" -#include "mixer_api.h" -#include "filter_plugin.h" -#include "filter_registry.h" -#include "filter/volume_filter_plugin.h" -#include "pcm_volume.h" - -#include <assert.h> -#include <math.h> - -struct software_mixer { - /** the base mixer class */ - struct mixer base; - - struct filter *filter; - - unsigned volume; -}; - -static struct mixer * -software_mixer_init(G_GNUC_UNUSED void *ao, - G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - struct software_mixer *sm = g_new(struct software_mixer, 1); - - mixer_init(&sm->base, &software_mixer_plugin); - - sm->filter = filter_new(&volume_filter_plugin, NULL, NULL); - assert(sm->filter != NULL); - - sm->volume = 100; - - return &sm->base; -} - -static void -software_mixer_finish(struct mixer *data) -{ - struct software_mixer *sm = (struct software_mixer *)data; - - g_free(sm); -} - -static int -software_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r) -{ - struct software_mixer *sm = (struct software_mixer *)mixer; - - return sm->volume; -} - -static bool -software_mixer_set_volume(struct mixer *mixer, unsigned volume, - G_GNUC_UNUSED GError **error_r) -{ - struct software_mixer *sm = (struct software_mixer *)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); - return true; -} - -const struct mixer_plugin software_mixer_plugin = { - .init = software_mixer_init, - .finish = software_mixer_finish, - .get_volume = software_mixer_get_volume, - .set_volume = software_mixer_set_volume, - .global = true, -}; - -struct filter * -software_mixer_get_filter(struct mixer *mixer) -{ - struct software_mixer *sm = (struct software_mixer *)mixer; - - assert(sm->base.plugin == &software_mixer_plugin); - - return sm->filter; -} |