Avoid a stateful unique_ptr deleter
[openal-soft.git] / alc / backends / pipewire.cpp
bloba19dcb6176c56cc9f43bb3547799f2b0a1600c13
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). */
54 _Pragma("GCC diagnostic push")
55 _Pragma("GCC diagnostic ignored \"-Weverything\"")
56 #include "pipewire/pipewire.h"
57 #include "pipewire/extensions/metadata.h"
58 #include "spa/buffer/buffer.h"
59 #include "spa/param/audio/format-utils.h"
60 #include "spa/param/audio/raw.h"
61 #include "spa/param/param.h"
62 #include "spa/pod/builder.h"
63 #include "spa/utils/json.h"
65 namespace {
66 /* Wrap some nasty macros here too... */
67 template<typename ...Args>
68 auto ppw_core_add_listener(pw_core *core, Args&& ...args)
69 { return pw_core_add_listener(core, std::forward<Args>(args)...); }
70 template<typename ...Args>
71 auto ppw_core_sync(pw_core *core, Args&& ...args)
72 { return pw_core_sync(core, std::forward<Args>(args)...); }
73 template<typename ...Args>
74 auto ppw_registry_add_listener(pw_registry *reg, Args&& ...args)
75 { return pw_registry_add_listener(reg, std::forward<Args>(args)...); }
76 template<typename ...Args>
77 auto ppw_node_add_listener(pw_node *node, Args&& ...args)
78 { return pw_node_add_listener(node, std::forward<Args>(args)...); }
79 template<typename ...Args>
80 auto ppw_node_subscribe_params(pw_node *node, Args&& ...args)
81 { return pw_node_subscribe_params(node, std::forward<Args>(args)...); }
82 template<typename ...Args>
83 auto ppw_metadata_add_listener(pw_metadata *mdata, Args&& ...args)
84 { return pw_metadata_add_listener(mdata, std::forward<Args>(args)...); }
87 constexpr auto get_pod_type(const spa_pod *pod) noexcept
88 { return SPA_POD_TYPE(pod); }
90 template<typename T>
91 constexpr auto get_pod_body(const spa_pod *pod, size_t count) noexcept
92 { return al::span<T>{static_cast<T*>(SPA_POD_BODY(pod)), count}; }
93 template<typename T, size_t N>
94 constexpr auto get_pod_body(const spa_pod *pod) noexcept
95 { return al::span<T,N>{static_cast<T*>(SPA_POD_BODY(pod)), N}; }
97 constexpr auto make_pod_builder(void *data, uint32_t size) noexcept
98 { return SPA_POD_BUILDER_INIT(data, size); }
100 constexpr auto get_array_value_type(const spa_pod *pod) noexcept
101 { return SPA_POD_ARRAY_VALUE_TYPE(pod); }
103 constexpr auto PwIdAny = PW_ID_ANY;
105 } // namespace
106 _Pragma("GCC diagnostic pop")
108 namespace {
110 using std::chrono::seconds;
111 using std::chrono::nanoseconds;
112 using uint = unsigned int;
114 constexpr char pwireDevice[] = "PipeWire Output";
115 constexpr char pwireInput[] = "PipeWire Input";
118 #ifdef HAVE_DYNLOAD
119 #define PWIRE_FUNCS(MAGIC) \
120 MAGIC(pw_context_connect) \
121 MAGIC(pw_context_destroy) \
122 MAGIC(pw_context_new) \
123 MAGIC(pw_core_disconnect) \
124 MAGIC(pw_init) \
125 MAGIC(pw_properties_free) \
126 MAGIC(pw_properties_new) \
127 MAGIC(pw_properties_set) \
128 MAGIC(pw_properties_setf) \
129 MAGIC(pw_proxy_add_object_listener) \
130 MAGIC(pw_proxy_destroy) \
131 MAGIC(pw_proxy_get_user_data) \
132 MAGIC(pw_stream_add_listener) \
133 MAGIC(pw_stream_connect) \
134 MAGIC(pw_stream_dequeue_buffer) \
135 MAGIC(pw_stream_destroy) \
136 MAGIC(pw_stream_get_state) \
137 MAGIC(pw_stream_get_time) \
138 MAGIC(pw_stream_new) \
139 MAGIC(pw_stream_queue_buffer) \
140 MAGIC(pw_stream_set_active) \
141 MAGIC(pw_thread_loop_new) \
142 MAGIC(pw_thread_loop_destroy) \
143 MAGIC(pw_thread_loop_get_loop) \
144 MAGIC(pw_thread_loop_start) \
145 MAGIC(pw_thread_loop_stop) \
146 MAGIC(pw_thread_loop_lock) \
147 MAGIC(pw_thread_loop_wait) \
148 MAGIC(pw_thread_loop_signal) \
149 MAGIC(pw_thread_loop_unlock) \
151 void *pwire_handle;
152 #define MAKE_FUNC(f) decltype(f) * p##f;
153 PWIRE_FUNCS(MAKE_FUNC)
154 #undef MAKE_FUNC
156 bool pwire_load()
158 if(pwire_handle)
159 return true;
161 static constexpr char pwire_library[] = "libpipewire-0.3.so.0";
162 std::string missing_funcs;
164 pwire_handle = LoadLib(pwire_library);
165 if(!pwire_handle)
167 WARN("Failed to load %s\n", pwire_library);
168 return false;
171 #define LOAD_FUNC(f) do { \
172 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \
173 if(p##f == nullptr) missing_funcs += "\n" #f; \
174 } while(0);
175 PWIRE_FUNCS(LOAD_FUNC)
176 #undef LOAD_FUNC
178 if(!missing_funcs.empty())
180 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
181 CloseLib(pwire_handle);
182 pwire_handle = nullptr;
183 return false;
186 return true;
189 #ifndef IN_IDE_PARSER
190 #define pw_context_connect ppw_context_connect
191 #define pw_context_destroy ppw_context_destroy
192 #define pw_context_new ppw_context_new
193 #define pw_core_disconnect ppw_core_disconnect
194 #define pw_init ppw_init
195 #define pw_properties_free ppw_properties_free
196 #define pw_properties_new ppw_properties_new
197 #define pw_properties_set ppw_properties_set
198 #define pw_properties_setf ppw_properties_setf
199 #define pw_proxy_add_object_listener ppw_proxy_add_object_listener
200 #define pw_proxy_destroy ppw_proxy_destroy
201 #define pw_proxy_get_user_data ppw_proxy_get_user_data
202 #define pw_stream_add_listener ppw_stream_add_listener
203 #define pw_stream_connect ppw_stream_connect
204 #define pw_stream_dequeue_buffer ppw_stream_dequeue_buffer
205 #define pw_stream_destroy ppw_stream_destroy
206 #define pw_stream_get_state ppw_stream_get_state
207 #define pw_stream_get_time ppw_stream_get_time
208 #define pw_stream_new ppw_stream_new
209 #define pw_stream_queue_buffer ppw_stream_queue_buffer
210 #define pw_stream_set_active ppw_stream_set_active
211 #define pw_thread_loop_destroy ppw_thread_loop_destroy
212 #define pw_thread_loop_get_loop ppw_thread_loop_get_loop
213 #define pw_thread_loop_lock ppw_thread_loop_lock
214 #define pw_thread_loop_new ppw_thread_loop_new
215 #define pw_thread_loop_signal ppw_thread_loop_signal
216 #define pw_thread_loop_start ppw_thread_loop_start
217 #define pw_thread_loop_stop ppw_thread_loop_stop
218 #define pw_thread_loop_unlock ppw_thread_loop_unlock
219 #define pw_thread_loop_wait ppw_thread_loop_wait
220 #endif
222 #else
224 constexpr bool pwire_load() { return true; }
225 #endif
227 /* Helpers for retrieving values from params */
228 template<uint32_t T> struct PodInfo { };
230 template<>
231 struct PodInfo<SPA_TYPE_Int> {
232 using Type = int32_t;
233 static auto get_value(const spa_pod *pod, int32_t *val)
234 { return spa_pod_get_int(pod, val); }
236 template<>
237 struct PodInfo<SPA_TYPE_Id> {
238 using Type = uint32_t;
239 static auto get_value(const spa_pod *pod, uint32_t *val)
240 { return spa_pod_get_id(pod, val); }
243 template<uint32_t T>
244 using Pod_t = typename PodInfo<T>::Type;
246 template<uint32_t T>
247 al::span<const Pod_t<T>> get_array_span(const spa_pod *pod)
249 uint32_t nvals;
250 if(void *v{spa_pod_get_array(pod, &nvals)})
252 if(get_array_value_type(pod) == T)
253 return {static_cast<const Pod_t<T>*>(v), nvals};
255 return {};
258 template<uint32_t T>
259 al::optional<Pod_t<T>> get_value(const spa_pod *value)
261 Pod_t<T> val{};
262 if(PodInfo<T>::get_value(value, &val) == 0)
263 return val;
264 return al::nullopt;
267 /* Internally, PipeWire types "inherit" from each other, but this is hidden
268 * from the API and the caller is expected to C-style cast to inherited types
269 * as needed. It's also not made very clear what types a given type can be
270 * casted to. To make it a bit safer, this as() method allows casting pw_*
271 * types to known inherited types, generating a compile-time error for
272 * unexpected/invalid casts.
274 template<typename To, typename From>
275 To as(From) noexcept = delete;
277 /* pw_proxy
278 * - pw_registry
279 * - pw_node
280 * - pw_metadata
282 template<>
283 pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); }
284 template<>
285 pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); }
286 template<>
287 pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); }
290 struct PwContextDeleter {
291 void operator()(pw_context *context) const { pw_context_destroy(context); }
293 using PwContextPtr = std::unique_ptr<pw_context,PwContextDeleter>;
295 struct PwCoreDeleter {
296 void operator()(pw_core *core) const { pw_core_disconnect(core); }
298 using PwCorePtr = std::unique_ptr<pw_core,PwCoreDeleter>;
300 struct PwRegistryDeleter {
301 void operator()(pw_registry *reg) const { pw_proxy_destroy(as<pw_proxy*>(reg)); }
303 using PwRegistryPtr = std::unique_ptr<pw_registry,PwRegistryDeleter>;
305 struct PwNodeDeleter {
306 void operator()(pw_node *node) const { pw_proxy_destroy(as<pw_proxy*>(node)); }
308 using PwNodePtr = std::unique_ptr<pw_node,PwNodeDeleter>;
310 struct PwMetadataDeleter {
311 void operator()(pw_metadata *mdata) const { pw_proxy_destroy(as<pw_proxy*>(mdata)); }
313 using PwMetadataPtr = std::unique_ptr<pw_metadata,PwMetadataDeleter>;
315 struct PwStreamDeleter {
316 void operator()(pw_stream *stream) const { pw_stream_destroy(stream); }
318 using PwStreamPtr = std::unique_ptr<pw_stream,PwStreamDeleter>;
320 /* Enums for bitflags... again... *sigh* */
321 constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept
322 { return static_cast<pw_stream_flags>(lhs | std::underlying_type_t<pw_stream_flags>{rhs}); }
324 class ThreadMainloop {
325 pw_thread_loop *mLoop{};
327 public:
328 ThreadMainloop() = default;
329 ThreadMainloop(const ThreadMainloop&) = delete;
330 ThreadMainloop(ThreadMainloop&& rhs) noexcept : mLoop{rhs.mLoop} { rhs.mLoop = nullptr; }
331 explicit ThreadMainloop(pw_thread_loop *loop) noexcept : mLoop{loop} { }
332 ~ThreadMainloop() { if(mLoop) pw_thread_loop_destroy(mLoop); }
334 ThreadMainloop& operator=(const ThreadMainloop&) = delete;
335 ThreadMainloop& operator=(ThreadMainloop&& rhs) noexcept
336 { std::swap(mLoop, rhs.mLoop); return *this; }
337 ThreadMainloop& operator=(std::nullptr_t) noexcept
339 if(mLoop)
340 pw_thread_loop_destroy(mLoop);
341 mLoop = nullptr;
342 return *this;
345 explicit operator bool() const noexcept { return mLoop != nullptr; }
347 auto start() const { return pw_thread_loop_start(mLoop); }
348 auto stop() const { return pw_thread_loop_stop(mLoop); }
350 auto getLoop() const { return pw_thread_loop_get_loop(mLoop); }
352 auto lock() const { return pw_thread_loop_lock(mLoop); }
353 auto unlock() const { return pw_thread_loop_unlock(mLoop); }
355 auto signal(bool wait) const { return pw_thread_loop_signal(mLoop, wait); }
357 auto newContext(pw_properties *props=nullptr, size_t user_data_size=0)
358 { return PwContextPtr{pw_context_new(getLoop(), props, user_data_size)}; }
360 static auto Create(const char *name, spa_dict *props=nullptr)
361 { return ThreadMainloop{pw_thread_loop_new(name, props)}; }
363 friend struct MainloopUniqueLock;
365 struct MainloopUniqueLock : public std::unique_lock<ThreadMainloop> {
366 using std::unique_lock<ThreadMainloop>::unique_lock;
367 MainloopUniqueLock& operator=(MainloopUniqueLock&&) = default;
369 auto wait() const -> void
370 { pw_thread_loop_wait(mutex()->mLoop); }
372 template<typename Predicate>
373 auto wait(Predicate done_waiting) const -> void
374 { while(!done_waiting()) wait(); }
376 using MainloopLockGuard = std::lock_guard<ThreadMainloop>;
379 /* There's quite a mess here, but the purpose is to track active devices and
380 * their default formats, so playback devices can be configured to match. The
381 * device list is updated asynchronously, so it will have the latest list of
382 * devices provided by the server.
385 struct NodeProxy;
386 struct MetadataProxy;
388 /* The global thread watching for global events. This particular class responds
389 * to objects being added to or removed from the registry.
391 struct EventManager {
392 ThreadMainloop mLoop{};
393 PwContextPtr mContext{};
394 PwCorePtr mCore{};
395 PwRegistryPtr mRegistry{};
396 spa_hook mRegistryListener{};
397 spa_hook mCoreListener{};
399 /* A list of proxy objects watching for events about changes to objects in
400 * the registry.
402 std::vector<NodeProxy*> mNodeList;
403 MetadataProxy *mDefaultMetadata{nullptr};
405 /* Initialization handling. When init() is called, mInitSeq is set to a
406 * SequenceID that marks the end of populating the registry. As objects of
407 * interest are found, events to parse them are generated and mInitSeq is
408 * updated with a newer ID. When mInitSeq stops being updated and the event
409 * corresponding to it is reached, mInitDone will be set to true.
411 std::atomic<bool> mInitDone{false};
412 std::atomic<bool> mHasAudio{false};
413 int mInitSeq{};
415 bool init();
416 ~EventManager();
418 void kill();
420 auto lock() const { return mLoop.lock(); }
421 auto unlock() const { return mLoop.unlock(); }
424 * Waits for initialization to finish. The event manager must *NOT* be
425 * locked when calling this.
427 void waitForInit()
429 if(unlikely(!mInitDone.load(std::memory_order_acquire)))
431 MainloopUniqueLock plock{mLoop};
432 plock.wait([this](){ return mInitDone.load(std::memory_order_acquire); });
437 * Waits for audio support to be detected, or initialization to finish,
438 * whichever is first. Returns true if audio support was detected. The
439 * event manager must *NOT* be locked when calling this.
441 bool waitForAudio()
443 MainloopUniqueLock plock{mLoop};
444 bool has_audio{};
445 plock.wait([this,&has_audio]()
447 has_audio = mHasAudio.load(std::memory_order_acquire);
448 return has_audio || mInitDone.load(std::memory_order_acquire);
450 return has_audio;
453 void syncInit()
455 /* If initialization isn't done, update the sequence ID so it won't
456 * complete until after currently scheduled events.
458 if(!mInitDone.load(std::memory_order_relaxed))
459 mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, mInitSeq);
462 void addCallback(uint32_t id, uint32_t permissions, const char *type, uint32_t version,
463 const spa_dict *props);
464 static void addCallbackC(void *object, uint32_t id, uint32_t permissions, const char *type,
465 uint32_t version, const spa_dict *props)
466 { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); }
468 void removeCallback(uint32_t id);
469 static void removeCallbackC(void *object, uint32_t id)
470 { static_cast<EventManager*>(object)->removeCallback(id); }
472 static constexpr pw_registry_events CreateRegistryEvents()
474 pw_registry_events ret{};
475 ret.version = PW_VERSION_REGISTRY_EVENTS;
476 ret.global = &EventManager::addCallbackC;
477 ret.global_remove = &EventManager::removeCallbackC;
478 return ret;
481 void coreCallback(uint32_t id, int seq);
482 static void coreCallbackC(void *object, uint32_t id, int seq)
483 { static_cast<EventManager*>(object)->coreCallback(id, seq); }
485 static constexpr pw_core_events CreateCoreEvents()
487 pw_core_events ret{};
488 ret.version = PW_VERSION_CORE_EVENTS;
489 ret.done = &EventManager::coreCallbackC;
490 return ret;
493 using EventWatcherUniqueLock = std::unique_lock<EventManager>;
494 using EventWatcherLockGuard = std::lock_guard<EventManager>;
496 EventManager gEventHandler;
498 /* Enumerated devices. This is updated asynchronously as the app runs, and the
499 * gEventHandler thread loop must be locked when accessing the list.
501 enum class NodeType : unsigned char {
502 Sink, Source, Duplex
504 constexpr auto InvalidChannelConfig = DevFmtChannels(255);
505 struct DeviceNode {
506 std::string mName;
507 std::string mDevName;
509 uint32_t mId{};
510 NodeType mType{};
511 bool mIsHeadphones{};
512 bool mIs51Rear{};
514 uint mSampleRate{};
515 DevFmtChannels mChannels{InvalidChannelConfig};
517 static std::vector<DeviceNode> sList;
518 static DeviceNode &Add(uint32_t id);
519 static DeviceNode *Find(uint32_t id);
520 static void Remove(uint32_t id);
521 static std::vector<DeviceNode> &GetList() noexcept { return sList; }
523 void parseSampleRate(const spa_pod *value) noexcept;
524 void parsePositions(const spa_pod *value) noexcept;
525 void parseChannelCount(const spa_pod *value) noexcept;
527 std::vector<DeviceNode> DeviceNode::sList;
528 std::string DefaultSinkDevice;
529 std::string DefaultSourceDevice;
531 const char *AsString(NodeType type) noexcept
533 switch(type)
535 case NodeType::Sink: return "sink";
536 case NodeType::Source: return "source";
537 case NodeType::Duplex: return "duplex";
539 return "<unknown>";
542 DeviceNode &DeviceNode::Add(uint32_t id)
544 auto match_id = [id](DeviceNode &n) noexcept -> bool
545 { return n.mId == id; };
547 /* If the node is already in the list, return the existing entry. */
548 auto match = std::find_if(sList.begin(), sList.end(), match_id);
549 if(match != sList.end()) return *match;
551 sList.emplace_back();
552 auto &n = sList.back();
553 n.mId = id;
554 return n;
557 DeviceNode *DeviceNode::Find(uint32_t id)
559 auto match_id = [id](DeviceNode &n) noexcept -> bool
560 { return n.mId == id; };
562 auto match = std::find_if(sList.begin(), sList.end(), match_id);
563 if(match != sList.end()) return std::addressof(*match);
565 return nullptr;
568 void DeviceNode::Remove(uint32_t id)
570 auto match_id = [id](DeviceNode &n) noexcept -> bool
572 if(n.mId != id)
573 return false;
574 TRACE("Removing device \"%s\"\n", n.mDevName.c_str());
575 return true;
578 auto end = std::remove_if(sList.begin(), sList.end(), match_id);
579 sList.erase(end, sList.end());
583 const spa_audio_channel MonoMap[]{
584 SPA_AUDIO_CHANNEL_MONO
585 }, StereoMap[] {
586 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR
587 }, QuadMap[]{
588 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR
589 }, X51Map[]{
590 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
591 SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
592 }, X51RearMap[]{
593 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
594 SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR
595 }, X61Map[]{
596 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
597 SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
598 }, X71Map[]{
599 SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE,
600 SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR
604 * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal
605 * to or a superset of map1).
607 template<size_t N>
608 bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channel (&map1)[N])
610 if(map0.size() < N)
611 return false;
612 for(const spa_audio_channel chid : map1)
614 if(std::find(map0.begin(), map0.end(), chid) == map0.end())
615 return false;
617 return true;
620 void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
622 /* TODO: Can this be anything else? Long, Float, Double? */
623 uint32_t nvals{}, choiceType{};
624 value = spa_pod_get_values(value, &nvals, &choiceType);
626 const uint podType{get_pod_type(value)};
627 if(podType != SPA_TYPE_Int)
629 WARN("Unhandled sample rate POD type: %u\n", podType);
630 return;
633 if(choiceType == SPA_CHOICE_Range)
635 if(nvals != 3)
637 WARN("Unexpected SPA_CHOICE_Range count: %u\n", nvals);
638 return;
640 auto srates = get_pod_body<int32_t,3>(value);
642 /* [0] is the default, [1] is the min, and [2] is the max. */
643 TRACE("Device ID %u sample rate: %d (range: %d -> %d)\n", mId, srates[0], srates[1],
644 srates[2]);
645 mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
646 return;
649 if(choiceType == SPA_CHOICE_Enum)
651 if(nvals == 0)
653 WARN("Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
654 return;
656 auto srates = get_pod_body<int32_t>(value, nvals);
658 /* [0] is the default, [1...size()-1] are available selections. */
659 std::string others{(srates.size() > 1) ? std::to_string(srates[1]) : std::string{}};
660 for(size_t i{2};i < srates.size();++i)
662 others += ", ";
663 others += std::to_string(srates[i]);
665 TRACE("Device ID %u sample rate: %d (%s)\n", mId, srates[0], others.c_str());
666 /* Pick the first rate listed that's within the allowed range (default
667 * rate if possible).
669 for(const auto &rate : srates)
671 if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE)
673 mSampleRate = static_cast<uint>(rate);
674 break;
677 return;
680 if(choiceType == SPA_CHOICE_None)
682 if(nvals != 1)
684 WARN("Unexpected SPA_CHOICE_None count: %u\n", nvals);
685 return;
687 auto srates = get_pod_body<int32_t,1>(value);
689 TRACE("Device ID %u sample rate: %d\n", mId, srates[0]);
690 mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
691 return;
694 WARN("Unhandled sample rate choice type: %u\n", choiceType);
697 void DeviceNode::parsePositions(const spa_pod *value) noexcept
699 const auto chanmap = get_array_span<SPA_TYPE_Id>(value);
700 if(chanmap.empty()) return;
702 mIs51Rear = false;
704 if(MatchChannelMap(chanmap, X71Map))
705 mChannels = DevFmtX71;
706 else if(MatchChannelMap(chanmap, X61Map))
707 mChannels = DevFmtX61;
708 else if(MatchChannelMap(chanmap, X51Map))
709 mChannels = DevFmtX51;
710 else if(MatchChannelMap(chanmap, X51RearMap))
712 mChannels = DevFmtX51;
713 mIs51Rear = true;
715 else if(MatchChannelMap(chanmap, QuadMap))
716 mChannels = DevFmtQuad;
717 else if(MatchChannelMap(chanmap, StereoMap))
718 mChannels = DevFmtStereo;
719 else
720 mChannels = DevFmtMono;
721 TRACE("Device ID %u got %zu position%s for %s%s\n", mId, chanmap.size(),
722 (chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
725 void DeviceNode::parseChannelCount(const spa_pod *value) noexcept
727 /* As a fallback with just a channel count, just assume mono or stereo. */
728 const auto chancount = get_value<SPA_TYPE_Int>(value);
729 if(!chancount) return;
731 mIs51Rear = false;
733 if(*chancount >= 2)
734 mChannels = DevFmtStereo;
735 else if(*chancount >= 1)
736 mChannels = DevFmtMono;
737 TRACE("Device ID %u got %d channel%s for %s\n", mId, *chancount, (*chancount==1)?"":"s",
738 DevFmtChannelsString(mChannels));
742 constexpr char MonitorPrefix[]{"Monitor of "};
743 constexpr auto MonitorPrefixLen = al::size(MonitorPrefix) - 1;
744 constexpr char AudioSinkClass[]{"Audio/Sink"};
745 constexpr char AudioSourceClass[]{"Audio/Source"};
746 constexpr char AudioDuplexClass[]{"Audio/Duplex"};
747 constexpr char StreamClass[]{"Stream/"};
749 /* A generic PipeWire node proxy object used to track changes to sink and
750 * source nodes.
752 struct NodeProxy {
753 static constexpr pw_node_events CreateNodeEvents()
755 pw_node_events ret{};
756 ret.version = PW_VERSION_NODE_EVENTS;
757 ret.info = &NodeProxy::infoCallbackC;
758 ret.param = &NodeProxy::paramCallbackC;
759 return ret;
762 uint32_t mId{};
764 PwNodePtr mNode{};
765 spa_hook mListener{};
767 NodeProxy(uint32_t id, PwNodePtr node)
768 : mId{id}, mNode{std::move(node)}
770 static constexpr pw_node_events nodeEvents{CreateNodeEvents()};
771 ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this);
773 /* Track changes to the enumerable formats (indicates the default
774 * format, which is what we're interested in).
776 uint32_t fmtids[]{SPA_PARAM_EnumFormat};
777 ppw_node_subscribe_params(mNode.get(), al::data(fmtids), al::size(fmtids));
779 ~NodeProxy()
780 { spa_hook_remove(&mListener); }
783 void infoCallback(const pw_node_info *info);
784 static void infoCallbackC(void *object, const pw_node_info *info)
785 { static_cast<NodeProxy*>(object)->infoCallback(info); }
787 void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param);
788 static void paramCallbackC(void *object, int seq, uint32_t id, uint32_t index, uint32_t next,
789 const spa_pod *param)
790 { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); }
793 void NodeProxy::infoCallback(const pw_node_info *info)
795 /* We only care about property changes here (media class, name/desc).
796 * Format changes will automatically invoke the param callback.
798 * TODO: Can the media class or name/desc change without being removed and
799 * readded?
801 if((info->change_mask&PW_NODE_CHANGE_MASK_PROPS))
803 /* Can this actually change? */
804 const char *media_class{spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS)};
805 if(unlikely(!media_class)) return;
807 NodeType ntype{};
808 if(al::strcasecmp(media_class, AudioSinkClass) == 0)
809 ntype = NodeType::Sink;
810 else if(al::strcasecmp(media_class, AudioSourceClass) == 0)
811 ntype = NodeType::Source;
812 else if(al::strcasecmp(media_class, AudioDuplexClass) == 0)
813 ntype = NodeType::Duplex;
814 else
816 TRACE("Dropping device node %u which became type \"%s\"\n", info->id, media_class);
817 DeviceNode::Remove(info->id);
818 return;
821 const char *devName{spa_dict_lookup(info->props, PW_KEY_NODE_NAME)};
822 const char *nodeName{spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION)};
823 if(!nodeName || !*nodeName) nodeName = spa_dict_lookup(info->props, PW_KEY_NODE_NICK);
824 if(!nodeName || !*nodeName) nodeName = devName;
826 const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
827 TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
828 form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
829 TRACE(" \"%s\" = ID %u\n", nodeName ? nodeName : "(nil)", info->id);
831 DeviceNode &node = DeviceNode::Add(info->id);
832 if(nodeName && *nodeName) node.mName = nodeName;
833 else node.mName = "PipeWire node #"+std::to_string(info->id);
834 node.mDevName = devName ? devName : "";
835 node.mType = ntype;
836 node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0
837 || al::strcasecmp(form_factor, "headset") == 0);
841 void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param)
843 if(id == SPA_PARAM_EnumFormat)
845 DeviceNode *node{DeviceNode::Find(mId)};
846 if(unlikely(!node)) return;
848 if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)})
849 node->parseSampleRate(&prop->value);
851 if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)})
852 node->parsePositions(&prop->value);
853 else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr)
854 node->parseChannelCount(&prop->value);
859 /* A metadata proxy object used to query the default sink and source. */
860 struct MetadataProxy {
861 static constexpr pw_metadata_events CreateMetadataEvents()
863 pw_metadata_events ret{};
864 ret.version = PW_VERSION_METADATA_EVENTS;
865 ret.property = &MetadataProxy::propertyCallbackC;
866 return ret;
869 uint32_t mId{};
871 PwMetadataPtr mMetadata{};
872 spa_hook mListener{};
874 MetadataProxy(uint32_t id, PwMetadataPtr mdata)
875 : mId{id}, mMetadata{std::move(mdata)}
877 static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()};
878 ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this);
880 ~MetadataProxy()
881 { spa_hook_remove(&mListener); }
884 int propertyCallback(uint32_t id, const char *key, const char *type, const char *value);
885 static int propertyCallbackC(void *object, uint32_t id, const char *key, const char *type,
886 const char *value)
887 { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); }
890 int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type,
891 const char *value)
893 if(id != PW_ID_CORE)
894 return 0;
896 bool isCapture{};
897 if(std::strcmp(key, "default.audio.sink") == 0)
898 isCapture = false;
899 else if(std::strcmp(key, "default.audio.source") == 0)
900 isCapture = true;
901 else
902 return 0;
904 if(!type)
906 TRACE("Default %s device cleared\n", isCapture ? "capture" : "playback");
907 if(!isCapture) DefaultSinkDevice.clear();
908 else DefaultSourceDevice.clear();
909 return 0;
911 if(std::strcmp(type, "Spa:String:JSON") != 0)
913 ERR("Unexpected %s property type: %s\n", key, type);
914 return 0;
917 spa_json it[2]{};
918 spa_json_init(&it[0], value, strlen(value));
919 if(spa_json_enter_object(&it[0], &it[1]) <= 0)
920 return 0;
922 auto get_json_string = [](spa_json *iter)
924 al::optional<std::string> str;
926 const char *val{};
927 int len{spa_json_next(iter, &val)};
928 if(len <= 0) return str;
930 str.emplace().resize(static_cast<uint>(len), '\0');
931 if(spa_json_parse_string(val, len, &str->front()) <= 0)
932 str.reset();
933 else while(!str->empty() && str->back() == '\0')
934 str->pop_back();
935 return str;
937 while(auto propKey = get_json_string(&it[1]))
939 if(*propKey == "name")
941 auto propValue = get_json_string(&it[1]);
942 if(!propValue) break;
944 TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback",
945 propValue->c_str());
946 if(!isCapture)
947 DefaultSinkDevice = std::move(*propValue);
948 else
949 DefaultSourceDevice = std::move(*propValue);
951 else
953 const char *v{};
954 if(spa_json_next(&it[1], &v) <= 0)
955 break;
958 return 0;
962 bool EventManager::init()
964 mLoop = ThreadMainloop::Create("PWEventThread");
965 if(!mLoop)
967 ERR("Failed to create PipeWire event thread loop (errno: %d)\n", errno);
968 return false;
971 mContext = mLoop.newContext(pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr));
972 if(!mContext)
974 ERR("Failed to create PipeWire event context (errno: %d)\n", errno);
975 return false;
978 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
979 if(!mCore)
981 ERR("Failed to connect PipeWire event context (errno: %d)\n", errno);
982 return false;
985 mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)};
986 if(!mRegistry)
988 ERR("Failed to get PipeWire event registry (errno: %d)\n", errno);
989 return false;
992 static constexpr pw_core_events coreEvents{CreateCoreEvents()};
993 static constexpr pw_registry_events registryEvents{CreateRegistryEvents()};
995 ppw_core_add_listener(mCore.get(), &mCoreListener, &coreEvents, this);
996 ppw_registry_add_listener(mRegistry.get(), &mRegistryListener, &registryEvents, this);
998 /* Set an initial sequence ID for initialization, to trigger after the
999 * registry is first populated.
1001 mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, 0);
1003 if(int res{mLoop.start()})
1005 ERR("Failed to start PipeWire event thread loop (res: %d)\n", res);
1006 return false;
1009 return true;
1012 EventManager::~EventManager()
1014 if(mLoop) mLoop.stop();
1016 for(NodeProxy *node : mNodeList)
1017 al::destroy_at(node);
1018 if(mDefaultMetadata)
1019 al::destroy_at(mDefaultMetadata);
1022 void EventManager::kill()
1024 if(mLoop) mLoop.stop();
1026 for(NodeProxy *node : mNodeList)
1027 al::destroy_at(node);
1028 mNodeList.clear();
1029 if(mDefaultMetadata)
1030 al::destroy_at(mDefaultMetadata);
1031 mDefaultMetadata = nullptr;
1033 mRegistry = nullptr;
1034 mCore = nullptr;
1035 mContext = nullptr;
1036 mLoop = nullptr;
1039 void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t version,
1040 const spa_dict *props)
1042 /* We're only interested in interface nodes. */
1043 if(std::strcmp(type, PW_TYPE_INTERFACE_Node) == 0)
1045 const char *media_class{spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)};
1046 if(!media_class) return;
1048 /* Specifically, audio sinks and sources (and duplexes). */
1049 const bool isGood{al::strcasecmp(media_class, AudioSinkClass) == 0
1050 || al::strcasecmp(media_class, AudioSourceClass) == 0
1051 || al::strcasecmp(media_class, AudioDuplexClass) == 0};
1052 if(!isGood)
1054 if(std::strstr(media_class, "/Video") == nullptr
1055 && std::strncmp(media_class, StreamClass, sizeof(StreamClass)-1) != 0)
1056 TRACE("Ignoring node class %s\n", media_class);
1057 return;
1060 /* Create the proxy object. */
1061 auto node = PwNodePtr{static_cast<pw_node*>(pw_registry_bind(mRegistry.get(), id, type,
1062 version, sizeof(NodeProxy)))};
1063 if(!node)
1065 ERR("Failed to create node proxy object (errno: %d)\n", errno);
1066 return;
1069 /* Initialize the NodeProxy to hold the node object, add it to the
1070 * active node list, and update the sync point.
1072 auto *proxy = static_cast<NodeProxy*>(pw_proxy_get_user_data(as<pw_proxy*>(node.get())));
1073 mNodeList.emplace_back(al::construct_at(proxy, id, std::move(node)));
1074 syncInit();
1076 /* Signal any waiters that we have found a source or sink for audio
1077 * support.
1079 if(!mHasAudio.exchange(true, std::memory_order_acq_rel))
1080 mLoop.signal(false);
1082 else if(std::strcmp(type, PW_TYPE_INTERFACE_Metadata) == 0)
1084 const char *data_class{spa_dict_lookup(props, PW_KEY_METADATA_NAME)};
1085 if(!data_class) return;
1087 if(std::strcmp(data_class, "default") != 0)
1089 TRACE("Ignoring metadata \"%s\"\n", data_class);
1090 return;
1093 if(mDefaultMetadata)
1095 ERR("Duplicate default metadata\n");
1096 return;
1099 auto mdata = PwMetadataPtr{static_cast<pw_metadata*>(pw_registry_bind(mRegistry.get(), id,
1100 type, version, sizeof(MetadataProxy)))};
1101 if(!mdata)
1103 ERR("Failed to create metadata proxy object (errno: %d)\n", errno);
1104 return;
1107 auto *proxy = static_cast<MetadataProxy*>(
1108 pw_proxy_get_user_data(as<pw_proxy*>(mdata.get())));
1109 mDefaultMetadata = al::construct_at(proxy, id, std::move(mdata));
1110 syncInit();
1114 void EventManager::removeCallback(uint32_t id)
1116 DeviceNode::Remove(id);
1118 auto clear_node = [id](NodeProxy *node) noexcept
1120 if(node->mId != id)
1121 return false;
1122 al::destroy_at(node);
1123 return true;
1125 auto node_end = std::remove_if(mNodeList.begin(), mNodeList.end(), clear_node);
1126 mNodeList.erase(node_end, mNodeList.end());
1128 if(mDefaultMetadata && mDefaultMetadata->mId == id)
1130 al::destroy_at(mDefaultMetadata);
1131 mDefaultMetadata = nullptr;
1135 void EventManager::coreCallback(uint32_t id, int seq)
1137 if(id == PW_ID_CORE && seq == mInitSeq)
1139 /* Initialization done. Remove this callback and signal anyone that may
1140 * be waiting.
1142 spa_hook_remove(&mCoreListener);
1144 mInitDone.store(true);
1145 mLoop.signal(false);
1150 enum use_f32p_e : bool { UseDevType=false, ForceF32Planar=true };
1151 spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e use_f32p)
1153 spa_audio_info_raw info{};
1154 if(use_f32p)
1156 device->FmtType = DevFmtFloat;
1157 info.format = SPA_AUDIO_FORMAT_F32P;
1159 else switch(device->FmtType)
1161 case DevFmtByte: info.format = SPA_AUDIO_FORMAT_S8; break;
1162 case DevFmtUByte: info.format = SPA_AUDIO_FORMAT_U8; break;
1163 case DevFmtShort: info.format = SPA_AUDIO_FORMAT_S16; break;
1164 case DevFmtUShort: info.format = SPA_AUDIO_FORMAT_U16; break;
1165 case DevFmtInt: info.format = SPA_AUDIO_FORMAT_S32; break;
1166 case DevFmtUInt: info.format = SPA_AUDIO_FORMAT_U32; break;
1167 case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break;
1170 info.rate = device->Frequency;
1172 al::span<const spa_audio_channel> map{};
1173 switch(device->FmtChans)
1175 case DevFmtMono: map = MonoMap; break;
1176 case DevFmtStereo: map = StereoMap; break;
1177 case DevFmtQuad: map = QuadMap; break;
1178 case DevFmtX51:
1179 if(is51rear) map = X51RearMap;
1180 else map = X51Map;
1181 break;
1182 case DevFmtX61: map = X61Map; break;
1183 case DevFmtX71: map = X71Map; break;
1184 case DevFmtAmbi3D:
1185 info.flags |= SPA_AUDIO_FLAG_UNPOSITIONED;
1186 info.channels = device->channelsFromFmt();
1187 break;
1189 if(!map.empty())
1191 info.channels = static_cast<uint32_t>(map.size());
1192 std::copy(map.begin(), map.end(), info.position);
1195 return info;
1198 class PipeWirePlayback final : public BackendBase {
1199 void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
1200 static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
1201 const char *error)
1202 { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); }
1204 void ioChangedCallback(uint32_t id, void *area, uint32_t size);
1205 static void ioChangedCallbackC(void *data, uint32_t id, void *area, uint32_t size)
1206 { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); }
1208 void outputCallback();
1209 static void outputCallbackC(void *data)
1210 { static_cast<PipeWirePlayback*>(data)->outputCallback(); }
1212 void open(const char *name) override;
1213 bool reset() override;
1214 void start() override;
1215 void stop() override;
1216 ClockLatency getClockLatency() override;
1218 uint32_t mTargetId{PwIdAny};
1219 nanoseconds mTimeBase{0};
1220 ThreadMainloop mLoop;
1221 PwContextPtr mContext;
1222 PwCorePtr mCore;
1223 PwStreamPtr mStream;
1224 spa_hook mStreamListener{};
1225 spa_io_rate_match *mRateMatch{};
1226 std::unique_ptr<float*[]> mChannelPtrs;
1227 uint mNumChannels{};
1229 static constexpr pw_stream_events CreateEvents()
1231 pw_stream_events ret{};
1232 ret.version = PW_VERSION_STREAM_EVENTS;
1233 ret.state_changed = &PipeWirePlayback::stateChangedCallbackC;
1234 ret.io_changed = &PipeWirePlayback::ioChangedCallbackC;
1235 ret.process = &PipeWirePlayback::outputCallbackC;
1236 return ret;
1239 public:
1240 PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
1241 ~PipeWirePlayback()
1243 /* Stop the mainloop so the stream can be properly destroyed. */
1244 if(mLoop) mLoop.stop();
1247 DEF_NEWDEL(PipeWirePlayback)
1251 void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
1252 { mLoop.signal(false); }
1254 void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size)
1256 switch(id)
1258 case SPA_IO_RateMatch:
1259 if(size >= sizeof(spa_io_rate_match))
1260 mRateMatch = static_cast<spa_io_rate_match*>(area);
1261 break;
1265 void PipeWirePlayback::outputCallback()
1267 pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
1268 if(unlikely(!pw_buf)) return;
1270 /* For planar formats, each datas[] seems to contain one channel, so store
1271 * the pointers in an array. Limit the render length in case the available
1272 * buffer length in any one channel is smaller than we wanted (shouldn't
1273 * be, but just in case).
1275 spa_data *datas{pw_buf->buffer->datas};
1276 const size_t chancount{minu(mNumChannels, pw_buf->buffer->n_datas)};
1277 /* TODO: How many samples should actually be written? 'maxsize' can be 16k
1278 * samples, which is excessive (~341ms @ 48khz). SPA_IO_RateMatch contains
1279 * a 'size' field that apparently indicates how many samples should be
1280 * written per update, but it's not obviously right.
1282 uint length{mRateMatch ? mRateMatch->size : mDevice->UpdateSize};
1283 for(size_t i{0};i < chancount;++i)
1285 length = minu(length, datas[i].maxsize/sizeof(float));
1286 mChannelPtrs[i] = static_cast<float*>(datas[i].data);
1289 mDevice->renderSamples({mChannelPtrs.get(), chancount}, length);
1291 for(size_t i{0};i < chancount;++i)
1293 datas[i].chunk->offset = 0;
1294 datas[i].chunk->stride = sizeof(float);
1295 datas[i].chunk->size = length * sizeof(float);
1297 pw_buf->size = length;
1298 pw_stream_queue_buffer(mStream.get(), pw_buf);
1302 void PipeWirePlayback::open(const char *name)
1304 static std::atomic<uint> OpenCount{0};
1306 uint32_t targetid{PwIdAny};
1307 std::string devname{};
1308 gEventHandler.waitForInit();
1309 if(!name)
1311 EventWatcherLockGuard _{gEventHandler};
1312 auto&& devlist = DeviceNode::GetList();
1314 auto match = devlist.cend();
1315 if(!DefaultSinkDevice.empty())
1317 auto match_default = [](const DeviceNode &n) -> bool
1318 { return n.mDevName == DefaultSinkDevice; };
1319 match = std::find_if(devlist.cbegin(), devlist.cend(), match_default);
1321 if(match == devlist.cend())
1323 auto match_playback = [](const DeviceNode &n) -> bool
1324 { return n.mType != NodeType::Source; };
1325 match = std::find_if(devlist.cbegin(), devlist.cend(), match_playback);
1326 if(match == devlist.cend())
1327 throw al::backend_exception{al::backend_error::NoDevice,
1328 "No PipeWire playback device found"};
1331 targetid = match->mId;
1332 devname = match->mName;
1334 else
1336 EventWatcherLockGuard _{gEventHandler};
1337 auto&& devlist = DeviceNode::GetList();
1339 auto match_name = [name](const DeviceNode &n) -> bool
1340 { return n.mType != NodeType::Source && n.mName == name; };
1341 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
1342 if(match == devlist.cend())
1343 throw al::backend_exception{al::backend_error::NoDevice,
1344 "Device name \"%s\" not found", name};
1346 targetid = match->mId;
1347 devname = match->mName;
1350 if(!mLoop)
1352 const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
1353 const std::string thread_name{"ALSoftP" + std::to_string(count)};
1354 mLoop = ThreadMainloop::Create(thread_name.c_str());
1355 if(!mLoop)
1356 throw al::backend_exception{al::backend_error::DeviceError,
1357 "Failed to create PipeWire mainloop (errno: %d)", errno};
1358 if(int res{mLoop.start()})
1359 throw al::backend_exception{al::backend_error::DeviceError,
1360 "Failed to start PipeWire mainloop (res: %d)", res};
1362 MainloopUniqueLock mlock{mLoop};
1363 if(!mContext)
1365 pw_properties *cprops{pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr)};
1366 mContext = mLoop.newContext(cprops);
1367 if(!mContext)
1368 throw al::backend_exception{al::backend_error::DeviceError,
1369 "Failed to create PipeWire event context (errno: %d)\n", errno};
1371 if(!mCore)
1373 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
1374 if(!mCore)
1375 throw al::backend_exception{al::backend_error::DeviceError,
1376 "Failed to connect PipeWire event context (errno: %d)\n", errno};
1378 mlock.unlock();
1380 /* TODO: Ensure the target ID is still valid/usable and accepts streams. */
1382 mTargetId = targetid;
1383 if(!devname.empty())
1384 mDevice->DeviceName = std::move(devname);
1385 else
1386 mDevice->DeviceName = pwireDevice;
1389 bool PipeWirePlayback::reset()
1391 if(mStream)
1393 MainloopLockGuard _{mLoop};
1394 mStream = nullptr;
1396 mStreamListener = {};
1397 mRateMatch = nullptr;
1398 mTimeBase = GetDeviceClockTime(mDevice);
1400 /* If connecting to a specific device, update various device parameters to
1401 * match its format.
1403 bool is51rear{false};
1404 mDevice->Flags.reset(DirectEar);
1405 if(mTargetId != PwIdAny)
1407 EventWatcherLockGuard _{gEventHandler};
1408 auto&& devlist = DeviceNode::GetList();
1410 auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
1411 { return targetid == n.mId; };
1412 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
1413 if(match != devlist.cend())
1415 if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0)
1417 /* Scale the update size if the sample rate changes. */
1418 const double scale{static_cast<double>(match->mSampleRate) / mDevice->Frequency};
1419 mDevice->Frequency = match->mSampleRate;
1420 mDevice->UpdateSize = static_cast<uint>(clampd(mDevice->UpdateSize*scale + 0.5,
1421 64.0, 8192.0));
1422 mDevice->BufferSize = mDevice->UpdateSize * 2;
1424 if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig)
1425 mDevice->FmtChans = match->mChannels;
1426 if(match->mChannels == DevFmtStereo && match->mIsHeadphones)
1427 mDevice->Flags.set(DirectEar);
1428 is51rear = match->mIs51Rear;
1431 /* Force planar 32-bit float output for playback. This is what PipeWire
1432 * handles internally, and it's easier for us too.
1434 spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)};
1436 /* TODO: How to tell what an appropriate size is? Examples just use this
1437 * magic value.
1439 constexpr uint32_t pod_buffer_size{1024};
1440 auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
1441 spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
1443 const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
1444 if(!params)
1445 throw al::backend_exception{al::backend_error::DeviceError,
1446 "Failed to set PipeWire audio format parameters"};
1448 pw_properties *props{pw_properties_new(
1449 PW_KEY_MEDIA_TYPE, "Audio",
1450 PW_KEY_MEDIA_CATEGORY, "Playback",
1451 PW_KEY_MEDIA_ROLE, "Game",
1452 PW_KEY_NODE_ALWAYS_PROCESS, "true",
1453 nullptr)};
1454 if(!props)
1455 throw al::backend_exception{al::backend_error::DeviceError,
1456 "Failed to create PipeWire stream properties (errno: %d)", errno};
1458 auto&& binary = GetProcBinary();
1459 const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
1460 /* TODO: Which properties are actually needed here? Any others that could
1461 * be useful?
1463 pw_properties_set(props, PW_KEY_NODE_NAME, appname);
1464 pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, appname);
1465 pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize,
1466 mDevice->Frequency);
1467 pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
1469 MainloopUniqueLock plock{mLoop};
1470 /* The stream takes overship of 'props', even in the case of failure. */
1471 mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)};
1472 if(!mStream)
1473 throw al::backend_exception{al::backend_error::NoDevice,
1474 "Failed to create PipeWire stream (errno: %d)", errno};
1475 static constexpr pw_stream_events streamEvents{CreateEvents()};
1476 pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
1478 constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
1479 | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
1480 if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, mTargetId, Flags, &params, 1)})
1481 throw al::backend_exception{al::backend_error::DeviceError,
1482 "Error connecting PipeWire stream (res: %d)", res};
1484 /* Wait for the stream to become paused (ready to start streaming). */
1485 pw_stream_state state{};
1486 const char *error{};
1487 plock.wait([stream=mStream.get(),&state,&error]()
1489 state = pw_stream_get_state(stream, &error);
1490 if(state == PW_STREAM_STATE_ERROR)
1491 throw al::backend_exception{al::backend_error::DeviceError,
1492 "Error connecting PipeWire stream: \"%s\"", error};
1493 return state == PW_STREAM_STATE_PAUSED;
1496 /* TODO: Update mDevice->BufferSize with the total known buffering delay
1497 * from the head of this playback stream to the tail of the device output.
1499 mDevice->BufferSize = mDevice->UpdateSize * 2;
1500 plock.unlock();
1502 mNumChannels = mDevice->channelsFromFmt();
1503 mChannelPtrs = std::make_unique<float*[]>(mNumChannels);
1505 setDefaultWFXChannelOrder();
1507 return true;
1510 void PipeWirePlayback::start()
1512 MainloopUniqueLock plock{mLoop};
1513 if(int res{pw_stream_set_active(mStream.get(), true)})
1514 throw al::backend_exception{al::backend_error::DeviceError,
1515 "Failed to start PipeWire stream (res: %d)", res};
1517 /* Wait for the stream to start playing (would be nice to not, but we need
1518 * the actual update size which is only available after starting).
1520 pw_stream_state state{};
1521 const char *error{};
1522 plock.wait([stream=mStream.get(),&state,&error]()
1524 state = pw_stream_get_state(stream, &error);
1525 return state != PW_STREAM_STATE_PAUSED;
1528 if(state == PW_STREAM_STATE_ERROR)
1529 throw al::backend_exception{al::backend_error::DeviceError,
1530 "PipeWire stream error: %s", error ? error : "(unknown)"};
1531 if(state == PW_STREAM_STATE_STREAMING && mRateMatch && mRateMatch->size)
1533 mDevice->UpdateSize = mRateMatch->size;
1534 mDevice->BufferSize = mDevice->UpdateSize * 2;
1538 void PipeWirePlayback::stop()
1540 MainloopUniqueLock plock{mLoop};
1541 if(int res{pw_stream_set_active(mStream.get(), false)})
1542 throw al::backend_exception{al::backend_error::DeviceError,
1543 "Failed to stop PipeWire stream (res: %d)", res};
1545 /* Wait for the stream to stop playing. */
1546 plock.wait([stream=mStream.get()]()
1547 { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
1550 ClockLatency PipeWirePlayback::getClockLatency()
1552 /* Given a real-time low-latency output, this is rather complicated to get
1553 * accurate timing. So, here we go.
1556 /* First, get the stream time info (tick delay, ticks played, and the
1557 * CLOCK_MONOTONIC time closest to when that last tick was played).
1559 pw_time ptime{};
1560 if(mStream)
1562 MainloopLockGuard _{mLoop};
1563 if(int res{pw_stream_get_time(mStream.get(), &ptime)})
1564 ERR("Failed to get PipeWire stream time (res: %d)\n", res);
1567 /* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the
1568 * monotonic clock closest to 'now', and the last mixer time at 'now').
1570 nanoseconds mixtime{};
1571 timespec tspec{};
1572 uint refcount;
1573 do {
1574 refcount = mDevice->waitForMix();
1575 mixtime = GetDeviceClockTime(mDevice);
1576 clock_gettime(CLOCK_MONOTONIC, &tspec);
1577 std::atomic_thread_fence(std::memory_order_acquire);
1578 } while(refcount != ReadRef(mDevice->MixCount));
1580 /* Convert the monotonic clock, stream ticks, and stream delay to
1581 * nanoseconds.
1583 nanoseconds monoclock{seconds{tspec.tv_sec} + nanoseconds{tspec.tv_nsec}};
1584 nanoseconds curtic{}, delay{};
1585 if(unlikely(ptime.rate.denom < 1))
1587 /* If there's no stream rate, the stream hasn't had a chance to get
1588 * going and return time info yet. Just use dummy values.
1590 ptime.now = monoclock.count();
1591 curtic = mixtime;
1592 delay = nanoseconds{seconds{mDevice->BufferSize}} / mDevice->Frequency;
1594 else
1596 /* The stream gets recreated with each reset, so include the time that
1597 * had already passed with previous streams.
1599 curtic = mTimeBase;
1600 /* More safely scale the ticks to avoid overflowing the pre-division
1601 * temporary as it gets larger.
1603 curtic += seconds{ptime.ticks / ptime.rate.denom} * ptime.rate.num;
1604 curtic += nanoseconds{seconds{ptime.ticks%ptime.rate.denom} * ptime.rate.num} /
1605 ptime.rate.denom;
1607 /* The delay should be small enough to not worry about overflow. */
1608 delay = nanoseconds{seconds{ptime.delay} * ptime.rate.num} / ptime.rate.denom;
1611 /* If the mixer time is ahead of the stream time, there's that much more
1612 * delay relative to the stream delay.
1614 if(mixtime > curtic)
1615 delay += mixtime - curtic;
1616 /* Reduce the delay according to how much time has passed since the known
1617 * stream time. This isn't 100% accurate since the system monotonic clock
1618 * doesn't tick at the exact same rate as the audio device, but it should
1619 * be good enough with ptime.now being constantly updated every few
1620 * milliseconds with ptime.ticks.
1622 delay -= monoclock - nanoseconds{ptime.now};
1624 /* Return the mixer time and delay. Clamp the delay to no less than 0,
1625 * incase timer drift got that severe.
1627 ClockLatency ret{};
1628 ret.ClockTime = mixtime;
1629 ret.Latency = std::max(delay, nanoseconds{});
1631 return ret;
1635 class PipeWireCapture final : public BackendBase {
1636 void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
1637 static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
1638 const char *error)
1639 { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); }
1641 void inputCallback();
1642 static void inputCallbackC(void *data)
1643 { static_cast<PipeWireCapture*>(data)->inputCallback(); }
1645 void open(const char *name) override;
1646 void start() override;
1647 void stop() override;
1648 void captureSamples(al::byte *buffer, uint samples) override;
1649 uint availableSamples() override;
1651 uint32_t mTargetId{PwIdAny};
1652 ThreadMainloop mLoop;
1653 PwContextPtr mContext;
1654 PwCorePtr mCore;
1655 PwStreamPtr mStream;
1656 spa_hook mStreamListener{};
1658 RingBufferPtr mRing{};
1660 static constexpr pw_stream_events CreateEvents()
1662 pw_stream_events ret{};
1663 ret.version = PW_VERSION_STREAM_EVENTS;
1664 ret.state_changed = &PipeWireCapture::stateChangedCallbackC;
1665 ret.process = &PipeWireCapture::inputCallbackC;
1666 return ret;
1669 public:
1670 PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { }
1671 ~PipeWireCapture() { if(mLoop) mLoop.stop(); }
1673 DEF_NEWDEL(PipeWireCapture)
1677 void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
1678 { mLoop.signal(false); }
1680 void PipeWireCapture::inputCallback()
1682 pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
1683 if(unlikely(!pw_buf)) return;
1685 spa_data *bufdata{pw_buf->buffer->datas};
1686 const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)};
1687 const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)};
1689 mRing->write(static_cast<char*>(bufdata->data) + offset, size / mRing->getElemSize());
1691 pw_stream_queue_buffer(mStream.get(), pw_buf);
1695 void PipeWireCapture::open(const char *name)
1697 static std::atomic<uint> OpenCount{0};
1699 uint32_t targetid{PwIdAny};
1700 std::string devname{};
1701 gEventHandler.waitForInit();
1702 if(!name)
1704 EventWatcherLockGuard _{gEventHandler};
1705 auto&& devlist = DeviceNode::GetList();
1707 auto match = devlist.cend();
1708 if(!DefaultSourceDevice.empty())
1710 auto match_default = [](const DeviceNode &n) -> bool
1711 { return n.mDevName == DefaultSourceDevice; };
1712 match = std::find_if(devlist.cbegin(), devlist.cend(), match_default);
1714 if(match == devlist.cend())
1716 auto match_capture = [](const DeviceNode &n) -> bool
1717 { return n.mType != NodeType::Sink; };
1718 match = std::find_if(devlist.cbegin(), devlist.cend(), match_capture);
1720 if(match == devlist.cend())
1722 match = devlist.cbegin();
1723 if(match == devlist.cend())
1724 throw al::backend_exception{al::backend_error::NoDevice,
1725 "No PipeWire capture device found"};
1728 targetid = match->mId;
1729 if(match->mType != NodeType::Sink) devname = match->mName;
1730 else devname = MonitorPrefix+match->mName;
1732 else
1734 EventWatcherLockGuard _{gEventHandler};
1735 auto&& devlist = DeviceNode::GetList();
1737 auto match_name = [name](const DeviceNode &n) -> bool
1738 { return n.mType != NodeType::Sink && n.mName == name; };
1739 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
1740 if(match == devlist.cend() && std::strncmp(name, MonitorPrefix, MonitorPrefixLen) == 0)
1742 const char *sinkname{name + MonitorPrefixLen};
1743 auto match_sinkname = [sinkname](const DeviceNode &n) -> bool
1744 { return n.mType == NodeType::Sink && n.mName == sinkname; };
1745 match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname);
1747 if(match == devlist.cend())
1748 throw al::backend_exception{al::backend_error::NoDevice,
1749 "Device name \"%s\" not found", name};
1751 targetid = match->mId;
1752 devname = name;
1755 if(!mLoop)
1757 const uint count{OpenCount.fetch_add(1, std::memory_order_relaxed)};
1758 const std::string thread_name{"ALSoftC" + std::to_string(count)};
1759 mLoop = ThreadMainloop::Create(thread_name.c_str());
1760 if(!mLoop)
1761 throw al::backend_exception{al::backend_error::DeviceError,
1762 "Failed to create PipeWire mainloop (errno: %d)", errno};
1763 if(int res{mLoop.start()})
1764 throw al::backend_exception{al::backend_error::DeviceError,
1765 "Failed to start PipeWire mainloop (res: %d)", res};
1767 MainloopUniqueLock mlock{mLoop};
1768 if(!mContext)
1770 pw_properties *cprops{pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr)};
1771 mContext = mLoop.newContext(cprops);
1772 if(!mContext)
1773 throw al::backend_exception{al::backend_error::DeviceError,
1774 "Failed to create PipeWire event context (errno: %d)\n", errno};
1776 if(!mCore)
1778 mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)};
1779 if(!mCore)
1780 throw al::backend_exception{al::backend_error::DeviceError,
1781 "Failed to connect PipeWire event context (errno: %d)\n", errno};
1783 mlock.unlock();
1785 /* TODO: Ensure the target ID is still valid/usable and accepts streams. */
1787 mTargetId = targetid;
1788 if(!devname.empty())
1789 mDevice->DeviceName = std::move(devname);
1790 else
1791 mDevice->DeviceName = pwireInput;
1794 bool is51rear{false};
1795 if(mTargetId != PwIdAny)
1797 EventWatcherLockGuard _{gEventHandler};
1798 auto&& devlist = DeviceNode::GetList();
1800 auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool
1801 { return targetid == n.mId; };
1802 auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_id);
1803 if(match != devlist.cend())
1804 is51rear = match->mIs51Rear;
1806 spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)};
1808 constexpr uint32_t pod_buffer_size{1024};
1809 auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
1810 spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
1812 const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
1813 if(!params[0])
1814 throw al::backend_exception{al::backend_error::DeviceError,
1815 "Failed to set PipeWire audio format parameters"};
1817 pw_properties *props{pw_properties_new(
1818 PW_KEY_MEDIA_TYPE, "Audio",
1819 PW_KEY_MEDIA_CATEGORY, "Capture",
1820 PW_KEY_MEDIA_ROLE, "Game",
1821 PW_KEY_NODE_ALWAYS_PROCESS, "true",
1822 nullptr)};
1823 if(!props)
1824 throw al::backend_exception{al::backend_error::DeviceError,
1825 "Failed to create PipeWire stream properties (errno: %d)", errno};
1827 auto&& binary = GetProcBinary();
1828 const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"};
1829 pw_properties_set(props, PW_KEY_NODE_NAME, appname);
1830 pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, appname);
1831 /* We don't actually care what the latency/update size is, as long as it's
1832 * reasonable. Unfortunately, when unspecified PipeWire seems to default to
1833 * around 40ms, which isn't great. So request 20ms instead.
1835 pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50,
1836 mDevice->Frequency);
1837 pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency);
1839 MainloopUniqueLock plock{mLoop};
1840 mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)};
1841 if(!mStream)
1842 throw al::backend_exception{al::backend_error::NoDevice,
1843 "Failed to create PipeWire stream (errno: %d)", errno};
1844 static constexpr pw_stream_events streamEvents{CreateEvents()};
1845 pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this);
1847 constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE
1848 | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS};
1849 if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, mTargetId, Flags, params, 1)})
1850 throw al::backend_exception{al::backend_error::DeviceError,
1851 "Error connecting PipeWire stream (res: %d)", res};
1853 /* Wait for the stream to become paused (ready to start streaming). */
1854 pw_stream_state state{};
1855 const char *error{};
1856 plock.wait([stream=mStream.get(),&state,&error]()
1858 state = pw_stream_get_state(stream, &error);
1859 if(state == PW_STREAM_STATE_ERROR)
1860 throw al::backend_exception{al::backend_error::DeviceError,
1861 "Error connecting PipeWire stream: \"%s\"", error};
1862 return state == PW_STREAM_STATE_PAUSED;
1864 plock.unlock();
1866 setDefaultWFXChannelOrder();
1868 /* Ensure at least a 100ms capture buffer. */
1869 mRing = RingBuffer::Create(maxu(mDevice->Frequency/10, mDevice->BufferSize),
1870 mDevice->frameSizeFromFmt(), false);
1874 void PipeWireCapture::start()
1876 MainloopUniqueLock plock{mLoop};
1877 if(int res{pw_stream_set_active(mStream.get(), true)})
1878 throw al::backend_exception{al::backend_error::DeviceError,
1879 "Failed to start PipeWire stream (res: %d)", res};
1881 pw_stream_state state{};
1882 const char *error{};
1883 plock.wait([stream=mStream.get(),&state,&error]()
1885 state = pw_stream_get_state(stream, &error);
1886 return state != PW_STREAM_STATE_PAUSED;
1889 if(state == PW_STREAM_STATE_ERROR)
1890 throw al::backend_exception{al::backend_error::DeviceError,
1891 "PipeWire stream error: %s", error ? error : "(unknown)"};
1894 void PipeWireCapture::stop()
1896 MainloopUniqueLock plock{mLoop};
1897 if(int res{pw_stream_set_active(mStream.get(), false)})
1898 throw al::backend_exception{al::backend_error::DeviceError,
1899 "Failed to stop PipeWire stream (res: %d)", res};
1901 plock.wait([stream=mStream.get()]()
1902 { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; });
1905 uint PipeWireCapture::availableSamples()
1906 { return static_cast<uint>(mRing->readSpace()); }
1908 void PipeWireCapture::captureSamples(al::byte *buffer, uint samples)
1909 { mRing->read(buffer, samples); }
1911 } // namespace
1914 bool PipeWireBackendFactory::init()
1916 if(!pwire_load())
1917 return false;
1919 pw_init(0, nullptr);
1920 if(!gEventHandler.init())
1921 return false;
1923 if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false)
1924 && !gEventHandler.waitForAudio())
1926 gEventHandler.kill();
1927 /* TODO: Temporary warning, until PipeWire gets a proper way to report
1928 * audio support.
1930 WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.\n");
1931 return false;
1933 return true;
1936 bool PipeWireBackendFactory::querySupport(BackendType type)
1937 { return type == BackendType::Playback || type == BackendType::Capture; }
1939 std::string PipeWireBackendFactory::probe(BackendType type)
1941 std::string outnames;
1943 gEventHandler.waitForInit();
1944 EventWatcherLockGuard _{gEventHandler};
1945 auto&& devlist = DeviceNode::GetList();
1947 auto match_defsink = [](const DeviceNode &n) -> bool
1948 { return n.mDevName == DefaultSinkDevice; };
1949 auto match_defsource = [](const DeviceNode &n) -> bool
1950 { return n.mDevName == DefaultSourceDevice; };
1952 auto sort_devnode = [](DeviceNode &lhs, DeviceNode &rhs) noexcept -> bool
1953 { return lhs.mId < rhs.mId; };
1954 std::sort(devlist.begin(), devlist.end(), sort_devnode);
1956 auto defmatch = devlist.cbegin();
1957 switch(type)
1959 case BackendType::Playback:
1960 defmatch = std::find_if(defmatch, devlist.cend(), match_defsink);
1961 if(defmatch != devlist.cend())
1963 /* Includes null char. */
1964 outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
1966 for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
1968 if(iter != defmatch && iter->mType != NodeType::Source)
1969 outnames.append(iter->mName.c_str(), iter->mName.length()+1);
1971 break;
1972 case BackendType::Capture:
1973 defmatch = std::find_if(defmatch, devlist.cend(), match_defsource);
1974 if(defmatch != devlist.cend())
1976 if(defmatch->mType == NodeType::Sink)
1977 outnames.append(MonitorPrefix);
1978 outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1);
1980 for(auto iter = devlist.cbegin();iter != devlist.cend();++iter)
1982 if(iter != defmatch)
1984 if(iter->mType == NodeType::Sink)
1985 outnames.append(MonitorPrefix);
1986 outnames.append(iter->mName.c_str(), iter->mName.length()+1);
1989 break;
1992 return outnames;
1995 BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type)
1997 if(type == BackendType::Playback)
1998 return BackendPtr{new PipeWirePlayback{device}};
1999 if(type == BackendType::Capture)
2000 return BackendPtr{new PipeWireCapture{device}};
2001 return nullptr;
2004 BackendFactory &PipeWireBackendFactory::getFactory()
2006 static PipeWireBackendFactory factory{};
2007 return factory;