From 68eda78704ec50f71333d661db5568700fe00355 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 24 Jan 2014 16:25:21 +0100 Subject: Mixer*: move to mixer/ --- Makefile.am | 34 +-- src/Main.cxx | 2 +- src/MixerAll.cxx | 175 ------------ src/MixerAll.hxx | 64 ----- src/MixerControl.cxx | 161 ----------- src/MixerControl.hxx | 60 ---- src/MixerInternal.hxx | 59 ---- src/MixerList.hxx | 35 --- src/MixerPlugin.hxx | 95 ------- src/MixerType.cxx | 39 --- src/MixerType.hxx | 47 ---- src/StateFile.cxx | 2 +- src/Volume.cxx | 130 --------- src/Volume.hxx | 49 ---- src/command/OtherCommands.cxx | 2 +- src/command/PlayerCommands.cxx | 2 +- src/filter/plugins/ReplayGainFilterPlugin.cxx | 2 +- src/mixer/AlsaMixerPlugin.cxx | 382 -------------------------- src/mixer/MixerAll.cxx | 175 ++++++++++++ src/mixer/MixerAll.hxx | 64 +++++ src/mixer/MixerControl.cxx | 161 +++++++++++ src/mixer/MixerControl.hxx | 60 ++++ src/mixer/MixerInternal.hxx | 59 ++++ src/mixer/MixerList.hxx | 35 +++ src/mixer/MixerPlugin.hxx | 95 +++++++ src/mixer/MixerType.cxx | 39 +++ src/mixer/MixerType.hxx | 47 ++++ src/mixer/OssMixerPlugin.cxx | 241 ---------------- src/mixer/PulseMixerPlugin.cxx | 225 --------------- src/mixer/PulseMixerPlugin.hxx | 36 --- src/mixer/RoarMixerPlugin.cxx | 74 ----- src/mixer/SoftwareMixerPlugin.cxx | 132 --------- src/mixer/SoftwareMixerPlugin.hxx | 33 --- src/mixer/Volume.cxx | 130 +++++++++ src/mixer/Volume.hxx | 49 ++++ src/mixer/WinmmMixerPlugin.cxx | 114 -------- src/mixer/plugins/AlsaMixerPlugin.cxx | 382 ++++++++++++++++++++++++++ src/mixer/plugins/OssMixerPlugin.cxx | 241 ++++++++++++++++ src/mixer/plugins/PulseMixerPlugin.cxx | 225 +++++++++++++++ src/mixer/plugins/PulseMixerPlugin.hxx | 36 +++ src/mixer/plugins/RoarMixerPlugin.cxx | 74 +++++ src/mixer/plugins/SoftwareMixerPlugin.cxx | 132 +++++++++ src/mixer/plugins/SoftwareMixerPlugin.hxx | 33 +++ src/mixer/plugins/WinmmMixerPlugin.cxx | 114 ++++++++ src/output/OutputCommand.cxx | 2 +- src/output/OutputControl.cxx | 2 +- src/output/OutputFinish.cxx | 2 +- src/output/OutputInit.cxx | 8 +- src/output/plugins/AlsaOutputPlugin.cxx | 2 +- src/output/plugins/OssOutputPlugin.cxx | 2 +- src/output/plugins/PulseOutputPlugin.cxx | 4 +- src/output/plugins/RoarOutputPlugin.cxx | 2 +- src/output/plugins/WinmmOutputPlugin.cxx | 2 +- test/read_mixer.cxx | 4 +- test/run_filter.cxx | 2 +- 55 files changed, 2189 insertions(+), 2189 deletions(-) delete mode 100644 src/MixerAll.cxx delete mode 100644 src/MixerAll.hxx delete mode 100644 src/MixerControl.cxx delete mode 100644 src/MixerControl.hxx delete mode 100644 src/MixerInternal.hxx delete mode 100644 src/MixerList.hxx delete mode 100644 src/MixerPlugin.hxx delete mode 100644 src/MixerType.cxx delete mode 100644 src/MixerType.hxx delete mode 100644 src/Volume.cxx delete mode 100644 src/Volume.hxx delete mode 100644 src/mixer/AlsaMixerPlugin.cxx create mode 100644 src/mixer/MixerAll.cxx create mode 100644 src/mixer/MixerAll.hxx create mode 100644 src/mixer/MixerControl.cxx create mode 100644 src/mixer/MixerControl.hxx create mode 100644 src/mixer/MixerInternal.hxx create mode 100644 src/mixer/MixerList.hxx create mode 100644 src/mixer/MixerPlugin.hxx create mode 100644 src/mixer/MixerType.cxx create mode 100644 src/mixer/MixerType.hxx delete mode 100644 src/mixer/OssMixerPlugin.cxx delete mode 100644 src/mixer/PulseMixerPlugin.cxx delete mode 100644 src/mixer/PulseMixerPlugin.hxx delete mode 100644 src/mixer/RoarMixerPlugin.cxx delete mode 100644 src/mixer/SoftwareMixerPlugin.cxx delete mode 100644 src/mixer/SoftwareMixerPlugin.hxx create mode 100644 src/mixer/Volume.cxx create mode 100644 src/mixer/Volume.hxx delete mode 100644 src/mixer/WinmmMixerPlugin.cxx create mode 100644 src/mixer/plugins/AlsaMixerPlugin.cxx create mode 100644 src/mixer/plugins/OssMixerPlugin.cxx create mode 100644 src/mixer/plugins/PulseMixerPlugin.cxx create mode 100644 src/mixer/plugins/PulseMixerPlugin.hxx create mode 100644 src/mixer/plugins/RoarMixerPlugin.cxx create mode 100644 src/mixer/plugins/SoftwareMixerPlugin.cxx create mode 100644 src/mixer/plugins/SoftwareMixerPlugin.hxx create mode 100644 src/mixer/plugins/WinmmMixerPlugin.cxx diff --git a/Makefile.am b/Makefile.am index e909bc111..59f887788 100644 --- a/Makefile.am +++ b/Makefile.am @@ -209,7 +209,7 @@ src_mpd_SOURCES = \ src/TagSave.cxx src/TagSave.hxx \ src/TagFile.cxx src/TagFile.hxx \ src/TagStream.cxx src/TagStream.hxx \ - src/Volume.cxx src/Volume.hxx \ + src/mixer/Volume.cxx src/mixer/Volume.hxx \ src/SongFilter.cxx src/SongFilter.hxx \ src/PlaylistFile.cxx src/PlaylistFile.hxx \ src/Timer.cxx @@ -935,16 +935,16 @@ MIXER_LIBS = \ $(PULSE_LIBS) MIXER_API_SRC = \ - src/MixerPlugin.hxx \ - src/MixerList.hxx \ - src/MixerControl.cxx src/MixerControl.hxx \ - src/MixerType.cxx src/MixerType.hxx \ - src/MixerAll.cxx src/MixerAll.hxx \ - src/MixerInternal.hxx + src/mixer/MixerPlugin.hxx \ + src/mixer/MixerList.hxx \ + src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx \ + src/mixer/MixerType.cxx src/mixer/MixerType.hxx \ + src/mixer/MixerAll.cxx src/mixer/MixerAll.hxx \ + src/mixer/MixerInternal.hxx libmixer_plugins_a_SOURCES = \ - src/mixer/SoftwareMixerPlugin.cxx \ - src/mixer/SoftwareMixerPlugin.hxx + src/mixer/plugins/SoftwareMixerPlugin.cxx \ + src/mixer/plugins/SoftwareMixerPlugin.hxx libmixer_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(ALSA_CFLAGS) \ $(PULSE_CFLAGS) @@ -953,14 +953,14 @@ if HAVE_ALSA liboutput_plugins_a_SOURCES += \ src/output/plugins/AlsaOutputPlugin.cxx \ src/output/plugins/AlsaOutputPlugin.hxx -libmixer_plugins_a_SOURCES += src/mixer/AlsaMixerPlugin.cxx +libmixer_plugins_a_SOURCES += src/mixer/plugins/AlsaMixerPlugin.cxx endif if HAVE_ROAR liboutput_plugins_a_SOURCES += \ src/output/plugins/RoarOutputPlugin.cxx \ src/output/plugins/RoarOutputPlugin.hxx -libmixer_plugins_a_SOURCES += src/mixer/RoarMixerPlugin.cxx +libmixer_plugins_a_SOURCES += src/mixer/plugins/RoarMixerPlugin.cxx endif if HAVE_AO @@ -991,7 +991,7 @@ if HAVE_OSS liboutput_plugins_a_SOURCES += \ src/output/plugins/OssOutputPlugin.cxx \ src/output/plugins/OssOutputPlugin.hxx -libmixer_plugins_a_SOURCES += src/mixer/OssMixerPlugin.cxx +libmixer_plugins_a_SOURCES += src/mixer/plugins/OssMixerPlugin.cxx endif if HAVE_OPENAL @@ -1011,7 +1011,7 @@ liboutput_plugins_a_SOURCES += \ src/output/plugins/PulseOutputPlugin.cxx \ src/output/plugins/PulseOutputPlugin.hxx libmixer_plugins_a_SOURCES += \ - src/mixer/PulseMixerPlugin.cxx src/mixer/PulseMixerPlugin.hxx + src/mixer/plugins/PulseMixerPlugin.cxx src/mixer/plugins/PulseMixerPlugin.hxx endif if HAVE_SHOUT @@ -1045,7 +1045,7 @@ if ENABLE_WINMM_OUTPUT liboutput_plugins_a_SOURCES += \ src/output/plugins/WinmmOutputPlugin.cxx \ src/output/plugins/WinmmOutputPlugin.hxx -libmixer_plugins_a_SOURCES += src/mixer/WinmmMixerPlugin.cxx +libmixer_plugins_a_SOURCES += src/mixer/plugins/WinmmMixerPlugin.cxx endif @@ -1540,8 +1540,8 @@ test_run_output_SOURCES = test/run_output.cxx \ src/output/OutputError.cxx \ src/output/OutputInit.cxx src/output/OutputFinish.cxx src/output/OutputList.cxx \ src/output/OutputPlugin.cxx \ - src/MixerControl.cxx \ - src/MixerType.cxx \ + src/mixer/MixerControl.cxx \ + src/mixer/MixerType.cxx \ src/filter/FilterPlugin.cxx \ src/filter/FilterConfig.cxx \ src/AudioCompress/compress.c \ @@ -1559,7 +1559,7 @@ test_read_mixer_LDADD = \ $(GLIB_LIBS) test_read_mixer_SOURCES = test/read_mixer.cxx \ src/Log.cxx src/LogBackend.cxx \ - src/MixerControl.cxx \ + src/mixer/MixerControl.cxx \ src/filter/FilterPlugin.cxx \ src/AudioFormat.cxx \ src/filter/plugins/VolumeFilterPlugin.cxx diff --git a/src/Main.cxx b/src/Main.cxx index e71814cff..fc7efd036 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -36,7 +36,7 @@ #include "client/ClientList.hxx" #include "command/AllCommands.hxx" #include "Partition.hxx" -#include "Volume.hxx" +#include "mixer/Volume.hxx" #include "output/OutputAll.hxx" #include "tag/TagConfig.hxx" #include "ReplayGainConfig.hxx" diff --git a/src/MixerAll.cxx b/src/MixerAll.cxx deleted file mode 100644 index 3cc92baee..000000000 --- a/src/MixerAll.cxx +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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 - -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/MixerAll.hxx b/src/MixerAll.hxx deleted file mode 100644 index f868f64b5..000000000 --- a/src/MixerAll.hxx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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/MixerControl.cxx b/src/MixerControl.cxx deleted file mode 100644 index bdf6fcc2b..000000000 --- a/src/MixerControl.cxx +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 - -Mixer * -mixer_new(const struct mixer_plugin *plugin, void *ao, - const config_param ¶m, - 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/MixerControl.hxx b/src/MixerControl.hxx deleted file mode 100644 index caa1c3054..000000000 --- a/src/MixerControl.hxx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 ¶m, - 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/MixerInternal.hxx b/src/MixerInternal.hxx deleted file mode 100644 index 1732b4f3b..000000000 --- a/src/MixerInternal.hxx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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/MixerList.hxx b/src/MixerList.hxx deleted file mode 100644 index eb3dba938..000000000 --- a/src/MixerList.hxx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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/MixerPlugin.hxx b/src/MixerPlugin.hxx deleted file mode 100644 index b29ecbab8..000000000 --- a/src/MixerPlugin.hxx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 ¶m, - 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/MixerType.cxx b/src/MixerType.cxx deleted file mode 100644 index cd45db0d9..000000000 --- a/src/MixerType.cxx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 -#include - -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/MixerType.hxx b/src/MixerType.hxx deleted file mode 100644 index bfa2637b7..000000000 --- a/src/MixerType.hxx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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/StateFile.cxx b/src/StateFile.cxx index 06b7f0779..b28e8c617 100644 --- a/src/StateFile.cxx +++ b/src/StateFile.cxx @@ -23,7 +23,7 @@ #include "PlaylistState.hxx" #include "fs/TextFile.hxx" #include "Partition.hxx" -#include "Volume.hxx" +#include "mixer/Volume.hxx" #include "fs/FileSystem.hxx" #include "util/Domain.hxx" diff --git a/src/Volume.cxx b/src/Volume.cxx deleted file mode 100644 index 4c897b21f..000000000 --- a/src/Volume.cxx +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 -#include - -#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/Volume.hxx b/src/Volume.hxx deleted file mode 100644 index 06d3551eb..000000000 --- a/src/Volume.hxx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 - -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/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index 67d2aecf3..6432ce4e7 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -34,7 +34,7 @@ #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "ls.hxx" -#include "Volume.hxx" +#include "mixer/Volume.hxx" #include "util/ASCII.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index f703057cf..28258dded 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -24,7 +24,7 @@ #include "PlaylistPrint.hxx" #include "db/update/UpdateGlue.hxx" #include "client/Client.hxx" -#include "Volume.hxx" +#include "mixer/Volume.hxx" #include "output/OutputAll.hxx" #include "Partition.hxx" #include "protocol/Result.hxx" diff --git a/src/filter/plugins/ReplayGainFilterPlugin.cxx b/src/filter/plugins/ReplayGainFilterPlugin.cxx index d89e79480..a5d9668cc 100644 --- a/src/filter/plugins/ReplayGainFilterPlugin.cxx +++ b/src/filter/plugins/ReplayGainFilterPlugin.cxx @@ -25,7 +25,7 @@ #include "AudioFormat.hxx" #include "ReplayGainInfo.hxx" #include "ReplayGainConfig.hxx" -#include "MixerControl.hxx" +#include "mixer/MixerControl.hxx" #include "pcm/Volume.hxx" #include "pcm/PcmBuffer.hxx" #include "util/ConstBuffer.hxx" diff --git a/src/mixer/AlsaMixerPlugin.cxx b/src/mixer/AlsaMixerPlugin.cxx deleted file mode 100644 index 9d04ad98b..000000000 --- a/src/mixer/AlsaMixerPlugin.cxx +++ /dev/null @@ -1,382 +0,0 @@ -/* - * 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 "MixerInternal.hxx" -#include "output/OutputAPI.hxx" -#include "GlobalEvents.hxx" -#include "Main.hxx" -#include "event/MultiSocketMonitor.hxx" -#include "event/DeferredMonitor.hxx" -#include "event/Loop.hxx" -#include "util/ASCII.hxx" -#include "util/ReusableArray.hxx" -#include "util/Clamp.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include - -#include - -#define VOLUME_MIXER_ALSA_DEFAULT "default" -#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" -static constexpr unsigned VOLUME_MIXER_ALSA_INDEX_DEFAULT = 0; - -class AlsaMixerMonitor final : MultiSocketMonitor, DeferredMonitor { - snd_mixer_t *mixer; - - ReusableArray pfd_buffer; - -public: - AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer) - :MultiSocketMonitor(_loop), DeferredMonitor(_loop), - mixer(_mixer) { - DeferredMonitor::Schedule(); - } - -private: - virtual void RunDeferred() override { - InvalidateSockets(); - } - - virtual int PrepareSockets() override; - virtual void DispatchSockets() override; -}; - -class AlsaMixer final : public Mixer { - const char *device; - const char *control; - unsigned int index; - - snd_mixer_t *handle; - snd_mixer_elem_t *elem; - long volume_min; - long volume_max; - int volume_set; - - AlsaMixerMonitor *monitor; - -public: - AlsaMixer():Mixer(alsa_mixer_plugin) {} - - void Configure(const config_param ¶m); - bool Setup(Error &error); - bool Open(Error &error); - void Close(); - - int GetVolume(Error &error); - bool SetVolume(unsigned volume, Error &error); -}; - -static constexpr Domain alsa_mixer_domain("alsa_mixer"); - -int -AlsaMixerMonitor::PrepareSockets() -{ - if (mixer == nullptr) { - ClearSocketList(); - return -1; - } - - int count = snd_mixer_poll_descriptors_count(mixer); - if (count < 0) - count = 0; - - struct pollfd *pfds = pfd_buffer.Get(count); - - count = snd_mixer_poll_descriptors(mixer, pfds, count); - if (count < 0) - count = 0; - - ReplaceSocketList(pfds, count); - return -1; -} - -void -AlsaMixerMonitor::DispatchSockets() -{ - assert(mixer != nullptr); - - int err = snd_mixer_handle_events(mixer); - if (err < 0) { - FormatError(alsa_mixer_domain, - "snd_mixer_handle_events() failed: %s", - snd_strerror(err)); - - if (err == -ENODEV) { - /* the sound device was unplugged; disable - this GSource */ - mixer = nullptr; - InvalidateSockets(); - return; - } - } -} - -/* - * libasound callbacks - * - */ - -static int -alsa_mixer_elem_callback(gcc_unused snd_mixer_elem_t *elem, unsigned mask) -{ - if (mask & SND_CTL_EVENT_MASK_VALUE) - GlobalEvents::Emit(GlobalEvents::MIXER); - - return 0; -} - -/* - * mixer_plugin methods - * - */ - -inline void -AlsaMixer::Configure(const config_param ¶m) -{ - device = param.GetBlockValue("mixer_device", - VOLUME_MIXER_ALSA_DEFAULT); - control = param.GetBlockValue("mixer_control", - VOLUME_MIXER_ALSA_CONTROL_DEFAULT); - index = param.GetBlockValue("mixer_index", - VOLUME_MIXER_ALSA_INDEX_DEFAULT); -} - -static Mixer * -alsa_mixer_init(gcc_unused void *ao, const config_param ¶m, - gcc_unused Error &error) -{ - AlsaMixer *am = new AlsaMixer(); - am->Configure(param); - - return am; -} - -static void -alsa_mixer_finish(Mixer *data) -{ - AlsaMixer *am = (AlsaMixer *)data; - - delete am; - - /* free libasound's config cache */ - snd_config_update_free_global(); -} - -gcc_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 != nullptr; elem = snd_mixer_elem_next(elem)) { - if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE && - StringEqualsCaseASCII(snd_mixer_selem_get_name(elem), - name) && - snd_mixer_selem_get_index(elem) == idx) - return elem; - } - - return nullptr; -} - -inline bool -AlsaMixer::Setup(Error &error) -{ - int err; - - if ((err = snd_mixer_attach(handle, device)) < 0) { - error.Format(alsa_mixer_domain, err, - "failed to attach to %s: %s", - device, snd_strerror(err)); - return false; - } - - if ((err = snd_mixer_selem_register(handle, nullptr, - nullptr)) < 0) { - error.Format(alsa_mixer_domain, err, - "snd_mixer_selem_register() failed: %s", - snd_strerror(err)); - return false; - } - - if ((err = snd_mixer_load(handle)) < 0) { - error.Format(alsa_mixer_domain, err, - "snd_mixer_load() failed: %s\n", - snd_strerror(err)); - return false; - } - - elem = alsa_mixer_lookup_elem(handle, control, index); - if (elem == nullptr) { - error.Format(alsa_mixer_domain, 0, - "no such mixer control: %s", control); - return false; - } - - snd_mixer_selem_get_playback_volume_range(elem, &volume_min, - &volume_max); - - snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback); - - monitor = new AlsaMixerMonitor(*main_loop, handle); - - return true; -} - -inline bool -AlsaMixer::Open(Error &error) -{ - int err; - - volume_set = -1; - - err = snd_mixer_open(&handle, 0); - if (err < 0) { - error.Format(alsa_mixer_domain, err, - "snd_mixer_open() failed: %s", snd_strerror(err)); - return false; - } - - if (!Setup(error)) { - snd_mixer_close(handle); - return false; - } - - return true; -} - -static bool -alsa_mixer_open(Mixer *data, Error &error) -{ - AlsaMixer *am = (AlsaMixer *)data; - - return am->Open(error); -} - -inline void -AlsaMixer::Close() -{ - assert(handle != nullptr); - - delete monitor; - - snd_mixer_elem_set_callback(elem, nullptr); - snd_mixer_close(handle); -} - -static void -alsa_mixer_close(Mixer *data) -{ - AlsaMixer *am = (AlsaMixer *)data; - am->Close(); -} - -inline int -AlsaMixer::GetVolume(Error &error) -{ - int err; - int ret; - long level; - - assert(handle != nullptr); - - err = snd_mixer_handle_events(handle); - if (err < 0) { - error.Format(alsa_mixer_domain, err, - "snd_mixer_handle_events() failed: %s", - snd_strerror(err)); - return false; - } - - err = snd_mixer_selem_get_playback_volume(elem, - SND_MIXER_SCHN_FRONT_LEFT, - &level); - if (err < 0) { - error.Format(alsa_mixer_domain, err, - "failed to read ALSA volume: %s", - snd_strerror(err)); - return false; - } - - ret = ((volume_set / 100.0) * (volume_max - volume_min) - + volume_min) + 0.5; - if (volume_set > 0 && ret == level) { - ret = volume_set; - } else { - ret = (int)(100 * (((float)(level - volume_min)) / - (volume_max - volume_min)) + 0.5); - } - - return ret; -} - -static int -alsa_mixer_get_volume(Mixer *mixer, Error &error) -{ - AlsaMixer *am = (AlsaMixer *)mixer; - return am->GetVolume(error); -} - -inline bool -AlsaMixer::SetVolume(unsigned volume, Error &error) -{ - float vol; - long level; - int err; - - assert(handle != nullptr); - - vol = volume; - - volume_set = vol + 0.5; - - level = (long)(((vol / 100.0) * (volume_max - volume_min) + - volume_min) + 0.5); - level = Clamp(level, volume_min, volume_max); - - err = snd_mixer_selem_set_playback_volume_all(elem, level); - if (err < 0) { - error.Format(alsa_mixer_domain, err, - "failed to set ALSA volume: %s", - snd_strerror(err)); - return false; - } - - return true; -} - -static bool -alsa_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) -{ - AlsaMixer *am = (AlsaMixer *)mixer; - return am->SetVolume(volume, error); -} - -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/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 + +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 + +Mixer * +mixer_new(const struct mixer_plugin *plugin, void *ao, + const config_param ¶m, + 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 ¶m, + 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 ¶m, + 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 +#include + +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/OssMixerPlugin.cxx b/src/mixer/OssMixerPlugin.cxx deleted file mode 100644 index 4e116c2fb..000000000 --- a/src/mixer/OssMixerPlugin.cxx +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 "MixerInternal.hxx" -#include "config/ConfigData.hxx" -#include "system/fd_util.h" -#include "util/ASCII.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include -#include -#include -#include -#include -#include - -#if defined(__OpenBSD__) || defined(__NetBSD__) -# include -#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ -# include -#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ - -#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" - -class OssMixer : public Mixer { - const char *device; - const char *control; - - int device_fd; - int volume_control; - -public: - OssMixer():Mixer(oss_mixer_plugin) {} - - bool Configure(const config_param ¶m, Error &error); - bool Open(Error &error); - void Close(); - - int GetVolume(Error &error); - bool SetVolume(unsigned volume, Error &error); -}; - -static constexpr Domain oss_mixer_domain("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 (StringEqualsCaseASCII(name, labels[i], name_length) && - (labels[i][name_length] == 0 || - labels[i][name_length] == ' ')) - return i; - } - return -1; -} - -inline bool -OssMixer::Configure(const config_param ¶m, Error &error) -{ - device = param.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT); - control = param.GetBlockValue("mixer_control"); - - if (control != NULL) { - volume_control = oss_find_mixer(control); - if (volume_control < 0) { - error.Format(oss_mixer_domain, 0, - "no such mixer control: %s", control); - return false; - } - } else - volume_control = SOUND_MIXER_PCM; - - return true; -} - -static Mixer * -oss_mixer_init(gcc_unused void *ao, const config_param ¶m, - Error &error) -{ - OssMixer *om = new OssMixer(); - - if (!om->Configure(param, error)) { - delete om; - return nullptr; - } - - return om; -} - -static void -oss_mixer_finish(Mixer *data) -{ - OssMixer *om = (OssMixer *) data; - - delete om; -} - -void -OssMixer::Close() -{ - assert(device_fd >= 0); - - close(device_fd); -} - -static void -oss_mixer_close(Mixer *data) -{ - OssMixer *om = (OssMixer *) data; - om->Close(); -} - -inline bool -OssMixer::Open(Error &error) -{ - device_fd = open_cloexec(device, O_RDONLY, 0); - if (device_fd < 0) { - error.FormatErrno("failed to open %s", device); - return false; - } - - if (control) { - int devmask = 0; - - if (ioctl(device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { - error.SetErrno("READ_DEVMASK failed"); - Close(); - return false; - } - - if (((1 << volume_control) & devmask) == 0) { - error.Format(oss_mixer_domain, 0, - "mixer control \"%s\" not usable", - control); - Close(); - return false; - } - } - - return true; -} - -static bool -oss_mixer_open(Mixer *data, Error &error) -{ - OssMixer *om = (OssMixer *) data; - - return om->Open(error); -} - -inline int -OssMixer::GetVolume(Error &error) -{ - int left, right, level; - int ret; - - assert(device_fd >= 0); - - ret = ioctl(device_fd, MIXER_READ(volume_control), &level); - if (ret < 0) { - error.SetErrno("failed to read OSS volume"); - return false; - } - - left = level & 0xff; - right = (level & 0xff00) >> 8; - - if (left != right) { - FormatWarning(oss_mixer_domain, - "volume for left and right is not the same, \"%i\" and " - "\"%i\"\n", left, right); - } - - return left; -} - -static int -oss_mixer_get_volume(Mixer *mixer, Error &error) -{ - OssMixer *om = (OssMixer *)mixer; - return om->GetVolume(error); -} - -inline bool -OssMixer::SetVolume(unsigned volume, Error &error) -{ - int level; - int ret; - - assert(device_fd >= 0); - assert(volume <= 100); - - level = (volume << 8) + volume; - - ret = ioctl(device_fd, MIXER_WRITE(volume_control), &level); - if (ret < 0) { - error.SetErrno("failed to set OSS volume"); - return false; - } - - return true; -} - -static bool -oss_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) -{ - OssMixer *om = (OssMixer *)mixer; - return om->SetVolume(volume, error); -} - -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 deleted file mode 100644 index a379cebae..000000000 --- a/src/mixer/PulseMixerPlugin.cxx +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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 "PulseMixerPlugin.hxx" -#include "MixerInternal.hxx" -#include "output/plugins/PulseOutputPlugin.hxx" -#include "GlobalEvents.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include -#include -#include -#include -#include - -#include - -struct PulseMixer final : public Mixer { - PulseOutput *output; - - bool online; - struct pa_cvolume volume; - - PulseMixer(PulseOutput *_output) - :Mixer(pulse_mixer_plugin), - output(_output), online(false) - { - } -}; - -static constexpr Domain pulse_mixer_domain("pulse_mixer"); - -static void -pulse_mixer_offline(PulseMixer *pm) -{ - if (!pm->online) - return; - - pm->online = false; - - GlobalEvents::Emit(GlobalEvents::MIXER); -} - -/** - * Callback invoked by pulse_mixer_update(). Receives the new mixer - * value. - */ -static void -pulse_mixer_volume_cb(gcc_unused pa_context *context, const pa_sink_input_info *i, - int eol, void *userdata) -{ - PulseMixer *pm = (PulseMixer *)userdata; - - if (eol) - return; - - if (i == nullptr) { - pulse_mixer_offline(pm); - return; - } - - pm->online = true; - pm->volume = i->volume; - - GlobalEvents::Emit(GlobalEvents::MIXER); -} - -static void -pulse_mixer_update(PulseMixer *pm, - struct pa_context *context, struct pa_stream *stream) -{ - pa_operation *o; - - assert(context != nullptr); - assert(stream != nullptr); - 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 == nullptr) { - FormatError(pulse_mixer_domain, - "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(gcc_unused PulseMixer *pm, - struct pa_context *context) -{ - pa_operation *o; - - assert(context != nullptr); - - o = pa_context_subscribe(context, - (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT, - nullptr, nullptr); - if (o == nullptr) { - FormatError(pulse_mixer_domain, - "pa_context_subscribe() failed: %s", - pa_strerror(pa_context_errno(context))); - return; - } - - pa_operation_unref(o); -} - -void -pulse_mixer_on_disconnect(PulseMixer *pm) -{ - pulse_mixer_offline(pm); -} - -void -pulse_mixer_on_change(PulseMixer *pm, - struct pa_context *context, struct pa_stream *stream) -{ - pulse_mixer_update(pm, context, stream); -} - -static Mixer * -pulse_mixer_init(void *ao, gcc_unused const config_param ¶m, - Error &error) -{ - PulseOutput *po = (PulseOutput *)ao; - - if (ao == nullptr) { - error.Set(pulse_mixer_domain, - "The pulse mixer cannot work without the audio output"); - return nullptr; - } - - PulseMixer *pm = new PulseMixer(po); - - pulse_output_set_mixer(po, pm); - - return pm; -} - -static void -pulse_mixer_finish(Mixer *data) -{ - PulseMixer *pm = (PulseMixer *) data; - - pulse_output_clear_mixer(pm->output, pm); - - delete pm; -} - -static int -pulse_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) -{ - PulseMixer *pm = (PulseMixer *) mixer; - int ret; - - pulse_output_lock(pm->output); - - ret = pm->online - ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM) - : -1; - - pulse_output_unlock(pm->output); - - return ret; -} - -static bool -pulse_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) -{ - PulseMixer *pm = (PulseMixer *) mixer; - struct pa_cvolume cvolume; - bool success; - - pulse_output_lock(pm->output); - - if (!pm->online) { - pulse_output_unlock(pm->output); - error.Set(pulse_mixer_domain, "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); - if (success) - pm->volume = cvolume; - - pulse_output_unlock(pm->output); - - return success; -} - -const struct mixer_plugin pulse_mixer_plugin = { - pulse_mixer_init, - pulse_mixer_finish, - nullptr, - nullptr, - pulse_mixer_get_volume, - pulse_mixer_set_volume, - false, -}; diff --git a/src/mixer/PulseMixerPlugin.hxx b/src/mixer/PulseMixerPlugin.hxx deleted file mode 100644 index 993945e9b..000000000 --- a/src/mixer/PulseMixerPlugin.hxx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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_PULSE_MIXER_PLUGIN_HXX -#define MPD_PULSE_MIXER_PLUGIN_HXX - -struct PulseMixer; -struct pa_context; -struct pa_stream; - -void -pulse_mixer_on_connect(PulseMixer *pm, pa_context *context); - -void -pulse_mixer_on_disconnect(PulseMixer *pm); - -void -pulse_mixer_on_change(PulseMixer *pm, pa_context *context, pa_stream *stream); - -#endif diff --git a/src/mixer/RoarMixerPlugin.cxx b/src/mixer/RoarMixerPlugin.cxx deleted file mode 100644 index 18f8c14f8..000000000 --- a/src/mixer/RoarMixerPlugin.cxx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2003-2014 The Music Player Daemon Project - * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft - * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - - -#include "config.h" -#include "MixerInternal.hxx" -#include "output/plugins/RoarOutputPlugin.hxx" -#include "Compiler.h" - -struct RoarMixer final : public Mixer { - /** the base mixer class */ - RoarOutput *self; - - RoarMixer(RoarOutput *_output) - :Mixer(roar_mixer_plugin), - self(_output) {} -}; - -static Mixer * -roar_mixer_init(void *ao, gcc_unused const config_param ¶m, - gcc_unused Error &error) -{ - return new RoarMixer((RoarOutput *)ao); -} - -static void -roar_mixer_finish(Mixer *data) -{ - RoarMixer *self = (RoarMixer *) data; - - delete self; -} - -static int -roar_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) -{ - RoarMixer *self = (RoarMixer *)mixer; - return roar_output_get_volume(self->self); -} - -static bool -roar_mixer_set_volume(Mixer *mixer, unsigned volume, - gcc_unused Error &error) -{ - 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 deleted file mode 100644 index a0de727af..000000000 --- a/src/mixer/SoftwareMixerPlugin.cxx +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 "SoftwareMixerPlugin.hxx" -#include "MixerInternal.hxx" -#include "filter/FilterPlugin.hxx" -#include "filter/FilterRegistry.hxx" -#include "filter/FilterInternal.hxx" -#include "filter/plugins/VolumeFilterPlugin.hxx" -#include "pcm/Volume.hxx" -#include "config/ConfigData.hxx" -#include "util/Error.hxx" - -#include -#include - -static Filter * -CreateVolumeFilter() -{ - Error error; - return filter_new(&volume_filter_plugin, config_param(), error); -} - -struct SoftwareMixer final : public Mixer { - Filter *filter; - - /** - * If this is true, then this object "owns" the #Filter - * instance (see above). It will be set to false by - * software_mixer_get_filter(); after that, the caller will be - * responsible for the #Filter. - */ - bool owns_filter; - - unsigned volume; - - SoftwareMixer() - :Mixer(software_mixer_plugin), - filter(CreateVolumeFilter()), - owns_filter(true), - volume(100) - { - assert(filter != nullptr); - } - - ~SoftwareMixer() { - if (owns_filter) - delete filter; - } -}; - -static Mixer * -software_mixer_init(gcc_unused void *ao, - gcc_unused const config_param ¶m, - gcc_unused Error &error) -{ - return new SoftwareMixer(); -} - -static void -software_mixer_finish(Mixer *data) -{ - SoftwareMixer *sm = (SoftwareMixer *)data; - - delete sm; -} - -static int -software_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) -{ - SoftwareMixer *sm = (SoftwareMixer *)mixer; - - return sm->volume; -} - -static bool -software_mixer_set_volume(Mixer *mixer, unsigned volume, - gcc_unused Error &error) -{ - SoftwareMixer *sm = (SoftwareMixer *)mixer; - - assert(volume <= 100); - - sm->volume = volume; - - if (volume >= 100) - volume = PCM_VOLUME_1; - else if (volume > 0) - volume = pcm_float_to_volume((exp(volume / 25.0) - 1) / - (54.5981500331F - 1)); - - volume_filter_set(sm->filter, volume); - return true; -} - -const struct mixer_plugin software_mixer_plugin = { - software_mixer_init, - software_mixer_finish, - nullptr, - nullptr, - software_mixer_get_volume, - software_mixer_set_volume, - true, -}; - -Filter * -software_mixer_get_filter(Mixer *mixer) -{ - SoftwareMixer *sm = (SoftwareMixer *)mixer; - assert(sm->IsPlugin(software_mixer_plugin)); - assert(sm->owns_filter); - - sm->owns_filter = false; - return sm->filter; -} diff --git a/src/mixer/SoftwareMixerPlugin.hxx b/src/mixer/SoftwareMixerPlugin.hxx deleted file mode 100644 index 581d2ac17..000000000 --- a/src/mixer/SoftwareMixerPlugin.hxx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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_SOFTWARE_MIXER_PLUGIN_HXX -#define MPD_SOFTWARE_MIXER_PLUGIN_HXX - -class Mixer; -class Filter; - -/** - * Returns the (volume) filter associated with this mixer. All users - * of this mixer plugin should install this filter. - */ -Filter * -software_mixer_get_filter(Mixer *mixer); - -#endif diff --git a/src/mixer/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 +#include + +#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 + +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/WinmmMixerPlugin.cxx b/src/mixer/WinmmMixerPlugin.cxx deleted file mode 100644 index 6f10fd71b..000000000 --- a/src/mixer/WinmmMixerPlugin.cxx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 "MixerInternal.hxx" -#include "output/OutputAPI.hxx" -#include "output/plugins/WinmmOutputPlugin.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" - -#include - -#include -#include -#include - -struct WinmmMixer final : public Mixer { - WinmmOutput *output; - - WinmmMixer(WinmmOutput *_output) - :Mixer(winmm_mixer_plugin), - output(_output) { - } -}; - -static constexpr Domain winmm_mixer_domain("winmm_mixer"); - -static inline int -winmm_volume_decode(DWORD volume) -{ - return lround((volume & 0xFFFF) / 655.35); -} - -static inline DWORD -winmm_volume_encode(int volume) -{ - int value = lround(volume * 655.35); - return MAKELONG(value, value); -} - -static Mixer * -winmm_mixer_init(void *ao, gcc_unused const config_param ¶m, - gcc_unused Error &error) -{ - assert(ao != nullptr); - - return new WinmmMixer((WinmmOutput *)ao); -} - -static void -winmm_mixer_finish(Mixer *data) -{ - WinmmMixer *wm = (WinmmMixer *)data; - - delete wm; -} - -static int -winmm_mixer_get_volume(Mixer *mixer, Error &error) -{ - WinmmMixer *wm = (WinmmMixer *) mixer; - DWORD volume; - HWAVEOUT handle = winmm_output_get_handle(wm->output); - MMRESULT result = waveOutGetVolume(handle, &volume); - - if (result != MMSYSERR_NOERROR) { - error.Set(winmm_mixer_domain, "Failed to get winmm volume"); - return -1; - } - - return winmm_volume_decode(volume); -} - -static bool -winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) -{ - WinmmMixer *wm = (WinmmMixer *) mixer; - DWORD value = winmm_volume_encode(volume); - HWAVEOUT handle = winmm_output_get_handle(wm->output); - MMRESULT result = waveOutSetVolume(handle, value); - - if (result != MMSYSERR_NOERROR) { - error.Set(winmm_mixer_domain, "Failed to set winmm volume"); - return false; - } - - return true; -} - -const struct mixer_plugin winmm_mixer_plugin = { - winmm_mixer_init, - winmm_mixer_finish, - nullptr, - nullptr, - winmm_mixer_get_volume, - winmm_mixer_set_volume, - false, -}; diff --git a/src/mixer/plugins/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx new file mode 100644 index 000000000..a15820fc7 --- /dev/null +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -0,0 +1,382 @@ +/* + * 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 "mixer/MixerInternal.hxx" +#include "output/OutputAPI.hxx" +#include "GlobalEvents.hxx" +#include "Main.hxx" +#include "event/MultiSocketMonitor.hxx" +#include "event/DeferredMonitor.hxx" +#include "event/Loop.hxx" +#include "util/ASCII.hxx" +#include "util/ReusableArray.hxx" +#include "util/Clamp.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include + +#include + +#define VOLUME_MIXER_ALSA_DEFAULT "default" +#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" +static constexpr unsigned VOLUME_MIXER_ALSA_INDEX_DEFAULT = 0; + +class AlsaMixerMonitor final : MultiSocketMonitor, DeferredMonitor { + snd_mixer_t *mixer; + + ReusableArray pfd_buffer; + +public: + AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer) + :MultiSocketMonitor(_loop), DeferredMonitor(_loop), + mixer(_mixer) { + DeferredMonitor::Schedule(); + } + +private: + virtual void RunDeferred() override { + InvalidateSockets(); + } + + virtual int PrepareSockets() override; + virtual void DispatchSockets() override; +}; + +class AlsaMixer final : public Mixer { + const char *device; + const char *control; + unsigned int index; + + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + long volume_min; + long volume_max; + int volume_set; + + AlsaMixerMonitor *monitor; + +public: + AlsaMixer():Mixer(alsa_mixer_plugin) {} + + void Configure(const config_param ¶m); + bool Setup(Error &error); + bool Open(Error &error); + void Close(); + + int GetVolume(Error &error); + bool SetVolume(unsigned volume, Error &error); +}; + +static constexpr Domain alsa_mixer_domain("alsa_mixer"); + +int +AlsaMixerMonitor::PrepareSockets() +{ + if (mixer == nullptr) { + ClearSocketList(); + return -1; + } + + int count = snd_mixer_poll_descriptors_count(mixer); + if (count < 0) + count = 0; + + struct pollfd *pfds = pfd_buffer.Get(count); + + count = snd_mixer_poll_descriptors(mixer, pfds, count); + if (count < 0) + count = 0; + + ReplaceSocketList(pfds, count); + return -1; +} + +void +AlsaMixerMonitor::DispatchSockets() +{ + assert(mixer != nullptr); + + int err = snd_mixer_handle_events(mixer); + if (err < 0) { + FormatError(alsa_mixer_domain, + "snd_mixer_handle_events() failed: %s", + snd_strerror(err)); + + if (err == -ENODEV) { + /* the sound device was unplugged; disable + this GSource */ + mixer = nullptr; + InvalidateSockets(); + return; + } + } +} + +/* + * libasound callbacks + * + */ + +static int +alsa_mixer_elem_callback(gcc_unused snd_mixer_elem_t *elem, unsigned mask) +{ + if (mask & SND_CTL_EVENT_MASK_VALUE) + GlobalEvents::Emit(GlobalEvents::MIXER); + + return 0; +} + +/* + * mixer_plugin methods + * + */ + +inline void +AlsaMixer::Configure(const config_param ¶m) +{ + device = param.GetBlockValue("mixer_device", + VOLUME_MIXER_ALSA_DEFAULT); + control = param.GetBlockValue("mixer_control", + VOLUME_MIXER_ALSA_CONTROL_DEFAULT); + index = param.GetBlockValue("mixer_index", + VOLUME_MIXER_ALSA_INDEX_DEFAULT); +} + +static Mixer * +alsa_mixer_init(gcc_unused void *ao, const config_param ¶m, + gcc_unused Error &error) +{ + AlsaMixer *am = new AlsaMixer(); + am->Configure(param); + + return am; +} + +static void +alsa_mixer_finish(Mixer *data) +{ + AlsaMixer *am = (AlsaMixer *)data; + + delete am; + + /* free libasound's config cache */ + snd_config_update_free_global(); +} + +gcc_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 != nullptr; elem = snd_mixer_elem_next(elem)) { + if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE && + StringEqualsCaseASCII(snd_mixer_selem_get_name(elem), + name) && + snd_mixer_selem_get_index(elem) == idx) + return elem; + } + + return nullptr; +} + +inline bool +AlsaMixer::Setup(Error &error) +{ + int err; + + if ((err = snd_mixer_attach(handle, device)) < 0) { + error.Format(alsa_mixer_domain, err, + "failed to attach to %s: %s", + device, snd_strerror(err)); + return false; + } + + if ((err = snd_mixer_selem_register(handle, nullptr, + nullptr)) < 0) { + error.Format(alsa_mixer_domain, err, + "snd_mixer_selem_register() failed: %s", + snd_strerror(err)); + return false; + } + + if ((err = snd_mixer_load(handle)) < 0) { + error.Format(alsa_mixer_domain, err, + "snd_mixer_load() failed: %s\n", + snd_strerror(err)); + return false; + } + + elem = alsa_mixer_lookup_elem(handle, control, index); + if (elem == nullptr) { + error.Format(alsa_mixer_domain, 0, + "no such mixer control: %s", control); + return false; + } + + snd_mixer_selem_get_playback_volume_range(elem, &volume_min, + &volume_max); + + snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback); + + monitor = new AlsaMixerMonitor(*main_loop, handle); + + return true; +} + +inline bool +AlsaMixer::Open(Error &error) +{ + int err; + + volume_set = -1; + + err = snd_mixer_open(&handle, 0); + if (err < 0) { + error.Format(alsa_mixer_domain, err, + "snd_mixer_open() failed: %s", snd_strerror(err)); + return false; + } + + if (!Setup(error)) { + snd_mixer_close(handle); + return false; + } + + return true; +} + +static bool +alsa_mixer_open(Mixer *data, Error &error) +{ + AlsaMixer *am = (AlsaMixer *)data; + + return am->Open(error); +} + +inline void +AlsaMixer::Close() +{ + assert(handle != nullptr); + + delete monitor; + + snd_mixer_elem_set_callback(elem, nullptr); + snd_mixer_close(handle); +} + +static void +alsa_mixer_close(Mixer *data) +{ + AlsaMixer *am = (AlsaMixer *)data; + am->Close(); +} + +inline int +AlsaMixer::GetVolume(Error &error) +{ + int err; + int ret; + long level; + + assert(handle != nullptr); + + err = snd_mixer_handle_events(handle); + if (err < 0) { + error.Format(alsa_mixer_domain, err, + "snd_mixer_handle_events() failed: %s", + snd_strerror(err)); + return false; + } + + err = snd_mixer_selem_get_playback_volume(elem, + SND_MIXER_SCHN_FRONT_LEFT, + &level); + if (err < 0) { + error.Format(alsa_mixer_domain, err, + "failed to read ALSA volume: %s", + snd_strerror(err)); + return false; + } + + ret = ((volume_set / 100.0) * (volume_max - volume_min) + + volume_min) + 0.5; + if (volume_set > 0 && ret == level) { + ret = volume_set; + } else { + ret = (int)(100 * (((float)(level - volume_min)) / + (volume_max - volume_min)) + 0.5); + } + + return ret; +} + +static int +alsa_mixer_get_volume(Mixer *mixer, Error &error) +{ + AlsaMixer *am = (AlsaMixer *)mixer; + return am->GetVolume(error); +} + +inline bool +AlsaMixer::SetVolume(unsigned volume, Error &error) +{ + float vol; + long level; + int err; + + assert(handle != nullptr); + + vol = volume; + + volume_set = vol + 0.5; + + level = (long)(((vol / 100.0) * (volume_max - volume_min) + + volume_min) + 0.5); + level = Clamp(level, volume_min, volume_max); + + err = snd_mixer_selem_set_playback_volume_all(elem, level); + if (err < 0) { + error.Format(alsa_mixer_domain, err, + "failed to set ALSA volume: %s", + snd_strerror(err)); + return false; + } + + return true; +} + +static bool +alsa_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +{ + AlsaMixer *am = (AlsaMixer *)mixer; + return am->SetVolume(volume, error); +} + +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/plugins/OssMixerPlugin.cxx b/src/mixer/plugins/OssMixerPlugin.cxx new file mode 100644 index 000000000..095ace015 --- /dev/null +++ b/src/mixer/plugins/OssMixerPlugin.cxx @@ -0,0 +1,241 @@ +/* + * 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 "mixer/MixerInternal.hxx" +#include "config/ConfigData.hxx" +#include "system/fd_util.h" +#include "util/ASCII.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include +#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ +# include +#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */ + +#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" + +class OssMixer : public Mixer { + const char *device; + const char *control; + + int device_fd; + int volume_control; + +public: + OssMixer():Mixer(oss_mixer_plugin) {} + + bool Configure(const config_param ¶m, Error &error); + bool Open(Error &error); + void Close(); + + int GetVolume(Error &error); + bool SetVolume(unsigned volume, Error &error); +}; + +static constexpr Domain oss_mixer_domain("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 (StringEqualsCaseASCII(name, labels[i], name_length) && + (labels[i][name_length] == 0 || + labels[i][name_length] == ' ')) + return i; + } + return -1; +} + +inline bool +OssMixer::Configure(const config_param ¶m, Error &error) +{ + device = param.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT); + control = param.GetBlockValue("mixer_control"); + + if (control != NULL) { + volume_control = oss_find_mixer(control); + if (volume_control < 0) { + error.Format(oss_mixer_domain, 0, + "no such mixer control: %s", control); + return false; + } + } else + volume_control = SOUND_MIXER_PCM; + + return true; +} + +static Mixer * +oss_mixer_init(gcc_unused void *ao, const config_param ¶m, + Error &error) +{ + OssMixer *om = new OssMixer(); + + if (!om->Configure(param, error)) { + delete om; + return nullptr; + } + + return om; +} + +static void +oss_mixer_finish(Mixer *data) +{ + OssMixer *om = (OssMixer *) data; + + delete om; +} + +void +OssMixer::Close() +{ + assert(device_fd >= 0); + + close(device_fd); +} + +static void +oss_mixer_close(Mixer *data) +{ + OssMixer *om = (OssMixer *) data; + om->Close(); +} + +inline bool +OssMixer::Open(Error &error) +{ + device_fd = open_cloexec(device, O_RDONLY, 0); + if (device_fd < 0) { + error.FormatErrno("failed to open %s", device); + return false; + } + + if (control) { + int devmask = 0; + + if (ioctl(device_fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { + error.SetErrno("READ_DEVMASK failed"); + Close(); + return false; + } + + if (((1 << volume_control) & devmask) == 0) { + error.Format(oss_mixer_domain, 0, + "mixer control \"%s\" not usable", + control); + Close(); + return false; + } + } + + return true; +} + +static bool +oss_mixer_open(Mixer *data, Error &error) +{ + OssMixer *om = (OssMixer *) data; + + return om->Open(error); +} + +inline int +OssMixer::GetVolume(Error &error) +{ + int left, right, level; + int ret; + + assert(device_fd >= 0); + + ret = ioctl(device_fd, MIXER_READ(volume_control), &level); + if (ret < 0) { + error.SetErrno("failed to read OSS volume"); + return false; + } + + left = level & 0xff; + right = (level & 0xff00) >> 8; + + if (left != right) { + FormatWarning(oss_mixer_domain, + "volume for left and right is not the same, \"%i\" and " + "\"%i\"\n", left, right); + } + + return left; +} + +static int +oss_mixer_get_volume(Mixer *mixer, Error &error) +{ + OssMixer *om = (OssMixer *)mixer; + return om->GetVolume(error); +} + +inline bool +OssMixer::SetVolume(unsigned volume, Error &error) +{ + int level; + int ret; + + assert(device_fd >= 0); + assert(volume <= 100); + + level = (volume << 8) + volume; + + ret = ioctl(device_fd, MIXER_WRITE(volume_control), &level); + if (ret < 0) { + error.SetErrno("failed to set OSS volume"); + return false; + } + + return true; +} + +static bool +oss_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +{ + OssMixer *om = (OssMixer *)mixer; + return om->SetVolume(volume, error); +} + +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/plugins/PulseMixerPlugin.cxx b/src/mixer/plugins/PulseMixerPlugin.cxx new file mode 100644 index 000000000..4df88d06b --- /dev/null +++ b/src/mixer/plugins/PulseMixerPlugin.cxx @@ -0,0 +1,225 @@ +/* + * 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 "PulseMixerPlugin.hxx" +#include "mixer/MixerInternal.hxx" +#include "output/plugins/PulseOutputPlugin.hxx" +#include "GlobalEvents.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include +#include +#include +#include +#include + +#include + +struct PulseMixer final : public Mixer { + PulseOutput *output; + + bool online; + struct pa_cvolume volume; + + PulseMixer(PulseOutput *_output) + :Mixer(pulse_mixer_plugin), + output(_output), online(false) + { + } +}; + +static constexpr Domain pulse_mixer_domain("pulse_mixer"); + +static void +pulse_mixer_offline(PulseMixer *pm) +{ + if (!pm->online) + return; + + pm->online = false; + + GlobalEvents::Emit(GlobalEvents::MIXER); +} + +/** + * Callback invoked by pulse_mixer_update(). Receives the new mixer + * value. + */ +static void +pulse_mixer_volume_cb(gcc_unused pa_context *context, const pa_sink_input_info *i, + int eol, void *userdata) +{ + PulseMixer *pm = (PulseMixer *)userdata; + + if (eol) + return; + + if (i == nullptr) { + pulse_mixer_offline(pm); + return; + } + + pm->online = true; + pm->volume = i->volume; + + GlobalEvents::Emit(GlobalEvents::MIXER); +} + +static void +pulse_mixer_update(PulseMixer *pm, + struct pa_context *context, struct pa_stream *stream) +{ + pa_operation *o; + + assert(context != nullptr); + assert(stream != nullptr); + 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 == nullptr) { + FormatError(pulse_mixer_domain, + "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(gcc_unused PulseMixer *pm, + struct pa_context *context) +{ + pa_operation *o; + + assert(context != nullptr); + + o = pa_context_subscribe(context, + (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT, + nullptr, nullptr); + if (o == nullptr) { + FormatError(pulse_mixer_domain, + "pa_context_subscribe() failed: %s", + pa_strerror(pa_context_errno(context))); + return; + } + + pa_operation_unref(o); +} + +void +pulse_mixer_on_disconnect(PulseMixer *pm) +{ + pulse_mixer_offline(pm); +} + +void +pulse_mixer_on_change(PulseMixer *pm, + struct pa_context *context, struct pa_stream *stream) +{ + pulse_mixer_update(pm, context, stream); +} + +static Mixer * +pulse_mixer_init(void *ao, gcc_unused const config_param ¶m, + Error &error) +{ + PulseOutput *po = (PulseOutput *)ao; + + if (ao == nullptr) { + error.Set(pulse_mixer_domain, + "The pulse mixer cannot work without the audio output"); + return nullptr; + } + + PulseMixer *pm = new PulseMixer(po); + + pulse_output_set_mixer(po, pm); + + return pm; +} + +static void +pulse_mixer_finish(Mixer *data) +{ + PulseMixer *pm = (PulseMixer *) data; + + pulse_output_clear_mixer(pm->output, pm); + + delete pm; +} + +static int +pulse_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) +{ + PulseMixer *pm = (PulseMixer *) mixer; + int ret; + + pulse_output_lock(pm->output); + + ret = pm->online + ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM) + : -1; + + pulse_output_unlock(pm->output); + + return ret; +} + +static bool +pulse_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +{ + PulseMixer *pm = (PulseMixer *) mixer; + struct pa_cvolume cvolume; + bool success; + + pulse_output_lock(pm->output); + + if (!pm->online) { + pulse_output_unlock(pm->output); + error.Set(pulse_mixer_domain, "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); + 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/plugins/PulseMixerPlugin.hxx b/src/mixer/plugins/PulseMixerPlugin.hxx new file mode 100644 index 000000000..993945e9b --- /dev/null +++ b/src/mixer/plugins/PulseMixerPlugin.hxx @@ -0,0 +1,36 @@ +/* + * 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_PULSE_MIXER_PLUGIN_HXX +#define MPD_PULSE_MIXER_PLUGIN_HXX + +struct PulseMixer; +struct pa_context; +struct pa_stream; + +void +pulse_mixer_on_connect(PulseMixer *pm, pa_context *context); + +void +pulse_mixer_on_disconnect(PulseMixer *pm); + +void +pulse_mixer_on_change(PulseMixer *pm, pa_context *context, pa_stream *stream); + +#endif diff --git a/src/mixer/plugins/RoarMixerPlugin.cxx b/src/mixer/plugins/RoarMixerPlugin.cxx new file mode 100644 index 000000000..958688cd7 --- /dev/null +++ b/src/mixer/plugins/RoarMixerPlugin.cxx @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003-2014 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/MixerInternal.hxx" +#include "output/plugins/RoarOutputPlugin.hxx" +#include "Compiler.h" + +struct RoarMixer final : public Mixer { + /** the base mixer class */ + RoarOutput *self; + + RoarMixer(RoarOutput *_output) + :Mixer(roar_mixer_plugin), + self(_output) {} +}; + +static Mixer * +roar_mixer_init(void *ao, gcc_unused const config_param ¶m, + gcc_unused Error &error) +{ + return new RoarMixer((RoarOutput *)ao); +} + +static void +roar_mixer_finish(Mixer *data) +{ + RoarMixer *self = (RoarMixer *) data; + + delete self; +} + +static int +roar_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) +{ + RoarMixer *self = (RoarMixer *)mixer; + return roar_output_get_volume(self->self); +} + +static bool +roar_mixer_set_volume(Mixer *mixer, unsigned volume, + gcc_unused Error &error) +{ + 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/plugins/SoftwareMixerPlugin.cxx b/src/mixer/plugins/SoftwareMixerPlugin.cxx new file mode 100644 index 000000000..67ddee1a4 --- /dev/null +++ b/src/mixer/plugins/SoftwareMixerPlugin.cxx @@ -0,0 +1,132 @@ +/* + * 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 "SoftwareMixerPlugin.hxx" +#include "mixer/MixerInternal.hxx" +#include "filter/FilterPlugin.hxx" +#include "filter/FilterRegistry.hxx" +#include "filter/FilterInternal.hxx" +#include "filter/plugins/VolumeFilterPlugin.hxx" +#include "pcm/Volume.hxx" +#include "config/ConfigData.hxx" +#include "util/Error.hxx" + +#include +#include + +static Filter * +CreateVolumeFilter() +{ + Error error; + return filter_new(&volume_filter_plugin, config_param(), error); +} + +struct SoftwareMixer final : public Mixer { + Filter *filter; + + /** + * If this is true, then this object "owns" the #Filter + * instance (see above). It will be set to false by + * software_mixer_get_filter(); after that, the caller will be + * responsible for the #Filter. + */ + bool owns_filter; + + unsigned volume; + + SoftwareMixer() + :Mixer(software_mixer_plugin), + filter(CreateVolumeFilter()), + owns_filter(true), + volume(100) + { + assert(filter != nullptr); + } + + ~SoftwareMixer() { + if (owns_filter) + delete filter; + } +}; + +static Mixer * +software_mixer_init(gcc_unused void *ao, + gcc_unused const config_param ¶m, + gcc_unused Error &error) +{ + return new SoftwareMixer(); +} + +static void +software_mixer_finish(Mixer *data) +{ + SoftwareMixer *sm = (SoftwareMixer *)data; + + delete sm; +} + +static int +software_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) +{ + SoftwareMixer *sm = (SoftwareMixer *)mixer; + + return sm->volume; +} + +static bool +software_mixer_set_volume(Mixer *mixer, unsigned volume, + gcc_unused Error &error) +{ + SoftwareMixer *sm = (SoftwareMixer *)mixer; + + assert(volume <= 100); + + sm->volume = volume; + + if (volume >= 100) + volume = PCM_VOLUME_1; + else if (volume > 0) + volume = pcm_float_to_volume((exp(volume / 25.0) - 1) / + (54.5981500331F - 1)); + + volume_filter_set(sm->filter, volume); + return true; +} + +const struct mixer_plugin software_mixer_plugin = { + software_mixer_init, + software_mixer_finish, + nullptr, + nullptr, + software_mixer_get_volume, + software_mixer_set_volume, + true, +}; + +Filter * +software_mixer_get_filter(Mixer *mixer) +{ + SoftwareMixer *sm = (SoftwareMixer *)mixer; + assert(sm->IsPlugin(software_mixer_plugin)); + assert(sm->owns_filter); + + sm->owns_filter = false; + return sm->filter; +} diff --git a/src/mixer/plugins/SoftwareMixerPlugin.hxx b/src/mixer/plugins/SoftwareMixerPlugin.hxx new file mode 100644 index 000000000..581d2ac17 --- /dev/null +++ b/src/mixer/plugins/SoftwareMixerPlugin.hxx @@ -0,0 +1,33 @@ +/* + * 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_SOFTWARE_MIXER_PLUGIN_HXX +#define MPD_SOFTWARE_MIXER_PLUGIN_HXX + +class Mixer; +class Filter; + +/** + * Returns the (volume) filter associated with this mixer. All users + * of this mixer plugin should install this filter. + */ +Filter * +software_mixer_get_filter(Mixer *mixer); + +#endif diff --git a/src/mixer/plugins/WinmmMixerPlugin.cxx b/src/mixer/plugins/WinmmMixerPlugin.cxx new file mode 100644 index 000000000..093a026a9 --- /dev/null +++ b/src/mixer/plugins/WinmmMixerPlugin.cxx @@ -0,0 +1,114 @@ +/* + * 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 "mixer/MixerInternal.hxx" +#include "output/OutputAPI.hxx" +#include "output/plugins/WinmmOutputPlugin.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +#include + +#include +#include +#include + +struct WinmmMixer final : public Mixer { + WinmmOutput *output; + + WinmmMixer(WinmmOutput *_output) + :Mixer(winmm_mixer_plugin), + output(_output) { + } +}; + +static constexpr Domain winmm_mixer_domain("winmm_mixer"); + +static inline int +winmm_volume_decode(DWORD volume) +{ + return lround((volume & 0xFFFF) / 655.35); +} + +static inline DWORD +winmm_volume_encode(int volume) +{ + int value = lround(volume * 655.35); + return MAKELONG(value, value); +} + +static Mixer * +winmm_mixer_init(void *ao, gcc_unused const config_param ¶m, + gcc_unused Error &error) +{ + assert(ao != nullptr); + + return new WinmmMixer((WinmmOutput *)ao); +} + +static void +winmm_mixer_finish(Mixer *data) +{ + WinmmMixer *wm = (WinmmMixer *)data; + + delete wm; +} + +static int +winmm_mixer_get_volume(Mixer *mixer, Error &error) +{ + WinmmMixer *wm = (WinmmMixer *) mixer; + DWORD volume; + HWAVEOUT handle = winmm_output_get_handle(wm->output); + MMRESULT result = waveOutGetVolume(handle, &volume); + + if (result != MMSYSERR_NOERROR) { + error.Set(winmm_mixer_domain, "Failed to get winmm volume"); + return -1; + } + + return winmm_volume_decode(volume); +} + +static bool +winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +{ + WinmmMixer *wm = (WinmmMixer *) mixer; + DWORD value = winmm_volume_encode(volume); + HWAVEOUT handle = winmm_output_get_handle(wm->output); + MMRESULT result = waveOutSetVolume(handle, value); + + if (result != MMSYSERR_NOERROR) { + error.Set(winmm_mixer_domain, "Failed to set winmm volume"); + return false; + } + + return true; +} + +const struct mixer_plugin winmm_mixer_plugin = { + winmm_mixer_init, + winmm_mixer_finish, + nullptr, + nullptr, + winmm_mixer_get_volume, + winmm_mixer_set_volume, + false, +}; diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx index 839e9bd88..d82b7c696 100644 --- a/src/output/OutputCommand.cxx +++ b/src/output/OutputCommand.cxx @@ -29,7 +29,7 @@ #include "OutputAll.hxx" #include "OutputInternal.hxx" #include "PlayerControl.hxx" -#include "MixerControl.hxx" +#include "mixer/MixerControl.hxx" #include "Idle.hxx" extern unsigned audio_output_state_version; diff --git a/src/output/OutputControl.cxx b/src/output/OutputControl.cxx index 234980a52..1532dbd83 100644 --- a/src/output/OutputControl.cxx +++ b/src/output/OutputControl.cxx @@ -24,7 +24,7 @@ #include "OutputInternal.hxx" #include "OutputPlugin.hxx" #include "OutputError.hxx" -#include "MixerControl.hxx" +#include "mixer/MixerControl.hxx" #include "notify.hxx" #include "filter/plugins/ReplayGainFilterPlugin.hxx" #include "util/Error.hxx" diff --git a/src/output/OutputFinish.cxx b/src/output/OutputFinish.cxx index 109662fb0..16b16f5ce 100644 --- a/src/output/OutputFinish.cxx +++ b/src/output/OutputFinish.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "OutputInternal.hxx" #include "OutputPlugin.hxx" -#include "MixerControl.hxx" +#include "mixer/MixerControl.hxx" #include "filter/FilterInternal.hxx" #include diff --git a/src/output/OutputInit.cxx b/src/output/OutputInit.cxx index 7815dc921..16180a415 100644 --- a/src/output/OutputInit.cxx +++ b/src/output/OutputInit.cxx @@ -24,10 +24,10 @@ #include "OutputAPI.hxx" #include "filter/FilterConfig.hxx" #include "AudioParser.hxx" -#include "MixerList.hxx" -#include "MixerType.hxx" -#include "MixerControl.hxx" -#include "mixer/SoftwareMixerPlugin.hxx" +#include "mixer/MixerList.hxx" +#include "mixer/MixerType.hxx" +#include "mixer/MixerControl.hxx" +#include "mixer/plugins/SoftwareMixerPlugin.hxx" #include "filter/FilterPlugin.hxx" #include "filter/FilterRegistry.hxx" #include "filter/plugins/AutoConvertFilterPlugin.hxx" diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index f2e4fc643..f2d6916a5 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "AlsaOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "MixerList.hxx" +#include "mixer/MixerList.hxx" #include "pcm/PcmExport.hxx" #include "util/Manual.hxx" #include "util/Error.hxx" diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx index cdf055df9..2157a9476 100644 --- a/src/output/plugins/OssOutputPlugin.cxx +++ b/src/output/plugins/OssOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "config.h" #include "OssOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "MixerList.hxx" +#include "mixer/MixerList.hxx" #include "system/fd_util.h" #include "util/Error.hxx" #include "util/Domain.hxx" diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index c133d9796..6bb66450e 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -20,8 +20,8 @@ #include "config.h" #include "PulseOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "MixerList.hxx" -#include "mixer/PulseMixerPlugin.hxx" +#include "mixer/MixerList.hxx" +#include "mixer/plugins/PulseMixerPlugin.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" diff --git a/src/output/plugins/RoarOutputPlugin.cxx b/src/output/plugins/RoarOutputPlugin.cxx index 7c1c41b47..1192b2625 100644 --- a/src/output/plugins/RoarOutputPlugin.cxx +++ b/src/output/plugins/RoarOutputPlugin.cxx @@ -21,7 +21,7 @@ #include "config.h" #include "RoarOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "MixerList.hxx" +#include "mixer/MixerList.hxx" #include "thread/Mutex.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" diff --git a/src/output/plugins/WinmmOutputPlugin.cxx b/src/output/plugins/WinmmOutputPlugin.cxx index 87861180f..66d49eb9a 100644 --- a/src/output/plugins/WinmmOutputPlugin.cxx +++ b/src/output/plugins/WinmmOutputPlugin.cxx @@ -21,7 +21,7 @@ #include "WinmmOutputPlugin.hxx" #include "../OutputAPI.hxx" #include "pcm/PcmBuffer.hxx" -#include "MixerList.hxx" +#include "mixer/MixerList.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" diff --git a/test/read_mixer.cxx b/test/read_mixer.cxx index d080f46ca..1401a6a1e 100644 --- a/test/read_mixer.cxx +++ b/test/read_mixer.cxx @@ -18,8 +18,8 @@ */ #include "config.h" -#include "MixerControl.hxx" -#include "MixerList.hxx" +#include "mixer/MixerControl.hxx" +#include "mixer/MixerList.hxx" #include "filter/FilterRegistry.hxx" #include "pcm/Volume.hxx" #include "GlobalEvents.hxx" diff --git a/test/run_filter.cxx b/test/run_filter.cxx index 3ce116280..7816827d7 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -26,7 +26,7 @@ #include "filter/FilterPlugin.hxx" #include "filter/FilterInternal.hxx" #include "pcm/Volume.hxx" -#include "MixerControl.hxx" +#include "mixer/MixerControl.hxx" #include "stdbin.h" #include "util/Error.hxx" #include "system/FatalError.hxx" -- cgit v1.2.3