mixer/pipewire: new plugin
This commit is contained in:
		
							
								
								
									
										91
									
								
								src/mixer/plugins/PipeWireMixerPlugin.cxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/mixer/plugins/PipeWireMixerPlugin.cxx
									
									
									
									
									
										Normal file
									
								
							| @@ -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, | ||||||
|  | }; | ||||||
							
								
								
									
										31
									
								
								src/mixer/plugins/PipeWireMixerPlugin.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/mixer/plugins/PipeWireMixerPlugin.hxx
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -22,6 +22,10 @@ if is_darwin | |||||||
|   mixer_plugins_sources += 'OSXMixerPlugin.cxx' |   mixer_plugins_sources += 'OSXMixerPlugin.cxx' | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | if pipewire_dep.found() | ||||||
|  |   mixer_plugins_sources += 'PipeWireMixerPlugin.cxx' | ||||||
|  | endif | ||||||
|  |  | ||||||
| if pulse_dep.found() | if pulse_dep.found() | ||||||
|   mixer_plugins_sources += 'PulseMixerPlugin.cxx' |   mixer_plugins_sources += 'PulseMixerPlugin.cxx' | ||||||
| endif | endif | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
| #include "lib/pipewire/ThreadLoop.hxx" | #include "lib/pipewire/ThreadLoop.hxx" | ||||||
| #include "../OutputAPI.hxx" | #include "../OutputAPI.hxx" | ||||||
| #include "../Error.hxx" | #include "../Error.hxx" | ||||||
|  | #include "mixer/plugins/PipeWireMixerPlugin.hxx" | ||||||
|  |  | ||||||
| #ifdef __GNUC__ | #ifdef __GNUC__ | ||||||
| #pragma GCC diagnostic push | #pragma GCC diagnostic push | ||||||
| @@ -32,6 +33,7 @@ | |||||||
|  |  | ||||||
| #include <pipewire/pipewire.h> | #include <pipewire/pipewire.h> | ||||||
| #include <spa/param/audio/format-utils.h> | #include <spa/param/audio/format-utils.h> | ||||||
|  | #include <spa/param/props.h> | ||||||
|  |  | ||||||
| #ifdef __GNUC__ | #ifdef __GNUC__ | ||||||
| #pragma GCC diagnostic pop | #pragma GCC diagnostic pop | ||||||
| @@ -56,7 +58,18 @@ class PipeWireOutput final : AudioOutput { | |||||||
|  |  | ||||||
| 	const uint32_t target_id; | 	const uint32_t target_id; | ||||||
|  |  | ||||||
|  | 	float volume = 1.0; | ||||||
|  |  | ||||||
| 	bool disconnected; | 	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 interrupted; | ||||||
| 	bool paused; | 	bool paused; | ||||||
| 	bool drained; | 	bool drained; | ||||||
| @@ -80,6 +93,8 @@ public: | |||||||
| 		return events; | 		return events; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	void SetVolume(float volume); | ||||||
|  |  | ||||||
| private: | private: | ||||||
| 	void CheckThrowError() { | 	void CheckThrowError() { | ||||||
| 		if (disconnected) | 		if (disconnected) | ||||||
| @@ -147,6 +162,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 | void | ||||||
| PipeWireOutput::Enable() | PipeWireOutput::Enable() | ||||||
| { | { | ||||||
| @@ -282,6 +311,7 @@ void | |||||||
| PipeWireOutput::Open(AudioFormat &audio_format) | PipeWireOutput::Open(AudioFormat &audio_format) | ||||||
| { | { | ||||||
| 	disconnected = false; | 	disconnected = false; | ||||||
|  | 	restore_volume = true; | ||||||
| 	paused = false; | 	paused = false; | ||||||
| 	drained = true; | 	drained = true; | ||||||
|  |  | ||||||
| @@ -292,6 +322,8 @@ PipeWireOutput::Open(AudioFormat &audio_format) | |||||||
| 				       PW_KEY_NODE_NAME, "mpd", | 				       PW_KEY_NODE_NAME, "mpd", | ||||||
| 				       nullptr); | 				       nullptr); | ||||||
|  |  | ||||||
|  | 	const PipeWire::ThreadLoopLock lock(thread_loop); | ||||||
|  |  | ||||||
| 	stream = pw_stream_new_simple(pw_thread_loop_get_loop(thread_loop), | 	stream = pw_stream_new_simple(pw_thread_loop_get_loop(thread_loop), | ||||||
| 				      "mpd", | 				      "mpd", | ||||||
| 				      props, | 				      props, | ||||||
| @@ -317,7 +349,6 @@ PipeWireOutput::Open(AudioFormat &audio_format) | |||||||
| 	params[0] = spa_format_audio_raw_build(&pod_builder, | 	params[0] = spa_format_audio_raw_build(&pod_builder, | ||||||
| 					       SPA_PARAM_EnumFormat, &raw); | 					       SPA_PARAM_EnumFormat, &raw); | ||||||
|  |  | ||||||
| 	const PipeWire::ThreadLoopLock lock(thread_loop); |  | ||||||
| 	pw_stream_connect(stream, | 	pw_stream_connect(stream, | ||||||
| 			  PW_DIRECTION_OUTPUT, | 			  PW_DIRECTION_OUTPUT, | ||||||
| 			  target_id, | 			  target_id, | ||||||
| @@ -333,6 +364,7 @@ PipeWireOutput::Close() noexcept | |||||||
| 	{ | 	{ | ||||||
| 		const PipeWire::ThreadLoopLock lock(thread_loop); | 		const PipeWire::ThreadLoopLock lock(thread_loop); | ||||||
| 		pw_stream_destroy(stream); | 		pw_stream_destroy(stream); | ||||||
|  | 		stream = nullptr; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	delete ring_buffer; | 	delete ring_buffer; | ||||||
| @@ -347,6 +379,15 @@ PipeWireOutput::StateChanged(enum pw_stream_state state, | |||||||
| 		state == PW_STREAM_STATE_UNCONNECTED; | 		state == PW_STREAM_STATE_UNCONNECTED; | ||||||
| 	if (!was_disconnected && disconnected) | 	if (!was_disconnected && disconnected) | ||||||
| 		pw_thread_loop_signal(thread_loop, false); | 		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 | inline void | ||||||
| @@ -442,5 +483,11 @@ const struct AudioOutputPlugin pipewire_output_plugin = { | |||||||
| 	"pipewire", | 	"pipewire", | ||||||
| 	nullptr, | 	nullptr, | ||||||
| 	&PipeWireOutput::Create, | 	&PipeWireOutput::Create, | ||||||
| 	nullptr, | 	&pipewire_mixer_plugin, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | void | ||||||
|  | pipewire_output_set_volume(PipeWireOutput &output, float volume) | ||||||
|  | { | ||||||
|  | 	output.SetVolume(volume); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,6 +20,11 @@ | |||||||
| #ifndef MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX | #ifndef MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX | ||||||
| #define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX | #define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX | ||||||
|  |  | ||||||
|  | class PipeWireOutput; | ||||||
|  |  | ||||||
| extern const struct AudioOutputPlugin pipewire_output_plugin; | extern const struct AudioOutputPlugin pipewire_output_plugin; | ||||||
|  |  | ||||||
|  | void | ||||||
|  | pipewire_output_set_volume(PipeWireOutput &output, float volume); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann