summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mixer/plugins/PipeWireMixerPlugin.cxx91
-rw-r--r--src/mixer/plugins/PipeWireMixerPlugin.hxx31
-rw-r--r--src/mixer/plugins/meson.build4
-rw-r--r--src/output/plugins/PipeWireOutputPlugin.cxx51
-rw-r--r--src/output/plugins/PipeWireOutputPlugin.hxx5
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