MayaFlux 1.0.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 bool m_pipewire_initialized = false;
55#endif
56};
57
58/**
59 * @class PipewireDevice
60 * @brief PipeWire implementation of the audio device interface
61 *
62 * Enumerates Audio/Sink and Audio/Source nodes from the PipeWire registry
63 * by running a synchronous roundtrip on the main loop.
64 * Default device selection uses priority.session from node properties,
65 * matching WirePlumber's default sink/source selection.
66 *
67 * Device IDs returned are PipeWire global object IDs (uint32_t). These
68 * are passed opaquely through the unsigned int interface and consumed
69 * by PipewireStream::open() via pw_stream_connect target object ID.
70 */
72public:
73#ifdef PIPEWIRE_BACKEND
74 explicit PipewireDevice(pw_main_loop* loop, pw_context* ctx, pw_core* core);
75#endif
76
77 [[nodiscard]] std::vector<DeviceInfo> get_output_devices() const override;
78 [[nodiscard]] std::vector<DeviceInfo> get_input_devices() const override;
79 [[nodiscard]] unsigned int get_default_output_device() const override;
80 [[nodiscard]] unsigned int get_default_input_device() const override;
81 [[nodiscard]] uint32_t find_output_node_id(std::string_view name) const;
82 [[nodiscard]] uint32_t find_input_node_id(std::string_view name) const;
83
84public:
85 struct NodeEntry {
86 uint32_t id = 0;
88 bool is_output = false;
89 uint32_t priority_session = 0;
90 };
91
92private:
93 std::vector<NodeEntry> m_nodes;
94 uint32_t m_default_output_id = 0;
95 uint32_t m_default_input_id = 0;
96
97#ifdef PIPEWIRE_BACKEND
98 void enumerate(pw_main_loop* loop, pw_context* ctx, pw_core* core);
99#endif
100};
101
102/**
103 * @class PipewireStream
104 * @brief PipeWire implementation of the audio stream interface
105 *
106 * Wraps a pw_stream follower node. The stream proposes SPA_AUDIO_FORMAT_F64
107 * interleaved during format negotiation, matching the engine's internal
108 * double representation and eliminating format conversion in the hot path.
109 *
110 * On param_changed the negotiated frame count is stored and used to validate
111 * the buffer size contract. If PipeWire adjusts the quantum away from the
112 * requested value, a warning is logged and m_stream_info.buffer_size is
113 * updated to match, consistent with the RtAudio backend's existing behaviour.
114 *
115 * The pw_thread_loop drives the PipeWire event loop on a dedicated thread.
116 * The process callback runs on that thread at SCHED_FIFO priority when
117 * rlimits are configured by the package installer. No locking occurs on
118 * the audio path; the process callback only reads atomic state and invokes
119 * the registered std::function.
120 */
122public:
123#ifdef PIPEWIRE_BACKEND
125 uint32_t output_node_id,
126 uint32_t input_node_id,
127 GlobalStreamInfo& stream_info,
128 void* user_data);
129
130 [[nodiscard]] pw_time get_stream_time() const;
131#endif
132
133 ~PipewireStream() override;
134
135 void open() override;
136 void start() override;
137 void stop() override;
138 void pause() override;
139 void resume() override;
140 void close() override;
141
142 [[nodiscard]] bool is_running() const override;
143 [[nodiscard]] bool is_open() const override;
144
146 std::function<int(void*, void*, unsigned int)> cb) override;
147
148private:
149#ifdef PIPEWIRE_BACKEND
150 static void on_output_process(void* userdata);
151 static void on_input_process(void* userdata);
152
153 static void on_param_changed(void* userdata, uint32_t id, const struct spa_pod* param);
154 static void on_state_changed(
155 void* userdata,
156 enum pw_stream_state old_state,
157 enum pw_stream_state new_state,
158 const char* error);
159
160 static void on_io_changed(void* userdata, uint32_t id, void* area, uint32_t size);
161
162 void build_output_format_params(uint8_t* buf, uint32_t buf_size,
163 const struct spa_pod** params, uint32_t& n_params);
164
165 void build_input_format_params(uint8_t* buf, uint32_t buf_size,
166 const struct spa_pod** params, uint32_t& n_params);
167
168 pw_thread_loop* m_thread_loop = nullptr;
169
170 pw_stream* m_output_stream = nullptr;
171 pw_stream* m_input_stream = nullptr;
172
173 pw_stream_events m_output_stream_events {};
174 pw_stream_events m_input_stream_events {};
175
176 uint32_t m_output_node_id;
177 uint32_t m_input_node_id;
178
179 spa_io_rate_match* m_rate_match = nullptr;
180
181 std::vector<double> m_input_staging;
182 std::atomic<bool> m_input_ready { false };
183
184 /** @brief Negotiated quantum; updated from param_changed before first process call */
185 std::atomic<uint32_t> m_negotiated_frames { 0 };
186
187#endif
188
191
192 std::function<int(void*, void*, unsigned int)> m_process_callback;
193
194 std::atomic<bool> m_is_open { false };
195 std::atomic<bool> m_is_running { false };
196 std::atomic<bool> m_is_paused { false };
197};
198
199} // namespace MayaFlux::Core
bool size
std::string name
Definition VKDevice.cpp:123
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.