diff --git a/src/filter/plugins/ReplayGainFilterPlugin.cxx b/src/filter/plugins/ReplayGainFilterPlugin.cxx index a5493a70e..bf080193f 100644 --- a/src/filter/plugins/ReplayGainFilterPlugin.cxx +++ b/src/filter/plugins/ReplayGainFilterPlugin.cxx @@ -170,7 +170,7 @@ ReplayGainFilter::Update() _volume = 100; try { - mixer_set_volume(*mixer, _volume); + mixer->LockSetVolume(_volume); /* invoke the mixer's listener manually, just in case the mixer implementation didn't do diff --git a/src/mixer/All.cxx b/src/mixer/All.cxx index 2fe9d7d37..306bd4961 100644 --- a/src/mixer/All.cxx +++ b/src/mixer/All.cxx @@ -45,7 +45,7 @@ output_mixer_get_volume(const AudioOutputControl &ao) noexcept return -1; try { - return mixer_get_volume(*mixer); + return mixer->LockGetVolume(); } catch (...) { FmtError(mixer_domain, "Failed to read mixer for '{}': {}", @@ -99,7 +99,7 @@ output_mixer_set_volume(AudioOutputControl &ao, unsigned volume) return SetVolumeResult::DISABLED; try { - mixer_set_volume(*mixer, volume); + mixer->LockSetVolume(volume); return SetVolumeResult::OK; } catch (...) { FmtError(mixer_domain, @@ -157,7 +157,7 @@ output_mixer_get_software_volume(const AudioOutputControl &ao) noexcept if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin)) return -1; - return mixer_get_volume(*mixer); + return mixer->LockGetVolume(); } int @@ -191,6 +191,6 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume) noexcept if (mixer != nullptr && (mixer->IsPlugin(software_mixer_plugin) || mixer->IsPlugin(null_mixer_plugin))) - mixer_set_volume(*mixer, volume); + mixer->LockSetVolume(volume); } } diff --git a/src/mixer/Control.cxx b/src/mixer/Control.cxx index 70e0ff41b..a36b58774 100644 --- a/src/mixer/Control.cxx +++ b/src/mixer/Control.cxx @@ -41,92 +41,8 @@ mixer_free(Mixer *mixer) noexcept assert(mixer != nullptr); /* mixers with the "global" flag set might still be open at - this point (see mixer_auto_close()) */ - mixer_close(*mixer); + this point (see Mixer::LockAutoClose()) */ + mixer->LockClose(); delete mixer; } - -void -mixer_open(Mixer &mixer) -{ - const std::scoped_lock protect(mixer.mutex); - - if (mixer.open) - return; - - try { - mixer.Open(); - mixer.open = true; - mixer.failure = {}; - } catch (...) { - mixer.failure = std::current_exception(); - throw; - } -} - -static void -mixer_close_internal(Mixer &mixer) noexcept -{ - assert(mixer.open); - - mixer.Close(); - mixer.open = false; - mixer.failure = {}; -} - -void -mixer_close(Mixer &mixer) noexcept -{ - const std::scoped_lock protect(mixer.mutex); - - if (mixer.open) - mixer_close_internal(mixer); -} - -void -mixer_auto_close(Mixer &mixer) noexcept -{ - if (!mixer.IsGlobal()) - mixer_close(mixer); -} - -int -mixer_get_volume(Mixer &mixer) -{ - int volume; - - if (mixer.IsGlobal() && !mixer.failure) - mixer_open(mixer); - - const std::scoped_lock protect(mixer.mutex); - - if (mixer.open) { - try { - volume = mixer.GetVolume(); - } catch (...) { - mixer_close_internal(mixer); - mixer.failure = std::current_exception(); - throw; - } - } else - volume = -1; - - return volume; -} - -void -mixer_set_volume(Mixer &mixer, unsigned volume) -{ - assert(volume <= 100); - - if (mixer.IsGlobal() && !mixer.failure) - mixer_open(mixer); - - const std::scoped_lock protect(mixer.mutex); - - if (mixer.open) - mixer.SetVolume(volume); - else if (mixer.failure) - std::rethrow_exception(mixer.failure); -} diff --git a/src/mixer/Control.hxx b/src/mixer/Control.hxx index 944f842ab..9ba5ab0ed 100644 --- a/src/mixer/Control.hxx +++ b/src/mixer/Control.hxx @@ -44,32 +44,4 @@ mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, void mixer_free(Mixer *mixer) noexcept; -/** - * Throws std::runtime_error on error. - */ -void -mixer_open(Mixer &mixer); - -void -mixer_close(Mixer &mixer) noexcept; - -/** - * 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) noexcept; - -/** - * Throws std::runtime_error on error. - */ -int -mixer_get_volume(Mixer &mixer); - -/** - * Throws std::runtime_error on error. - */ -void -mixer_set_volume(Mixer &mixer, unsigned volume); - #endif diff --git a/src/mixer/Mixer.cxx b/src/mixer/Mixer.cxx new file mode 100644 index 000000000..8024b64a9 --- /dev/null +++ b/src/mixer/Mixer.cxx @@ -0,0 +1,103 @@ +/* + * Copyright 2003-2022 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Mixer.hxx" + +#include + +void +Mixer::LockOpen() +{ + const std::scoped_lock lock{mutex}; + + if (open) + return; + + _Open(); +} + +void +Mixer::_Open() +{ + assert(!open); + + try { + Open(); + open = true; + failure = {}; + } catch (...) { + failure = std::current_exception(); + throw; + } +} + +void +Mixer::LockClose() noexcept +{ + const std::scoped_lock lock{mutex}; + + if (open) + _Close(); +} + +void +Mixer::_Close() noexcept +{ + assert(open); + + Close(); + open = false; + failure = {}; +} + +int +Mixer::LockGetVolume() +{ + if (IsGlobal() && !failure) + LockOpen(); + + const std::scoped_lock lock{mutex}; + + if (open) { + try { + return GetVolume(); + } catch (...) { + _Close(); + failure = std::current_exception(); + throw; + } + } else + return -1; +} + +void +Mixer::LockSetVolume(unsigned volume) +{ + assert(volume <= 100); + + if (IsGlobal() && !failure) + LockOpen(); + + const std::scoped_lock lock{mutex}; + + if (open) + SetVolume(volume); + else if (failure) + std::rethrow_exception(failure); +} diff --git a/src/mixer/Mixer.hxx b/src/mixer/Mixer.hxx index 2efbd7308..35f8baeb9 100644 --- a/src/mixer/Mixer.hxx +++ b/src/mixer/Mixer.hxx @@ -17,12 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_MIXER_INTERNAL_HXX -#define MPD_MIXER_INTERNAL_HXX +#pragma once #include "MixerPlugin.hxx" #include "thread/Mutex.hxx" -#include "util/Compiler.h" #include @@ -68,6 +66,37 @@ public: return plugin.global; } + /** + * Throws on error. + */ + void LockOpen(); + + void LockClose() noexcept; + + /** + * Close the mixer unless the plugin's "global" flag is set. + * This is called when the #AudioOutput is closed. + */ + void LockAutoClose() noexcept { + if (!IsGlobal()) + LockClose(); + } + + /** + * Throws on error. + */ + int LockGetVolume(); + + /** + * Throws on error. + */ + void LockSetVolume(unsigned volume); + +private: + void _Open(); + void _Close() noexcept; + +protected: /** * Open mixer device * @@ -107,5 +136,3 @@ public: */ virtual void SetVolume(unsigned volume) = 0; }; - -#endif diff --git a/src/mixer/meson.build b/src/mixer/meson.build index 2d83630fa..2227c8d45 100644 --- a/src/mixer/meson.build +++ b/src/mixer/meson.build @@ -4,6 +4,7 @@ subdir('plugins') mixer_glue = static_library( 'mixer_glue', + 'Mixer.cxx', 'Control.cxx', 'Type.cxx', 'All.cxx', diff --git a/src/output/Control.cxx b/src/output/Control.cxx index c46715cb8..32dfce84a 100644 --- a/src/output/Control.cxx +++ b/src/output/Control.cxx @@ -22,7 +22,7 @@ #include "Client.hxx" #include "Domain.hxx" #include "lib/fmt/ExceptionFormatter.hxx" -#include "mixer/Control.hxx" +#include "mixer/Mixer.hxx" #include "config/Block.hxx" #include "Log.hxx" @@ -276,7 +276,7 @@ AudioOutputControl::Open(std::unique_lock &lock, if (open2 && output->mixer != nullptr) { const ScopeUnlock unlock(mutex); try { - mixer_open(*output->mixer); + output->mixer->LockOpen(); } catch (...) { FmtError(output_domain, "Failed to open mixer for '{}': {}", @@ -296,7 +296,7 @@ AudioOutputControl::CloseWait(std::unique_lock &lock) noexcept return; if (output->mixer != nullptr) - mixer_auto_close(*output->mixer); + output->mixer->LockAutoClose(); assert(!open || !fail_timer.IsDefined()); @@ -359,8 +359,8 @@ AudioOutputControl::LockPauseAsync() noexcept if (output && output->mixer != nullptr && !output->SupportsPause()) /* the device has no pause mode: close the mixer, unless its "global" flag is set (checked by - mixer_auto_close()) */ - mixer_auto_close(*output->mixer); + Mixer::LockAutoClose()) */ + output->mixer->LockAutoClose(); if (output) output->Interrupt(); @@ -418,8 +418,8 @@ AudioOutputControl::LockRelease() noexcept (!always_on || !output->SupportsPause())) /* the device has no pause mode: close the mixer, unless its "global" flag is set (checked by - mixer_auto_close()) */ - mixer_auto_close(*output->mixer); + Mixer::LockAutoClose()) */ + output->mixer->LockAutoClose(); std::unique_lock lock(mutex); diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx index e59b4b0e1..41e51976d 100644 --- a/src/output/OutputCommand.cxx +++ b/src/output/OutputCommand.cxx @@ -27,7 +27,7 @@ #include "OutputCommand.hxx" #include "MultipleOutputs.hxx" #include "Client.hxx" -#include "mixer/Control.hxx" +#include "mixer/Mixer.hxx" #include "mixer/Memento.hxx" #include "Idle.hxx" @@ -75,7 +75,7 @@ audio_output_disable_index(MultipleOutputs &outputs, auto *mixer = ao.GetMixer(); if (mixer != nullptr) { - mixer_close(*mixer); + mixer->LockClose(); mixer_memento.InvalidateHardwareVolume(); idle_add(IDLE_MIXER); } @@ -102,7 +102,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, if (!enabled) { auto *mixer = ao.GetMixer(); if (mixer != nullptr) { - mixer_close(*mixer); + mixer->LockClose(); mixer_memento.InvalidateHardwareVolume(); idle_add(IDLE_MIXER); } diff --git a/test/read_mixer.cxx b/test/read_mixer.cxx index b9329491a..c9ab0d552 100644 --- a/test/read_mixer.cxx +++ b/test/read_mixer.cxx @@ -19,6 +19,7 @@ #include "NullMixerListener.hxx" #include "mixer/Control.hxx" +#include "mixer/Mixer.hxx" #include "mixer/plugins/AlsaMixerPlugin.hxx" #include "filter/Registry.hxx" #include "event/Loop.hxx" @@ -57,10 +58,7 @@ try { mixer_listener, ConfigBlock()); - mixer_open(*mixer); - - volume = mixer_get_volume(*mixer); - mixer_close(*mixer); + volume = mixer->LockGetVolume(); mixer_free(mixer); assert(volume >= -1 && volume <= 100); diff --git a/test/run_filter.cxx b/test/run_filter.cxx index b82181be2..0546001ee 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -42,12 +42,6 @@ #include #include -void -mixer_set_volume([[maybe_unused]] Mixer &mixer, - [[maybe_unused]] unsigned volume) -{ -} - static std::unique_ptr LoadFilter(const ConfigData &config, const char *name) {