diff options
Diffstat (limited to '')
-rw-r--r-- | src/mixer/AlsaMixerPlugin.cxx | 370 | ||||
-rw-r--r-- | src/mixer/OssMixerPlugin.cxx | 256 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.cxx | 235 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.hxx | 47 | ||||
-rw-r--r-- | src/mixer/RoarMixerPlugin.cxx | 74 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.cxx | 113 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.hxx | 33 | ||||
-rw-r--r-- | src/mixer/WinmmMixerPlugin.cxx | 121 | ||||
-rw-r--r-- | src/mixer/alsa_mixer_plugin.c | 431 | ||||
-rw-r--r-- | src/mixer/oss_mixer_plugin.c | 216 | ||||
-rw-r--r-- | src/mixer/pulse_mixer_plugin.c | 236 | ||||
-rw-r--r-- | src/mixer/pulse_mixer_plugin.h | 39 | ||||
-rw-r--r-- | src/mixer/roar_mixer_plugin.c | 104 | ||||
-rw-r--r-- | src/mixer/software_mixer_plugin.c | 109 | ||||
-rw-r--r-- | src/mixer/software_mixer_plugin.h | 33 | ||||
-rw-r--r-- | src/mixer/winmm_mixer_plugin.c | 114 | ||||
-rw-r--r-- | src/mixer_all.c | 181 | ||||
-rw-r--r-- | src/mixer_all.h | 62 | ||||
-rw-r--r-- | src/mixer_api.c | 33 | ||||
-rw-r--r-- | src/mixer_api.h | 52 | ||||
-rw-r--r-- | src/mixer_control.c | 185 | ||||
-rw-r--r-- | src/mixer_control.h | 63 | ||||
-rw-r--r-- | src/mixer_list.h | 35 | ||||
-rw-r--r-- | src/mixer_plugin.h | 99 | ||||
-rw-r--r-- | src/mixer_type.c | 39 | ||||
-rw-r--r-- | src/mixer_type.h | 47 |
26 files changed, 1249 insertions, 2078 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/OssMixerPlugin.cxx b/src/mixer/OssMixerPlugin.cxx new file mode 100644 index 000000000..bbb5b6c88 --- /dev/null +++ b/src/mixer/OssMixerPlugin.cxx @@ -0,0 +1,256 @@ +/* + * 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 "fd_util.h" + +#include <glib.h> + +#include <assert.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include <soundcard.h> +#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ +# include <sys/soundcard.h> +#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ + +#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" + +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); +}; + +/** + * The quark used for GError.domain. + */ +static inline GQuark +oss_mixer_quark(void) +{ + return g_quark_from_static_string("oss_mixer"); +} + +static int +oss_find_mixer(const char *name) +{ + const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; + size_t name_length = strlen(name); + + for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (g_ascii_strncasecmp(name, labels[i], name_length) == 0 && + (labels[i][name_length] == 0 || + labels[i][name_length] == ' ')) + return i; + } + return -1; +} + +inline bool +OssMixer::Configure(const config_param ¶m, GError **error_r) +{ + device = param.GetBlockValue("mixer_device", + VOLUME_MIXER_OSS_DEFAULT); + control = param.GetBlockValue("mixer_control"); + + 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", control); + return false; + } + } else + volume_control = SOUND_MIXER_PCM; + + return true; +} + +static Mixer * +oss_mixer_init(G_GNUC_UNUSED void *ao, const config_param ¶m, + GError **error_r) +{ + OssMixer *om = new OssMixer(); + + if (!om->Configure(param, error_r)) { + delete om; + return nullptr; + } + + return om; +} + +static void +oss_mixer_finish(Mixer *data) +{ + OssMixer *om = (OssMixer *) data; + + delete om; +} + +void +OssMixer::Close() +{ + assert(device_fd >= 0); + + close(device_fd); +} + +static void +oss_mixer_close(Mixer *data) +{ + OssMixer *om = (OssMixer *) data; + om->Close(); +} + +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", + device, g_strerror(errno)); + return false; + } + + if (control) { + int 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)); + Close(); + return false; + } + + if (((1 << volume_control) & devmask) == 0) { + g_set_error(error_r, oss_mixer_quark(), 0, + "mixer control \"%s\" not usable", + control); + Close(); + return false; + } + } + + return true; +} + +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) +{ + int left, right, level; + int ret; + + assert(device_fd >= 0); + + 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", + g_strerror(errno)); + return false; + } + + left = level & 0xff; + right = (level & 0xff00) >> 8; + + if (left != right) { + g_warning("volume for left and right is not the same, \"%i\" and " + "\"%i\"\n", left, right); + } + + return left; +} + +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) +{ + int level; + int ret; + + assert(device_fd >= 0); + assert(volume <= 100); + + level = (volume << 8) + volume; + + 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", + g_strerror(errno)); + return false; + } + + 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 = { + 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/PulseMixerPlugin.cxx b/src/mixer/PulseMixerPlugin.cxx new file mode 100644 index 000000000..9cfd2dcf8 --- /dev/null +++ b/src/mixer/PulseMixerPlugin.cxx @@ -0,0 +1,235 @@ +/* + * 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 "PulseMixerPlugin.hxx" +#include "MixerInternal.hxx" +#include "output/PulseOutputPlugin.hxx" +#include "conf.h" +#include "GlobalEvents.hxx" + +#include <glib.h> + +#include <pulse/thread-mainloop.h> +#include <pulse/context.h> +#include <pulse/introspect.h> +#include <pulse/stream.h> +#include <pulse/subscribe.h> +#include <pulse/error.h> + +#include <assert.h> +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "pulse_mixer" + +struct PulseMixer final : public Mixer { + PulseOutput *output; + + bool online; + struct pa_cvolume volume; + + PulseMixer(PulseOutput *_output) + :Mixer(pulse_mixer_plugin), + output(_output), online(false) + { + } +}; + +/** + * The quark used for GError.domain. + */ +static inline GQuark +pulse_mixer_quark(void) +{ + return g_quark_from_static_string("pulse_mixer"); +} + +static void +pulse_mixer_offline(PulseMixer *pm) +{ + if (!pm->online) + return; + + pm->online = false; + + GlobalEvents::Emit(GlobalEvents::MIXER); +} + +/** + * Callback invoked by pulse_mixer_update(). Receives the new mixer + * value. + */ +static void +pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i, + int eol, void *userdata) +{ + PulseMixer *pm = (PulseMixer *)userdata; + + if (eol) + return; + + if (i == NULL) { + pulse_mixer_offline(pm); + return; + } + + pm->online = true; + pm->volume = i->volume; + + GlobalEvents::Emit(GlobalEvents::MIXER); +} + +static void +pulse_mixer_update(PulseMixer *pm, + struct pa_context *context, struct pa_stream *stream) +{ + pa_operation *o; + + assert(context != NULL); + assert(stream != NULL); + 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); + if (o == NULL) { + g_warning("pa_context_get_sink_input_info() failed: %s", + pa_strerror(pa_context_errno(context))); + pulse_mixer_offline(pm); + return; + } + + pa_operation_unref(o); +} + +void +pulse_mixer_on_connect(G_GNUC_UNUSED PulseMixer *pm, + struct pa_context *context) +{ + pa_operation *o; + + assert(context != NULL); + + o = pa_context_subscribe(context, + (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT, + NULL, NULL); + if (o == NULL) { + g_warning("pa_context_subscribe() failed: %s", + pa_strerror(pa_context_errno(context))); + return; + } + + pa_operation_unref(o); +} + +void +pulse_mixer_on_disconnect(PulseMixer *pm) +{ + pulse_mixer_offline(pm); +} + +void +pulse_mixer_on_change(PulseMixer *pm, + struct pa_context *context, struct pa_stream *stream) +{ + pulse_mixer_update(pm, context, stream); +} + +static Mixer * +pulse_mixer_init(void *ao, gcc_unused const config_param ¶m, + GError **error_r) +{ + 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 nullptr; + } + + PulseMixer *pm = new PulseMixer(po); + + pulse_output_set_mixer(po, pm); + + return pm; +} + +static void +pulse_mixer_finish(Mixer *data) +{ + PulseMixer *pm = (PulseMixer *) data; + + pulse_output_clear_mixer(pm->output, pm); + + delete pm; +} + +static int +pulse_mixer_get_volume(Mixer *mixer, G_GNUC_UNUSED GError **error_r) +{ + PulseMixer *pm = (PulseMixer *) mixer; + int ret; + + pulse_output_lock(pm->output); + + ret = pm->online + ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM) + : -1; + + pulse_output_unlock(pm->output); + + return ret; +} + +static bool +pulse_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) +{ + PulseMixer *pm = (PulseMixer *) mixer; + struct pa_cvolume cvolume; + bool success; + + pulse_output_lock(pm->output); + + if (!pm->online) { + pulse_output_unlock(pm->output); + g_set_error(error_r, pulse_mixer_quark(), 0, "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_r); + if (success) + pm->volume = cvolume; + + pulse_output_unlock(pm->output); + + return success; +} + +const struct mixer_plugin pulse_mixer_plugin = { + pulse_mixer_init, + pulse_mixer_finish, + nullptr, + nullptr, + pulse_mixer_get_volume, + pulse_mixer_set_volume, + false, +}; diff --git a/src/mixer/PulseMixerPlugin.hxx b/src/mixer/PulseMixerPlugin.hxx new file mode 100644 index 000000000..debc2cf3c --- /dev/null +++ b/src/mixer/PulseMixerPlugin.hxx @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef MPD_PULSE_MIXER_PLUGIN_HXX +#define MPD_PULSE_MIXER_PLUGIN_HXX + +#include <pulse/def.h> + +struct PulseMixer; +struct pa_context; +struct pa_stream; + +#ifdef __cplusplus +extern "C" { +#endif + +void +pulse_mixer_on_connect(PulseMixer *pm, struct pa_context *context); + +void +pulse_mixer_on_disconnect(PulseMixer *pm); + +void +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/SoftwareMixerPlugin.hxx b/src/mixer/SoftwareMixerPlugin.hxx new file mode 100644 index 000000000..be59c08db --- /dev/null +++ b/src/mixer/SoftwareMixerPlugin.hxx @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef MPD_SOFTWARE_MIXER_PLUGIN_HXX +#define MPD_SOFTWARE_MIXER_PLUGIN_HXX + +class Mixer; +class Filter; + +/** + * Returns the (volume) filter associated with this mixer. All users + * of this mixer plugin should install this filter. + */ +Filter * +software_mixer_get_filter(Mixer *mixer); + +#endif diff --git a/src/mixer/WinmmMixerPlugin.cxx b/src/mixer/WinmmMixerPlugin.cxx new file mode 100644 index 000000000..139cb1399 --- /dev/null +++ b/src/mixer/WinmmMixerPlugin.cxx @@ -0,0 +1,121 @@ +/* + * 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 "output/WinmmOutputPlugin.hxx" + +#include <mmsystem.h> + +#include <assert.h> +#include <math.h> +#include <windows.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "winmm_mixer" + +struct WinmmMixer final : public Mixer { + WinmmOutput *output; + + WinmmMixer(WinmmOutput *_output) + :Mixer(winmm_mixer_plugin), + output(_output) { + } +}; + +static inline GQuark +winmm_mixer_quark(void) +{ + return g_quark_from_static_string("winmm_mixer"); +} + +static inline int +winmm_volume_decode(DWORD volume) +{ + return lround((volume & 0xFFFF) / 655.35); +} + +static inline DWORD +winmm_volume_encode(int volume) +{ + int value = lround(volume * 655.35); + return MAKELONG(value, value); +} + +static Mixer * +winmm_mixer_init(void *ao, gcc_unused const config_param ¶m, + G_GNUC_UNUSED GError **error_r) +{ + assert(ao != nullptr); + + 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, GError **error_r) +{ + WinmmMixer *wm = (WinmmMixer *) mixer; + DWORD volume; + HWAVEOUT handle = winmm_output_get_handle(wm->output); + MMRESULT result = waveOutGetVolume(handle, &volume); + + if (result != MMSYSERR_NOERROR) { + g_set_error(error_r, 0, winmm_mixer_quark(), + "Failed to get winmm volume"); + return -1; + } + + return winmm_volume_decode(volume); +} + +static bool +winmm_mixer_set_volume(Mixer *mixer, unsigned volume, GError **error_r) +{ + WinmmMixer *wm = (WinmmMixer *) mixer; + DWORD value = winmm_volume_encode(volume); + HWAVEOUT handle = winmm_output_get_handle(wm->output); + MMRESULT result = waveOutSetVolume(handle, value); + + if (result != MMSYSERR_NOERROR) { + g_set_error(error_r, 0, winmm_mixer_quark(), + "Failed to set winmm volume"); + return false; + } + + return true; +} + +const struct mixer_plugin winmm_mixer_plugin = { + 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/oss_mixer_plugin.c b/src/mixer/oss_mixer_plugin.c deleted file mode 100644 index 608f1f9b8..000000000 --- a/src/mixer/oss_mixer_plugin.c +++ /dev/null @@ -1,216 +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 "fd_util.h" - -#include <glib.h> - -#include <assert.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> - -#if defined(__OpenBSD__) || defined(__NetBSD__) -# include <soundcard.h> -#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ -# include <sys/soundcard.h> -#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ - -#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" - -struct oss_mixer { - /** the base mixer class */ - struct mixer base; - - const char *device; - const char *control; - - int device_fd; - int volume_control; -}; - -/** - * The quark used for GError.domain. - */ -static inline GQuark -oss_mixer_quark(void) -{ - return g_quark_from_static_string("oss_mixer"); -} - -static int -oss_find_mixer(const char *name) -{ - const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; - size_t name_length = strlen(name); - - for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (g_ascii_strncasecmp(name, labels[i], name_length) == 0 && - (labels[i][name_length] == 0 || - labels[i][name_length] == ' ')) - return i; - } - return -1; -} - -static struct mixer * -oss_mixer_init(G_GNUC_UNUSED void *ao, const struct config_param *param, - 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", - VOLUME_MIXER_OSS_DEFAULT); - om->control = config_get_block_string(param, "mixer_control", NULL); - - if (om->control != NULL) { - om->volume_control = oss_find_mixer(om->control); - if (om->volume_control < 0) { - g_free(om); - g_set_error(error_r, oss_mixer_quark(), 0, - "no such mixer control: %s", om->control); - return NULL; - } - } else - om->volume_control = SOUND_MIXER_PCM; - - return &om->base; -} - -static void -oss_mixer_finish(struct mixer *data) -{ - struct oss_mixer *om = (struct oss_mixer *) data; - - g_free(om); -} - -static void -oss_mixer_close(struct mixer *data) -{ - struct oss_mixer *om = (struct oss_mixer *) data; - - assert(om->device_fd >= 0); - - close(om->device_fd); -} - -static bool -oss_mixer_open(struct mixer *data, GError **error_r) -{ - struct oss_mixer *om = (struct oss_mixer *) data; - - om->device_fd = open_cloexec(om->device, O_RDONLY, 0); - if (om->device_fd < 0) { - g_set_error(error_r, oss_mixer_quark(), errno, - "failed to open %s: %s", - om->device, g_strerror(errno)); - return false; - } - - if (om->control) { - int devmask = 0; - - if (ioctl(om->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); - return false; - } - - if (((1 << om->volume_control) & devmask) == 0) { - g_set_error(error_r, oss_mixer_quark(), 0, - "mixer control \"%s\" not usable", - om->control); - oss_mixer_close(data); - return false; - } - } - return true; -} - -static int -oss_mixer_get_volume(struct mixer *mixer, GError **error_r) -{ - struct oss_mixer *om = (struct oss_mixer *)mixer; - int left, right, level; - int ret; - - assert(om->device_fd >= 0); - - ret = ioctl(om->device_fd, MIXER_READ(om->volume_control), &level); - if (ret < 0) { - g_set_error(error_r, oss_mixer_quark(), errno, - "failed to read OSS volume: %s", - g_strerror(errno)); - return false; - } - - left = level & 0xff; - right = (level & 0xff00) >> 8; - - if (left != right) { - g_warning("volume for left and right is not the same, \"%i\" and " - "\"%i\"\n", left, right); - } - - return left; -} - -static bool -oss_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) -{ - struct oss_mixer *om = (struct oss_mixer *)mixer; - int level; - int ret; - - assert(om->device_fd >= 0); - assert(volume <= 100); - - level = (volume << 8) + volume; - - ret = ioctl(om->device_fd, MIXER_WRITE(om->volume_control), &level); - if (ret < 0) { - g_set_error(error_r, oss_mixer_quark(), errno, - "failed to set OSS volume: %s", - g_strerror(errno)); - return false; - } - - return true; -} - -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, -}; diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/pulse_mixer_plugin.c deleted file mode 100644 index a82c032b3..000000000 --- a/src/mixer/pulse_mixer_plugin.c +++ /dev/null @@ -1,236 +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 "pulse_mixer_plugin.h" -#include "mixer_api.h" -#include "output/pulse_output_plugin.h" -#include "conf.h" -#include "event_pipe.h" - -#include <glib.h> - -#include <pulse/thread-mainloop.h> -#include <pulse/context.h> -#include <pulse/introspect.h> -#include <pulse/stream.h> -#include <pulse/subscribe.h> -#include <pulse/error.h> - -#include <assert.h> -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pulse_mixer" - -struct pulse_mixer { - struct mixer base; - - struct pulse_output *output; - - bool online; - struct pa_cvolume volume; - -}; - -/** - * The quark used for GError.domain. - */ -static inline GQuark -pulse_mixer_quark(void) -{ - return g_quark_from_static_string("pulse_mixer"); -} - -static void -pulse_mixer_offline(struct pulse_mixer *pm) -{ - if (!pm->online) - return; - - pm->online = false; - - event_pipe_emit(PIPE_EVENT_MIXER); -} - -/** - * Callback invoked by pulse_mixer_update(). Receives the new mixer - * value. - */ -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; - - if (eol) - return; - - if (i == NULL) { - pulse_mixer_offline(pm); - return; - } - - pm->online = true; - pm->volume = i->volume; - - event_pipe_emit(PIPE_EVENT_MIXER); -} - -static void -pulse_mixer_update(struct pulse_mixer *pm, - struct pa_context *context, struct pa_stream *stream) -{ - pa_operation *o; - - assert(context != NULL); - assert(stream != NULL); - 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); - if (o == NULL) { - g_warning("pa_context_get_sink_input_info() failed: %s", - pa_strerror(pa_context_errno(context))); - pulse_mixer_offline(pm); - return; - } - - pa_operation_unref(o); -} - -void -pulse_mixer_on_connect(G_GNUC_UNUSED struct pulse_mixer *pm, - struct pa_context *context) -{ - pa_operation *o; - - assert(context != NULL); - - o = pa_context_subscribe(context, - (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT, - NULL, NULL); - if (o == NULL) { - g_warning("pa_context_subscribe() failed: %s", - pa_strerror(pa_context_errno(context))); - return; - } - - pa_operation_unref(o); -} - -void -pulse_mixer_on_disconnect(struct pulse_mixer *pm) -{ - pulse_mixer_offline(pm); -} - -void -pulse_mixer_on_change(struct pulse_mixer *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, - GError **error_r) -{ - struct pulse_mixer *pm; - struct pulse_output *po = ao; - - if (ao == NULL) { - g_set_error(error_r, pulse_mixer_quark(), 0, - "The pulse mixer cannot work without the audio output"); - return false; - } - - pm = g_new(struct pulse_mixer,1); - mixer_init(&pm->base, &pulse_mixer_plugin); - - pm->online = false; - pm->output = po; - - pulse_output_set_mixer(po, pm); - - return &pm->base; -} - -static void -pulse_mixer_finish(struct mixer *data) -{ - struct pulse_mixer *pm = (struct pulse_mixer *) data; - - pulse_output_clear_mixer(pm->output, pm); - - /* free resources */ - - g_free(pm); -} - -static int -pulse_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r) -{ - struct pulse_mixer *pm = (struct pulse_mixer *) mixer; - int ret; - - pulse_output_lock(pm->output); - - ret = pm->online - ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM) - : -1; - - pulse_output_unlock(pm->output); - - return ret; -} - -static bool -pulse_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) -{ - struct pulse_mixer *pm = (struct pulse_mixer *) mixer; - struct pa_cvolume cvolume; - bool success; - - pulse_output_lock(pm->output); - - if (!pm->online) { - pulse_output_unlock(pm->output); - g_set_error(error_r, pulse_mixer_quark(), 0, "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_r); - if (success) - pm->volume = cvolume; - - pulse_output_unlock(pm->output); - - return success; -} - -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, -}; diff --git a/src/mixer/pulse_mixer_plugin.h b/src/mixer/pulse_mixer_plugin.h deleted file mode 100644 index 461633d37..000000000 --- a/src/mixer/pulse_mixer_plugin.h +++ /dev/null @@ -1,39 +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. - */ - -#ifndef MPD_PULSE_MIXER_PLUGIN_H -#define MPD_PULSE_MIXER_PLUGIN_H - -#include <pulse/def.h> - -struct pulse_mixer; -struct pa_context; -struct pa_stream; - -void -pulse_mixer_on_connect(struct pulse_mixer *pm, struct pa_context *context); - -void -pulse_mixer_on_disconnect(struct pulse_mixer *pm); - -void -pulse_mixer_on_change(struct pulse_mixer *pm, - struct pa_context *context, struct pa_stream *stream); - -#endif 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; -} diff --git a/src/mixer/software_mixer_plugin.h b/src/mixer/software_mixer_plugin.h deleted file mode 100644 index ee2b2023c..000000000 --- a/src/mixer/software_mixer_plugin.h +++ /dev/null @@ -1,33 +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. - */ - -#ifndef SOFTWARE_MIXER_PLUGIN_H -#define SOFTWARE_MIXER_PLUGIN_H - -struct mixer; -struct 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); - -#endif diff --git a/src/mixer/winmm_mixer_plugin.c b/src/mixer/winmm_mixer_plugin.c deleted file mode 100644 index ceddf6afd..000000000 --- a/src/mixer/winmm_mixer_plugin.c +++ /dev/null @@ -1,114 +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 "output/winmm_output_plugin.h" - -#include <assert.h> -#include <math.h> -#include <windows.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "winmm_mixer" - -struct winmm_mixer { - struct mixer base; - struct winmm_output *output; -}; - -static inline GQuark -winmm_mixer_quark(void) -{ - return g_quark_from_static_string("winmm_mixer"); -} - -static inline int -winmm_volume_decode(DWORD volume) -{ - return lround((volume & 0xFFFF) / 655.35); -} - -static inline DWORD -winmm_volume_encode(int volume) -{ - int value = lround(volume * 655.35); - return MAKELONG(value, value); -} - -static struct mixer * -winmm_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error_r) -{ - assert(ao != NULL); - - 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; -} - -static void -winmm_mixer_finish(struct mixer *data) -{ - g_free(data); -} - -static int -winmm_mixer_get_volume(struct mixer *mixer, GError **error_r) -{ - struct winmm_mixer *wm = (struct winmm_mixer *) mixer; - DWORD volume; - HWAVEOUT handle = winmm_output_get_handle(wm->output); - MMRESULT result = waveOutGetVolume(handle, &volume); - - if (result != MMSYSERR_NOERROR) { - g_set_error(error_r, 0, winmm_mixer_quark(), - "Failed to get winmm volume"); - return -1; - } - - return winmm_volume_decode(volume); -} - -static bool -winmm_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) -{ - struct winmm_mixer *wm = (struct winmm_mixer *) mixer; - DWORD value = winmm_volume_encode(volume); - HWAVEOUT handle = winmm_output_get_handle(wm->output); - MMRESULT result = waveOutSetVolume(handle, value); - - if (result != MMSYSERR_NOERROR) { - g_set_error(error_r, 0, winmm_mixer_quark(), - "Failed to set winmm volume"); - return false; - } - - return true; -} - -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, -}; diff --git a/src/mixer_all.c b/src/mixer_all.c deleted file mode 100644 index 95ba90793..000000000 --- a/src/mixer_all.c +++ /dev/null @@ -1,181 +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_all.h" -#include "mixer_control.h" -#include "output_all.h" -#include "output_plugin.h" -#include "output_internal.h" -#include "pcm_volume.h" -#include "mixer_api.h" -#include "mixer_list.h" - -#include <glib.h> - -#include <assert.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mixer" - -static int -output_mixer_get_volume(unsigned i) -{ - struct audio_output *output; - struct mixer *mixer; - int volume; - GError *error = NULL; - - assert(i < audio_output_count()); - - output = audio_output_get(i); - if (!output->enabled) - return -1; - - mixer = output->mixer; - if (mixer == NULL) - return -1; - - volume = mixer_get_volume(mixer, &error); - if (volume < 0 && error != NULL) { - g_warning("Failed to read mixer for '%s': %s", - output->name, error->message); - g_error_free(error); - } - - return volume; -} - -int -mixer_all_get_volume(void) -{ - unsigned count = audio_output_count(), ok = 0; - int volume, total = 0; - - for (unsigned i = 0; i < count; i++) { - volume = output_mixer_get_volume(i); - if (volume >= 0) { - total += volume; - ++ok; - } - } - - if (ok == 0) - return -1; - - return total / ok; -} - -static bool -output_mixer_set_volume(unsigned i, unsigned volume) -{ - struct audio_output *output; - struct mixer *mixer; - bool success; - GError *error = NULL; - - assert(i < audio_output_count()); - assert(volume <= 100); - - output = audio_output_get(i); - if (!output->enabled) - return false; - - mixer = output->mixer; - if (mixer == NULL) - return false; - - success = mixer_set_volume(mixer, volume, &error); - if (!success && error != NULL) { - g_warning("Failed to set mixer for '%s': %s", - output->name, error->message); - g_error_free(error); - } - - return success; -} - -bool -mixer_all_set_volume(unsigned volume) -{ - bool success = false; - unsigned count = audio_output_count(); - - assert(volume <= 100); - - for (unsigned i = 0; i < count; i++) - success = output_mixer_set_volume(i, volume) - || success; - - return success; -} - -static int -output_mixer_get_software_volume(unsigned i) -{ - struct audio_output *output; - struct mixer *mixer; - - assert(i < audio_output_count()); - - output = audio_output_get(i); - if (!output->enabled) - return -1; - - mixer = output->mixer; - if (mixer == NULL || mixer->plugin != &software_mixer_plugin) - return -1; - - return mixer_get_volume(mixer, NULL); -} - -int -mixer_all_get_software_volume(void) -{ - unsigned count = audio_output_count(), ok = 0; - int volume, total = 0; - - for (unsigned i = 0; i < count; i++) { - volume = output_mixer_get_software_volume(i); - if (volume >= 0) { - total += volume; - ++ok; - } - } - - if (ok == 0) - return -1; - - return total / ok; -} - -void -mixer_all_set_software_volume(unsigned volume) -{ - unsigned count = audio_output_count(); - - assert(volume <= PCM_VOLUME_1); - - for (unsigned i = 0; i < count; i++) { - struct audio_output *output = audio_output_get(i); - if (output->mixer != NULL && - output->mixer->plugin == &software_mixer_plugin) - mixer_set_volume(output->mixer, volume, NULL); - } -} diff --git a/src/mixer_all.h b/src/mixer_all.h deleted file mode 100644 index fe873e713..000000000 --- a/src/mixer_all.h +++ /dev/null @@ -1,62 +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. - */ - -/** \file - * - * Functions which affect the mixers of all audio outputs. - */ - -#ifndef MPD_MIXER_ALL_H -#define MPD_MIXER_ALL_H - -#include <stdbool.h> - -/** - * Returns the average volume of all available mixers (range 0..100). - * Returns -1 if no mixer can be queried. - */ -int -mixer_all_get_volume(void); - -/** - * Sets the volume on all available mixers. - * - * @param volume the volume (range 0..100) - * @return true on success, false on failure - */ -bool -mixer_all_set_volume(unsigned volume); - -/** - * Similar to mixer_all_get_volume(), but gets the volume only for - * software mixers. See #software_mixer_plugin. This function fails - * if no software mixer is configured. - */ -int -mixer_all_get_software_volume(void); - -/** - * Similar to mixer_all_set_volume(), but sets the volume only for - * software mixers. See #software_mixer_plugin. This function cannot - * fail, because the underlying software mixers cannot fail either. - */ -void -mixer_all_set_software_volume(unsigned volume); - -#endif diff --git a/src/mixer_api.c b/src/mixer_api.c deleted file mode 100644 index c85916c94..000000000 --- a/src/mixer_api.c +++ /dev/null @@ -1,33 +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" - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mixer" - -void -mixer_init(struct mixer *mixer, const struct mixer_plugin *plugin) -{ - mixer->plugin = plugin; - mixer->mutex = g_mutex_new(); - mixer->open = false; - mixer->failed = false; -} diff --git a/src/mixer_api.h b/src/mixer_api.h deleted file mode 100644 index 29c1e00ca..000000000 --- a/src/mixer_api.h +++ /dev/null @@ -1,52 +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. - */ - -#ifndef MPD_MIXER_H -#define MPD_MIXER_H - -#include "mixer_plugin.h" -#include "mixer_list.h" - -#include <glib.h> - -struct mixer { - const struct mixer_plugin *plugin; - - /** - * This mutex protects all of the mixer struct, including its - * implementation, so plugins don't have to deal with that. - */ - GMutex *mutex; - - /** - * Is the mixer device currently open? - */ - bool open; - - /** - * Has this mixer failed, and should not be reopened - * automatically? - */ - bool failed; -}; - -void -mixer_init(struct mixer *mixer, const struct mixer_plugin *plugin); - -#endif diff --git a/src/mixer_control.c b/src/mixer_control.c deleted file mode 100644 index 3e984dd04..000000000 --- a/src/mixer_control.c +++ /dev/null @@ -1,185 +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_control.h" -#include "mixer_api.h" - -#include <assert.h> -#include <stddef.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mixer" - -struct mixer * -mixer_new(const struct mixer_plugin *plugin, void *ao, - const struct config_param *param, - GError **error_r) -{ - struct mixer *mixer; - - assert(plugin != NULL); - - mixer = plugin->init(ao, param, error_r); - - assert(mixer == NULL || mixer->plugin == plugin); - - return mixer; -} - -void -mixer_free(struct mixer *mixer) -{ - assert(mixer != NULL); - assert(mixer->plugin != NULL); - assert(mixer->mutex != NULL); - - /* mixers with the "global" flag set might still be open at - this point (see mixer_auto_close()) */ - mixer_close(mixer); - - g_mutex_free(mixer->mutex); - - mixer->plugin->finish(mixer); -} - -bool -mixer_open(struct mixer *mixer, GError **error_r) -{ - bool success; - - assert(mixer != NULL); - assert(mixer->plugin != NULL); - - g_mutex_lock(mixer->mutex); - - if (mixer->open) - success = true; - else if (mixer->plugin->open == NULL) - success = mixer->open = true; - else - success = mixer->open = mixer->plugin->open(mixer, error_r); - - mixer->failed = !success; - - g_mutex_unlock(mixer->mutex); - - return success; -} - -static void -mixer_close_internal(struct mixer *mixer) -{ - assert(mixer != NULL); - assert(mixer->plugin != NULL); - assert(mixer->open); - - if (mixer->plugin->close != NULL) - mixer->plugin->close(mixer); - - mixer->open = false; -} - -void -mixer_close(struct mixer *mixer) -{ - assert(mixer != NULL); - assert(mixer->plugin != NULL); - - g_mutex_lock(mixer->mutex); - - if (mixer->open) - mixer_close_internal(mixer); - - g_mutex_unlock(mixer->mutex); -} - -void -mixer_auto_close(struct mixer *mixer) -{ - if (!mixer->plugin->global) - mixer_close(mixer); -} - -/* - * Close the mixer due to failure. The mutex must be locked before - * calling this function. - */ -static void -mixer_failed(struct mixer *mixer) -{ - assert(mixer->open); - - mixer_close_internal(mixer); - - mixer->failed = true; -} - -int -mixer_get_volume(struct mixer *mixer, GError **error_r) -{ - int volume; - - assert(mixer != NULL); - - if (mixer->plugin->global && !mixer->failed && - !mixer_open(mixer, error_r)) - return -1; - - g_mutex_lock(mixer->mutex); - - if (mixer->open) { - GError *error = NULL; - - volume = mixer->plugin->get_volume(mixer, &error); - if (volume < 0 && error != NULL) { - g_propagate_error(error_r, error); - mixer_failed(mixer); - } - } else - volume = -1; - - g_mutex_unlock(mixer->mutex); - - return volume; -} - -bool -mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r) -{ - bool success; - - assert(mixer != NULL); - assert(volume <= 100); - - if (mixer->plugin->global && !mixer->failed && - !mixer_open(mixer, error_r)) - return false; - - g_mutex_lock(mixer->mutex); - - if (mixer->open) { - success = mixer->plugin->set_volume(mixer, volume, error_r); - } else - success = false; - - g_mutex_unlock(mixer->mutex); - - return success; -} diff --git a/src/mixer_control.h b/src/mixer_control.h deleted file mode 100644 index 6c3468aca..000000000 --- a/src/mixer_control.h +++ /dev/null @@ -1,63 +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. - */ - -/** \file - * - * Functions which manipulate a #mixer object. - */ - -#ifndef MPD_MIXER_CONTROL_H -#define MPD_MIXER_CONTROL_H - -#include <glib.h> - -#include <stdbool.h> - -struct mixer; -struct mixer_plugin; -struct config_param; - -struct mixer * -mixer_new(const struct mixer_plugin *plugin, void *ao, - const struct config_param *param, - GError **error_r); - -void -mixer_free(struct mixer *mixer); - -bool -mixer_open(struct mixer *mixer, GError **error_r); - -void -mixer_close(struct mixer *mixer); - -/** - * Close the mixer unless the plugin's "global" flag is set. This is - * called when the #audio_output is closed. - */ -void -mixer_auto_close(struct mixer *mixer); - -int -mixer_get_volume(struct mixer *mixer, GError **error_r); - -bool -mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r); - -#endif diff --git a/src/mixer_list.h b/src/mixer_list.h deleted file mode 100644 index 078358ec3..000000000 --- a/src/mixer_list.h +++ /dev/null @@ -1,35 +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. - */ - -/** \file - * - * This header provides "extern" declarations for all mixer plugins. - */ - -#ifndef MPD_MIXER_LIST_H -#define MPD_MIXER_LIST_H - -extern const struct mixer_plugin software_mixer_plugin; -extern const struct mixer_plugin alsa_mixer_plugin; -extern const struct mixer_plugin oss_mixer_plugin; -extern const struct mixer_plugin roar_mixer_plugin; -extern const struct mixer_plugin pulse_mixer_plugin; -extern const struct mixer_plugin winmm_mixer_plugin; - -#endif diff --git a/src/mixer_plugin.h b/src/mixer_plugin.h deleted file mode 100644 index 9532b95cb..000000000 --- a/src/mixer_plugin.h +++ /dev/null @@ -1,99 +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. - */ - -/** \file - * - * This header declares the mixer_plugin class. It should not be - * included directly; use mixer_api.h instead in mixer - * implementations. - */ - -#ifndef MPD_MIXER_PLUGIN_H -#define MPD_MIXER_PLUGIN_H - -#include <glib.h> - -#include <stdbool.h> - -struct config_param; -struct mixer; - -struct mixer_plugin { - /** - * Alocates and configures a mixer device. - * - * @param ao the pointer returned by audio_output_plugin.init - * @param param the configuration section, or NULL if there is - * no configuration - * @param error_r location to store the error occurring, or - * NULL to ignore errors - * @return a mixer object, or NULL on error - */ - struct mixer *(*init)(void *ao, const struct config_param *param, - GError **error_r); - - /** - * Finish and free mixer data - */ - void (*finish)(struct mixer *data); - - /** - * Open mixer device - * - * @param error_r location to store the error occurring, or - * NULL to ignore errors - * @return true on success, false on error - */ - bool (*open)(struct mixer *data, GError **error_r); - - /** - * Close mixer device - */ - void (*close)(struct mixer *data); - - /** - * Reads the current volume. - * - * @param error_r location to store the error occurring, or - * NULL to ignore errors - * @return the current volume (0..100 including) or -1 if - * unavailable or on error (error_r set, mixer will be closed) - */ - int (*get_volume)(struct mixer *mixer, GError **error_r); - - /** - * Sets the volume. - * - * @param error_r location to store the error occurring, or - * NULL to ignore errors - * @param volume the new volume (0..100 including) - * @return true on success, false on error - */ - bool (*set_volume)(struct mixer *mixer, unsigned volume, - GError **error_r); - - /** - * 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. - */ - bool global; -}; - -#endif diff --git a/src/mixer_type.c b/src/mixer_type.c deleted file mode 100644 index a479caf16..000000000 --- a/src/mixer_type.c +++ /dev/null @@ -1,39 +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_type.h" - -#include <assert.h> -#include <string.h> - -enum mixer_type -mixer_type_parse(const char *input) -{ - assert(input != NULL); - - if (strcmp(input, "none") == 0 || strcmp(input, "disabled") == 0) - return MIXER_TYPE_NONE; - else if (strcmp(input, "hardware") == 0) - return MIXER_TYPE_HARDWARE; - else if (strcmp(input, "software") == 0) - return MIXER_TYPE_SOFTWARE; - else - return MIXER_TYPE_UNKNOWN; -} diff --git a/src/mixer_type.h b/src/mixer_type.h deleted file mode 100644 index 15d136b5b..000000000 --- a/src/mixer_type.h +++ /dev/null @@ -1,47 +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. - */ - -#ifndef MPD_MIXER_TYPE_H -#define MPD_MIXER_TYPE_H - -enum mixer_type { - /** parser error */ - MIXER_TYPE_UNKNOWN, - - /** mixer disabled */ - MIXER_TYPE_NONE, - - /** software mixer with pcm_volume() */ - MIXER_TYPE_SOFTWARE, - - /** hardware mixer (output's plugin) */ - MIXER_TYPE_HARDWARE, -}; - -/** - * Parses a "mixer_type" setting from the configuration file. - * - * @param input the configured string value; must not be NULL - * @return a #mixer_type value; MIXER_TYPE_UNKNOWN means #input could - * not be parsed - */ -enum mixer_type -mixer_type_parse(const char *input); - -#endif |