More agressively hide PipeWire and SDL header warnings
[openal-soft.git] / alc / backends / pipewire.cpp
blobb573bd5a8a0748f0a3f56180472dbad15ec1e0c2
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2010 by Chris Robinson
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "pipewire.h"
25 #include <algorithm>
26 #include <atomic>
27 #include <cstring>
28 #include <cerrno>
29 #include <chrono>
30 #include <ctime>
31 #include <list>
32 #include <memory>
33 #include <mutex>
34 #include <stdint.h>
35 #include <type_traits>
36 #include <utility>
38 #include "albyte.h"
39 #include "alc/alconfig.h"
40 #include "almalloc.h"
41 #include "alnumeric.h"
42 #include "aloptional.h"
43 #include "alspan.h"
44 #include "alstring.h"
45 #include "core/devformat.h"
46 #include "core/device.h"
47 #include "core/helpers.h"
48 #include "core/logging.h"
49 #include "dynload.h"
50 #include "opthelpers.h"
51 #include "ringbuffer.h"
53 /* Ignore warnings caused by PipeWire headers (lots in standard C++ mode). GCC
54 * doesn't support ignoring -Weverything, so we have the list the individual
55 * warnings to ignore (and ignoring -Winline doesn't seem to work).
57 _Pragma("GCC diagnostic push")
58 _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
59 _Pragma("GCC diagnostic ignored \"-Wconversion\"")
60 _Pragma("GCC diagnostic ignored \"-Wfloat-conversion\"")
61 _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
62 _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
63 _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
64 _Pragma("GCC diagnostic ignored \"-Wsign-compare\"")
65 _Pragma("GCC diagnostic ignored \"-Winline\"")
66 _Pragma("GCC diagnostic ignored \"-Wpragmas\"")
67 _Pragma("GCC diagnostic ignored \"-Weverything\"")
68 #include "pipewire/pipewire.h"
69 #include "pipewire/extensions/metadata.h"
70 #include "spa/buffer/buffer.h"
71 #include "spa/param/audio/format-utils.h"
72 #include "spa/param/audio/raw.h"
73 #include "spa/param/param.h"
74 #include "spa/pod/builder.h"
75 #include "spa/utils/json.h"
77 namespace {
78 /* Wrap some nasty macros here too... */
79 template<typename ...Args>
80 auto ppw_core_add_listener(pw_core *core, Args&& ...args)
81 { return pw_core_add_listener(core, std::forward<Args>(args)...); }
82 template<typename ...Args>
83 auto ppw_core_sync(pw_core *core, Args&& ...args)
84 { return pw_core_sync(core, std::forward<Args>(args)...); }
85 template<typename ...Args>
86 auto ppw_registry_add_listener(pw_registry *reg, Args&& ...args)
87 { return pw_registry_add_listener(reg, std::forward<Args>(args)...); }
88 template<typename ...Args>
89 auto ppw_node_add_listener(pw_node *node, Args&& ...args)
90 { return pw_node_add_listener(node, std::forward<Args>(args)...); }
91 template<typename ...Args>
92 auto ppw_node_subscribe_params(pw_node *node, Args&& ...args)
93 { return pw_node_subscribe_params(node, std::forward<Args>(args)...); }
94 template<typename ...Args>
95 auto ppw_metadata_add_listener(pw_metadata *mdata, Args&& ...args)
96 { return pw_metadata_add_listener(mdata, std::forward<Args>(args)...); }
99 constexpr auto get_pod_type(const spa_pod *pod) noexcept
100 { return SPA_POD_TYPE(pod); }
102 template<typename T>
103 constexpr auto get_pod_body(const spa_pod *pod, size_t count) noexcept
104 { return al::span<T>{static_cast<T*>(SPA_POD_BODY(pod)), count}; }
105 template<typename T, size_t N>
106 constexpr auto get_pod_body(const spa_pod *pod) noexcept
107 { return al::span<T,N>{static_cast<T*>(SPA_POD_BODY(pod)), N}; }
109 constexpr auto make_pod_builder(void *data, uint32_t size) noexcept
110 { return SPA_POD_BUILDER_INIT(data, size); }
112 constexpr auto get_array_value_type(const spa_pod *pod) noexcept
113 { return SPA_POD_ARRAY_VALUE_TYPE(pod); }
115 constexpr auto PwIdAny = PW_ID_ANY;
117 } // namespace
118 _Pragma("GCC diagnostic pop")
120 namespace {
122 using std::chrono::seconds;
123 using std::chrono::nanoseconds;
124 using uint = unsigned int;
126 constexpr char pwireDevice[] = "PipeWire Output";
127 constexpr char pwireInput[] = "PipeWire Input";
130 bool check_version(const char *version)
132 /* There doesn't seem to be a function to get the version as an integer, so
133 * instead we have to parse the string, which hopefully won't break in the
134 * future.
136 int major{0}, minor{0}, revision{0};
137 int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)};
138 if(ret == 3 && PW_CHECK_VERSION(major, minor, revision))
139 return true;
140 return false;
143 #ifdef HAVE_DYNLOAD
144 #define PWIRE_FUNCS(MAGIC) \
145 MAGIC(pw_context_connect) \
146 MAGIC(pw_context_destroy) \
147 MAGIC(pw_context_new) \
148 MAGIC(pw_core_disconnect) \
149 MAGIC(pw_get_library_version) \
150 MAGIC(pw_init) \
151 MAGIC(pw_properties_free) \
152 MAGIC(pw_properties_new) \
153 MAGIC(pw_properties_set) \
154 MAGIC(pw_properties_setf) \
155 MAGIC(pw_proxy_add_object_listener) \
156 MAGIC(pw_proxy_destroy) \
157 MAGIC(pw_proxy_get_user_data) \
158 MAGIC(pw_stream_add_listener) \
159 MAGIC(pw_stream_connect) \
160 MAGIC(pw_stream_dequeue_buffer) \
161 MAGIC(pw_stream_destroy) \
162 MAGIC(pw_stream_get_state) \
163 MAGIC(pw_stream_new) \
164 MAGIC(pw_stream_queue_buffer) \
165 MAGIC(pw_stream_set_active) \
166 MAGIC(pw_thread_loop_new) \
167 MAGIC(pw_thread_loop_destroy) \
168 MAGIC(pw_thread_loop_get_loop) \
169 MAGIC(pw_thread_loop_start) \
170 MAGIC(pw_thread_loop_stop) \
171 MAGIC(pw_thread_loop_lock) \
172 MAGIC(pw_thread_loop_wait) \
173 MAGIC(pw_thread_loop_signal) \
174 MAGIC(pw_thread_loop_unlock)
175 #if PW_CHECK_VERSION(0,3,50)
176 #define PWIRE_FUNCS2(MAGIC) \
177 MAGIC(pw_stream_get_time_n)
178 #else
179 #define PWIRE_FUNCS2(MAGIC) \
180 MAGIC(pw_stream_get_time)
181 #endif
183 void *pwire_handle;
184 #define MAKE_FUNC(f) decltype(f) * p##f;
185 PWIRE_FUNCS(MAKE_FUNC)
186 PWIRE_FUNCS2(MAKE_FUNC)
187 #undef MAKE_FUNC
189 bool pwire_load()
191 if(pwire_handle)
192 return true;
194 static constexpr char pwire_library[] = "libpipewire-0.3.so.0";
195 std::string missing_funcs;
197 pwire_handle = LoadLib(pwire_library);
198 if(!pwire_handle)
200 WARN("Failed to load %s\n", pwire_library);
201 return false;
204 #define LOAD_FUNC(f) do { \
205 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \
206 if(p##f == nullptr) missing_funcs += "\n" #f; \
207 } while(0);
208 PWIRE_FUNCS(LOAD_FUNC)
209 PWIRE_FUNCS2(LOAD_FUNC)
210 #undef LOAD_FUNC
212 if(!missing_funcs.empty())
214 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
215 CloseLib(pwire_handle);
216 pwire_handle = nullptr;
217 return false;
220 return true;
223 #ifndef IN_IDE_PARSER
224 #define pw_context_connect ppw_context_connect
225 #define pw_context_destroy ppw_context_destroy
226 #define pw_context_new ppw_context_new
227 #define pw_core_disconnect ppw_core_disconnect
228 #define pw_get_library_version ppw_get_library_version
229 #define pw_init ppw_init
230 #define pw_properties_free ppw_properties_free
231 #define pw_properties_new ppw_properties_new
232 #define pw_properties_set ppw_properties_set
233 #define pw_properties_setf ppw_properties_setf
234 #define pw_proxy_add_object_listener ppw_proxy_add_object_listener
235 #define pw_proxy_destroy ppw_proxy_destroy
236 #define pw_proxy_get_user_data ppw_proxy_get_user_data
237 #define pw_stream_add_listener ppw_stream_add_listener
238 #define pw_stream_connect ppw_stream_connect
239 #define pw_stream_dequeue_buffer ppw_stream_dequeue_buffer
240 #define pw_stream_destroy ppw_stream_destroy
241 #define pw_stream_get_state ppw_stream_get_state
242 #define pw_stream_new ppw_stream_new
243 #define pw_stream_queue_buffer ppw_stream_queue_buffer
244 #define pw_stream_set_active ppw_stream_set_active
245 #define pw_thread_loop_destroy ppw_thread_loop_destroy
246 #define pw_thread_loop_get_loop ppw_thread_loop_get_loop
247 #define pw_thread_loop_lock ppw_thread_loop_lock
248 #define pw_thread_loop_new ppw_thread_loop_new
249 #define pw_thread_loop_signal ppw_thread_loop_signal
250 #define pw_thread_loop_start ppw_thread_loop_start
251 #define pw_thread_loop_stop ppw_thread_loop_stop
252 #define pw_thread_loop_unlock ppw_thread_loop_unlock
253 #define pw_thread_loop_wait ppw_thread_loop_wait
254 #if PW_CHECK_VERSION(0,3,50)
255 #define pw_stream_get_time_n ppw_stream_get_time_n
256 #else
257 inline auto pw_stream_get_time_n(pw_stream *stream, pw_time *ptime, size_t /*size*/)
258 { return ppw_stream_get_time(stream, ptime); }
259 #endif
260 #endif
262 #else
264 constexpr bool pwire_load() { return true; }
265 #endif
267 /* Helpers for retrieving values from params */
268 template<uint32_t T> struct PodInfo { };
270 template<>
271 struct PodInfo<SPA_TYPE_Int> {
272 using Type = int32_t;
273 static auto get_value(const spa_pod *pod, int32_t *val)
274 { return spa_pod_get_int(pod, val); }
276 template<>
277 struct PodInfo<SPA_TYPE_Id> {
278 using Type = uint32_t;
279 static auto get_value(const spa_pod *pod, uint32_t *val)
280 { return spa_pod_get_id(pod, val); }
283 template<uint32_t T>
284 using Pod_t = typename PodInfo<T>::Type;
286 template<uint32_t T>
287 al::span<const Pod_t<T>> get_array_span(const spa_pod *pod)
289 uint32_t nvals;
290 if(void *v{spa_pod_get_array(pod, &nvals)})
292 if(get_array_value_type(pod) == T)
293 return {static_cast<const Pod_t<T>*>(v), nvals};
295 return {};
298 template<uint32_t T>
299 al::optional<Pod_t<T>> get_value(const spa_pod *value)
301 Pod_t<T> val{};
302 if(PodInfo<T>::get_value(value, &val) == 0)
303 return val;
304 return al::nullopt;
307 /* Internally, PipeWire types "inherit" from each other, but this is hidden
308 * from the API and the caller is expected to C-style cast to inherited types
309 * as needed. It's also not made very clear what types a given type can be
310 * casted to. To make it a bit safer, this as() method allows casting pw_*
311 * types to known inherited types, generating a compile-time error for
312 * unexpected/invalid casts.
314 template<typename To, typename From>
315 To as(From) noexcept = delete;
317 /* pw_proxy
318 * - pw_registry
319 * - pw_node
320 * - pw_metadata
322 template<>
323 pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); }
324 template<>
325 pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); }
326 template<>
327 pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); }
330 struct PwContextDeleter {
331 void operator()(pw_context *context) const { pw_context_destroy(context); }
333 using PwContextPtr = std::unique_ptr<pw_context,PwContextDeleter>;
335 struct PwCoreDeleter {
336 void operator()(pw_core *core) const { pw_core_disconnect(core); }
338 using PwCorePtr = std::unique_ptr<pw_core,PwCoreDeleter>;
340 struct PwRegistryDeleter {
341 void operator()(pw_registry *reg) const { pw_proxy_destroy(as<pw_proxy*>(reg)); }
343 using PwRegistryPtr = std::unique_ptr<pw_registry,PwRegistryDeleter>;
345 struct PwNodeDeleter {
346 void operator()(pw_node *node) const { pw_proxy_destroy(as<pw_proxy*>(node)); }
348 using PwNodePtr = std::unique_ptr<pw_node,PwNodeDeleter>;
350 struct PwMetadataDeleter {
351 void operator()(pw_metadata *mdata) const { pw_proxy_destroy(as<pw_proxy*>(mdata)); }
353 using PwMetadataPtr = std::unique_ptr<pw_metadata,PwMetadataDeleter>;
355 struct PwStreamDeleter {
356 void operator()(pw_stream *stream) const { pw_stream_destroy(stream); }
358 using PwStreamPtr = std::unique_ptr<pw_stream,PwStreamDeleter>;
360 /* Enums for bitflags... again... *sigh* */
361 constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept
362 { return static_cast<pw_stream_flags>(lhs | std::underlying_type_t<pw_stream_flags>{rhs}); }
364 class ThreadMainloop {
365 pw_thread_loop *mLoop{};
367 public:
368 ThreadMainloop() = default;
369 ThreadMainloop(const ThreadMainloop&) = delete;
370 ThreadMainloop(ThreadMainloop&& rhs) noexcept : mLoop{rhs.mLoop} { rhs.mLoop = nullptr; }
371 explicit ThreadMainloop(pw_thread_loop *loop) noexcept : mLoop{loop} { }
372 ~ThreadMainloop() { if(mLoop) pw_thread_loop_destroy(mLoop); }
374 ThreadMainloop& operator=(const ThreadMainloop&) = delete;
375 ThreadMainloop& operator=(ThreadMainloop&& rhs) noexcept
376 { std::swap(mLoop, rhs.mLoop); return *this; }
377 ThreadMainloop& operator=(std::nullptr_t) noexcept
379 if(mLoop)
380 pw_thread_loop_destroy(mLoop);
381 mLoop = nullptr;
382 return *this;
385 explicit operator bool() const noexcept { return mLoop != nullptr; }
387 auto start() const { return pw_thread_loop_start(mLoop); }
388 auto stop() const { return pw_thread_loop_stop(mLoop); }
390 auto getLoop() const { return pw_thread_loop_get_loop(mLoop); }
392 auto lock() const { return pw_thread_loop_lock(mLoop); }
393 auto unlock() const { return pw_thread_loop_unlock(mLoop); }
395 auto signal(bool wait) const { return pw_thread_loop_signal(mLoop, wait); }
397 auto newContext(pw_properties *props=nullptr, size_t user_data_size=0)
398 { return PwContextPtr{pw_context_new(getLoop(), props, user_data_size)}; }
400 static auto Create(const char *name, spa_dict *props=nullptr)
401 { return ThreadMainloop{pw_thread_loop_new(name, props)}; }
403 friend struct MainloopUniqueLock;
405 struct MainloopUniqueLock : public std::unique_lock<ThreadMainloop> {
406 using std::unique_lock<ThreadMainloop>::unique_lock;
407 MainloopUniqueLock& operator=(MainloopUniqueLock&&) = default;
409 auto wait() const -> void
410 { pw_thread_loop_wait(mutex()->mLoop); }
412 template<typename Predicate>
413 auto wait(Predicate done_waiting) const -> void
414 { while(!done_waiting()) wait(); }
416 using MainloopLockGuard = std::lock_guard<ThreadMainloop>;
419 /* There's quite a mess here, but the purpose is to track active devices and
420 * their default formats, so playback devices can be configured to match. The
421 * device list is updated asynchronously, so it will have the latest list of
422 * devices provided by the server.
425 struct NodeProxy;
426 struct MetadataProxy;
428 /* The global thread watching for global events. This particular class responds
429 * to objects being added to or removed from the registry.
431 struct EventManager {
432 ThreadMainloop mLoop{};
433 PwContextPtr mContext{};
434 PwCorePtr mCore{};
435 PwRegistryPtr mRegistry{};
436 spa_hook mRegistryListener{};
437 spa_hook mCoreListener{};
439 /* A list of proxy objects watching for events about changes to objects in
440 * the registry.
442 std::vector<NodeProxy*> mNodeList;
443 MetadataProxy *mDefaultMetadata{nullptr};
445 /* Initialization handling. When init() is called, mInitSeq is set to a
446 * SequenceID that marks the end of populating the registry. As objects of
447 * interest are found, events to parse them are generated and mInitSeq is
448 * updated with a newer ID. When mInitSeq stops being updated and the event
449 * corresponding to it is reached, mInitDone will be set to true.
451 std::atomic<bool> mInitDone{false};
452 std::atomic<bool> mHasAudio{false};
453 int mInitSeq{};
455 bool init();
456 ~EventManager();
458 void kill();
460 auto lock() const { return mLoop.lock(); }
461 auto unlock() const { return mLoop.unlock(); }
464 * Waits for initialization to finish. The event manager must *NOT* be
465 * locked when calling this.
467 void waitForInit()
469 if(unlikely(!mInitDone.load(std::memory_order_acquire)))
471 MainloopUniqueLock plock{mLoop};
472 plock.wait([this](){ return mInitDone.load(std::memory_order_acquire); });
477 * Waits for audio support to be detected, or initialization to finish,
478 * whichever is first. Returns true if audio support was detected. The
479 * event manager must *NOT* be locked when calling this.
481 bool waitForAudio()
483 MainloopUniqueLock plock{mLoop};
484 bool has_audio{};
485 plock.wait([this,&has_audio]()
487 has_audio = mHasAudio.load(std::memory_order_acquire);
488 return has_audio || mInitDone.load(std::memory_order_acquire);
490 return has_audio;
493 void syncInit()
495 /* If initialization isn't done, update the sequence ID so it won't
496 * complete until after currently scheduled events.
498 if(!mInitDone.load(std::memory_order_relaxed))
499 mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, mInitSeq);
502 void addCallback(uint32_t id, uint32_t permissions, const char *type, uint32_t version,
503 const spa_dict *props);
504 static void addCallbackC(void *object, uint32_t id, uint32_t permissions, const char *type,
505 uint32_t version, const spa_dict *props)
506 { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); }
508 void removeCallback(uint32_t id);
509 static void removeCallbackC(void *object, uint32_t id)
510 { static_cast<EventManager*>(object)->removeCallback(id); }
512 static constexpr pw_registry_events CreateRegistryEvents()
514 pw_registry_events ret{};
515 ret.version = PW_VERSION_REGISTRY_EVENTS;
516 ret.global = &EventManager::addCallbackC;
517 ret.global_remove = &EventManager::removeCallbackC;
518 return ret;
521 void coreCallback(uint32_t id, int seq);
522 static void coreCallbackC(void *object, uint32_t id, int seq)
523 { static_cast<EventManager*>(object)->coreCallback(id, seq); }
525 static constexpr pw_core_events CreateCoreEvents()
527 pw_core_events ret{};
528 ret.version = PW_VERSION_CORE_EVENTS;
529 ret.done = &EventManager::coreCallbackC;
530 return ret;
533 using EventWatcherUniqueLock = std::unique_lock<EventManager>;
534 using EventWatcherLockGuard = std::lock_guard<EventManager>;
536 EventManager gEventHandler;
538 /* Enumerated devices. This is updated asynchronously as the app runs, and the
539 * gEventHandler thread loop must be locked when accessing the list.
541 enum class NodeType : unsigned char {
542 Sink, Source, Duplex
544 constexpr auto InvalidChannelConfig = DevFmtChannels(255);
545 struct DeviceNode {
546 std::string mName;
547 std::string mDevName;
549 uint32_t mId{};
550 NodeType mType{};
551 bool mIsHeadphones{};
552 bool mIs51Rear{};
554 uint mSampleRate{};
555 DevFmtChannels mChannels{InvalidChannelConfig};
557 static std::vector<DeviceNode> sList;
558 static DeviceNode &Add(uint32_t id);
559 static DeviceNode *Find(uint32_t id);
560 static void Remove(uint32_t id);
561 static std::vector<DeviceNode> &GetList() noexcept { return sList; }
563 void parseSampleRate(const spa_pod *value) noexcept;
564 void parsePositions(const spa_pod *value) noexcept;
565 void parseChannelCount(const spa_pod *value) noexcept;
567 std::vector<DeviceNode> DeviceNode::sList;
568 std::string DefaultSinkDevice;
569 std::string DefaultSourceDevice;
571 const char *AsString(NodeType type) noexcept
573 switch(type)
575 case NodeType::Sink: return "sink";
576 case NodeType::Source: return "source";
577 case NodeType::Duplex: return "duplex";
579 return "<unknown>";
582 DeviceNode &DeviceNode::Add(uint32_t id)
584 auto match_id = [id](DeviceNode &n) noexcept -> bool
585 { return n.mId == id; };
587 /* If the node is already in the list, return the existing entry. */
588 auto match = std::find_if(sList.begin(), sList.end(), match_id);
589 if(match != sList.end()) return *match;
591 sList.emplace_back();
592 auto &n = sList.back();
593 n.mId = id;
594 return n;
597 DeviceNode *DeviceNode::Find(uint32_t id)
599 auto match_id = [id](DeviceNode &n) noexcept -> bool
600 { return n.mId == id; };
602 auto match = std::find_if(sList.begin(), sList.end(), match_id);
603 if(match != sList.end()) return std::addressof(*match);
605 return nullptr;
608 void DeviceNode::Remove(uint32_t id)
610 auto match_id = [id](DeviceNode &n) noexcept -> bool
612 if(n.mId != id)
613 return false;
614 TRACE("Removing device \"%s\"\n", n.mDevName.c_str());
615 return true;
618 auto end = std::remove_if(sList.begin(), sList.end(), match_id);
619 sList.erase(end, sList.end());
623 const spa_audio_channel MonoMap[]{
624 SPA_AUDIO_CHANNEL_MONO
625 }, StereoMap[] {
626 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR
627 }, QuadMap[]{
628 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR
629 }, X51Map[]{
630 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
631 SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
632 }, X51RearMap[]{
633 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
634 SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR
635 }, X61Map[]{
636 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
637 SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
638 }, X71Map[]{
639 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
640 SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
644 * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal
645 * to or a superset of map1).
647 template<size_t N>
648 bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channel (&map1)[N])
650 if(map0.size() < N)
651 return false;
652 for(const spa_audio_channel chid : map1)
654 if(std::find(map0.begin(), map0.end(), chid) == map0.end())
655 return false;
657 return true;
660 void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
662 /* TODO: Can this be anything else? Long, Float, Double? */
663 uint32_t nvals{}, choiceType{};
664 value = spa_pod_get_values(value, &nvals, &choiceType);
666 const uint podType{get_pod_type(value)};
667 if(podType != SPA_TYPE_Int)
669 WARN("Unhandled sample rate POD type: %u\n", podType);
670 return;
673 if(choiceType == SPA_CHOICE_Range)
675 if(nvals != 3)
677 WARN("Unexpected SPA_CHOICE_Range count: %u\n", nvals);
678 return;
680 auto srates = get_pod_body<int32_t,3>(value);
682 /* [0] is the default, [1] is the min, and [2] is the max. */
683 TRACE("Device ID %u sample rate: %d (range: %d -> %d)\n", mId, srates[0], srates[1],
684 srates[2]);
685 mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
686 return;
689 if(choiceType == SPA_CHOICE_Enum)
691 if(nvals == 0)
693 WARN("Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
694 return;
696 auto srates = get_pod_body<int32_t>(value, nvals);
698 /* [0] is the default, [1...size()-1] are available selections. */
699 std::string others{(srates.size() > 1) ? std::to_string(srates[1]) : std::string{}};
700 for(size_t i{2};i < srates.size();++i)
702 others += ", ";
703 others += std::to_string(srates[i]);
705 TRACE("Device ID %u sample rate: %d (%s)\n", mId, srates[0], others.c_str());
706 /* Pick the first rate listed that's within the allowed range (default
707 * rate if possible).
709 for(const auto &rate : srates)
711 if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE)
713 mSampleRate = static_cast<uint>(rate);
714 break;
717 return;
720 if(choiceType == SPA_CHOICE_None)
722 if(nvals != 1)
724 WARN("Unexpected SPA_CHOICE_None count: %u\n", nvals);
725 return;
727 auto srates = get_pod_body<int32_t,1>(value);
729 TRACE("Device ID %u sample rate: %d\n", mId, srates[0]);
730 mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
731 return;
734 WARN("Unhandled sample rate choice type: %u\n", choiceType);
737 void DeviceNode::parsePositions(const spa_pod *value) noexcept
739 const auto chanmap = get_array_span<SPA_TYPE_Id>(value);
740 if(chanmap.empty()) return;
742 mIs51Rear = false;
744 if(MatchChannelMap(chanmap, X71Map))
745 mChannels = DevFmtX71;
746 else if(MatchChannelMap(chanmap, X61Map))
747 mChannels = DevFmtX61;
748 else if(MatchChannelMap(chanmap, X51Map))
749 mChannels = DevFmtX51;
750 else if(MatchChannelMap(chanmap, X51RearMap))
752 mChannels = DevFmtX51;
753 mIs51Rear = true;
755 else if(MatchChannelMap(chanmap, QuadMap))
756 mChannels = DevFmtQuad;
757 else if(MatchChannelMap(chanmap, StereoMap))
758 mChannels = DevFmtStereo;
759 else
760 mChannels = DevFmtMono;
761 TRACE("Device ID %u got %zu position%s for %s%s\n", mId, chanmap.size(),
762 (chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
765 void DeviceNode::parseChannelCount(const spa_pod *value) noexcept
767 /* As a fallback with just a channel count, just assume mono or stereo. */
768 const auto chancount = get_value<SPA_TYPE_Int>(value);
769 if(!chancount) return;
771 mIs51Rear = false;
773 if(*chancount >= 2)
774 mChannels = DevFmtStereo;
775 else if(*chancount >= 1)
776 mChannels = DevFmtMono;
777 TRACE("Device ID %u got %d channel%s for %s\n", mId, *chancount, (*chancount==1)?"":"s",
778 DevFmtChannelsString(mChannels));
782 constexpr char MonitorPrefix[]{"Monitor of "};
783 constexpr auto MonitorPrefixLen = al::size(MonitorPrefix) - 1;
784 constexpr char AudioSinkClass[]{"Audio/Sink"};
785 constexpr char AudioSourceClass[]{"Audio/Source"};
786 constexpr char AudioDuplexClass[]{"Audio/Duplex"};
787 constexpr char StreamClass[]{"Stream/"};
789 /* A generic PipeWire node proxy object used to track changes to sink and
790 * source nodes.
792 struct NodeProxy {
793 static constexpr pw_node_events CreateNodeEvents()
795 pw_node_events ret{};
796 ret.version = PW_VERSION_NODE_EVENTS;
797 ret.info = &NodeProxy::infoCallbackC;
798 ret.param = &NodeProxy::paramCallbackC;
799 return ret;
802 uint32_t mId{};
804 PwNodePtr mNode{};
805 spa_hook mListener{};
807 NodeProxy(uint32_t id, PwNodePtr node)
808 : mId{id}, mNode{std::move(node)}
810 static constexpr pw_node_events nodeEvents{CreateNodeEvents()};
811 ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this);
813 /* Track changes to the enumerable formats (indicates the default
814 * format, which is what we're interested in).
816 uint32_t fmtids[]{SPA_PARAM_EnumFormat};
817 ppw_node_subscribe_params(mNode.get(), al::data(fmtids), al::size(fmtids));
819 ~NodeProxy()
820 { spa_hook_remove(&mListener); }
823 void infoCallback(const pw_node_info *info);
824 static void infoCallbackC(void *object, const pw_node_info *info)
825 { static_cast<NodeProxy*>(object)->infoCallback(info); }
827 void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param);
828 static void paramCallbackC(void *object, int seq, uint32_t id, uint32_t index, uint32_t next,
829 const spa_pod *param)
830 { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); }
833 void NodeProxy::infoCallback(const pw_node_info *info)
835 /* We only care about property changes here (media class, name/desc).
836 * Format changes will automatically invoke the param callback.
838 * TODO: Can the media class or name/desc change without being removed and
839 * readded?
841 if((info->change_mask&PW_NODE_CHANGE_MASK_PROPS))
843 /* Can this actually change? */
844 const char *media_class{spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS)};
845 if(unlikely(!media_class)) return;
847 NodeType ntype{};
848 if(al::strcasecmp(media_class, AudioSinkClass) == 0)
849 ntype = NodeType::Sink;
850 else if(al::strcasecmp(media_class, AudioSourceClass) == 0)
851 ntype = NodeType::Source;
852 else if(al::strcasecmp(media_class, AudioDuplexClass) == 0)
853 ntype = NodeType::Duplex;
854 else
856 TRACE("Dropping device node %u which became type \"%s\"\n", info->id, media_class);
857 DeviceNode::Remove(info->id);
858 return;
861 const char *devName{spa_dict_lookup(info->props, PW_KEY_NODE_NAME)};
862 const char *nodeName{spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION)};
863 if(!nodeName || !*nodeName) nodeName = spa_dict_lookup(info->props, PW_KEY_NODE_NICK);
864 if(!nodeName || !*nodeName) nodeName = devName;
866 const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
867 TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
868 form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
869 TRACE(" \"%s\" = ID %u\n", nodeName ? nodeName : "(nil)", info->id);
871 DeviceNode &node = DeviceNode::Add(info->id);
872 if(nodeName && *nodeName) node.mName = nodeName;
873 else node.mName = "PipeWire node #"+std::to_string(info->id);
874 node.mDevName = devName ? devName : "";
875 node.mType = ntype;
876 node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0
877 || al::strcasecmp(form_factor, "headset") == 0);
881 void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param)
883 if(id == SPA_PARAM_EnumFormat)
885 DeviceNode *node{DeviceNode::Find(mId)};
886 if(unlikely(!node)) return;
888 if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)})
889 node->parseSampleRate(&prop->value);
891 if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)})
892 node->parsePositions(&prop->value);
893 else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr)
894 node->parseChannelCount(&prop->value);
899 /* A metadata proxy object used to query the default sink and source. */
900 struct MetadataProxy {
901 static constexpr pw_metadata_events CreateMetadataEvents()
903 pw_metadata_events ret{};
904 ret.version = PW_VERSION_METADATA_EVENTS;
905 ret.property = &MetadataProxy::propertyCallbackC;
906 return ret;
909 uint32_t mId{};
911 PwMetadataPtr mMetadata{};
912 spa_hook mListener{};
914 MetadataProxy(uint32_t id, PwMetadataPtr mdata)
915 : mId{id}, mMetadata{std::move(mdata)}
917 static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()};
918 ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this);
920 ~MetadataProxy()
921 { spa_hook_remove(&mListener); }
924 int propertyCallback(uint32_t id, const char *key, const char *type, const char *value);
925 static int propertyCallbackC(void *object, uint32_t id, const char *key, const char *type,
926 const char *value)
927 { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); }
930 int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type,
931 const char *value)
933 if(id != PW_ID_CORE)
934 return 0;
936 bool isCapture{};
937 if(std::strcmp(key, "default.audio.sink") == 0)
938 isCapture = false;
939 else if(std::strcmp(key, "default.audio.source") == 0)
940 isCapture = true;
941 else
942 return 0;
944 if(!type)
946 TRACE("Default %s device cleared\n", isCapture ? "capture" : "playback");
947 if(!isCapture) DefaultSinkDevice.clear();
948 else DefaultSourceDevice.clear();
949 return 0;
951 if(std::strcmp(type, "Spa:String:JSON") != 0)
953 ERR("Unexpected %s property type: %s\n", key, type);
954 return 0;
957 spa_json it[2]{};
958 spa_json_init(&it[0], value, strlen(value));
959 if(spa_json_enter_object(&it[0], &it[1]) <= 0)
960 return 0;
962 auto get_json_string = [](spa_json *iter)
964 al::optional<std::string> str;
966 const char *val{};
967 int len{spa_json_next(iter, &val)};
968 if(len <= 0) return str;
970 str.emplace().resize(static_cast<uint>(len), '\0');
971 if(spa_json_parse_string(val, len, &str->front()) <= 0)
972 str.reset();
973 else while(!str->empty() && str->back() == '\0')
974 str->pop_back();
975 return str;
977 while(auto propKey = get_json_string(&it[1]))
979 if(*propKey == "name")
981 auto propValue = get_json_string(&it[1]);
982 if(!propValue) break;
984 TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback",
985 propValue->c_str());
986 if(!isCapture)
987 DefaultSinkDevice = std::move(*propValue);
988 else
989 DefaultSourceDevice = std::move(*propValue);
991 else
993 const char *v{};
994 if(spa_json_next(&it[1], &v) <= 0)
995 break;
998 return 0;
1002 bool EventManager::init()
1004 mLoop = ThreadMainloop::Create("PWEventThread");
1005 if(!mLoop)
1007 ERR("Failed to create PipeWire event thread loop (errno: %d)\n", errno);
1008 return false;
1011 mContext = mLoop.newContext(pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr));
1012 if(!mContext)
1014 ERR("Failed to create PipeWire event context (errno: %d)\n", errno);
1015 return false;
1018 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
1019 if(!mCore)
1021 ERR("Failed to connect PipeWire event context (errno: %d)\n", errno);
1022 return false;
1025 mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)};
1026 if(!mRegistry)
1028 ERR("Failed to get PipeWire event registry (errno: %d)\n", errno);
1029 return false;
1032 static constexpr pw_core_events coreEvents{CreateCoreEvents()};
1033 static constexpr pw_registry_events registryEvents{CreateRegistryEvents()};
1035 ppw_core_add_listener(mCore.get(), &mCoreListener, &coreEvents, this);
1036 ppw_registry_add_listener(mRegistry.get(), &mRegistryListener, &registryEvents, this);
1038 /* Set an initial sequence ID for initialization, to trigger after the
1039 * registry is first populated.
1041 mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, 0);
1043 if(int res{mLoop.start()})
1045 ERR("Failed to start PipeWire event thread loop (res: %d)\n", res);
1046 return false;
1049 return true;
1052 EventManager::~EventManager()
1054 if(mLoop) mLoop.stop();
1056 for(NodeProxy *node : mNodeList)
1057 al::destroy_at(node);
1058 if(mDefaultMetadata)
1059 al::destroy_at(mDefaultMetadata);
1062 void EventManager::kill()
1064 if(mLoop) mLoop.stop();
1066 for(NodeProxy *node : mNodeList)
1067 al::destroy_at(node);
1068 mNodeList.clear();
1069 if(mDefaultMetadata)
1070 al::destroy_at(mDefaultMetadata);
1071 mDefaultMetadata = nullptr;
1073 mRegistry = nullptr;
1074 mCore = nullptr;
1075 mContext = nullptr;
1076 mLoop = nullptr;
1079 void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t version,
1080 const spa_dict *props)
1082 /* We're only interested in interface nodes. */
1083 if(std::strcmp(type, PW_TYPE_INTERFACE_Node) == 0)
1085 const char *media_class{spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)};
1086 if(!media_class) return;
1088 /* Specifically, audio sinks and sources (and duplexes). */
1089 const bool isGood{al::strcasecmp(media_class, AudioSinkClass) == 0
1090 || al::strcasecmp(media_class, AudioSourceClass) == 0
1091 || al::strcasecmp(media_class, AudioDuplexClass) == 0};
1092 if(!isGood)
1094 if(std::strstr(media_class, "/Video") == nullptr
1095 && std::strncmp(media_class, StreamClass, sizeof(StreamClass)-1) != 0)
1096 TRACE("Ignoring node class %s\n", media_class);
1097 return;
1100 /* Create the proxy object. */
1101 auto node = PwNodePtr{static_cast<pw_node*>(pw_registry_bind(mRegistry.get(), id, type,
1102 version, sizeof(NodeProxy)))};
1103 if(!node)
1105 ERR("Failed to create node proxy object (errno: %d)\n", errno);
1106 return;
1109 /* Initialize the NodeProxy to hold the node object, add it to the
1110 * active node list, and update the sync point.
1112 auto *proxy = static_cast<NodeProxy*>(pw_proxy_get_user_data(as<pw_proxy*>(node.get())));
1113 mNodeList.emplace_back(al::construct_at(proxy, id, std::move(node)));
1114 syncInit();
1116 /* Signal any waiters that we have found a source or sink for audio
1117 * support.
1119 if(!mHasAudio.exchange(true, std::memory_order_acq_rel))
1120 mLoop.signal(false);
1122 else if(std::strcmp(type, PW_TYPE_INTERFACE_Metadata) == 0)
1124 const char *data_class{spa_dict_lookup(props, PW_KEY_METADATA_NAME)};
1125 if(!data_class) return;
1127 if(std::strcmp(data_class, "default") != 0)
1129 TRACE("Ignoring metadata \"%s\"\n", data_class);
1130 return;
1133 if(mDefaultMetadata)
1135 ERR("Duplicate default metadata\n");
1136 return;
1139 auto mdata = PwMetadataPtr{static_cast<pw_metadata*>(pw_registry_bind(mRegistry.get(), id,
1140 type, version, sizeof(MetadataProxy)))};
1141 if(!mdata)
1143 ERR("Failed to create metadata proxy object (errno: %d)\n", errno);
1144 return;
1147 auto *proxy = static_cast<MetadataProxy*>(
1148 pw_proxy_get_user_data(as<pw_proxy*>(mdata.get())));
1149 mDefaultMetadata = al::construct_at(proxy, id, std::move(mdata));
1150 syncInit();
1154 void EventManager::removeCallback(uint32_t id)
1156 DeviceNode::Remove(id);
1158 auto clear_node = [id](NodeProxy *node) noexcept
1160 if(node->mId != id)
1161 return false;
1162 al::destroy_at(node);
1163 return true;
1165 auto node_end = std::remove_if(mNodeList.begin(), mNodeList.end(), clear_node);
1166 mNodeList.erase(node_end, mNodeList.end());
1168 if(mDefaultMetadata && mDefaultMetadata->mId == id)
1170 al::destroy_at(mDefaultMetadata);
1171 mDefaultMetadata = nullptr;
1175 void EventManager::coreCallback(uint32_t id, int seq)
1177 if(id == PW_ID_CORE && seq == mInitSeq)
1179 /* Initialization done. Remove this callback and signal anyone that may
1180 * be waiting.
1182 spa_hook_remove(&mCoreListener);
1184 mInitDone.store(true);
1185 mLoop.signal(false);
1190 enum use_f32p_e : bool { UseDevType=false, ForceF32Planar=true };
1191 spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e use_f32p)
1193 spa_audio_info_raw info{};
1194 if(use_f32p)
1196 device->FmtType = DevFmtFloat;
1197 info.format = SPA_AUDIO_FORMAT_F32P;
1199 else switch(device->FmtType)
1201 case DevFmtByte: info.format = SPA_AUDIO_FORMAT_S8; break;
1202 case DevFmtUByte: info.format = SPA_AUDIO_FORMAT_U8; break;
1203 case DevFmtShort: info.format = SPA_AUDIO_FORMAT_S16; break;
1204 case DevFmtUShort: info.format = SPA_AUDIO_FORMAT_U16; break;
1205 case DevFmtInt: info.format = SPA_AUDIO_FORMAT_S32; break;
1206 case DevFmtUInt: info.format = SPA_AUDIO_FORMAT_U32; break;
1207 case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break;
1210 info.rate = device->Frequency;
1212 al::span<const spa_audio_channel> map{};
1213 switch(device->FmtChans)
1215 case DevFmtMono: map = MonoMap; break;
1216 case DevFmtStereo: map = StereoMap; break;
1217 case DevFmtQuad: map = QuadMap; break;
1218 case DevFmtX51:
1219 if(is51rear) map = X51RearMap;
1220 else map = X51Map;
1221 break;
1222 case DevFmtX61: map = X61Map; break;
1223 case DevFmtX71: map = X71Map; break;
1224 case DevFmtX3D71: map = X71Map; break;
1225 case DevFmtAmbi3D:
1226 info.flags |= SPA_AUDIO_FLAG_UNPOSITIONED;
1227 info.channels = device->channelsFromFmt();
1228 break;
1230 if(!map.empty())
1232 info.channels = static_cast<uint32_t>(map.size());
1233 std::copy(map.begin(), map.end(), info.position);
1236 return info;
1239 class PipeWirePlayback final : public BackendBase {
1240 void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
1241 static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
1242 const char *error)
1243 { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); }
1245 void ioChangedCallback(uint32_t id, void *area, uint32_t size);
1246 static void ioChangedCallbackC(void *data, uint32_t id, void *area, uint32_t size)
1247 { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); }
1249 void outputCallback();
1250 static void outputCallbackC(void *data)
1251 { static_cast<PipeWirePlayback*>(data)->outputCallback(); }
1253 void open(const char *name) override;
1254 bool reset() override;
1255 void start() override;
1256 void stop() override;
1257 ClockLatency getClockLatency() override;
1259 uint32_t mTargetId{PwIdAny};
1260 nanoseconds mTimeBase{0};
1261 ThreadMainloop mLoop;
1262 PwContextPtr mContext;
1263 PwCorePtr mCore;
1264 PwStreamPtr mStream;
1265 spa_hook mStreamListener{};
1266 spa_io_rate_match *mRateMatch{};
1267 std::unique_ptr<float*[]> mChannelPtrs;
1268 uint mNumChannels{};
1270 static constexpr pw_stream_events CreateEvents()
1272 pw_stream_events ret{};
1273 ret.version = PW_VERSION_STREAM_EVENTS;
1274 ret.state_changed = &PipeWirePlayback::stateChangedCallbackC;
1275 ret.io_changed = &PipeWirePlayback::ioChangedCallbackC;
1276 ret.process = &PipeWirePlayback::outputCallbackC;
1277 return ret;
1280 public:
1281 PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
1282 ~PipeWirePlayback()
1284 /* Stop the mainloop so the stream can be properly destroyed. */
1285 if(mLoop) mLoop.stop();
1288 DEF_NEWDEL(PipeWirePlayback)
1292 void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
1293 { mLoop.signal(false); }
1295 void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size)
1297 switch(id)
1299 case SPA_IO_RateMatch:
1300 if(size >= sizeof(spa_io_rate_match))
1301 mRateMatch = static_cast<spa_io_rate_match*>(area);
1302 break;
1306 void PipeWirePlayback::outputCallback()
1308 pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
1309 if(unlikely(!pw_buf)) return;
1311 /* For planar formats, each datas[] seems to contain one channel, so store
1312 * the pointers in an array. Limit the render length in case the available
1313 * buffer length in any one channel is smaller than we wanted (shouldn't
1314 * be, but just in case).
1316 spa_data *datas{pw_buf->buffer->datas};
1317 const size_t chancount{minu(mNumChannels, pw_buf->buffer->n_datas)};
1318 /* TODO: How many samples should actually be written? 'maxsize' can be 16k
1319 * samples, which is excessive (~341ms @ 48khz). SPA_IO_RateMatch contains
1320 * a 'size' field that apparently indicates how many samples should be
1321 * written per update, but it's not obviously right.
1323 uint length{mRateMatch ? mRateMatch->size : mDevice->UpdateSize};
1324 for(size_t i{0};i < chancount;++i)
1326 length = minu(length, datas[i].maxsize/sizeof(float));
1327 mChannelPtrs[i] = static_cast<float*>(datas[i].data);
1330 mDevice->renderSamples({mChannelPtrs.get(), chancount}, length);
1332 for(size_t i{0};i < chancount;++i)
1334 datas[i].chunk->offset = 0;
1335 datas[i].chunk->stride = sizeof(float);
1336 datas[i].chunk->size = length * sizeof(float);
1338 pw_buf->size = length;
1339 pw_stream_queue_buffer(mStream.get(), pw_buf);
1343 void PipeWirePlayback::open(const char *name)
1345 static std::atomic<uint> OpenCount{0};
1347 uint32_t targetid{PwIdAny};
1348 std::string devname{};
1349 gEventHandler.waitForInit();
1350 if(!name)
1352 EventWatcherLockGuard _{gEventHandler};
1353 auto&& devlist = DeviceNode::GetList();
1355 auto match = devlist.cend();
1356 if(!DefaultSinkDevice.empty())
1358 auto match_default = [](const DeviceNode &n) -> bool
1359 { return n.mDevName == DefaultSinkDevice; };
1360 match = std::find_if(devlist.cbegin(), devlist.cend(), match_default);
1362 if(match == devlist.cend())
1364 auto match_playback = [](const DeviceNode &n) -> bool
1365 { return n.mType != NodeType::Source; };
1366 match = std::find_if(devlist.cbegin(), devlist.cend(), match_playback);
1367 if(match == devlist.cend())
1368 throw al::backend_exception{al::backend_error::NoDevice,
1369 "No PipeWire playback device found"};
1372 targetid = match->mId;
1373 devname = match->mName;
1375 else
1377 EventWatcherLockGuard _{gEventHandler};
1378 auto&& devlist = DeviceNode::GetList();
1380 auto match_name = [name](const DeviceNode &n) -> bool
1381 { return n.mType != NodeType::Source && n.mName == name; };
1382 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
1383 if(match == devlist.cend())
1384 throw al::backend_exception{al::backend_error::NoDevice,
1385 "Device name \"%s\" not found", name};
1387 targetid = match->mId;
1388 devname = match->mName;
1391 if(!mLoop)
1393 const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
1394 const std::string thread_name{"ALSoftP" + std::to_string(count)};
1395 mLoop = ThreadMainloop::Create(thread_name.c_str());
1396 if(!mLoop)
1397 throw al::backend_exception{al::backend_error::DeviceError,
1398 "Failed to create PipeWire mainloop (errno: %d)", errno};
1399 if(int res{mLoop.start()})
1400 throw al::backend_exception{al::backend_error::DeviceError,
1401 "Failed to start PipeWire mainloop (res: %d)", res};
1403 MainloopUniqueLock mlock{mLoop};
1404 if(!mContext)
1406 pw_properties *cprops{pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr)};
1407 mContext = mLoop.newContext(cprops);
1408 if(!mContext)
1409 throw al::backend_exception{al::backend_error::DeviceError,
1410 "Failed to create PipeWire event context (errno: %d)\n", errno};
1412 if(!mCore)
1414 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
1415 if(!mCore)
1416 throw al::backend_exception{al::backend_error::DeviceError,
1417 "Failed to connect PipeWire event context (errno: %d)\n", errno};
1419 mlock.unlock();
1421 /* TODO: Ensure the target ID is still valid/usable and accepts streams. */
1423 mTargetId = targetid;
1424 if(!devname.empty())
1425 mDevice->DeviceName = std::move(devname);
1426 else
1427 mDevice->DeviceName = pwireDevice;
1430 bool PipeWirePlayback::reset()
1432 if(mStream)
1434 MainloopLockGuard _{mLoop};
1435 mStream = nullptr;
1437 mStreamListener = {};
1438 mRateMatch = nullptr;
1439 mTimeBase = GetDeviceClockTime(mDevice);
1441 /* If connecting to a specific device, update various device parameters to
1442 * match its format.
1444 bool is51rear{false};
1445 mDevice->Flags.reset(DirectEar);
1446 if(mTargetId != PwIdAny)
1448 EventWatcherLockGuard _{gEventHandler};
1449 auto&& devlist = DeviceNode::GetList();
1451 auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
1452 { return targetid == n.mId; };
1453 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
1454 if(match != devlist.cend())
1456 if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0)
1458 /* Scale the update size if the sample rate changes. */
1459 const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency};
1460 mDevice->Frequency = match->mSampleRate;
1461 mDevice->UpdateSize = static_cast<uint>(clampd(mDevice->UpdateSize*scale + 0.5,
1462 64.0, 8192.0));
1463 mDevice->BufferSize = mDevice->UpdateSize * 2;
1465 if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig)
1466 mDevice->FmtChans = match->mChannels;
1467 if(match->mChannels == DevFmtStereo && match->mIsHeadphones)
1468 mDevice->Flags.set(DirectEar);
1469 is51rear = match->mIs51Rear;
1472 /* Force planar 32-bit float output for playback. This is what PipeWire
1473 * handles internally, and it's easier for us too.
1475 spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)};
1477 /* TODO: How to tell what an appropriate size is? Examples just use this
1478 * magic value.
1480 constexpr uint32_t pod_buffer_size{1024};
1481 auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
1482 spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
1484 const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
1485 if(!params)
1486 throw al::backend_exception{al::backend_error::DeviceError,
1487 "Failed to set PipeWire audio format parameters"};
1489 pw_properties *props{pw_properties_new(
1490 PW_KEY_MEDIA_TYPE, "Audio",
1491 PW_KEY_MEDIA_CATEGORY, "Playback",
1492 PW_KEY_MEDIA_ROLE, "Game",
1493 PW_KEY_NODE_ALWAYS_PROCESS, "true",
1494 nullptr)};
1495 if(!props)
1496 throw al::backend_exception{al::backend_error::DeviceError,
1497 "Failed to create PipeWire stream properties (errno: %d)", errno};
1499 auto&& binary = GetProcBinary();
1500 const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
1501 /* TODO: Which properties are actually needed here? Any others that could
1502 * be useful?
1504 pw_properties_set(props, PW_KEY_NODE_NAME, appname);
1505 pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, appname);
1506 pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize,
1507 mDevice->Frequency);
1508 pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
1510 MainloopUniqueLock plock{mLoop};
1511 /* The stream takes overship of 'props', even in the case of failure. */
1512 mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)};
1513 if(!mStream)
1514 throw al::backend_exception{al::backend_error::NoDevice,
1515 "Failed to create PipeWire stream (errno: %d)", errno};
1516 static constexpr pw_stream_events streamEvents{CreateEvents()};
1517 pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
1519 constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
1520 | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
1521 if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, mTargetId, Flags, &params, 1)})
1522 throw al::backend_exception{al::backend_error::DeviceError,
1523 "Error connecting PipeWire stream (res: %d)", res};
1525 /* Wait for the stream to become paused (ready to start streaming). */
1526 pw_stream_state state{};
1527 const char *error{};
1528 plock.wait([stream=mStream.get(),&state,&error]()
1530 state = pw_stream_get_state(stream, &error);
1531 if(state == PW_STREAM_STATE_ERROR)
1532 throw al::backend_exception{al::backend_error::DeviceError,
1533 "Error connecting PipeWire stream: \"%s\"", error};
1534 return state == PW_STREAM_STATE_PAUSED;
1537 /* TODO: Update mDevice->BufferSize with the total known buffering delay
1538 * from the head of this playback stream to the tail of the device output.
1540 mDevice->BufferSize = mDevice->UpdateSize * 2;
1541 plock.unlock();
1543 mNumChannels = mDevice->channelsFromFmt();
1544 mChannelPtrs = std::make_unique<float*[]>(mNumChannels);
1546 setDefaultWFXChannelOrder();
1548 return true;
1551 void PipeWirePlayback::start()
1553 MainloopUniqueLock plock{mLoop};
1554 if(int res{pw_stream_set_active(mStream.get(), true)})
1555 throw al::backend_exception{al::backend_error::DeviceError,
1556 "Failed to start PipeWire stream (res: %d)", res};
1558 /* Wait for the stream to start playing (would be nice to not, but we need
1559 * the actual update size which is only available after starting).
1561 pw_stream_state state{};
1562 const char *error{};
1563 plock.wait([stream=mStream.get(),&state,&error]()
1565 state = pw_stream_get_state(stream, &error);
1566 return state != PW_STREAM_STATE_PAUSED;
1569 if(state == PW_STREAM_STATE_ERROR)
1570 throw al::backend_exception{al::backend_error::DeviceError,
1571 "PipeWire stream error: %s", error ? error : "(unknown)"};
1572 if(state == PW_STREAM_STATE_STREAMING && mRateMatch && mRateMatch->size)
1574 mDevice->UpdateSize = mRateMatch->size;
1575 mDevice->BufferSize = mDevice->UpdateSize * 2;
1579 void PipeWirePlayback::stop()
1581 MainloopUniqueLock plock{mLoop};
1582 if(int res{pw_stream_set_active(mStream.get(), false)})
1583 throw al::backend_exception{al::backend_error::DeviceError,
1584 "Failed to stop PipeWire stream (res: %d)", res};
1586 /* Wait for the stream to stop playing. */
1587 plock.wait([stream=mStream.get()]()
1588 { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
1591 ClockLatency PipeWirePlayback::getClockLatency()
1593 /* Given a real-time low-latency output, this is rather complicated to get
1594 * accurate timing. So, here we go.
1597 /* First, get the stream time info (tick delay, ticks played, and the
1598 * CLOCK_MONOTONIC time closest to when that last tick was played).
1600 pw_time ptime{};
1601 if(mStream)
1603 MainloopLockGuard _{mLoop};
1604 if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))})
1605 ERR("Failed to get PipeWire stream time (res: %d)\n", res);
1608 /* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the
1609 * monotonic clock closest to 'now', and the last mixer time at 'now').
1611 nanoseconds mixtime{};
1612 timespec tspec{};
1613 uint refcount;
1614 do {
1615 refcount = mDevice->waitForMix();
1616 mixtime = GetDeviceClockTime(mDevice);
1617 clock_gettime(CLOCK_MONOTONIC, &tspec);
1618 std::atomic_thread_fence(std::memory_order_acquire);
1619 } while(refcount != ReadRef(mDevice->MixCount));
1621 /* Convert the monotonic clock, stream ticks, and stream delay to
1622 * nanoseconds.
1624 nanoseconds monoclock{seconds{tspec.tv_sec} + nanoseconds{tspec.tv_nsec}};
1625 nanoseconds curtic{}, delay{};
1626 if(unlikely(ptime.rate.denom < 1))
1628 /* If there's no stream rate, the stream hasn't had a chance to get
1629 * going and return time info yet. Just use dummy values.
1631 ptime.now = monoclock.count();
1632 curtic = mixtime;
1633 delay = nanoseconds{seconds{mDevice->BufferSize}} / mDevice->Frequency;
1635 else
1637 /* The stream gets recreated with each reset, so include the time that
1638 * had already passed with previous streams.
1640 curtic = mTimeBase;
1641 /* More safely scale the ticks to avoid overflowing the pre-division
1642 * temporary as it gets larger.
1644 curtic += seconds{ptime.ticks / ptime.rate.denom} * ptime.rate.num;
1645 curtic += nanoseconds{seconds{ptime.ticks%ptime.rate.denom} * ptime.rate.num} /
1646 ptime.rate.denom;
1648 /* The delay should be small enough to not worry about overflow. */
1649 delay = nanoseconds{seconds{ptime.delay} * ptime.rate.num} / ptime.rate.denom;
1652 /* If the mixer time is ahead of the stream time, there's that much more
1653 * delay relative to the stream delay.
1655 if(mixtime > curtic)
1656 delay += mixtime - curtic;
1657 /* Reduce the delay according to how much time has passed since the known
1658 * stream time. This isn't 100% accurate since the system monotonic clock
1659 * doesn't tick at the exact same rate as the audio device, but it should
1660 * be good enough with ptime.now being constantly updated every few
1661 * milliseconds with ptime.ticks.
1663 delay -= monoclock - nanoseconds{ptime.now};
1665 /* Return the mixer time and delay. Clamp the delay to no less than 0,
1666 * incase timer drift got that severe.
1668 ClockLatency ret{};
1669 ret.ClockTime = mixtime;
1670 ret.Latency = std::max(delay, nanoseconds{});
1672 return ret;
1676 class PipeWireCapture final : public BackendBase {
1677 void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
1678 static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
1679 const char *error)
1680 { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); }
1682 void inputCallback();
1683 static void inputCallbackC(void *data)
1684 { static_cast<PipeWireCapture*>(data)->inputCallback(); }
1686 void open(const char *name) override;
1687 void start() override;
1688 void stop() override;
1689 void captureSamples(al::byte *buffer, uint samples) override;
1690 uint availableSamples() override;
1692 uint32_t mTargetId{PwIdAny};
1693 ThreadMainloop mLoop;
1694 PwContextPtr mContext;
1695 PwCorePtr mCore;
1696 PwStreamPtr mStream;
1697 spa_hook mStreamListener{};
1699 RingBufferPtr mRing{};
1701 static constexpr pw_stream_events CreateEvents()
1703 pw_stream_events ret{};
1704 ret.version = PW_VERSION_STREAM_EVENTS;
1705 ret.state_changed = &PipeWireCapture::stateChangedCallbackC;
1706 ret.process = &PipeWireCapture::inputCallbackC;
1707 return ret;
1710 public:
1711 PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
1712 ~PipeWireCapture() { if(mLoop) mLoop.stop(); }
1714 DEF_NEWDEL(PipeWireCapture)
1718 void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
1719 { mLoop.signal(false); }
1721 void PipeWireCapture::inputCallback()
1723 pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
1724 if(unlikely(!pw_buf)) return;
1726 spa_data *bufdata{pw_buf->buffer->datas};
1727 const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)};
1728 const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)};
1730 mRing->write(static_cast<char*>(bufdata->data) + offset, size / mRing->getElemSize());
1732 pw_stream_queue_buffer(mStream.get(), pw_buf);
1736 void PipeWireCapture::open(const char *name)
1738 static std::atomic<uint> OpenCount{0};
1740 uint32_t targetid{PwIdAny};
1741 std::string devname{};
1742 gEventHandler.waitForInit();
1743 if(!name)
1745 EventWatcherLockGuard _{gEventHandler};
1746 auto&& devlist = DeviceNode::GetList();
1748 auto match = devlist.cend();
1749 if(!DefaultSourceDevice.empty())
1751 auto match_default = [](const DeviceNode &n) -> bool
1752 { return n.mDevName == DefaultSourceDevice; };
1753 match = std::find_if(devlist.cbegin(), devlist.cend(), match_default);
1755 if(match == devlist.cend())
1757 auto match_capture = [](const DeviceNode &n) -> bool
1758 { return n.mType != NodeType::Sink; };
1759 match = std::find_if(devlist.cbegin(), devlist.cend(), match_capture);
1761 if(match == devlist.cend())
1763 match = devlist.cbegin();
1764 if(match == devlist.cend())
1765 throw al::backend_exception{al::backend_error::NoDevice,
1766 "No PipeWire capture device found"};
1769 targetid = match->mId;
1770 if(match->mType != NodeType::Sink) devname = match->mName;
1771 else devname = MonitorPrefix+match->mName;
1773 else
1775 EventWatcherLockGuard _{gEventHandler};
1776 auto&& devlist = DeviceNode::GetList();
1778 auto match_name = [name](const DeviceNode &n) -> bool
1779 { return n.mType != NodeType::Sink && n.mName == name; };
1780 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
1781 if(match == devlist.cend() && std::strncmp(name, MonitorPrefix, MonitorPrefixLen) == 0)
1783 const char *sinkname{name + MonitorPrefixLen};
1784 auto match_sinkname = [sinkname](const DeviceNode &n) -> bool
1785 { return n.mType == NodeType::Sink && n.mName == sinkname; };
1786 match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname);
1788 if(match == devlist.cend())
1789 throw al::backend_exception{al::backend_error::NoDevice,
1790 "Device name \"%s\" not found", name};
1792 targetid = match->mId;
1793 devname = name;
1796 if(!mLoop)
1798 const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
1799 const std::string thread_name{"ALSoftC" + std::to_string(count)};
1800 mLoop = ThreadMainloop::Create(thread_name.c_str());
1801 if(!mLoop)
1802 throw al::backend_exception{al::backend_error::DeviceError,
1803 "Failed to create PipeWire mainloop (errno: %d)", errno};
1804 if(int res{mLoop.start()})
1805 throw al::backend_exception{al::backend_error::DeviceError,
1806 "Failed to start PipeWire mainloop (res: %d)", res};
1808 MainloopUniqueLock mlock{mLoop};
1809 if(!mContext)
1811 pw_properties *cprops{pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr)};
1812 mContext = mLoop.newContext(cprops);
1813 if(!mContext)
1814 throw al::backend_exception{al::backend_error::DeviceError,
1815 "Failed to create PipeWire event context (errno: %d)\n", errno};
1817 if(!mCore)
1819 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
1820 if(!mCore)
1821 throw al::backend_exception{al::backend_error::DeviceError,
1822 "Failed to connect PipeWire event context (errno: %d)\n", errno};
1824 mlock.unlock();
1826 /* TODO: Ensure the target ID is still valid/usable and accepts streams. */
1828 mTargetId = targetid;
1829 if(!devname.empty())
1830 mDevice->DeviceName = std::move(devname);
1831 else
1832 mDevice->DeviceName = pwireInput;
1835 bool is51rear{false};
1836 if(mTargetId != PwIdAny)
1838 EventWatcherLockGuard _{gEventHandler};
1839 auto&& devlist = DeviceNode::GetList();
1841 auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
1842 { return targetid == n.mId; };
1843 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
1844 if(match != devlist.cend())
1845 is51rear = match->mIs51Rear;
1847 spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)};
1849 constexpr uint32_t pod_buffer_size{1024};
1850 auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
1851 spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
1853 const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
1854 if(!params[0])
1855 throw al::backend_exception{al::backend_error::DeviceError,
1856 "Failed to set PipeWire audio format parameters"};
1858 pw_properties *props{pw_properties_new(
1859 PW_KEY_MEDIA_TYPE, "Audio",
1860 PW_KEY_MEDIA_CATEGORY, "Capture",
1861 PW_KEY_MEDIA_ROLE, "Game",
1862 PW_KEY_NODE_ALWAYS_PROCESS, "true",
1863 nullptr)};
1864 if(!props)
1865 throw al::backend_exception{al::backend_error::DeviceError,
1866 "Failed to create PipeWire stream properties (errno: %d)", errno};
1868 auto&& binary = GetProcBinary();
1869 const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
1870 pw_properties_set(props, PW_KEY_NODE_NAME, appname);
1871 pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, appname);
1872 /* We don't actually care what the latency/update size is, as long as it's
1873 * reasonable. Unfortunately, when unspecified PipeWire seems to default to
1874 * around 40ms, which isn't great. So request 20ms instead.
1876 pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50,
1877 mDevice->Frequency);
1878 pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
1880 MainloopUniqueLock plock{mLoop};
1881 mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
1882 if(!mStream)
1883 throw al::backend_exception{al::backend_error::NoDevice,
1884 "Failed to create PipeWire stream (errno: %d)", errno};
1885 static constexpr pw_stream_events streamEvents{CreateEvents()};
1886 pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
1888 constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
1889 | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
1890 if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, mTargetId, Flags, params, 1)})
1891 throw al::backend_exception{al::backend_error::DeviceError,
1892 "Error connecting PipeWire stream (res: %d)", res};
1894 /* Wait for the stream to become paused (ready to start streaming). */
1895 pw_stream_state state{};
1896 const char *error{};
1897 plock.wait([stream=mStream.get(),&state,&error]()
1899 state = pw_stream_get_state(stream, &error);
1900 if(state == PW_STREAM_STATE_ERROR)
1901 throw al::backend_exception{al::backend_error::DeviceError,
1902 "Error connecting PipeWire stream: \"%s\"", error};
1903 return state == PW_STREAM_STATE_PAUSED;
1905 plock.unlock();
1907 setDefaultWFXChannelOrder();
1909 /* Ensure at least a 100ms capture buffer. */
1910 mRing = RingBuffer::Create(maxu(mDevice->Frequency/10, mDevice->BufferSize),
1911 mDevice->frameSizeFromFmt(), false);
1915 void PipeWireCapture::start()
1917 MainloopUniqueLock plock{mLoop};
1918 if(int res{pw_stream_set_active(mStream.get(), true)})
1919 throw al::backend_exception{al::backend_error::DeviceError,
1920 "Failed to start PipeWire stream (res: %d)", res};
1922 pw_stream_state state{};
1923 const char *error{};
1924 plock.wait([stream=mStream.get(),&state,&error]()
1926 state = pw_stream_get_state(stream, &error);
1927 return state != PW_STREAM_STATE_PAUSED;
1930 if(state == PW_STREAM_STATE_ERROR)
1931 throw al::backend_exception{al::backend_error::DeviceError,
1932 "PipeWire stream error: %s", error ? error : "(unknown)"};
1935 void PipeWireCapture::stop()
1937 MainloopUniqueLock plock{mLoop};
1938 if(int res{pw_stream_set_active(mStream.get(), false)})
1939 throw al::backend_exception{al::backend_error::DeviceError,
1940 "Failed to stop PipeWire stream (res: %d)", res};
1942 plock.wait([stream=mStream.get()]()
1943 { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
1946 uint PipeWireCapture::availableSamples()
1947 { return static_cast<uint>(mRing->readSpace()); }
1949 void PipeWireCapture::captureSamples(al::byte *buffer, uint samples)
1950 { mRing->read(buffer, samples); }
1952 } // namespace
1955 bool PipeWireBackendFactory::init()
1957 if(!pwire_load())
1958 return false;
1960 const char *version{pw_get_library_version()};
1961 if(!check_version(version))
1963 WARN("PipeWire version \"%s\" too old (%s or newer required)\n", version,
1964 pw_get_headers_version());
1965 return false;
1967 TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version());
1969 pw_init(0, nullptr);
1970 if(!gEventHandler.init())
1971 return false;
1973 if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false)
1974 && !gEventHandler.waitForAudio())
1976 gEventHandler.kill();
1977 /* TODO: Temporary warning, until PipeWire gets a proper way to report
1978 * audio support.
1980 WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.\n");
1981 return false;
1983 return true;
1986 bool PipeWireBackendFactory::querySupport(BackendType type)
1987 { return type == BackendType::Playback || type == BackendType::Capture; }
1989 std::string PipeWireBackendFactory::probe(BackendType type)
1991 std::string outnames;
1993 gEventHandler.waitForInit();
1994 EventWatcherLockGuard _{gEventHandler};
1995 auto&& devlist = DeviceNode::GetList();
1997 auto match_defsink = [](const DeviceNode &n) -> bool
1998 { return n.mDevName == DefaultSinkDevice; };
1999 auto match_defsource = [](const DeviceNode &n) -> bool
2000 { return n.mDevName == DefaultSourceDevice; };
2002 auto sort_devnode = [](DeviceNode &lhs, DeviceNode &rhs) noexcept -> bool
2003 { return lhs.mId < rhs.mId; };
2004 std::sort(devlist.begin(), devlist.end(), sort_devnode);
2006 auto defmatch = devlist.cbegin();
2007 switch(type)
2009 case BackendType::Playback:
2010 defmatch = std::find_if(defmatch, devlist.cend(), match_defsink);
2011 if(defmatch != devlist.cend())
2013 /* Includes null char. */
2014 outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
2016 for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
2018 if(iter != defmatch && iter->mType != NodeType::Source)
2019 outnames.append(iter->mName.c_str(), iter->mName.length()+1);
2021 break;
2022 case BackendType::Capture:
2023 defmatch = std::find_if(defmatch, devlist.cend(), match_defsource);
2024 if(defmatch != devlist.cend())
2026 if(defmatch->mType == NodeType::Sink)
2027 outnames.append(MonitorPrefix);
2028 outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
2030 for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
2032 if(iter != defmatch)
2034 if(iter->mType == NodeType::Sink)
2035 outnames.append(MonitorPrefix);
2036 outnames.append(iter->mName.c_str(), iter->mName.length()+1);
2039 break;
2042 return outnames;
2045 BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type)
2047 if(type == BackendType::Playback)
2048 return BackendPtr{new PipeWirePlayback{device}};
2049 if(type == BackendType::Capture)
2050 return BackendPtr{new PipeWireCapture{device}};
2051 return nullptr;
2054 BackendFactory &PipeWireBackendFactory::getFactory()
2056 static PipeWireBackendFactory factory{};
2057 return factory;