aboutsummaryrefslogtreecommitdiffstats
path: root/src/mixer
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/mixer/alsa_mixer_plugin.c (renamed from src/mixer/alsa_mixer.c)64
-rw-r--r--src/mixer/oss_mixer_plugin.c (renamed from src/mixer/oss_mixer.c)55
-rw-r--r--src/mixer/pulse_mixer.c382
-rw-r--r--src/mixer/pulse_mixer_plugin.c234
-rw-r--r--src/mixer/pulse_mixer_plugin.h39
-rw-r--r--src/mixer/software_mixer_plugin.c109
-rw-r--r--src/mixer/software_mixer_plugin.h33
-rw-r--r--src/mixer_all.c98
-rw-r--r--src/mixer_all.h21
-rw-r--r--src/mixer_api.c1
-rw-r--r--src/mixer_control.c54
-rw-r--r--src/mixer_control.h15
-rw-r--r--src/mixer_list.h7
-rw-r--r--src/mixer_plugin.h33
-rw-r--r--src/mixer_type.c39
-rw-r--r--src/mixer_type.h47
16 files changed, 744 insertions, 487 deletions
diff --git a/src/mixer/alsa_mixer.c b/src/mixer/alsa_mixer_plugin.c
index 52e553cc5..6726f785a 100644
--- a/src/mixer/alsa_mixer.c
+++ b/src/mixer/alsa_mixer_plugin.c
@@ -17,8 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "../output_api.h"
-#include "../mixer_api.h"
+#include "config.h"
+#include "mixer_api.h"
+#include "output_api.h"
#include <glib.h>
#include <alsa/asoundlib.h>
@@ -42,12 +43,22 @@ struct alsa_mixer {
int volume_set;
};
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+alsa_mixer_quark(void)
+{
+ return g_quark_from_static_string("alsa_mixer");
+}
+
static struct mixer *
-alsa_mixer_init(const struct config_param *param)
+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);
+ mixer_init(&am->base, &alsa_mixer_plugin);
am->device = config_get_block_string(param, "mixer_device",
VOLUME_MIXER_ALSA_DEFAULT);
@@ -81,7 +92,7 @@ alsa_mixer_close(struct mixer *data)
}
static bool
-alsa_mixer_open(struct mixer *data)
+alsa_mixer_open(struct mixer *data, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)data;
int err;
@@ -91,29 +102,33 @@ alsa_mixer_open(struct mixer *data)
err = snd_mixer_open(&am->handle, 0);
if (err < 0) {
- g_warning("problems opening alsa mixer: %s\n", snd_strerror(err));
+ g_set_error(error_r, alsa_mixer_quark(), err,
+ "snd_mixer_open() failed: %s", snd_strerror(err));
return false;
}
if ((err = snd_mixer_attach(am->handle, am->device)) < 0) {
- g_warning("problems attaching alsa mixer: %s\n",
- snd_strerror(err));
alsa_mixer_close(data);
+ 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_warning("problems snd_mixer_selem_register'ing: %s\n",
- snd_strerror(err));
alsa_mixer_close(data);
+ 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_warning("problems snd_mixer_selem_register'ing: %s\n",
- snd_strerror(err));
alsa_mixer_close(data);
+ g_set_error(error_r, alsa_mixer_quark(), err,
+ "snd_mixer_load() failed: %s\n",
+ snd_strerror(err));
return false;
}
@@ -138,14 +153,14 @@ alsa_mixer_open(struct mixer *data)
return true;
}
- g_warning("can't find alsa mixer control \"%s\"\n", am->control);
-
alsa_mixer_close(data);
+ g_set_error(error_r, alsa_mixer_quark(), 0,
+ "no such mixer control: %s", am->control);
return false;
}
static int
-alsa_mixer_get_volume(struct mixer *mixer)
+alsa_mixer_get_volume(struct mixer *mixer, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)mixer;
int err;
@@ -156,8 +171,9 @@ alsa_mixer_get_volume(struct mixer *mixer)
err = snd_mixer_handle_events(am->handle);
if (err < 0) {
- g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
- snd_strerror(err), "handle_events");
+ g_set_error(error_r, alsa_mixer_quark(), err,
+ "snd_mixer_handle_events() failed: %s",
+ snd_strerror(err));
return false;
}
@@ -165,8 +181,9 @@ alsa_mixer_get_volume(struct mixer *mixer)
SND_MIXER_SCHN_FRONT_LEFT,
&level);
if (err < 0) {
- g_warning("problems getting alsa volume: %s (snd_mixer_%s)\n",
- snd_strerror(err), "selem_get_playback_volume");
+ g_set_error(error_r, alsa_mixer_quark(), err,
+ "failed to read ALSA volume: %s",
+ snd_strerror(err));
return false;
}
@@ -183,7 +200,7 @@ alsa_mixer_get_volume(struct mixer *mixer)
}
static bool
-alsa_mixer_set_volume(struct mixer *mixer, unsigned volume)
+alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
{
struct alsa_mixer *am = (struct alsa_mixer *)mixer;
float vol;
@@ -203,15 +220,16 @@ alsa_mixer_set_volume(struct mixer *mixer, unsigned volume)
err = snd_mixer_selem_set_playback_volume_all(am->elem, level);
if (err < 0) {
- g_warning("problems setting alsa volume: %s\n",
- snd_strerror(err));
+ 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 = {
+const struct mixer_plugin alsa_mixer_plugin = {
.init = alsa_mixer_init,
.finish = alsa_mixer_finish,
.open = alsa_mixer_open,
diff --git a/src/mixer/oss_mixer.c b/src/mixer/oss_mixer_plugin.c
index f2db01ff4..6e75edd9b 100644
--- a/src/mixer/oss_mixer.c
+++ b/src/mixer/oss_mixer_plugin.c
@@ -17,8 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "../output_api.h"
-#include "../mixer_api.h"
+#include "config.h"
+#include "mixer_api.h"
+#include "output_api.h"
+#include "fd_util.h"
#include <glib.h>
@@ -49,6 +51,15 @@ struct oss_mixer {
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)
{
@@ -65,11 +76,12 @@ oss_find_mixer(const char *name)
}
static struct mixer *
-oss_mixer_init(const struct config_param *param)
+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);
+ mixer_init(&om->base, &oss_mixer_plugin);
om->device = config_get_block_string(param, "mixer_device",
VOLUME_MIXER_OSS_DEFAULT);
@@ -78,9 +90,9 @@ oss_mixer_init(const struct config_param *param)
if (om->control != NULL) {
om->volume_control = oss_find_mixer(om->control);
if (om->volume_control < 0) {
- g_warning("mixer control \"%s\" not found",
- om->control);
g_free(om);
+ g_set_error(error_r, oss_mixer_quark(), 0,
+ "no such mixer control: %s", om->control);
return NULL;
}
} else
@@ -108,13 +120,15 @@ oss_mixer_close(struct mixer *data)
}
static bool
-oss_mixer_open(struct mixer *data)
+oss_mixer_open(struct mixer *data, GError **error_r)
{
struct oss_mixer *om = (struct oss_mixer *) data;
- om->device_fd = open(om->device, O_RDONLY);
+ om->device_fd = open_cloexec(om->device, O_RDONLY, 0);
if (om->device_fd < 0) {
- g_warning("Unable to open oss mixer \"%s\"\n", om->device);
+ g_set_error(error_r, oss_mixer_quark(), errno,
+ "failed to open %s: %s",
+ om->device, g_strerror(errno));
return false;
}
@@ -122,14 +136,17 @@ oss_mixer_open(struct mixer *data)
int devmask = 0;
if (ioctl(om->device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
- g_warning("errors getting read_devmask for oss mixer\n");
+ 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_warning("mixer control \"%s\" not usable\n",
- om->control);
+ g_set_error(error_r, oss_mixer_quark(), 0,
+ "mixer control \"%s\" not usable",
+ om->control);
oss_mixer_close(data);
return false;
}
@@ -138,7 +155,7 @@ oss_mixer_open(struct mixer *data)
}
static int
-oss_mixer_get_volume(struct mixer *mixer)
+oss_mixer_get_volume(struct mixer *mixer, GError **error_r)
{
struct oss_mixer *om = (struct oss_mixer *)mixer;
int left, right, level;
@@ -148,7 +165,9 @@ oss_mixer_get_volume(struct mixer *mixer)
ret = ioctl(om->device_fd, MIXER_READ(om->volume_control), &level);
if (ret < 0) {
- g_warning("unable to read oss volume\n");
+ g_set_error(error_r, oss_mixer_quark(), errno,
+ "failed to read OSS volume: %s",
+ g_strerror(errno));
return false;
}
@@ -164,7 +183,7 @@ oss_mixer_get_volume(struct mixer *mixer)
}
static bool
-oss_mixer_set_volume(struct mixer *mixer, unsigned volume)
+oss_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
{
struct oss_mixer *om = (struct oss_mixer *)mixer;
int level;
@@ -177,14 +196,16 @@ oss_mixer_set_volume(struct mixer *mixer, unsigned volume)
ret = ioctl(om->device_fd, MIXER_WRITE(om->volume_control), &level);
if (ret < 0) {
- g_warning("unable to set oss volume\n");
+ 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 = {
+const struct mixer_plugin oss_mixer_plugin = {
.init = oss_mixer_init,
.finish = oss_mixer_finish,
.open = oss_mixer_open,
diff --git a/src/mixer/pulse_mixer.c b/src/mixer/pulse_mixer.c
deleted file mode 100644
index 5d9ce0475..000000000
--- a/src/mixer/pulse_mixer.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Copyright (C) 2003-2009 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 "mixer_api.h"
-#include "conf.h"
-
-#include <glib.h>
-#include <pulse/volume.h>
-#include <pulse/pulseaudio.h>
-
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "pulse_mixer"
-
-struct pulse_mixer {
- struct mixer base;
-
- const char *server;
- const char *sink;
- const char *output_name;
-
- uint32_t index;
- bool online;
-
- struct pa_context *context;
- struct pa_threaded_mainloop *mainloop;
- struct pa_cvolume volume;
-
-};
-
-/**
- * \brief waits for a pulseaudio operation to finish, frees it and
- * unlocks the mainloop
- * \param operation the operation to wait for
- * \return true if operation has finished normally (DONE state),
- * false otherwise
- */
-static bool
-pulse_wait_for_operation(struct pa_threaded_mainloop *mainloop,
- struct pa_operation *operation)
-{
- pa_operation_state_t state;
-
- assert(mainloop != NULL);
- assert(operation != NULL);
-
- state = pa_operation_get_state(operation);
- while (state == PA_OPERATION_RUNNING) {
- pa_threaded_mainloop_wait(mainloop);
- state = pa_operation_get_state(operation);
- }
-
- pa_operation_unref(operation);
-
- return state == PA_OPERATION_DONE;
-}
-
-static void
-sink_input_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
- int eol, void *userdata)
-{
-
- struct pulse_mixer *pm = userdata;
-
- if (eol) {
- g_debug("eol error sink_input_cb");
- return;
- }
-
- if (i == NULL) {
- g_debug("Sink input callback failure");
- return;
- }
-
- g_debug("sink input cb %s, index %d ",i->name,i->index);
-
- if (strcmp(i->name,pm->output_name) == 0) {
- pm->index = i->index;
- pm->online = true;
- pm->volume = i->volume;
- } else
- g_debug("bad name");
-}
-
-static void
-sink_input_vol(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
- int eol, void *userdata)
-{
-
- struct pulse_mixer *pm = userdata;
-
- if (eol) {
- g_debug("eol error sink_input_vol");
- return;
- }
-
- if (i == NULL) {
- g_debug("Sink input callback failure");
- return;
- }
-
- g_debug("sink input vol %s, index %d ", i->name, i->index);
-
- pm->volume = i->volume;
-
- pa_threaded_mainloop_signal(pm->mainloop, 0);
-}
-
-static void
-subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
- uint32_t idx, void *userdata)
-{
-
- struct pulse_mixer *pm = userdata;
-
- g_debug("subscribe call back");
-
- switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
- case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
- PA_SUBSCRIPTION_EVENT_REMOVE &&
- pm->index == idx)
- pm->online = false;
- else {
- pa_operation *o;
-
- o = pa_context_get_sink_input_info(c, idx,
- sink_input_cb, pm);
- if (o == NULL) {
- g_debug("pa_context_get_sink_input_info() failed");
- return;
- }
-
- pa_operation_unref(o);
- }
-
- break;
- }
-}
-
-static void
-context_state_cb(pa_context *context, void *userdata)
-{
- struct pulse_mixer *pm = userdata;
-
- switch (pa_context_get_state(context)) {
- case PA_CONTEXT_READY: {
- pa_operation *o;
-
- pa_context_set_subscribe_callback(context, subscribe_cb, pm);
-
- o = pa_context_subscribe(context,
- (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT,
- NULL, NULL);
- if (o == NULL) {
- g_debug("pa_context_subscribe() failed");
- return;
- }
-
- pa_operation_unref(o);
-
- o = pa_context_get_sink_input_info_list(context,
- sink_input_cb, pm);
- if (o == NULL) {
- g_debug("pa_context_get_sink_input_info_list() failed");
- return;
- }
-
- pa_operation_unref(o);
-
- pa_threaded_mainloop_signal(pm->mainloop, 0);
- break;
- }
-
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal(pm->mainloop, 0);
- break;
- }
-}
-
-
-static struct mixer *
-pulse_mixer_init(const struct config_param *param)
-{
- struct pulse_mixer *pm = g_new(struct pulse_mixer,1);
- mixer_init(&pm->base, &pulse_mixer);
-
- pm->online = false;
-
- pm->server = config_get_block_string(param, "server", NULL);
- pm->sink = config_get_block_string(param, "sink", NULL);
- pm->output_name = config_get_block_string(param, "name", NULL);
-
- return &pm->base;
-}
-
-static void
-pulse_mixer_finish(struct mixer *data)
-{
- struct pulse_mixer *pm = (struct pulse_mixer *) data;
-
- g_free(pm);
-}
-
-static bool
-pulse_mixer_setup(struct pulse_mixer *pm)
-{
- pa_context_set_state_callback(pm->context, context_state_cb, pm);
-
- if (pa_context_connect(pm->context, pm->server,
- (pa_context_flags_t)0, NULL) < 0) {
- g_debug("context server fail");
- return false;
- }
-
- pa_threaded_mainloop_lock(pm->mainloop);
-
- if (pa_threaded_mainloop_start(pm->mainloop) < 0) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- g_debug("error start mainloop");
- return false;
- }
-
- pa_threaded_mainloop_wait(pm->mainloop);
-
- if (pa_context_get_state(pm->context) != PA_CONTEXT_READY) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- g_debug("error context not ready");
- return false;
- }
-
- pa_threaded_mainloop_unlock(pm->mainloop);
-
- return true;
-}
-
-static bool
-pulse_mixer_open(struct mixer *data)
-{
- struct pulse_mixer *pm = (struct pulse_mixer *) data;
-
- g_debug("pulse mixer open");
-
- pm->index = 0;
- pm->online = false;
-
- pm->mainloop = pa_threaded_mainloop_new();
- if (pm->mainloop == NULL) {
- g_debug("failed mainloop");
- return false;
- }
-
- pm->context = pa_context_new(pa_threaded_mainloop_get_api(pm->mainloop),
- "Mixer mpd");
- if (pm->context == NULL) {
- pa_threaded_mainloop_stop(pm->mainloop);
- pa_threaded_mainloop_free(pm->mainloop);
- g_debug("failed context");
- return false;
- }
-
- if (!pulse_mixer_setup(pm)) {
- pa_threaded_mainloop_stop(pm->mainloop);
- pa_context_disconnect(pm->context);
- pa_context_unref(pm->context);
- pa_threaded_mainloop_free(pm->mainloop);
- return false;
- }
-
- return true;
-}
-
-static void
-pulse_mixer_close(struct mixer *data)
-{
- struct pulse_mixer *pm = (struct pulse_mixer *) data;
-
- pa_threaded_mainloop_stop(pm->mainloop);
- pa_context_disconnect(pm->context);
- pa_context_unref(pm->context);
- pa_threaded_mainloop_free(pm->mainloop);
-
- pm->online = false;
-}
-
-static int
-pulse_mixer_get_volume(struct mixer *mixer)
-{
- struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
- int ret;
- pa_operation *o;
-
- pa_threaded_mainloop_lock(pm->mainloop);
-
- if (!pm->online) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- return false;
- }
-
- o = pa_context_get_sink_input_info(pm->context, pm->index,
- sink_input_vol, pm);
- if (o == NULL) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- g_debug("pa_context_get_sink_input_info() failed");
- return false;
- }
-
- if (!pulse_wait_for_operation(pm->mainloop, o)) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- return false;
- }
-
- ret = pm->online
- ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
- : -1;
-
- pa_threaded_mainloop_unlock(pm->mainloop);
-
- return ret;
-}
-
-static bool
-pulse_mixer_set_volume(struct mixer *mixer, unsigned volume)
-{
- struct pulse_mixer *pm = (struct pulse_mixer *) mixer;
- struct pa_cvolume cvolume;
- pa_operation *o;
-
- pa_threaded_mainloop_lock(pm->mainloop);
-
- if (!pm->online) {
- pa_threaded_mainloop_unlock(pm->mainloop);
- return false;
- }
-
- pa_cvolume_set(&cvolume, pm->volume.channels,
- (pa_volume_t)volume * PA_VOLUME_NORM / 100 + 0.5);
-
- o = pa_context_set_sink_input_volume(pm->context, pm->index,
- &cvolume, NULL, NULL);
- pa_threaded_mainloop_unlock(pm->mainloop);
- if (o == NULL) {
- g_debug("pa_context_set_sink_input_volume() failed");
- return false;
- }
-
- pa_operation_unref(o);
-
- return true;
-}
-
-const struct mixer_plugin pulse_mixer = {
- .init = pulse_mixer_init,
- .finish = pulse_mixer_finish,
- .open = pulse_mixer_open,
- .close = pulse_mixer_close,
- .get_volume = pulse_mixer_get_volume,
- .set_volume = pulse_mixer_set_volume,
-};
diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/pulse_mixer_plugin.c
new file mode 100644
index 000000000..5669e05c4
--- /dev/null
+++ b/src/mixer/pulse_mixer_plugin.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2003-2009 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;
+
+ pa_threaded_mainloop_lock(pm->output->mainloop);
+
+ ret = pm->online
+ ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM)
+ : -1;
+
+ pa_threaded_mainloop_unlock(pm->output->mainloop);
+
+ 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;
+
+ pa_threaded_mainloop_lock(pm->output->mainloop);
+ if (!pm->online) {
+ pa_threaded_mainloop_unlock(pm->output->mainloop);
+ 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;
+ pa_threaded_mainloop_unlock(pm->output->mainloop);
+
+ 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
new file mode 100644
index 000000000..2318b94be
--- /dev/null
+++ b/src/mixer/pulse_mixer_plugin.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003-2009 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/software_mixer_plugin.c b/src/mixer/software_mixer_plugin.c
new file mode 100644
index 000000000..30ae13013
--- /dev/null
+++ b/src/mixer/software_mixer_plugin.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003-2009 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
new file mode 100644
index 000000000..a59f7edeb
--- /dev/null
+++ b/src/mixer/software_mixer_plugin.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2009 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_all.c b/src/mixer_all.c
index 252cb61ab..71f5c3c95 100644
--- a/src/mixer_all.c
+++ b/src/mixer_all.c
@@ -17,11 +17,15 @@
* 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>
@@ -35,6 +39,8 @@ output_mixer_get_volume(unsigned i)
{
struct audio_output *output;
struct mixer *mixer;
+ int volume;
+ GError *error = NULL;
assert(i < audio_output_count());
@@ -46,7 +52,14 @@ output_mixer_get_volume(unsigned i)
if (mixer == NULL)
return -1;
- return mixer_get_volume(mixer);
+ 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
@@ -70,12 +83,15 @@ mixer_all_get_volume(void)
}
static bool
-output_mixer_set_volume(unsigned i, int volume, bool relative)
+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)
@@ -85,31 +101,81 @@ output_mixer_set_volume(unsigned i, int volume, bool relative)
if (mixer == NULL)
return false;
- if (relative) {
- int prev = mixer_get_volume(mixer);
- if (prev < 0)
- return false;
-
- volume += prev;
+ 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);
}
- if (volume > 100)
- volume = 100;
- else if (volume < 0)
- volume = 0;
-
- return mixer_set_volume(mixer, volume);
+ return success;
}
bool
-mixer_all_set_volume(int volume, bool relative)
+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, relative)
+ 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
index 66c4988de..ebe8fed68 100644
--- a/src/mixer_all.h
+++ b/src/mixer_all.h
@@ -37,11 +37,26 @@ mixer_all_get_volume(void);
/**
* Sets the volume on all available mixers.
*
- * @param volume the volume (range 0..100 or -100..100 if #relative)
- * @param relative if true, then the #volume is added to the current value
+ * @param volume the volume (range 0..100)
* @return true on success, false on failure
*/
bool
-mixer_all_set_volume(int volume, bool relative);
+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
index cff23a397..67b7037ef 100644
--- a/src/mixer_api.c
+++ b/src/mixer_api.c
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "config.h"
#include "mixer_api.h"
#undef G_LOG_DOMAIN
diff --git a/src/mixer_control.c b/src/mixer_control.c
index e19b82d65..af01600a1 100644
--- a/src/mixer_control.c
+++ b/src/mixer_control.c
@@ -17,38 +17,26 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "config.h"
#include "mixer_control.h"
#include "mixer_api.h"
-#include <glib.h>
-
#include <assert.h>
#include <stddef.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "mixer"
-static bool mixers_enabled = true;
-
-void
-mixer_disable_all(void)
-{
- g_debug("mixer api is disabled");
- mixers_enabled = false;
-}
-
struct mixer *
-mixer_new(const struct mixer_plugin *plugin, const struct config_param *param)
+mixer_new(const struct mixer_plugin *plugin, void *ao,
+ const struct config_param *param,
+ GError **error_r)
{
struct mixer *mixer;
- //mixers are disabled (by using software volume)
- if (!mixers_enabled) {
- return NULL;
- }
assert(plugin != NULL);
- mixer = plugin->init(param);
+ mixer = plugin->init(ao, param, error_r);
assert(mixer == NULL || mixer->plugin == plugin);
@@ -72,7 +60,7 @@ mixer_free(struct mixer *mixer)
}
bool
-mixer_open(struct mixer *mixer)
+mixer_open(struct mixer *mixer, GError **error_r)
{
bool success;
@@ -83,8 +71,10 @@ mixer_open(struct mixer *mixer)
if (mixer->open)
success = true;
+ else if (mixer->plugin->open == NULL)
+ success = mixer->open = true;
else
- success = mixer->open = mixer->plugin->open(mixer);
+ success = mixer->open = mixer->plugin->open(mixer, error_r);
mixer->failed = !success;
@@ -100,7 +90,9 @@ mixer_close_internal(struct mixer *mixer)
assert(mixer->plugin != NULL);
assert(mixer->open);
- mixer->plugin->close(mixer);
+ if (mixer->plugin->close != NULL)
+ mixer->plugin->close(mixer);
+
mixer->open = false;
}
@@ -140,21 +132,26 @@ mixer_failed(struct mixer *mixer)
}
int
-mixer_get_volume(struct mixer *mixer)
+mixer_get_volume(struct mixer *mixer, GError **error_r)
{
int volume;
assert(mixer != NULL);
- if (mixer->plugin->global && !mixer->failed && !mixer_open(mixer))
+ if (mixer->plugin->global && !mixer->failed &&
+ !mixer_open(mixer, error_r))
return -1;
g_mutex_lock(mixer->mutex);
if (mixer->open) {
- volume = mixer->plugin->get_volume(mixer);
- if (volume < 0)
+ 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;
@@ -164,22 +161,21 @@ mixer_get_volume(struct mixer *mixer)
}
bool
-mixer_set_volume(struct mixer *mixer, unsigned volume)
+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))
+ 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);
- if (!success)
- mixer_failed(mixer);
+ success = mixer->plugin->set_volume(mixer, volume, error_r);
} else
success = false;
diff --git a/src/mixer_control.h b/src/mixer_control.h
index 0f73e8f75..a550e0864 100644
--- a/src/mixer_control.h
+++ b/src/mixer_control.h
@@ -25,23 +25,24 @@
#ifndef MPD_MIXER_CONTROL_H
#define MPD_MIXER_CONTROL_H
+#include <glib.h>
+
#include <stdbool.h>
struct mixer;
struct mixer_plugin;
struct config_param;
-void
-mixer_disable_all(void);
-
struct mixer *
-mixer_new(const struct mixer_plugin *plugin, const struct config_param *param);
+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);
+mixer_open(struct mixer *mixer, GError **error_r);
void
mixer_close(struct mixer *mixer);
@@ -54,9 +55,9 @@ void
mixer_auto_close(struct mixer *mixer);
int
-mixer_get_volume(struct mixer *mixer);
+mixer_get_volume(struct mixer *mixer, GError **error_r);
bool
-mixer_set_volume(struct mixer *mixer, unsigned volume);
+mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r);
#endif
diff --git a/src/mixer_list.h b/src/mixer_list.h
index 7db4a00d8..2d9c773f6 100644
--- a/src/mixer_list.h
+++ b/src/mixer_list.h
@@ -25,8 +25,9 @@
#ifndef MPD_MIXER_LIST_H
#define MPD_MIXER_LIST_H
-extern const struct mixer_plugin alsa_mixer;
-extern const struct mixer_plugin oss_mixer;
-extern const struct mixer_plugin pulse_mixer;
+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 pulse_mixer_plugin;
#endif
diff --git a/src/mixer_plugin.h b/src/mixer_plugin.h
index 2b9b440e5..b6e67e8ff 100644
--- a/src/mixer_plugin.h
+++ b/src/mixer_plugin.h
@@ -27,6 +27,8 @@
#ifndef MPD_MIXER_PLUGIN_H
#define MPD_MIXER_PLUGIN_H
+#include <glib.h>
+
#include <stdbool.h>
struct config_param;
@@ -35,8 +37,16 @@ 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 occuring, or
+ * NULL to ignore errors
+ * @return a mixer object, or NULL on error
*/
- struct mixer *(*init)(const struct config_param *param);
+ struct mixer *(*init)(void *ao, const struct config_param *param,
+ GError **error_r);
/**
* Finish and free mixer data
@@ -45,8 +55,12 @@ struct mixer_plugin {
/**
* Open mixer device
+ *
+ * @param error_r location to store the error occuring, or
+ * NULL to ignore errors
+ * @return true on success, false on error
*/
- bool (*open)(struct mixer *data);
+ bool (*open)(struct mixer *data, GError **error_r);
/**
* Close mixer device
@@ -56,18 +70,23 @@ struct mixer_plugin {
/**
* Reads the current volume.
*
- * @return the current volume (0..100 including) or -1 on
- * error
+ * @param error_r location to store the error occuring, 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);
+ int (*get_volume)(struct mixer *mixer, GError **error_r);
/**
* Sets the volume.
*
+ * @param error_r location to store the error occuring, or
+ * NULL to ignore errors
* @param volume the new volume (0..100 including)
- * @return true on success
+ * @return true on success, false on error
*/
- bool (*set_volume)(struct mixer *mixer, unsigned volume);
+ bool (*set_volume)(struct mixer *mixer, unsigned volume,
+ GError **error_r);
/**
* If true, then the mixer is automatically opened, even if
diff --git a/src/mixer_type.c b/src/mixer_type.c
new file mode 100644
index 000000000..804ecafef
--- /dev/null
+++ b/src/mixer_type.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003-2009 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
new file mode 100644
index 000000000..ec3cbf9cc
--- /dev/null
+++ b/src/mixer_type.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2003-2009 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