aboutsummaryrefslogtreecommitdiffstats
path: root/src/mixer
diff options
context:
space:
mode:
Diffstat (limited to 'src/mixer')
-rw-r--r--src/mixer/MixerAll.cxx175
-rw-r--r--src/mixer/MixerAll.hxx64
-rw-r--r--src/mixer/MixerControl.cxx161
-rw-r--r--src/mixer/MixerControl.hxx60
-rw-r--r--src/mixer/MixerInternal.hxx59
-rw-r--r--src/mixer/MixerList.hxx35
-rw-r--r--src/mixer/MixerPlugin.hxx95
-rw-r--r--src/mixer/MixerType.cxx39
-rw-r--r--src/mixer/MixerType.hxx47
-rw-r--r--src/mixer/Volume.cxx130
-rw-r--r--src/mixer/Volume.hxx49
-rw-r--r--src/mixer/plugins/AlsaMixerPlugin.cxx (renamed from src/mixer/AlsaMixerPlugin.cxx)2
-rw-r--r--src/mixer/plugins/OssMixerPlugin.cxx (renamed from src/mixer/OssMixerPlugin.cxx)2
-rw-r--r--src/mixer/plugins/PulseMixerPlugin.cxx (renamed from src/mixer/PulseMixerPlugin.cxx)2
-rw-r--r--src/mixer/plugins/PulseMixerPlugin.hxx (renamed from src/mixer/PulseMixerPlugin.hxx)0
-rw-r--r--src/mixer/plugins/RoarMixerPlugin.cxx (renamed from src/mixer/RoarMixerPlugin.cxx)2
-rw-r--r--src/mixer/plugins/SoftwareMixerPlugin.cxx (renamed from src/mixer/SoftwareMixerPlugin.cxx)2
-rw-r--r--src/mixer/plugins/SoftwareMixerPlugin.hxx (renamed from src/mixer/SoftwareMixerPlugin.hxx)0
-rw-r--r--src/mixer/plugins/WinmmMixerPlugin.cxx (renamed from src/mixer/WinmmMixerPlugin.cxx)2
19 files changed, 920 insertions, 6 deletions
diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx
new file mode 100644
index 000000000..3cc92baee
--- /dev/null
+++ b/src/mixer/MixerAll.cxx
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2003-2014 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 "MixerAll.hxx"
+#include "MixerControl.hxx"
+#include "MixerInternal.hxx"
+#include "MixerList.hxx"
+#include "output/OutputAll.hxx"
+#include "output/OutputInternal.hxx"
+#include "pcm/Volume.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+
+static constexpr Domain mixer_domain("mixer");
+
+static int
+output_mixer_get_volume(unsigned i)
+{
+ struct audio_output *output;
+ int volume;
+
+ assert(i < audio_output_count());
+
+ output = audio_output_get(i);
+ if (!output->enabled)
+ return -1;
+
+ Mixer *mixer = output->mixer;
+ if (mixer == nullptr)
+ return -1;
+
+ Error error;
+ volume = mixer_get_volume(mixer, error);
+ if (volume < 0 && error.IsDefined())
+ FormatError(error,
+ "Failed to read mixer for '%s'",
+ output->name);
+
+ 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;
+ bool success;
+
+ assert(i < audio_output_count());
+ assert(volume <= 100);
+
+ output = audio_output_get(i);
+ if (!output->enabled)
+ return false;
+
+ Mixer *mixer = output->mixer;
+ if (mixer == nullptr)
+ return false;
+
+ Error error;
+ success = mixer_set_volume(mixer, volume, error);
+ if (!success && error.IsDefined())
+ FormatError(error,
+ "Failed to set mixer for '%s'",
+ output->name);
+
+ 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;
+
+ assert(i < audio_output_count());
+
+ output = audio_output_get(i);
+ if (!output->enabled)
+ return -1;
+
+ Mixer *mixer = output->mixer;
+ if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
+ return -1;
+
+ return mixer_get_volume(mixer, IgnoreError());
+}
+
+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 != nullptr &&
+ output->mixer->plugin == &software_mixer_plugin)
+ mixer_set_volume(output->mixer, volume, IgnoreError());
+ }
+}
diff --git a/src/mixer/MixerAll.hxx b/src/mixer/MixerAll.hxx
new file mode 100644
index 000000000..f868f64b5
--- /dev/null
+++ b/src/mixer/MixerAll.hxx
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003-2014 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_HXX
+#define MPD_MIXER_ALL_HXX
+
+#include "Compiler.h"
+
+/**
+ * Returns the average volume of all available mixers (range 0..100).
+ * Returns -1 if no mixer can be queried.
+ */
+gcc_pure
+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.
+ */
+gcc_pure
+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/MixerControl.cxx b/src/mixer/MixerControl.cxx
new file mode 100644
index 000000000..bdf6fcc2b
--- /dev/null
+++ b/src/mixer/MixerControl.cxx
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2003-2014 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 "MixerControl.hxx"
+#include "MixerInternal.hxx"
+#include "util/Error.hxx"
+
+#include <assert.h>
+
+Mixer *
+mixer_new(const struct mixer_plugin *plugin, void *ao,
+ const config_param &param,
+ Error &error)
+{
+ Mixer *mixer;
+
+ assert(plugin != nullptr);
+
+ mixer = plugin->init(ao, param, error);
+
+ assert(mixer == nullptr || mixer->IsPlugin(*plugin));
+
+ return mixer;
+}
+
+void
+mixer_free(Mixer *mixer)
+{
+ assert(mixer != nullptr);
+ assert(mixer->plugin != nullptr);
+
+ /* mixers with the "global" flag set might still be open at
+ this point (see mixer_auto_close()) */
+ mixer_close(mixer);
+
+ mixer->plugin->finish(mixer);
+}
+
+bool
+mixer_open(Mixer *mixer, Error &error)
+{
+ bool success;
+
+ assert(mixer != nullptr);
+ assert(mixer->plugin != nullptr);
+
+ const ScopeLock protect(mixer->mutex);
+
+ if (mixer->open)
+ success = true;
+ else if (mixer->plugin->open == nullptr)
+ success = mixer->open = true;
+ else
+ success = mixer->open = mixer->plugin->open(mixer, error);
+
+ mixer->failed = !success;
+
+ return success;
+}
+
+static void
+mixer_close_internal(Mixer *mixer)
+{
+ assert(mixer != nullptr);
+ assert(mixer->plugin != nullptr);
+ assert(mixer->open);
+
+ if (mixer->plugin->close != nullptr)
+ mixer->plugin->close(mixer);
+
+ mixer->open = false;
+}
+
+void
+mixer_close(Mixer *mixer)
+{
+ assert(mixer != nullptr);
+ assert(mixer->plugin != nullptr);
+
+ const ScopeLock protect(mixer->mutex);
+
+ if (mixer->open)
+ mixer_close_internal(mixer);
+}
+
+void
+mixer_auto_close(Mixer *mixer)
+{
+ if (!mixer->plugin->global)
+ mixer_close(mixer);
+}
+
+/*
+ * Close the mixer due to failure. The mutex must be locked before
+ * calling this function.
+ */
+static void
+mixer_failed(Mixer *mixer)
+{
+ assert(mixer->open);
+
+ mixer_close_internal(mixer);
+
+ mixer->failed = true;
+}
+
+int
+mixer_get_volume(Mixer *mixer, Error &error)
+{
+ int volume;
+
+ assert(mixer != nullptr);
+
+ if (mixer->plugin->global && !mixer->failed &&
+ !mixer_open(mixer, error))
+ return -1;
+
+ const ScopeLock protect(mixer->mutex);
+
+ if (mixer->open) {
+ volume = mixer->plugin->get_volume(mixer, error);
+ if (volume < 0 && error.IsDefined())
+ mixer_failed(mixer);
+ } else
+ volume = -1;
+
+ return volume;
+}
+
+bool
+mixer_set_volume(Mixer *mixer, unsigned volume, Error &error)
+{
+ assert(mixer != nullptr);
+ assert(volume <= 100);
+
+ if (mixer->plugin->global && !mixer->failed &&
+ !mixer_open(mixer, error))
+ return false;
+
+ const ScopeLock protect(mixer->mutex);
+
+ return mixer->open &&
+ mixer->plugin->set_volume(mixer, volume, error);
+}
diff --git a/src/mixer/MixerControl.hxx b/src/mixer/MixerControl.hxx
new file mode 100644
index 000000000..caa1c3054
--- /dev/null
+++ b/src/mixer/MixerControl.hxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * Functions which manipulate a #mixer object.
+ */
+
+#ifndef MPD_MIXER_CONTROL_HXX
+#define MPD_MIXER_CONTROL_HXX
+
+class Error;
+class Mixer;
+struct mixer_plugin;
+struct config_param;
+
+Mixer *
+mixer_new(const mixer_plugin *plugin, void *ao,
+ const config_param &param,
+ Error &error);
+
+void
+mixer_free(Mixer *mixer);
+
+bool
+mixer_open(Mixer *mixer, Error &error);
+
+void
+mixer_close(Mixer *mixer);
+
+/**
+ * Close the mixer unless the plugin's "global" flag is set. This is
+ * called when the #audio_output is closed.
+ */
+void
+mixer_auto_close(Mixer *mixer);
+
+int
+mixer_get_volume(Mixer *mixer, Error &error);
+
+bool
+mixer_set_volume(Mixer *mixer, unsigned volume, Error &error);
+
+#endif
diff --git a/src/mixer/MixerInternal.hxx b/src/mixer/MixerInternal.hxx
new file mode 100644
index 000000000..1732b4f3b
--- /dev/null
+++ b/src/mixer/MixerInternal.hxx
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2003-2014 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_INTERNAL_HXX
+#define MPD_MIXER_INTERNAL_HXX
+
+#include "MixerPlugin.hxx"
+#include "MixerList.hxx"
+#include "thread/Mutex.hxx"
+
+class Mixer {
+public:
+ const struct mixer_plugin *plugin;
+
+ /**
+ * This mutex protects all of the mixer struct, including its
+ * implementation, so plugins don't have to deal with that.
+ */
+ Mutex mutex;
+
+ /**
+ * Is the mixer device currently open?
+ */
+ bool open;
+
+ /**
+ * Has this mixer failed, and should not be reopened
+ * automatically?
+ */
+ bool failed;
+
+public:
+ Mixer(const mixer_plugin &_plugin)
+ :plugin(&_plugin),
+ open(false),
+ failed(false) {}
+
+ bool IsPlugin(const mixer_plugin &other) const {
+ return plugin == &other;
+ }
+};
+
+#endif
diff --git a/src/mixer/MixerList.hxx b/src/mixer/MixerList.hxx
new file mode 100644
index 000000000..eb3dba938
--- /dev/null
+++ b/src/mixer/MixerList.hxx
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * This header provides "extern" declarations for all mixer plugins.
+ */
+
+#ifndef MPD_MIXER_LIST_HXX
+#define MPD_MIXER_LIST_HXX
+
+extern const struct mixer_plugin software_mixer_plugin;
+extern const struct mixer_plugin alsa_mixer_plugin;
+extern const struct mixer_plugin oss_mixer_plugin;
+extern const struct mixer_plugin roar_mixer_plugin;
+extern const struct mixer_plugin pulse_mixer_plugin;
+extern const struct mixer_plugin winmm_mixer_plugin;
+
+#endif
diff --git a/src/mixer/MixerPlugin.hxx b/src/mixer/MixerPlugin.hxx
new file mode 100644
index 000000000..b29ecbab8
--- /dev/null
+++ b/src/mixer/MixerPlugin.hxx
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * This header declares the mixer_plugin class. It should not be
+ * included directly; use MixerInternal.hxx instead in mixer
+ * implementations.
+ */
+
+#ifndef MPD_MIXER_PLUGIN_HXX
+#define MPD_MIXER_PLUGIN_HXX
+
+struct config_param;
+class Mixer;
+class Error;
+
+struct mixer_plugin {
+ /**
+ * Alocates and configures a mixer device.
+ *
+ * @param ao the pointer returned by audio_output_plugin.init
+ * @param param the configuration section
+ * @param error_r location to store the error occurring, or
+ * nullptr to ignore errors
+ * @return a mixer object, or nullptr on error
+ */
+ Mixer *(*init)(void *ao, const config_param &param,
+ Error &error);
+
+ /**
+ * Finish and free mixer data
+ */
+ void (*finish)(Mixer *data);
+
+ /**
+ * Open mixer device
+ *
+ * @param error_r location to store the error occurring, or
+ * nullptr to ignore errors
+ * @return true on success, false on error
+ */
+ bool (*open)(Mixer *data, Error &error);
+
+ /**
+ * Close mixer device
+ */
+ void (*close)(Mixer *data);
+
+ /**
+ * Reads the current volume.
+ *
+ * @param error_r location to store the error occurring, or
+ * nullptr to ignore errors
+ * @return the current volume (0..100 including) or -1 if
+ * unavailable or on error (error set, mixer will be closed)
+ */
+ int (*get_volume)(Mixer *mixer, Error &error);
+
+ /**
+ * Sets the volume.
+ *
+ * @param error_r location to store the error occurring, or
+ * nullptr to ignore errors
+ * @param volume the new volume (0..100 including)
+ * @return true on success, false on error
+ */
+ bool (*set_volume)(Mixer *mixer, unsigned volume,
+ Error &error);
+
+ /**
+ * If true, then the mixer is automatically opened, even if
+ * its audio output is not open. If false, then the mixer is
+ * disabled as long as its audio output is closed.
+ */
+ bool global;
+};
+
+#endif
diff --git a/src/mixer/MixerType.cxx b/src/mixer/MixerType.cxx
new file mode 100644
index 000000000..cd45db0d9
--- /dev/null
+++ b/src/mixer/MixerType.cxx
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003-2014 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 "MixerType.hxx"
+
+#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/MixerType.hxx b/src/mixer/MixerType.hxx
new file mode 100644
index 000000000..bfa2637b7
--- /dev/null
+++ b/src/mixer/MixerType.hxx
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2003-2014 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_HXX
+#define MPD_MIXER_TYPE_HXX
+
+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
diff --git a/src/mixer/Volume.cxx b/src/mixer/Volume.cxx
new file mode 100644
index 000000000..4c897b21f
--- /dev/null
+++ b/src/mixer/Volume.cxx
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2003-2014 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 "Volume.hxx"
+#include "MixerAll.hxx"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+#include "util/StringUtil.hxx"
+#include "util/Domain.hxx"
+#include "system/PeriodClock.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#define SW_VOLUME_STATE "sw_volume: "
+
+static constexpr Domain volume_domain("volume");
+
+static unsigned volume_software_set = 100;
+
+/** the cached hardware mixer value; invalid if negative */
+static int last_hardware_volume = -1;
+/** the age of #last_hardware_volume */
+static PeriodClock hardware_volume_clock;
+
+/**
+ * Handler for #GlobalEvents::MIXER.
+ */
+static void
+mixer_event_callback(void)
+{
+ /* flush the hardware volume cache */
+ last_hardware_volume = -1;
+
+ /* notify clients */
+ idle_add(IDLE_MIXER);
+}
+
+void volume_init(void)
+{
+ GlobalEvents::Register(GlobalEvents::MIXER, mixer_event_callback);
+}
+
+int volume_level_get(void)
+{
+ if (last_hardware_volume >= 0 &&
+ !hardware_volume_clock.CheckUpdate(1000))
+ /* throttle access to hardware mixers */
+ return last_hardware_volume;
+
+ last_hardware_volume = mixer_all_get_volume();
+ return last_hardware_volume;
+}
+
+static bool software_volume_change(unsigned volume)
+{
+ assert(volume <= 100);
+
+ volume_software_set = volume;
+ mixer_all_set_software_volume(volume);
+
+ return true;
+}
+
+static bool hardware_volume_change(unsigned volume)
+{
+ /* reset the cache */
+ last_hardware_volume = -1;
+
+ return mixer_all_set_volume(volume);
+}
+
+bool volume_level_change(unsigned volume)
+{
+ assert(volume <= 100);
+
+ volume_software_set = volume;
+
+ idle_add(IDLE_MIXER);
+
+ return hardware_volume_change(volume);
+}
+
+bool
+read_sw_volume_state(const char *line)
+{
+ char *end = nullptr;
+ long int sv;
+
+ if (!StringStartsWith(line, SW_VOLUME_STATE))
+ return false;
+
+ line += sizeof(SW_VOLUME_STATE) - 1;
+ sv = strtol(line, &end, 10);
+ if (*end == 0 && sv >= 0 && sv <= 100)
+ software_volume_change(sv);
+ else
+ FormatWarning(volume_domain,
+ "Can't parse software volume: %s", line);
+ return true;
+}
+
+void save_sw_volume_state(FILE *fp)
+{
+ fprintf(fp, SW_VOLUME_STATE "%u\n", volume_software_set);
+}
+
+unsigned
+sw_volume_state_get_hash(void)
+{
+ return volume_software_set;
+}
diff --git a/src/mixer/Volume.hxx b/src/mixer/Volume.hxx
new file mode 100644
index 000000000..06d3551eb
--- /dev/null
+++ b/src/mixer/Volume.hxx
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2003-2014 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_VOLUME_HXX
+#define MPD_VOLUME_HXX
+
+#include "Compiler.h"
+
+#include <stdio.h>
+
+void volume_init(void);
+
+gcc_pure
+int volume_level_get(void);
+
+bool volume_level_change(unsigned volume);
+
+bool
+read_sw_volume_state(const char *line);
+
+void save_sw_volume_state(FILE *fp);
+
+/**
+ * Generates a hash number for the current state of the software
+ * volume control. This is used by timer_save_state_file() to
+ * determine whether the state has changed and the state file should
+ * be saved.
+ */
+gcc_pure
+unsigned
+sw_volume_state_get_hash(void);
+
+#endif
diff --git a/src/mixer/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx
index 9d04ad98b..a15820fc7 100644
--- a/src/mixer/AlsaMixerPlugin.cxx
+++ b/src/mixer/plugins/AlsaMixerPlugin.cxx
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "output/OutputAPI.hxx"
#include "GlobalEvents.hxx"
#include "Main.hxx"
diff --git a/src/mixer/OssMixerPlugin.cxx b/src/mixer/plugins/OssMixerPlugin.cxx
index 4e116c2fb..095ace015 100644
--- a/src/mixer/OssMixerPlugin.cxx
+++ b/src/mixer/plugins/OssMixerPlugin.cxx
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "config/ConfigData.hxx"
#include "system/fd_util.h"
#include "util/ASCII.hxx"
diff --git a/src/mixer/PulseMixerPlugin.cxx b/src/mixer/plugins/PulseMixerPlugin.cxx
index a379cebae..4df88d06b 100644
--- a/src/mixer/PulseMixerPlugin.cxx
+++ b/src/mixer/plugins/PulseMixerPlugin.cxx
@@ -19,7 +19,7 @@
#include "config.h"
#include "PulseMixerPlugin.hxx"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "output/plugins/PulseOutputPlugin.hxx"
#include "GlobalEvents.hxx"
#include "util/Error.hxx"
diff --git a/src/mixer/PulseMixerPlugin.hxx b/src/mixer/plugins/PulseMixerPlugin.hxx
index 993945e9b..993945e9b 100644
--- a/src/mixer/PulseMixerPlugin.hxx
+++ b/src/mixer/plugins/PulseMixerPlugin.hxx
diff --git a/src/mixer/RoarMixerPlugin.cxx b/src/mixer/plugins/RoarMixerPlugin.cxx
index 18f8c14f8..958688cd7 100644
--- a/src/mixer/RoarMixerPlugin.cxx
+++ b/src/mixer/plugins/RoarMixerPlugin.cxx
@@ -20,7 +20,7 @@
#include "config.h"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "output/plugins/RoarOutputPlugin.hxx"
#include "Compiler.h"
diff --git a/src/mixer/SoftwareMixerPlugin.cxx b/src/mixer/plugins/SoftwareMixerPlugin.cxx
index a0de727af..67ddee1a4 100644
--- a/src/mixer/SoftwareMixerPlugin.cxx
+++ b/src/mixer/plugins/SoftwareMixerPlugin.cxx
@@ -19,7 +19,7 @@
#include "config.h"
#include "SoftwareMixerPlugin.hxx"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "filter/FilterPlugin.hxx"
#include "filter/FilterRegistry.hxx"
#include "filter/FilterInternal.hxx"
diff --git a/src/mixer/SoftwareMixerPlugin.hxx b/src/mixer/plugins/SoftwareMixerPlugin.hxx
index 581d2ac17..581d2ac17 100644
--- a/src/mixer/SoftwareMixerPlugin.hxx
+++ b/src/mixer/plugins/SoftwareMixerPlugin.hxx
diff --git a/src/mixer/WinmmMixerPlugin.cxx b/src/mixer/plugins/WinmmMixerPlugin.cxx
index 6f10fd71b..093a026a9 100644
--- a/src/mixer/WinmmMixerPlugin.cxx
+++ b/src/mixer/plugins/WinmmMixerPlugin.cxx
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "MixerInternal.hxx"
+#include "mixer/MixerInternal.hxx"
#include "output/OutputAPI.hxx"
#include "output/plugins/WinmmOutputPlugin.hxx"
#include "util/Error.hxx"