diff options
Diffstat (limited to 'src/mixer')
-rw-r--r-- | src/mixer/MixerAll.cxx | 155 | ||||
-rw-r--r-- | src/mixer/MixerControl.cxx | 146 | ||||
-rw-r--r-- | src/mixer/MixerControl.hxx | 62 | ||||
-rw-r--r-- | src/mixer/MixerInternal.hxx | 93 | ||||
-rw-r--r-- | src/mixer/MixerList.hxx | 37 | ||||
-rw-r--r-- | src/mixer/MixerPlugin.hxx | 58 | ||||
-rw-r--r-- | src/mixer/MixerType.cxx | 39 | ||||
-rw-r--r-- | src/mixer/MixerType.hxx | 47 | ||||
-rw-r--r-- | src/mixer/Volume.cxx | 134 | ||||
-rw-r--r-- | src/mixer/Volume.hxx | 53 | ||||
-rw-r--r-- | src/mixer/plugins/AlsaMixerPlugin.cxx (renamed from src/mixer/AlsaMixerPlugin.cxx) | 121 | ||||
-rw-r--r-- | src/mixer/plugins/OssMixerPlugin.cxx (renamed from src/mixer/OssMixerPlugin.cxx) | 72 | ||||
-rw-r--r-- | src/mixer/plugins/PulseMixerPlugin.cxx (renamed from src/mixer/PulseMixerPlugin.cxx) | 166 | ||||
-rw-r--r-- | src/mixer/plugins/PulseMixerPlugin.hxx (renamed from src/mixer/PulseMixerPlugin.hxx) | 13 | ||||
-rw-r--r-- | src/mixer/plugins/RoarMixerPlugin.cxx (renamed from src/mixer/RoarMixerPlugin.cxx) | 61 | ||||
-rw-r--r-- | src/mixer/plugins/SoftwareMixerPlugin.cxx (renamed from src/mixer/SoftwareMixerPlugin.cxx) | 105 | ||||
-rw-r--r-- | src/mixer/plugins/SoftwareMixerPlugin.hxx (renamed from src/mixer/SoftwareMixerPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/mixer/plugins/WinmmMixerPlugin.cxx (renamed from src/mixer/WinmmMixerPlugin.cxx) | 62 |
18 files changed, 1069 insertions, 357 deletions
diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx new file mode 100644 index 000000000..5fef6a92f --- /dev/null +++ b/src/mixer/MixerAll.cxx @@ -0,0 +1,155 @@ +/* + * 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 "output/MultipleOutputs.hxx" +#include "MixerControl.hxx" +#include "MixerInternal.hxx" +#include "MixerList.hxx" +#include "output/Internal.hxx" +#include "pcm/Volume.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include <assert.h> + +static constexpr Domain mixer_domain("mixer"); + +static int +output_mixer_get_volume(const AudioOutput &ao) +{ + if (!ao.enabled) + return -1; + + Mixer *mixer = ao.mixer; + if (mixer == nullptr) + return -1; + + Error error; + int volume = mixer_get_volume(mixer, error); + if (volume < 0 && error.IsDefined()) + FormatError(error, + "Failed to read mixer for '%s'", + ao.name); + + return volume; +} + +int +MultipleOutputs::GetVolume() const +{ + unsigned ok = 0; + int total = 0; + + for (auto ao : outputs) { + int volume = output_mixer_get_volume(*ao); + if (volume >= 0) { + total += volume; + ++ok; + } + } + + if (ok == 0) + return -1; + + return total / ok; +} + +static bool +output_mixer_set_volume(AudioOutput &ao, unsigned volume) +{ + assert(volume <= 100); + + if (!ao.enabled) + return false; + + Mixer *mixer = ao.mixer; + if (mixer == nullptr) + return false; + + Error error; + bool success = mixer_set_volume(mixer, volume, error); + if (!success && error.IsDefined()) + FormatError(error, + "Failed to set mixer for '%s'", + ao.name); + + return success; +} + +bool +MultipleOutputs::SetVolume(unsigned volume) +{ + assert(volume <= 100); + + bool success = false; + for (auto ao : outputs) + success = output_mixer_set_volume(*ao, volume) + || success; + + return success; +} + +static int +output_mixer_get_software_volume(const AudioOutput &ao) +{ + if (!ao.enabled) + return -1; + + Mixer *mixer = ao.mixer; + if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin)) + return -1; + + return mixer_get_volume(mixer, IgnoreError()); +} + +int +MultipleOutputs::GetSoftwareVolume() const +{ + unsigned ok = 0; + int total = 0; + + for (auto ao : outputs) { + int volume = output_mixer_get_software_volume(*ao); + if (volume >= 0) { + total += volume; + ++ok; + } + } + + if (ok == 0) + return -1; + + return total / ok; +} + +void +MultipleOutputs::SetSoftwareVolume(unsigned volume) +{ + assert(volume <= PCM_VOLUME_1); + + for (auto ao : outputs) { + const auto mixer = ao->mixer; + + if (mixer != nullptr && + &mixer->plugin == &software_mixer_plugin) + mixer_set_volume(mixer, volume, IgnoreError()); + } +} diff --git a/src/mixer/MixerControl.cxx b/src/mixer/MixerControl.cxx new file mode 100644 index 000000000..568fad058 --- /dev/null +++ b/src/mixer/MixerControl.cxx @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "MixerControl.hxx" +#include "MixerInternal.hxx" +#include "util/Error.hxx" + +#include <assert.h> + +Mixer * +mixer_new(EventLoop &event_loop, + const MixerPlugin &plugin, AudioOutput &ao, + const config_param ¶m, + Error &error) +{ + Mixer *mixer = plugin.init(event_loop, ao, param, error); + + assert(mixer == nullptr || mixer->IsPlugin(plugin)); + + return mixer; +} + +void +mixer_free(Mixer *mixer) +{ + assert(mixer != nullptr); + + /* mixers with the "global" flag set might still be open at + this point (see mixer_auto_close()) */ + mixer_close(mixer); + + delete mixer; +} + +bool +mixer_open(Mixer *mixer, Error &error) +{ + bool success; + + assert(mixer != nullptr); + + const ScopeLock protect(mixer->mutex); + + success = mixer->open || (mixer->open = mixer->Open(error)); + + mixer->failed = !success; + + return success; +} + +static void +mixer_close_internal(Mixer *mixer) +{ + assert(mixer != nullptr); + assert(mixer->open); + + mixer->Close(); + mixer->open = false; +} + +void +mixer_close(Mixer *mixer) +{ + assert(mixer != 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->GetVolume(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->SetVolume(volume, error); +} diff --git a/src/mixer/MixerControl.hxx b/src/mixer/MixerControl.hxx new file mode 100644 index 000000000..46a9138e6 --- /dev/null +++ b/src/mixer/MixerControl.hxx @@ -0,0 +1,62 @@ +/* + * 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; +class EventLoop; +struct AudioOutput; +struct MixerPlugin; +struct config_param; + +Mixer * +mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &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 #AudioOutput 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..2c6282ebb --- /dev/null +++ b/src/mixer/MixerInternal.hxx @@ -0,0 +1,93 @@ +/* + * 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" +#include "Compiler.h" + +class Mixer { +public: + const MixerPlugin &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: + explicit Mixer(const MixerPlugin &_plugin) + :plugin(_plugin), + open(false), + failed(false) {} + + Mixer(const Mixer &) = delete; + + virtual ~Mixer() {} + + bool IsPlugin(const MixerPlugin &other) const { + return &plugin == &other; + } + + /** + * Open mixer device + * + * @return true on success, false on error + */ + virtual bool Open(Error &error) = 0; + + /** + * Close mixer device + */ + virtual void Close() = 0; + + /** + * Reads the current volume. + * + * @return the current volume (0..100 including) or -1 if + * unavailable or on error (error set, mixer will be closed) + */ + gcc_pure + virtual int GetVolume(Error &error) = 0; + + /** + * Sets the volume. + * + * @param volume the new volume (0..100 including) @return + * true on success, false on error + */ + virtual bool SetVolume(unsigned volume, Error &error) = 0; +}; + +#endif diff --git a/src/mixer/MixerList.hxx b/src/mixer/MixerList.hxx new file mode 100644 index 000000000..e75b2e6ff --- /dev/null +++ b/src/mixer/MixerList.hxx @@ -0,0 +1,37 @@ +/* + * 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 + +struct MixerPlugin; + +extern const MixerPlugin software_mixer_plugin; +extern const MixerPlugin alsa_mixer_plugin; +extern const MixerPlugin oss_mixer_plugin; +extern const MixerPlugin roar_mixer_plugin; +extern const MixerPlugin pulse_mixer_plugin; +extern const MixerPlugin winmm_mixer_plugin; + +#endif diff --git a/src/mixer/MixerPlugin.hxx b/src/mixer/MixerPlugin.hxx new file mode 100644 index 000000000..bf9af14e3 --- /dev/null +++ b/src/mixer/MixerPlugin.hxx @@ -0,0 +1,58 @@ +/* + * 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; +struct AudioOutput; +class Mixer; +class EventLoop; +class Error; + +struct MixerPlugin { + /** + * Alocates and configures a mixer device. + * + * @param ao the associated AudioOutput + * @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)(EventLoop &event_loop, AudioOutput &ao, + const config_param ¶m, + Error &error); + + /** + * If true, then the mixer is automatically opened, even if + * its audio output is not open. If false, then the mixer is + * disabled as long as its audio output is closed. + */ + bool global; +}; + +#endif diff --git a/src/mixer/MixerType.cxx b/src/mixer/MixerType.cxx new file mode 100644 index 000000000..cd45db0d9 --- /dev/null +++ b/src/mixer/MixerType.cxx @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "MixerType.hxx" + +#include <assert.h> +#include <string.h> + +enum mixer_type +mixer_type_parse(const char *input) +{ + assert(input != NULL); + + if (strcmp(input, "none") == 0 || strcmp(input, "disabled") == 0) + return MIXER_TYPE_NONE; + else if (strcmp(input, "hardware") == 0) + return MIXER_TYPE_HARDWARE; + else if (strcmp(input, "software") == 0) + return MIXER_TYPE_SOFTWARE; + else + return MIXER_TYPE_UNKNOWN; +} diff --git a/src/mixer/MixerType.hxx b/src/mixer/MixerType.hxx new file mode 100644 index 000000000..bfa2637b7 --- /dev/null +++ b/src/mixer/MixerType.hxx @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_MIXER_TYPE_HXX +#define MPD_MIXER_TYPE_HXX + +enum mixer_type { + /** parser error */ + MIXER_TYPE_UNKNOWN, + + /** mixer disabled */ + MIXER_TYPE_NONE, + + /** software mixer with pcm_volume() */ + MIXER_TYPE_SOFTWARE, + + /** hardware mixer (output's plugin) */ + MIXER_TYPE_HARDWARE, +}; + +/** + * Parses a "mixer_type" setting from the configuration file. + * + * @param input the configured string value; must not be NULL + * @return a #mixer_type value; MIXER_TYPE_UNKNOWN means #input could + * not be parsed + */ +enum mixer_type +mixer_type_parse(const char *input); + +#endif diff --git a/src/mixer/Volume.cxx b/src/mixer/Volume.cxx new file mode 100644 index 000000000..aaae5d9ee --- /dev/null +++ b/src/mixer/Volume.cxx @@ -0,0 +1,134 @@ +/* + * 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 "output/MultipleOutputs.hxx" +#include "Idle.hxx" +#include "GlobalEvents.hxx" +#include "util/StringUtil.hxx" +#include "util/Domain.hxx" +#include "system/PeriodClock.hxx" +#include "Log.hxx" + +#include <assert.h> +#include <stdlib.h> + +#define SW_VOLUME_STATE "sw_volume: " + +static constexpr Domain volume_domain("volume"); + +static unsigned volume_software_set = 100; + +/** the cached hardware mixer value; invalid if negative */ +static int last_hardware_volume = -1; +/** the age of #last_hardware_volume */ +static PeriodClock hardware_volume_clock; + +/** + * Handler for #GlobalEvents::MIXER. + */ +static void +mixer_event_callback(void) +{ + /* flush the hardware volume cache */ + last_hardware_volume = -1; + + /* notify clients */ + idle_add(IDLE_MIXER); +} + +void volume_init(void) +{ + GlobalEvents::Register(GlobalEvents::MIXER, mixer_event_callback); +} + +int +volume_level_get(const MultipleOutputs &outputs) +{ + if (last_hardware_volume >= 0 && + !hardware_volume_clock.CheckUpdate(1000)) + /* throttle access to hardware mixers */ + return last_hardware_volume; + + last_hardware_volume = outputs.GetVolume(); + return last_hardware_volume; +} + +static bool +software_volume_change(MultipleOutputs &outputs, unsigned volume) +{ + assert(volume <= 100); + + volume_software_set = volume; + outputs.SetSoftwareVolume(volume); + + return true; +} + +static bool +hardware_volume_change(MultipleOutputs &outputs, unsigned volume) +{ + /* reset the cache */ + last_hardware_volume = -1; + + return outputs.SetVolume(volume); +} + +bool +volume_level_change(MultipleOutputs &outputs, unsigned volume) +{ + assert(volume <= 100); + + volume_software_set = volume; + + idle_add(IDLE_MIXER); + + return hardware_volume_change(outputs, volume); +} + +bool +read_sw_volume_state(const char *line, MultipleOutputs &outputs) +{ + 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(outputs, 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..b86328d88 --- /dev/null +++ b/src/mixer/Volume.hxx @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_VOLUME_HXX +#define MPD_VOLUME_HXX + +#include "Compiler.h" + +#include <stdio.h> + +class MultipleOutputs; + +void volume_init(void); + +gcc_pure +int +volume_level_get(const MultipleOutputs &outputs); + +bool +volume_level_change(MultipleOutputs &outputs, unsigned volume); + +bool +read_sw_volume_state(const char *line, MultipleOutputs &outputs); + +void save_sw_volume_state(FILE *fp); + +/** + * Generates a hash number for the current state of the software + * volume control. This is used by timer_save_state_file() to + * determine whether the state has changed and the state file should + * be saved. + */ +gcc_pure +unsigned +sw_volume_state_get_hash(void); + +#endif diff --git a/src/mixer/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx index 4a4ca433c..26007fef3 100644 --- a/src/mixer/AlsaMixerPlugin.cxx +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -18,15 +18,15 @@ */ #include "config.h" -#include "MixerInternal.hxx" -#include "OutputAPI.hxx" +#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 "event/Call.hxx" #include "util/ASCII.hxx" #include "util/ReusableArray.hxx" +#include "util/Clamp.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -39,35 +39,30 @@ #define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" static constexpr unsigned VOLUME_MIXER_ALSA_INDEX_DEFAULT = 0; -class AlsaMixerMonitor final : private MultiSocketMonitor { +class AlsaMixerMonitor final : MultiSocketMonitor, DeferredMonitor { snd_mixer_t *mixer; ReusableArray<pollfd> pfd_buffer; public: AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer) - :MultiSocketMonitor(_loop), mixer(_mixer) { -#ifdef USE_EPOLL - _loop.AddCall([this](){ InvalidateSockets(); }); -#else - _loop.AddIdle(InitAlsaMixerMonitor, this); -#endif + :MultiSocketMonitor(_loop), DeferredMonitor(_loop), + mixer(_mixer) { + DeferredMonitor::Schedule(); } private: -#ifndef USE_EPOLL - static gboolean InitAlsaMixerMonitor(gpointer data) { - AlsaMixerMonitor &amm = *(AlsaMixerMonitor *)data; - amm.InvalidateSockets(); - return false; + virtual void RunDeferred() override { + InvalidateSockets(); } -#endif virtual int PrepareSockets() override; virtual void DispatchSockets() override; }; class AlsaMixer final : public Mixer { + EventLoop &event_loop; + const char *device; const char *control; unsigned int index; @@ -81,15 +76,19 @@ class AlsaMixer final : public Mixer { AlsaMixerMonitor *monitor; public: - AlsaMixer():Mixer(alsa_mixer_plugin) {} + AlsaMixer(EventLoop &_event_loop) + :Mixer(alsa_mixer_plugin), event_loop(_event_loop) {} + + virtual ~AlsaMixer(); 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); + /* virtual methods from class Mixer */ + virtual bool Open(Error &error) override; + virtual void Close() override; + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; }; static constexpr Domain alsa_mixer_domain("alsa_mixer"); @@ -97,8 +96,10 @@ static constexpr Domain alsa_mixer_domain("alsa_mixer"); int AlsaMixerMonitor::PrepareSockets() { - if (mixer == nullptr) + if (mixer == nullptr) { + ClearSocketList(); return -1; + } int count = snd_mixer_poll_descriptors_count(mixer); if (count < 0) @@ -110,24 +111,7 @@ AlsaMixerMonitor::PrepareSockets() if (count < 0) count = 0; - struct pollfd *end = pfds + count; - - UpdateSocketList([pfds, end](int fd) -> unsigned { - auto i = std::find_if(pfds, end, [fd](const struct pollfd &pfd){ - return pfd.fd == fd; - }); - if (i == end) - return 0; - - auto events = i->events; - i->events = 0; - return events; - }); - - for (auto i = pfds; i != end; ++i) - if (i->events != 0) - AddSocket(i->fd, i->events); - + ReplaceSocketList(pfds, count); return -1; } @@ -183,22 +167,18 @@ AlsaMixer::Configure(const config_param ¶m) } static Mixer * -alsa_mixer_init(gcc_unused void *ao, const config_param ¶m, +alsa_mixer_init(EventLoop &event_loop, gcc_unused AudioOutput &ao, + const config_param ¶m, gcc_unused Error &error) { - AlsaMixer *am = new AlsaMixer(); + AlsaMixer *am = new AlsaMixer(event_loop); am->Configure(param); return am; } -static void -alsa_mixer_finish(Mixer *data) +AlsaMixer::~AlsaMixer() { - AlsaMixer *am = (AlsaMixer *)data; - - delete am; - /* free libasound's config cache */ snd_config_update_free_global(); } @@ -258,7 +238,7 @@ AlsaMixer::Setup(Error &error) snd_mixer_elem_set_callback(elem, alsa_mixer_elem_callback); - monitor = new AlsaMixerMonitor(*main_loop, handle); + monitor = new AlsaMixerMonitor(event_loop, handle); return true; } @@ -285,14 +265,6 @@ AlsaMixer::Open(Error &error) return true; } -static bool -alsa_mixer_open(Mixer *data, Error &error) -{ - AlsaMixer *am = (AlsaMixer *)data; - - return am->Open(error); -} - inline void AlsaMixer::Close() { @@ -304,13 +276,6 @@ AlsaMixer::Close() snd_mixer_close(handle); } -static void -alsa_mixer_close(Mixer *data) -{ - AlsaMixer *am = (AlsaMixer *)data; - am->Close(); -} - inline int AlsaMixer::GetVolume(Error &error) { @@ -350,13 +315,6 @@ AlsaMixer::GetVolume(Error &error) 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) { @@ -372,8 +330,7 @@ AlsaMixer::SetVolume(unsigned volume, Error &error) level = (long)(((vol / 100.0) * (volume_max - volume_min) + volume_min) + 0.5); - level = level > volume_max ? volume_max : level; - level = level < volume_min ? volume_min : level; + level = Clamp(level, volume_min, volume_max); err = snd_mixer_selem_set_playback_volume_all(elem, level); if (err < 0) { @@ -386,19 +343,7 @@ AlsaMixer::SetVolume(unsigned volume, Error &error) 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 = { +const MixerPlugin alsa_mixer_plugin = { alsa_mixer_init, - alsa_mixer_finish, - alsa_mixer_open, - alsa_mixer_close, - alsa_mixer_get_volume, - alsa_mixer_set_volume, true, }; diff --git a/src/mixer/OssMixerPlugin.cxx b/src/mixer/plugins/OssMixerPlugin.cxx index 0a459bc97..939fe0824 100644 --- a/src/mixer/OssMixerPlugin.cxx +++ b/src/mixer/plugins/OssMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -18,8 +18,8 @@ */ #include "config.h" -#include "MixerInternal.hxx" -#include "OutputAPI.hxx" +#include "mixer/MixerInternal.hxx" +#include "config/ConfigData.hxx" #include "system/fd_util.h" #include "util/ASCII.hxx" #include "util/Error.hxx" @@ -28,10 +28,8 @@ #include <assert.h> #include <string.h> -#include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> -#include <errno.h> #include <stdlib.h> #include <unistd.h> @@ -43,7 +41,7 @@ #define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" -class OssMixer : public Mixer { +class OssMixer final : public Mixer { const char *device; const char *control; @@ -54,11 +52,12 @@ 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); + /* virtual methods from class Mixer */ + virtual bool Open(Error &error) override; + virtual void Close() override; + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; }; static constexpr Domain oss_mixer_domain("oss_mixer"); @@ -98,7 +97,8 @@ OssMixer::Configure(const config_param ¶m, Error &error) } static Mixer * -oss_mixer_init(gcc_unused void *ao, const config_param ¶m, +oss_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused AudioOutput &ao, + const config_param ¶m, Error &error) { OssMixer *om = new OssMixer(); @@ -111,14 +111,6 @@ oss_mixer_init(gcc_unused void *ao, const config_param ¶m, return om; } -static void -oss_mixer_finish(Mixer *data) -{ - OssMixer *om = (OssMixer *) data; - - delete om; -} - void OssMixer::Close() { @@ -127,14 +119,7 @@ OssMixer::Close() close(device_fd); } -static void -oss_mixer_close(Mixer *data) -{ - OssMixer *om = (OssMixer *) data; - om->Close(); -} - -inline bool +bool OssMixer::Open(Error &error) { device_fd = open_cloexec(device, O_RDONLY, 0); @@ -164,15 +149,7 @@ OssMixer::Open(Error &error) return true; } -static bool -oss_mixer_open(Mixer *data, Error &error) -{ - OssMixer *om = (OssMixer *) data; - - return om->Open(error); -} - -inline int +int OssMixer::GetVolume(Error &error) { int left, right, level; @@ -198,14 +175,7 @@ OssMixer::GetVolume(Error &error) return left; } -static int -oss_mixer_get_volume(Mixer *mixer, Error &error) -{ - OssMixer *om = (OssMixer *)mixer; - return om->GetVolume(error); -} - -inline bool +bool OssMixer::SetVolume(unsigned volume, Error &error) { int level; @@ -225,19 +195,7 @@ OssMixer::SetVolume(unsigned volume, Error &error) 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 = { +const MixerPlugin 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/plugins/PulseMixerPlugin.cxx index ff10256cb..b838de689 100644 --- a/src/mixer/PulseMixerPlugin.cxx +++ b/src/mixer/plugins/PulseMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -19,14 +19,13 @@ #include "config.h" #include "PulseMixerPlugin.hxx" -#include "MixerInternal.hxx" -#include "output/PulseOutputPlugin.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 <pulse/thread-mainloop.h> #include <pulse/context.h> #include <pulse/introspect.h> #include <pulse/stream.h> @@ -34,76 +33,96 @@ #include <pulse/error.h> #include <assert.h> -#include <string.h> -struct PulseMixer final : public Mixer { - PulseOutput *output; +class PulseMixer final : public Mixer { + PulseOutput &output; bool online; struct pa_cvolume volume; - PulseMixer(PulseOutput *_output) +public: + PulseMixer(PulseOutput &_output) :Mixer(pulse_mixer_plugin), output(_output), online(false) { } + + virtual ~PulseMixer(); + + void Offline(); + void VolumeCallback(const pa_sink_input_info *i, int eol); + void Update(pa_context *context, pa_stream *stream); + + /* virtual methods from class Mixer */ + virtual bool Open(gcc_unused Error &error) override { + return true; + } + + virtual void Close() override { + } + + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; }; static constexpr Domain pulse_mixer_domain("pulse_mixer"); -static void -pulse_mixer_offline(PulseMixer *pm) +void +PulseMixer::Offline() { - if (!pm->online) + if (!online) return; - pm->online = false; + 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) +inline void +PulseMixer::VolumeCallback(const pa_sink_input_info *i, int eol) { - PulseMixer *pm = (PulseMixer *)userdata; - if (eol) return; if (i == nullptr) { - pulse_mixer_offline(pm); + Offline(); return; } - pm->online = true; - pm->volume = i->volume; + online = true; + volume = i->volume; GlobalEvents::Emit(GlobalEvents::MIXER); } +/** + * Callback invoked by pulse_mixer_update(). Receives the new mixer + * value. + */ static void -pulse_mixer_update(PulseMixer *pm, - struct pa_context *context, struct pa_stream *stream) +pulse_mixer_volume_cb(gcc_unused pa_context *context, const pa_sink_input_info *i, + int eol, void *userdata) { - pa_operation *o; + PulseMixer *pm = (PulseMixer *)userdata; + pm->VolumeCallback(i, eol); +} +inline void +PulseMixer::Update(pa_context *context, pa_stream *stream) +{ 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); + pa_operation *o = + pa_context_get_sink_input_info(context, + pa_stream_get_index(stream), + pulse_mixer_volume_cb, this); 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); + Offline(); return; } @@ -111,7 +130,7 @@ pulse_mixer_update(PulseMixer *pm, } void -pulse_mixer_on_connect(gcc_unused PulseMixer *pm, +pulse_mixer_on_connect(gcc_unused PulseMixer &pm, struct pa_context *context) { pa_operation *o; @@ -132,96 +151,73 @@ pulse_mixer_on_connect(gcc_unused PulseMixer *pm, } void -pulse_mixer_on_disconnect(PulseMixer *pm) +pulse_mixer_on_disconnect(PulseMixer &pm) { - pulse_mixer_offline(pm); + pm.Offline(); } void -pulse_mixer_on_change(PulseMixer *pm, +pulse_mixer_on_change(PulseMixer &pm, struct pa_context *context, struct pa_stream *stream) { - pulse_mixer_update(pm, context, stream); + pm.Update(context, stream); } static Mixer * -pulse_mixer_init(void *ao, gcc_unused const config_param ¶m, - Error &error) +pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao, + gcc_unused const config_param ¶m, + gcc_unused 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; - } - + PulseOutput &po = (PulseOutput &)ao; PulseMixer *pm = new PulseMixer(po); - pulse_output_set_mixer(po, pm); + pulse_output_set_mixer(po, *pm); return pm; } -static void -pulse_mixer_finish(Mixer *data) +PulseMixer::~PulseMixer() { - PulseMixer *pm = (PulseMixer *) data; - - pulse_output_clear_mixer(pm->output, pm); - - delete pm; + pulse_output_clear_mixer(output, *this); } -static int -pulse_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) +int +PulseMixer::GetVolume(gcc_unused Error &error) { - PulseMixer *pm = (PulseMixer *) mixer; - int ret; - - pulse_output_lock(pm->output); + pulse_output_lock(output); - ret = pm->online - ? (int)((100*(pa_cvolume_avg(&pm->volume)+1))/PA_VOLUME_NORM) + int result = online + ? (int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM) : -1; - pulse_output_unlock(pm->output); + pulse_output_unlock(output); - return ret; + return result; } -static bool -pulse_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +bool +PulseMixer::SetVolume(unsigned new_volume, Error &error) { - PulseMixer *pm = (PulseMixer *) mixer; - struct pa_cvolume cvolume; - bool success; - - pulse_output_lock(pm->output); + pulse_output_lock(output); - if (!pm->online) { - pulse_output_unlock(pm->output); + if (!online) { + pulse_output_unlock(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); + struct pa_cvolume cvolume; + pa_cvolume_set(&cvolume, volume.channels, + (pa_volume_t)new_volume * PA_VOLUME_NORM / 100 + 0.5); + bool success = pulse_output_set_volume(output, &cvolume, error); if (success) - pm->volume = cvolume; - - pulse_output_unlock(pm->output); + volume = cvolume; + pulse_output_unlock(output); return success; } -const struct mixer_plugin pulse_mixer_plugin = { +const MixerPlugin 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/plugins/PulseMixerPlugin.hxx index fa73e0f5e..9b3a6daf1 100644 --- a/src/mixer/PulseMixerPlugin.hxx +++ b/src/mixer/plugins/PulseMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -20,20 +20,17 @@ #ifndef MPD_PULSE_MIXER_PLUGIN_HXX #define MPD_PULSE_MIXER_PLUGIN_HXX -#include <pulse/def.h> - -struct PulseMixer; +class PulseMixer; struct pa_context; struct pa_stream; void -pulse_mixer_on_connect(PulseMixer *pm, struct pa_context *context); +pulse_mixer_on_connect(PulseMixer &pm, pa_context *context); void -pulse_mixer_on_disconnect(PulseMixer *pm); +pulse_mixer_on_disconnect(PulseMixer &pm); void -pulse_mixer_on_change(PulseMixer *pm, - struct pa_context *context, struct pa_stream *stream); +pulse_mixer_on_change(PulseMixer &pm, pa_context *context, pa_stream *stream); #endif diff --git a/src/mixer/RoarMixerPlugin.cxx b/src/mixer/plugins/RoarMixerPlugin.cxx index 6bd700551..7c2a25dfb 100644 --- a/src/mixer/RoarMixerPlugin.cxx +++ b/src/mixer/plugins/RoarMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 * @@ -20,55 +20,52 @@ #include "config.h" -#include "MixerInternal.hxx" -#include "OutputAPI.hxx" -#include "output/RoarOutputPlugin.hxx" +#include "mixer/MixerInternal.hxx" +#include "output/plugins/RoarOutputPlugin.hxx" +#include "Compiler.h" -struct RoarMixer final : public Mixer { +class RoarMixer final : public Mixer { /** the base mixer class */ - RoarOutput *self; + RoarOutput &self; - RoarMixer(RoarOutput *_output) +public: + RoarMixer(RoarOutput &_output) :Mixer(roar_mixer_plugin), self(_output) {} + + /* virtual methods from class Mixer */ + virtual bool Open(gcc_unused Error &error) override { + return true; + } + + virtual void Close() override { + } + + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; }; static Mixer * -roar_mixer_init(void *ao, gcc_unused const config_param ¶m, +roar_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &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; + return new RoarMixer((RoarOutput &)ao); } -static int -roar_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) +int +RoarMixer::GetVolume(gcc_unused Error &error) { - RoarMixer *self = (RoarMixer *)mixer; - return roar_output_get_volume(self->self); + return roar_output_get_volume(self); } -static bool -roar_mixer_set_volume(Mixer *mixer, unsigned volume, - gcc_unused Error &error) +bool +RoarMixer::SetVolume(unsigned volume, gcc_unused Error &error) { - RoarMixer *self = (RoarMixer *)mixer; - return roar_output_set_volume(self->self, volume); + return roar_output_set_volume(self, volume); } -const struct mixer_plugin roar_mixer_plugin = { +const MixerPlugin 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/plugins/SoftwareMixerPlugin.cxx index 193e68f23..78f0769ab 100644 --- a/src/mixer/SoftwareMixerPlugin.cxx +++ b/src/mixer/plugins/SoftwareMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -19,13 +19,13 @@ #include "config.h" #include "SoftwareMixerPlugin.hxx" -#include "MixerInternal.hxx" -#include "FilterPlugin.hxx" -#include "FilterRegistry.hxx" -#include "FilterInternal.hxx" -#include "filter/VolumeFilterPlugin.hxx" -#include "pcm/PcmVolume.hxx" -#include "ConfigData.hxx" +#include "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 <assert.h> @@ -34,11 +34,11 @@ static Filter * CreateVolumeFilter() { - Error error; - return filter_new(&volume_filter_plugin, config_param(), error); + return filter_new(&volume_filter_plugin, config_param(), + IgnoreError()); } -struct SoftwareMixer final : public Mixer { +class SoftwareMixer final : public Mixer { Filter *filter; /** @@ -51,6 +51,7 @@ struct SoftwareMixer final : public Mixer { unsigned volume; +public: SoftwareMixer() :Mixer(software_mixer_plugin), filter(CreateVolumeFilter()), @@ -60,73 +61,71 @@ struct SoftwareMixer final : public Mixer { assert(filter != nullptr); } - ~SoftwareMixer() { + virtual ~SoftwareMixer() { if (owns_filter) delete filter; } + + Filter *GetFilter(); + + /* virtual methods from class Mixer */ + virtual bool Open(gcc_unused Error &error) override { + return true; + } + + virtual void Close() override { + } + + virtual int GetVolume(gcc_unused Error &error) override { + return volume; + } + + virtual bool SetVolume(unsigned volume, Error &error) override; }; static Mixer * -software_mixer_init(gcc_unused void *ao, +software_mixer_init(gcc_unused EventLoop &event_loop, + gcc_unused AudioOutput &ao, gcc_unused const config_param ¶m, gcc_unused Error &error) { return new SoftwareMixer(); } -static void -software_mixer_finish(Mixer *data) +bool +SoftwareMixer::SetVolume(unsigned new_volume, gcc_unused Error &error) { - SoftwareMixer *sm = (SoftwareMixer *)data; - - delete sm; -} + assert(new_volume <= 100); -static int -software_mixer_get_volume(Mixer *mixer, gcc_unused Error &error) -{ - SoftwareMixer *sm = (SoftwareMixer *)mixer; + if (new_volume >= 100) + new_volume = PCM_VOLUME_1; + else if (new_volume > 0) + new_volume = pcm_float_to_volume((exp(new_volume / 25.0) - 1) / + (54.5981500331F - 1)); - 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); + volume = new_volume; + volume_filter_set(filter, new_volume); return true; } -const struct mixer_plugin software_mixer_plugin = { +const MixerPlugin software_mixer_plugin = { software_mixer_init, - software_mixer_finish, - nullptr, - nullptr, - software_mixer_get_volume, - software_mixer_set_volume, true, }; +inline Filter * +SoftwareMixer::GetFilter() +{ + assert(owns_filter); + + owns_filter = false; + return filter; +} + 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; + return sm->GetFilter(); } diff --git a/src/mixer/SoftwareMixerPlugin.hxx b/src/mixer/plugins/SoftwareMixerPlugin.hxx index be59c08db..581d2ac17 100644 --- a/src/mixer/SoftwareMixerPlugin.hxx +++ b/src/mixer/plugins/SoftwareMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 diff --git a/src/mixer/WinmmMixerPlugin.cxx b/src/mixer/plugins/WinmmMixerPlugin.cxx index dbb43dce4..6b81fa095 100644 --- a/src/mixer/WinmmMixerPlugin.cxx +++ b/src/mixer/plugins/WinmmMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * 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 @@ -18,9 +18,9 @@ */ #include "config.h" -#include "MixerInternal.hxx" -#include "OutputAPI.hxx" -#include "output/WinmmOutputPlugin.hxx" +#include "mixer/MixerInternal.hxx" +#include "output/OutputAPI.hxx" +#include "output/plugins/WinmmOutputPlugin.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -30,13 +30,25 @@ #include <math.h> #include <windows.h> -struct WinmmMixer final : public Mixer { - WinmmOutput *output; +class WinmmMixer final : public Mixer { + WinmmOutput &output; - WinmmMixer(WinmmOutput *_output) +public: + WinmmMixer(WinmmOutput &_output) :Mixer(winmm_mixer_plugin), output(_output) { } + + /* virtual methods from class Mixer */ + virtual bool Open(gcc_unused Error &error) override { + return true; + } + + virtual void Close() override { + } + + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; }; static constexpr Domain winmm_mixer_domain("winmm_mixer"); @@ -55,28 +67,18 @@ winmm_volume_encode(int volume) } static Mixer * -winmm_mixer_init(void *ao, gcc_unused const config_param ¶m, +winmm_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &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; + return new WinmmMixer((WinmmOutput &)ao); } -static int -winmm_mixer_get_volume(Mixer *mixer, Error &error) +int +WinmmMixer::GetVolume(Error &error) { - WinmmMixer *wm = (WinmmMixer *) mixer; DWORD volume; - HWAVEOUT handle = winmm_output_get_handle(wm->output); + HWAVEOUT handle = winmm_output_get_handle(output); MMRESULT result = waveOutGetVolume(handle, &volume); if (result != MMSYSERR_NOERROR) { @@ -87,12 +89,11 @@ winmm_mixer_get_volume(Mixer *mixer, Error &error) return winmm_volume_decode(volume); } -static bool -winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) +bool +WinmmMixer::SetVolume(unsigned volume, Error &error) { - WinmmMixer *wm = (WinmmMixer *) mixer; DWORD value = winmm_volume_encode(volume); - HWAVEOUT handle = winmm_output_get_handle(wm->output); + HWAVEOUT handle = winmm_output_get_handle(output); MMRESULT result = waveOutSetVolume(handle, value); if (result != MMSYSERR_NOERROR) { @@ -103,12 +104,7 @@ winmm_mixer_set_volume(Mixer *mixer, unsigned volume, Error &error) return true; } -const struct mixer_plugin winmm_mixer_plugin = { +const MixerPlugin winmm_mixer_plugin = { winmm_mixer_init, - winmm_mixer_finish, - nullptr, - nullptr, - winmm_mixer_get_volume, - winmm_mixer_set_volume, false, }; |