MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PipewireBackend.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "AudioBackend.hpp"
4
5#ifdef PIPEWIRE_BACKEND
6#include <pipewire/pipewire.h>
7#include <spa/param/audio/format-utils.h>
8#include <spa/param/props.h>
9#endif
10
11namespace MayaFlux::Core {
12
13class PipewireDevice;
14
15/**
16 * @class PipewireBackend
17 * @brief PipeWire native implementation of the audio backend interface
18 *
19 * Owns the pw_main_loop, pw_context, and pw_core for the process lifetime.
20 * Device enumeration uses the PipeWire registry to discover Audio/Sink
21 * and Audio/Source nodes. Stream creation produces a PipewireStream that
22 * wraps a pw_stream follower node and translates PipeWire buffer delivery
23 * into the engine's (void*, void*, uint32_t) callback contract.
24 */
26public:
28 ~PipewireBackend() override;
29
30 std::unique_ptr<AudioDevice> create_device_manager() override;
31
32 std::unique_ptr<AudioStream> create_stream(
33 unsigned int output_node_id,
34 unsigned int input_node_id,
35 GlobalStreamInfo& stream_info,
36 void* user_data) override;
37
38 [[nodiscard]] std::string get_version_string() const override;
39
40 void cleanup() override;
41
42#ifdef PIPEWIRE_BACKEND
43 [[nodiscard]] pw_main_loop* loop() const { return m_loop; }
44 [[nodiscard]] pw_context* context() const { return m_context; }
45 [[nodiscard]] pw_core* core() const { return m_core; }
46#endif
47
48private:
49#ifdef PIPEWIRE_BACKEND
50 pw_main_loop* m_loop = nullptr;
51 pw_context* m_context = nullptr;
52 pw_core* m_core = nullptr;
53 PipewireDevice* m_device_manager = nullptr;
54#endif
55};
56
57/**
58 * @class PipewireDevice
59 * @brief PipeWire implementation of the audio device interface
60 *
61 * Enumerates Audio/Sink and Audio/Source nodes from the PipeWire registry
62 * by running a synchronous roundtrip on the main loop.
63 * Default device selection uses priority.session from node properties,
64 * matching WirePlumber's default sink/source selection.
65 *
66 * Device IDs returned are PipeWire global object IDs (uint32_t). These
67 * are passed opaquely through the unsigned int interface and consumed
68 * by PipewireStream::open() via pw_stream_connect target object ID.
69 */
71public:
72#ifdef PIPEWIRE_BACKEND
73 explicit PipewireDevice(pw_main_loop* loop, pw_context* ctx, pw_core* core);
74#endif
75
76 [[nodiscard]] std::vector<DeviceInfo> get_output_devices() const override;
77 [[nodiscard]] std::vector<DeviceInfo> get_input_devices() const override;
78 [[nodiscard]] unsigned int get_default_output_device() const override;
79 [[nodiscard]] unsigned int get_default_input_device() const override;
80 [[nodiscard]] uint32_t find_output_node_id(std::string_view name) const;
81 [[nodiscard]] uint32_t find_input_node_id(std::string_view name) const;
82
83public:
84 struct NodeEntry {
85 uint32_t id = 0;
87 bool is_output = false;
88 uint32_t priority_session = 0;
89 };
90
91private:
92 std::vector<NodeEntry> m_nodes;
93 uint32_t m_default_output_id = 0;
94 uint32_t m_default_input_id = 0;
95
96#ifdef PIPEWIRE_BACKEND
97 void enumerate(pw_main_loop* loop, pw_context* ctx, pw_core* core);
98#endif
99};
100
101/**
102 * @class PipewireStream
103 * @brief PipeWire implementation of the audio stream interface
104 *
105 * Wraps a pw_stream follower node. The stream proposes SPA_AUDIO_FORMAT_F64
106 * interleaved during format negotiation, matching the engine's internal
107 * double representation and eliminating format conversion in the hot path.
108 *
109 * On param_changed the negotiated frame count is stored and used to validate
110 * the buffer size contract. If PipeWire adjusts the quantum away from the
111 * requested value, a warning is logged and m_stream_info.buffer_size is
112 * updated to match, consistent with the RtAudio backend's existing behaviour.
113 *
114 * The pw_thread_loop drives the PipeWire event loop on a dedicated thread.
115 * The process callback runs on that thread at SCHED_FIFO priority when
116 * rlimits are configured by the package installer. No locking occurs on
117 * the audio path; the process callback only reads atomic state and invokes
118 * the registered std::function.
119 */
121public:
122#ifdef PIPEWIRE_BACKEND
124 uint32_t output_node_id,
125 uint32_t input_node_id,
126 GlobalStreamInfo& stream_info,
127 void* user_data);
128
129 [[nodiscard]] pw_time get_stream_time() const;
130#endif
131
132 ~PipewireStream() override;
133
134 void open() override;
135 void start() override;
136 void stop() override;
137 void pause() override;
138 void resume() override;
139 void close() override;
140
141 [[nodiscard]] bool is_running() const override;
142 [[nodiscard]] bool is_open() const override;
143
145 std::function<int(void*, void*, unsigned int)> cb) override;
146
147private:
148#ifdef PIPEWIRE_BACKEND
149 static void on_output_process(void* userdata);
150 static void on_input_process(void* userdata);
151
152 static void on_param_changed(void* userdata, uint32_t id, const struct spa_pod* param);
153 static void on_state_changed(
154 void* userdata,
155 enum pw_stream_state old_state,
156 enum pw_stream_state new_state,
157 const char* error);
158
159 static void on_io_changed(void* userdata, uint32_t id, void* area, uint32_t size);
160
161 void build_output_format_params(uint8_t* buf, uint32_t buf_size,
162 const struct spa_pod** params, uint32_t& n_params);
163
164 void build_input_format_params(uint8_t* buf, uint32_t buf_size,
165 const struct spa_pod** params, uint32_t& n_params);
166
167 pw_thread_loop* m_thread_loop = nullptr;
168
169 pw_stream* m_output_stream = nullptr;
170 pw_stream* m_input_stream = nullptr;
171
172 pw_stream_events m_output_stream_events {};
173 pw_stream_events m_input_stream_events {};
174
175 uint32_t m_output_node_id;
176 uint32_t m_input_node_id;
177
178 spa_io_rate_match* m_rate_match = nullptr;
179
180 std::vector<double> m_input_staging;
181 std::atomic<bool> m_input_ready { false };
182
183 /** @brief Negotiated quantum; updated from param_changed before first process call */
184 std::atomic<uint32_t> m_negotiated_frames { 0 };
185
186#endif
187
190
191 std::function<int(void*, void*, unsigned int)> m_process_callback;
192
193 std::atomic<bool> m_is_open { false };
194 std::atomic<bool> m_is_running { false };
195 std::atomic<bool> m_is_paused { false };
196};
197
198} // namespace MayaFlux::Core
Manages audio endpoint discovery and enumeration.
Manages digital audio data flow between the engine and hardware.
Interface for audio system abstraction layer.
void cleanup() override
Releases all resources held by the backend.
std::unique_ptr< AudioStream > create_stream(unsigned int output_node_id, unsigned int input_node_id, GlobalStreamInfo &stream_info, void *user_data) override
Creates an audio stream for a specific device.
std::unique_ptr< AudioDevice > create_device_manager() override
Creates a device manager for audio endpoint discovery.
std::string get_version_string() const override
Retrieves the backend implementation version.
PipeWire native implementation of the audio backend interface.
unsigned int get_default_input_device() const override
Gets the system's primary input device identifier.
std::vector< NodeEntry > m_nodes
std::vector< DeviceInfo > get_output_devices() const override
Retrieves information about all available output devices.
std::vector< DeviceInfo > get_input_devices() const override
Retrieves information about all available input devices.
unsigned int get_default_output_device() const override
Gets the system's primary output device identifier.
uint32_t find_input_node_id(std::string_view name) const
uint32_t find_output_node_id(std::string_view name) const
PipeWire implementation of the audio device interface.
bool is_running() const override
Checks if the stream is actively processing audio data.
void resume() override
Resumes audio processing after a pause.
void close() override
Terminates the audio stream and releases all resources.
bool is_open() const override
Checks if the stream is initialized and ready for activation.
std::function< int(void *, void *, unsigned int)> m_process_callback
void open() override
Initializes the audio stream and allocates required resources.
void set_process_callback(std::function< int(void *, void *, unsigned int)> cb) override
Sets the function to process audio data.
void stop() override
Deactivates the audio stream and halts data transfer.
void pause() override
Temporarily suspends audio processing without releasing resources.
void start() override
Activates the audio stream and begins data transfer.
PipeWire implementation of the audio stream interface.
Contains digital audio device configuration parameters.
Comprehensive configuration for digital audio stream processing.