diff options
Diffstat (limited to '')
-rw-r--r-- | src/mixer/AlsaMixerPlugin.cxx | 336 | ||||
-rw-r--r-- | src/mixer/OssMixerPlugin.cxx | 216 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.cxx | 238 | ||||
-rw-r--r-- | src/mixer/PulseMixerPlugin.h | 47 | ||||
-rw-r--r-- | src/mixer/RoarMixerPlugin.cxx | 76 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.cxx | 111 | ||||
-rw-r--r-- | src/mixer/SoftwareMixerPlugin.hxx | 33 | ||||
-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 | 2 | ||||
-rw-r--r-- | src/mixer_all.c | 181 | ||||
-rw-r--r-- | src/mixer_all.h | 62 | ||||
-rw-r--r-- | src/mixer_api.h | 8 | ||||
-rw-r--r-- | src/mixer_control.h | 10 | ||||
-rw-r--r-- | src/mixer_plugin.h | 2 |
20 files changed, 1077 insertions, 1413 deletions
diff --git a/src/mixer/AlsaMixerPlugin.cxx b/src/mixer/AlsaMixerPlugin.cxx new file mode 100644 index 000000000..17f8b9a6f --- /dev/null +++ b/src/mixer/AlsaMixerPlugin.cxx @@ -0,0 +1,336 @@ +/* + * 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 "mixer_api.h" +#include "output_api.h" +#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" +#define 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; +}; + +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; + + AlsaMixerMonitor *monitor; +}; + +/** + * 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 + * + */ + +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->monitor = new AlsaMixerMonitor(*main_loop, am->handle); + + 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); + + delete am->monitor; + + 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 = { + 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..490a65414 --- /dev/null +++ b/src/mixer/OssMixerPlugin.cxx @@ -0,0 +1,216 @@ +/* + * 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 "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 = { + 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..65dbc01fe --- /dev/null +++ b/src/mixer/PulseMixerPlugin.cxx @@ -0,0 +1,238 @@ +/* + * 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.h" +#include "mixer_api.h" +#include "output/pulse_output_plugin.h" +#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 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; + + 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) +{ + struct pulse_mixer *pm = (struct pulse_mixer *)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(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_output *po = (struct pulse_output *)ao; + + if (ao == NULL) { + g_set_error(error_r, pulse_mixer_quark(), 0, + "The pulse mixer cannot work without the audio output"); + return nullptr; + } + + struct pulse_mixer *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 = { + pulse_mixer_init, + pulse_mixer_finish, + nullptr, + nullptr, + pulse_mixer_get_volume, + pulse_mixer_set_volume, + false, +}; diff --git a/src/mixer/PulseMixerPlugin.h b/src/mixer/PulseMixerPlugin.h new file mode 100644 index 000000000..f432c44a0 --- /dev/null +++ b/src/mixer/PulseMixerPlugin.h @@ -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_H +#define MPD_PULSE_MIXER_PLUGIN_H + +#include <pulse/def.h> + +struct pulse_mixer; +struct pa_context; +struct pa_stream; + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/mixer/RoarMixerPlugin.cxx b/src/mixer/RoarMixerPlugin.cxx new file mode 100644 index 000000000..2803203b7 --- /dev/null +++ b/src/mixer/RoarMixerPlugin.cxx @@ -0,0 +1,76 @@ +/* + * 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 "mixer_api.h" +#include "output_api.h" +#include "output/RoarOutputPlugin.hxx" + +struct RoarMixer { + /** the base mixer class */ + struct mixer base; + RoarOutput *self; + + RoarMixer(RoarOutput *_output):self(_output) { + mixer_init(&base, &roar_mixer_plugin); + } +}; + +static struct mixer * +roar_mixer_init(void *ao, gcc_unused const struct config_param *param, + gcc_unused GError **error_r) +{ + RoarMixer *self = new RoarMixer((RoarOutput *)ao); + return &self->base; +} + +static void +roar_mixer_finish(struct mixer *data) +{ + RoarMixer *self = (RoarMixer *) data; + + delete self; +} + +static int +roar_mixer_get_volume(struct mixer *mixer, gcc_unused GError **error_r) +{ + RoarMixer *self = (RoarMixer *)mixer; + return roar_output_get_volume(self->self); +} + +static bool +roar_mixer_set_volume(struct 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..16463938f --- /dev/null +++ b/src/mixer/SoftwareMixerPlugin.cxx @@ -0,0 +1,111 @@ +/* + * 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 "mixer_api.h" +#include "FilterPlugin.hxx" +#include "FilterRegistry.hxx" +#include "filter/VolumeFilterPlugin.hxx" +#include "PcmVolume.hxx" + +#include <assert.h> +#include <math.h> + +struct software_mixer { + /** the base mixer class */ + struct mixer base; + + 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 = { + software_mixer_init, + software_mixer_finish, + nullptr, + nullptr, + software_mixer_get_volume, + software_mixer_set_volume, + true, +}; + +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/SoftwareMixerPlugin.hxx b/src/mixer/SoftwareMixerPlugin.hxx new file mode 100644 index 000000000..33e9e6c6f --- /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 + +struct 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(struct mixer *mixer); + +#endif 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 index ceddf6afd..99da60cce 100644 --- a/src/mixer/winmm_mixer_plugin.c +++ b/src/mixer/winmm_mixer_plugin.c @@ -22,6 +22,8 @@ #include "output_api.h" #include "output/winmm_output_plugin.h" +#include <mmsystem.h> + #include <assert.h> #include <math.h> #include <windows.h> 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.h b/src/mixer_api.h index 29c1e00ca..f0c9a0937 100644 --- a/src/mixer_api.h +++ b/src/mixer_api.h @@ -46,7 +46,15 @@ struct mixer { bool failed; }; +#ifdef __cplusplus +extern "C" { +#endif + void mixer_init(struct mixer *mixer, const struct mixer_plugin *plugin); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/mixer_control.h b/src/mixer_control.h index 6c3468aca..46219e5dd 100644 --- a/src/mixer_control.h +++ b/src/mixer_control.h @@ -25,7 +25,7 @@ #ifndef MPD_MIXER_CONTROL_H #define MPD_MIXER_CONTROL_H -#include <glib.h> +#include "gerror.h" #include <stdbool.h> @@ -33,6 +33,10 @@ struct mixer; struct mixer_plugin; struct config_param; +#ifdef __cplusplus +extern "C" { +#endif + struct mixer * mixer_new(const struct mixer_plugin *plugin, void *ao, const struct config_param *param, @@ -60,4 +64,8 @@ mixer_get_volume(struct mixer *mixer, GError **error_r); bool mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/mixer_plugin.h b/src/mixer_plugin.h index 9532b95cb..2f3beed1d 100644 --- a/src/mixer_plugin.h +++ b/src/mixer_plugin.h @@ -27,7 +27,7 @@ #ifndef MPD_MIXER_PLUGIN_H #define MPD_MIXER_PLUGIN_H -#include <glib.h> +#include "gerror.h" #include <stdbool.h> |