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' | ||||
| endif | ||||
|  | ||||
| if pipewire_dep.found() | ||||
|   mixer_plugins_sources += 'PipeWireMixerPlugin.cxx' | ||||
| endif | ||||
|  | ||||
| if pulse_dep.found() | ||||
|   mixer_plugins_sources += 'PulseMixerPlugin.cxx' | ||||
| endif | ||||
|   | ||||
| @@ -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) | ||||
| @@ -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 | ||||
| PipeWireOutput::Enable() | ||||
| { | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann