diff options
-rw-r--r-- | src/mixer/plugins/PipeWireMixerPlugin.cxx | 91 | ||||
-rw-r--r-- | src/mixer/plugins/PipeWireMixerPlugin.hxx | 31 | ||||
-rw-r--r-- | src/mixer/plugins/meson.build | 4 | ||||
-rw-r--r-- | src/output/plugins/PipeWireOutputPlugin.cxx | 51 | ||||
-rw-r--r-- | src/output/plugins/PipeWireOutputPlugin.hxx | 5 |
5 files changed, 180 insertions, 2 deletions
diff --git a/src/mixer/plugins/PipeWireMixerPlugin.cxx b/src/mixer/plugins/PipeWireMixerPlugin.cxx new file mode 100644 index 000000000..2a8bb3a6c --- /dev/null +++ b/src/mixer/plugins/PipeWireMixerPlugin.cxx @@ -0,0 +1,91 @@ +/* + * Copyright 2003-2021 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 "PipeWireMixerPlugin.hxx" +#include "mixer/MixerInternal.hxx" +#include "mixer/Listener.hxx" +#include "output/plugins/PipeWireOutputPlugin.hxx" + +#include <cmath> + +class PipeWireMixer final : public Mixer { + PipeWireOutput &output; + + int volume = 100; + +public: + PipeWireMixer(PipeWireOutput &_output, + MixerListener &_listener) noexcept + :Mixer(pipewire_mixer_plugin, _listener), + output(_output) + { + } + + PipeWireMixer(const PipeWireMixer &) = delete; + PipeWireMixer &operator=(const PipeWireMixer &) = delete; + + void OnVolumeChanged(float new_volume) noexcept { + volume = std::lround(new_volume * 100.f); + + listener.OnMixerVolumeChanged(*this, volume); + } + + /* virtual methods from class Mixer */ + void Open() override { + } + + void Close() noexcept override { + } + + int GetVolume() override; + void SetVolume(unsigned volume) override; +}; + +void +pipewire_mixer_on_change(PipeWireMixer &pm, float new_volume) noexcept +{ + pm.OnVolumeChanged(new_volume); +} + +int +PipeWireMixer::GetVolume() +{ + return volume; +} + +void +PipeWireMixer::SetVolume(unsigned new_volume) +{ + pipewire_output_set_volume(output, float(new_volume) * 0.01f); + volume = new_volume; +} + +static Mixer * +pipewire_mixer_init([[maybe_unused]] EventLoop &event_loop, AudioOutput &ao, + MixerListener &listener, + const ConfigBlock &) +{ + auto &po = (PipeWireOutput &)ao; + return new PipeWireMixer(po, listener); +} + +const MixerPlugin pipewire_mixer_plugin = { + pipewire_mixer_init, + true, +}; diff --git a/src/mixer/plugins/PipeWireMixerPlugin.hxx b/src/mixer/plugins/PipeWireMixerPlugin.hxx new file mode 100644 index 000000000..40c3af836 --- /dev/null +++ b/src/mixer/plugins/PipeWireMixerPlugin.hxx @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2021 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_PIPEWIRE_MIXER_PLUGIN_HXX +#define MPD_PIPEWIRE_MIXER_PLUGIN_HXX + +struct MixerPlugin; +class PipeWireMixer; + +extern const MixerPlugin pipewire_mixer_plugin; + +void +pipewire_mixer_on_change(PipeWireMixer &pm, float new_volume) noexcept; + +#endif diff --git a/src/mixer/plugins/meson.build b/src/mixer/plugins/meson.build index 6c23d6af0..7189ed2d8 100644 --- a/src/mixer/plugins/meson.build +++ b/src/mixer/plugins/meson.build @@ -22,6 +22,10 @@ if is_darwin mixer_plugins_sources += 'OSXMixerPlugin.cxx' endif +if pipewire_dep.found() + mixer_plugins_sources += 'PipeWireMixerPlugin.cxx' +endif + if pulse_dep.found() mixer_plugins_sources += 'PulseMixerPlugin.cxx' endif diff --git a/src/output/plugins/PipeWireOutputPlugin.cxx b/src/output/plugins/PipeWireOutputPlugin.cxx index 957bcbaf9..d71505e6a 100644 --- a/src/output/plugins/PipeWireOutputPlugin.cxx +++ b/src/output/plugins/PipeWireOutputPlugin.cxx @@ -21,6 +21,7 @@ #include "lib/pipewire/ThreadLoop.hxx" #include "../OutputAPI.hxx" #include "../Error.hxx" +#include "mixer/plugins/PipeWireMixerPlugin.hxx" #ifdef __GNUC__ #pragma GCC diagnostic push @@ -32,6 +33,7 @@ #include <pipewire/pipewire.h> #include <spa/param/audio/format-utils.h> +#include <spa/param/props.h> #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -56,7 +58,18 @@ class PipeWireOutput final : AudioOutput { const uint32_t target_id; + float volume = 1.0; + bool disconnected; + + /** + * Shall the previously known volume be restored as soon as + * PW_STREAM_STATE_STREAMING is reached? This needs to be + * done each time after the pw_stream got created, thus this + * flag gets set by Open(). + */ + bool restore_volume; + bool interrupted; bool paused; bool drained; @@ -80,6 +93,8 @@ public: return events; } + void SetVolume(float volume); + private: void CheckThrowError() { if (disconnected) @@ -148,6 +163,20 @@ PipeWireOutput::PipeWireOutput(const ConfigBlock &block) } void +PipeWireOutput::SetVolume(float _volume) +{ + const PipeWire::ThreadLoopLock lock(thread_loop); + + if (stream != nullptr && !restore_volume && + pw_stream_set_control(stream, + SPA_PROP_volume, 1, &_volume, + 0) != 0) + throw std::runtime_error("pw_stream_set_control() failed"); + + volume = _volume; +} + +void PipeWireOutput::Enable() { thread_loop = pw_thread_loop_new(name, nullptr); @@ -282,6 +311,7 @@ void PipeWireOutput::Open(AudioFormat &audio_format) { disconnected = false; + restore_volume = true; paused = false; drained = true; @@ -292,6 +322,8 @@ PipeWireOutput::Open(AudioFormat &audio_format) PW_KEY_NODE_NAME, "mpd", nullptr); + const PipeWire::ThreadLoopLock lock(thread_loop); + stream = pw_stream_new_simple(pw_thread_loop_get_loop(thread_loop), "mpd", props, @@ -317,7 +349,6 @@ PipeWireOutput::Open(AudioFormat &audio_format) params[0] = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_EnumFormat, &raw); - const PipeWire::ThreadLoopLock lock(thread_loop); pw_stream_connect(stream, PW_DIRECTION_OUTPUT, target_id, @@ -333,6 +364,7 @@ PipeWireOutput::Close() noexcept { const PipeWire::ThreadLoopLock lock(thread_loop); pw_stream_destroy(stream); + stream = nullptr; } delete ring_buffer; @@ -347,6 +379,15 @@ PipeWireOutput::StateChanged(enum pw_stream_state state, state == PW_STREAM_STATE_UNCONNECTED; if (!was_disconnected && disconnected) pw_thread_loop_signal(thread_loop, false); + + if (state == PW_STREAM_STATE_STREAMING && restore_volume) { + /* restore the last known volume after creating a new + pw_stream */ + restore_volume = false; + pw_stream_set_control(stream, + SPA_PROP_volume, 1, &volume, + 0); + } } inline void @@ -442,5 +483,11 @@ const struct AudioOutputPlugin pipewire_output_plugin = { "pipewire", nullptr, &PipeWireOutput::Create, - nullptr, + &pipewire_mixer_plugin, }; + +void +pipewire_output_set_volume(PipeWireOutput &output, float volume) +{ + output.SetVolume(volume); +} diff --git a/src/output/plugins/PipeWireOutputPlugin.hxx b/src/output/plugins/PipeWireOutputPlugin.hxx index cf942ec64..9e8cd2c23 100644 --- a/src/output/plugins/PipeWireOutputPlugin.hxx +++ b/src/output/plugins/PipeWireOutputPlugin.hxx @@ -20,6 +20,11 @@ #ifndef MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX #define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX +class PipeWireOutput; + extern const struct AudioOutputPlugin pipewire_output_plugin; +void +pipewire_output_set_volume(PipeWireOutput &output, float volume); + #endif |