Don't return a large-ish array on the stack
[openal-soft.git] / alc / backends / pulseaudio.cpp
blob9b7e2c0804fcf249109c61101e3e1f00e0be3057
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>
4 * Copyright (C) 2010 by Chris Robinson <chris.kcat@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 * Or go to http://www.gnu.org/copyleft/lgpl.html
22 #include "config.h"
24 #include "pulseaudio.h"
26 #include <algorithm>
27 #include <array>
28 #include <atomic>
29 #include <bitset>
30 #include <chrono>
31 #include <cstring>
32 #include <limits>
33 #include <mutex>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <string>
37 #include <sys/types.h>
38 #include <utility>
40 #include "albyte.h"
41 #include "alc/alconfig.h"
42 #include "almalloc.h"
43 #include "alnumeric.h"
44 #include "aloptional.h"
45 #include "alspan.h"
46 #include "core/devformat.h"
47 #include "core/device.h"
48 #include "core/logging.h"
49 #include "dynload.h"
50 #include "opthelpers.h"
51 #include "strutils.h"
52 #include "vector.h"
54 #include <pulse/pulseaudio.h>
57 namespace {
59 using uint = unsigned int;
61 #ifdef HAVE_DYNLOAD
62 #define PULSE_FUNCS(MAGIC) \
63 MAGIC(pa_context_new); \
64 MAGIC(pa_context_unref); \
65 MAGIC(pa_context_get_state); \
66 MAGIC(pa_context_disconnect); \
67 MAGIC(pa_context_set_state_callback); \
68 MAGIC(pa_context_errno); \
69 MAGIC(pa_context_connect); \
70 MAGIC(pa_context_get_server_info); \
71 MAGIC(pa_context_get_sink_info_by_name); \
72 MAGIC(pa_context_get_sink_info_list); \
73 MAGIC(pa_context_get_source_info_by_name); \
74 MAGIC(pa_context_get_source_info_list); \
75 MAGIC(pa_stream_new); \
76 MAGIC(pa_stream_unref); \
77 MAGIC(pa_stream_drop); \
78 MAGIC(pa_stream_get_state); \
79 MAGIC(pa_stream_peek); \
80 MAGIC(pa_stream_write); \
81 MAGIC(pa_stream_connect_record); \
82 MAGIC(pa_stream_connect_playback); \
83 MAGIC(pa_stream_readable_size); \
84 MAGIC(pa_stream_writable_size); \
85 MAGIC(pa_stream_is_corked); \
86 MAGIC(pa_stream_cork); \
87 MAGIC(pa_stream_is_suspended); \
88 MAGIC(pa_stream_get_device_name); \
89 MAGIC(pa_stream_get_latency); \
90 MAGIC(pa_stream_set_write_callback); \
91 MAGIC(pa_stream_set_buffer_attr); \
92 MAGIC(pa_stream_get_buffer_attr); \
93 MAGIC(pa_stream_get_sample_spec); \
94 MAGIC(pa_stream_get_time); \
95 MAGIC(pa_stream_set_read_callback); \
96 MAGIC(pa_stream_set_state_callback); \
97 MAGIC(pa_stream_set_moved_callback); \
98 MAGIC(pa_stream_set_underflow_callback); \
99 MAGIC(pa_stream_new_with_proplist); \
100 MAGIC(pa_stream_disconnect); \
101 MAGIC(pa_stream_set_buffer_attr_callback); \
102 MAGIC(pa_stream_begin_write); \
103 MAGIC(pa_threaded_mainloop_free); \
104 MAGIC(pa_threaded_mainloop_get_api); \
105 MAGIC(pa_threaded_mainloop_lock); \
106 MAGIC(pa_threaded_mainloop_new); \
107 MAGIC(pa_threaded_mainloop_signal); \
108 MAGIC(pa_threaded_mainloop_start); \
109 MAGIC(pa_threaded_mainloop_stop); \
110 MAGIC(pa_threaded_mainloop_unlock); \
111 MAGIC(pa_threaded_mainloop_wait); \
112 MAGIC(pa_channel_map_init_auto); \
113 MAGIC(pa_channel_map_parse); \
114 MAGIC(pa_channel_map_snprint); \
115 MAGIC(pa_channel_map_equal); \
116 MAGIC(pa_channel_map_superset); \
117 MAGIC(pa_channel_position_to_string); \
118 MAGIC(pa_operation_get_state); \
119 MAGIC(pa_operation_unref); \
120 MAGIC(pa_sample_spec_valid); \
121 MAGIC(pa_frame_size); \
122 MAGIC(pa_strerror); \
123 MAGIC(pa_path_get_filename); \
124 MAGIC(pa_get_binary_name); \
125 MAGIC(pa_xmalloc); \
126 MAGIC(pa_xfree);
128 void *pulse_handle;
129 #define MAKE_FUNC(x) decltype(x) * p##x
130 PULSE_FUNCS(MAKE_FUNC)
131 #undef MAKE_FUNC
133 #ifndef IN_IDE_PARSER
134 #define pa_context_new ppa_context_new
135 #define pa_context_unref ppa_context_unref
136 #define pa_context_get_state ppa_context_get_state
137 #define pa_context_disconnect ppa_context_disconnect
138 #define pa_context_set_state_callback ppa_context_set_state_callback
139 #define pa_context_errno ppa_context_errno
140 #define pa_context_connect ppa_context_connect
141 #define pa_context_get_server_info ppa_context_get_server_info
142 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
143 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
144 #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
145 #define pa_context_get_source_info_list ppa_context_get_source_info_list
146 #define pa_stream_new ppa_stream_new
147 #define pa_stream_unref ppa_stream_unref
148 #define pa_stream_disconnect ppa_stream_disconnect
149 #define pa_stream_drop ppa_stream_drop
150 #define pa_stream_set_write_callback ppa_stream_set_write_callback
151 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
152 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
153 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
154 #define pa_stream_get_time ppa_stream_get_time
155 #define pa_stream_set_read_callback ppa_stream_set_read_callback
156 #define pa_stream_set_state_callback ppa_stream_set_state_callback
157 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
158 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
159 #define pa_stream_connect_record ppa_stream_connect_record
160 #define pa_stream_connect_playback ppa_stream_connect_playback
161 #define pa_stream_readable_size ppa_stream_readable_size
162 #define pa_stream_writable_size ppa_stream_writable_size
163 #define pa_stream_is_corked ppa_stream_is_corked
164 #define pa_stream_cork ppa_stream_cork
165 #define pa_stream_is_suspended ppa_stream_is_suspended
166 #define pa_stream_get_device_name ppa_stream_get_device_name
167 #define pa_stream_get_latency ppa_stream_get_latency
168 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
169 #define pa_stream_begin_write ppa_stream_begin_write
170 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free
171 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api
172 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock
173 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new
174 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal
175 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start
176 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop
177 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock
178 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait
179 #define pa_channel_map_init_auto ppa_channel_map_init_auto
180 #define pa_channel_map_parse ppa_channel_map_parse
181 #define pa_channel_map_snprint ppa_channel_map_snprint
182 #define pa_channel_map_equal ppa_channel_map_equal
183 #define pa_channel_map_superset ppa_channel_map_superset
184 #define pa_channel_position_to_string ppa_channel_position_to_string
185 #define pa_operation_get_state ppa_operation_get_state
186 #define pa_operation_unref ppa_operation_unref
187 #define pa_sample_spec_valid ppa_sample_spec_valid
188 #define pa_frame_size ppa_frame_size
189 #define pa_strerror ppa_strerror
190 #define pa_stream_get_state ppa_stream_get_state
191 #define pa_stream_peek ppa_stream_peek
192 #define pa_stream_write ppa_stream_write
193 #define pa_xfree ppa_xfree
194 #define pa_path_get_filename ppa_path_get_filename
195 #define pa_get_binary_name ppa_get_binary_name
196 #define pa_xmalloc ppa_xmalloc
197 #endif /* IN_IDE_PARSER */
199 #endif
202 constexpr pa_channel_map MonoChanMap{
203 1, {PA_CHANNEL_POSITION_MONO}
204 }, StereoChanMap{
205 2, {PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT}
206 }, QuadChanMap{
207 4, {
208 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
209 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
211 }, X51ChanMap{
212 6, {
213 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
214 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
215 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
217 }, X51RearChanMap{
218 6, {
219 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
220 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
221 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
223 }, X61ChanMap{
224 7, {
225 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
226 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
227 PA_CHANNEL_POSITION_REAR_CENTER,
228 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
230 }, X71ChanMap{
231 8, {
232 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
233 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
234 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
235 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
237 }, X714ChanMap{
238 12, {
239 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
240 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
241 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
242 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT,
243 PA_CHANNEL_POSITION_TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
244 PA_CHANNEL_POSITION_TOP_REAR_LEFT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT
249 /* *grumble* Don't use enums for bitflags. */
250 constexpr pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
251 { return pa_stream_flags_t(lhs | al::to_underlying(rhs)); }
252 constexpr pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
254 lhs = lhs | rhs;
255 return lhs;
257 constexpr pa_stream_flags_t operator~(pa_stream_flags_t flag)
258 { return pa_stream_flags_t(~al::to_underlying(flag)); }
259 constexpr pa_stream_flags_t& operator&=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
261 lhs = pa_stream_flags_t(al::to_underlying(lhs) & rhs);
262 return lhs;
265 constexpr pa_context_flags_t operator|(pa_context_flags_t lhs, pa_context_flags_t rhs)
266 { return pa_context_flags_t(lhs | al::to_underlying(rhs)); }
267 constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
269 lhs = lhs | rhs;
270 return lhs;
274 struct DevMap {
275 std::string name;
276 std::string device_name;
279 bool checkName(const al::span<const DevMap> list, const std::string &name)
281 auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
282 return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
285 al::vector<DevMap> PlaybackDevices;
286 al::vector<DevMap> CaptureDevices;
289 /* Global flags and properties */
290 pa_context_flags_t pulse_ctx_flags;
292 class PulseMainloop {
293 pa_threaded_mainloop *mLoop{};
295 public:
296 PulseMainloop() = default;
297 PulseMainloop(const PulseMainloop&) = delete;
298 PulseMainloop(PulseMainloop&& rhs) noexcept : mLoop{rhs.mLoop} { rhs.mLoop = nullptr; }
299 explicit PulseMainloop(pa_threaded_mainloop *loop) noexcept : mLoop{loop} { }
300 ~PulseMainloop() { if(mLoop) pa_threaded_mainloop_free(mLoop); }
302 PulseMainloop& operator=(const PulseMainloop&) = delete;
303 PulseMainloop& operator=(PulseMainloop&& rhs) noexcept
304 { std::swap(mLoop, rhs.mLoop); return *this; }
305 PulseMainloop& operator=(std::nullptr_t) noexcept
307 if(mLoop)
308 pa_threaded_mainloop_free(mLoop);
309 mLoop = nullptr;
310 return *this;
313 explicit operator bool() const noexcept { return mLoop != nullptr; }
315 auto start() const { return pa_threaded_mainloop_start(mLoop); }
316 auto stop() const { return pa_threaded_mainloop_stop(mLoop); }
318 auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); }
320 auto lock() const { return pa_threaded_mainloop_lock(mLoop); }
321 auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); }
323 auto signal(bool wait=false) const { return pa_threaded_mainloop_signal(mLoop, wait); }
325 static auto Create() { return PulseMainloop{pa_threaded_mainloop_new()}; }
328 void streamSuccessCallback(pa_stream*, int) noexcept { signal(); }
329 static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept
330 { static_cast<PulseMainloop*>(pdata)->streamSuccessCallback(stream, success); }
332 void close(pa_context *context, pa_stream *stream=nullptr);
335 void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
337 if(eol)
339 signal();
340 return;
343 /* Skip this device is if it's already in the list. */
344 auto match_devname = [info](const DevMap &entry) -> bool
345 { return entry.device_name == info->name; };
346 if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_devname) != PlaybackDevices.cend())
347 return;
349 /* Make sure the display name (description) is unique. Append a number
350 * counter as needed.
352 int count{1};
353 std::string newname{info->description};
354 while(checkName(PlaybackDevices, newname))
356 newname = info->description;
357 newname += " #";
358 newname += std::to_string(++count);
360 PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
361 DevMap &newentry = PlaybackDevices.back();
363 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
366 void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) noexcept
368 if(eol)
370 signal();
371 return;
374 /* Skip this device is if it's already in the list. */
375 auto match_devname = [info](const DevMap &entry) -> bool
376 { return entry.device_name == info->name; };
377 if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_devname) != CaptureDevices.cend())
378 return;
380 /* Make sure the display name (description) is unique. Append a number
381 * counter as needed.
383 int count{1};
384 std::string newname{info->description};
385 while(checkName(CaptureDevices, newname))
387 newname = info->description;
388 newname += " #";
389 newname += std::to_string(++count);
391 CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
392 DevMap &newentry = CaptureDevices.back();
394 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
397 void probePlaybackDevices();
398 void probeCaptureDevices();
400 friend struct MainloopUniqueLock;
402 struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> {
403 using std::unique_lock<PulseMainloop>::unique_lock;
404 MainloopUniqueLock& operator=(MainloopUniqueLock&&) = default;
406 auto wait() const -> void
407 { pa_threaded_mainloop_wait(mutex()->mLoop); }
409 template<typename Predicate>
410 auto wait(Predicate done_waiting) const -> void
411 { while(!done_waiting()) wait(); }
413 void waitForOperation(pa_operation *op)
415 if(op)
417 wait([op]{ return pa_operation_get_state(op) != PA_OPERATION_RUNNING; });
418 pa_operation_unref(op);
423 void contextStateCallback(pa_context *context) noexcept
425 pa_context_state_t state{pa_context_get_state(context)};
426 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
427 mutex()->signal();
430 void streamStateCallback(pa_stream *stream) noexcept
432 pa_stream_state_t state{pa_stream_get_state(stream)};
433 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
434 mutex()->signal();
437 pa_context *connectContext();
438 pa_stream *connectStream(const char *device_name, pa_context *context, pa_stream_flags_t flags,
439 pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type);
441 using MainloopLockGuard = std::lock_guard<PulseMainloop>;
444 pa_context *MainloopUniqueLock::connectContext()
446 pa_context *context{pa_context_new(mutex()->getApi(), nullptr)};
447 if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
448 "pa_context_new() failed"};
450 pa_context_set_state_callback(context, [](pa_context *ctx, void *pdata) noexcept
451 { return static_cast<MainloopUniqueLock*>(pdata)->contextStateCallback(ctx); }, this);
453 int err;
454 if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
456 pa_context_state_t state;
457 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
459 if(!PA_CONTEXT_IS_GOOD(state))
461 err = pa_context_errno(context);
462 if(err > 0) err = -err;
463 break;
466 wait();
469 pa_context_set_state_callback(context, nullptr, nullptr);
471 if(err < 0)
473 pa_context_unref(context);
474 throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)",
475 pa_strerror(err)};
478 return context;
481 pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context *context,
482 pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap,
483 BackendType type)
485 const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
486 pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
487 if(!stream)
488 throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)",
489 pa_strerror(pa_context_errno(context))};
491 pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept
492 { return static_cast<MainloopUniqueLock*>(pdata)->streamStateCallback(strm); }, this);
494 int err{(type==BackendType::Playback) ?
495 pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
496 pa_stream_connect_record(stream, device_name, attr, flags)};
497 if(err < 0)
499 pa_stream_unref(stream);
500 throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect (%s)",
501 stream_id, pa_strerror(err)};
504 pa_stream_state_t state;
505 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
507 if(!PA_STREAM_IS_GOOD(state))
509 err = pa_context_errno(context);
510 pa_stream_unref(stream);
511 throw al::backend_exception{al::backend_error::DeviceError,
512 "%s did not get ready (%s)", stream_id, pa_strerror(err)};
515 wait();
517 pa_stream_set_state_callback(stream, nullptr, nullptr);
519 return stream;
522 void PulseMainloop::close(pa_context *context, pa_stream *stream)
524 MainloopUniqueLock _{*this};
525 if(stream)
527 pa_stream_set_state_callback(stream, nullptr, nullptr);
528 pa_stream_set_moved_callback(stream, nullptr, nullptr);
529 pa_stream_set_write_callback(stream, nullptr, nullptr);
530 pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
531 pa_stream_disconnect(stream);
532 pa_stream_unref(stream);
535 pa_context_disconnect(context);
536 pa_context_unref(context);
540 void PulseMainloop::probePlaybackDevices()
542 pa_context *context{};
544 PlaybackDevices.clear();
545 try {
546 MainloopUniqueLock plock{*this};
547 auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept
548 { return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); };
550 context = plock.connectContext();
551 pa_operation *op{pa_context_get_sink_info_by_name(context, nullptr, sink_callback, this)};
552 plock.waitForOperation(op);
554 op = pa_context_get_sink_info_list(context, sink_callback, this);
555 plock.waitForOperation(op);
557 pa_context_disconnect(context);
558 pa_context_unref(context);
559 context = nullptr;
561 catch(std::exception &e) {
562 ERR("Error enumerating devices: %s\n", e.what());
563 if(context) close(context);
567 void PulseMainloop::probeCaptureDevices()
569 pa_context *context{};
571 CaptureDevices.clear();
572 try {
573 MainloopUniqueLock plock{*this};
574 auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept
575 { return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx, info, eol); };
577 context = plock.connectContext();
578 pa_operation *op{pa_context_get_source_info_by_name(context, nullptr, src_callback, this)};
579 plock.waitForOperation(op);
581 op = pa_context_get_source_info_list(context, src_callback, this);
582 plock.waitForOperation(op);
584 pa_context_disconnect(context);
585 pa_context_unref(context);
586 context = nullptr;
588 catch(std::exception &e) {
589 ERR("Error enumerating devices: %s\n", e.what());
590 if(context) close(context);
595 /* Used for initial connection test and enumeration. */
596 PulseMainloop gGlobalMainloop;
599 struct PulsePlayback final : public BackendBase {
600 PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
601 ~PulsePlayback() override;
603 void bufferAttrCallback(pa_stream *stream) noexcept;
604 void streamStateCallback(pa_stream *stream) noexcept;
605 void streamWriteCallback(pa_stream *stream, size_t nbytes) noexcept;
606 void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept;
607 void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept;
608 void streamMovedCallback(pa_stream *stream) noexcept;
610 void open(const char *name) override;
611 bool reset() override;
612 void start() override;
613 void stop() override;
614 ClockLatency getClockLatency() override;
616 PulseMainloop mMainloop;
618 al::optional<std::string> mDeviceName{al::nullopt};
620 bool mIs51Rear{false};
621 pa_buffer_attr mAttr;
622 pa_sample_spec mSpec;
624 pa_stream *mStream{nullptr};
625 pa_context *mContext{nullptr};
627 uint mFrameSize{0u};
629 DEF_NEWDEL(PulsePlayback)
632 PulsePlayback::~PulsePlayback()
634 if(!mContext)
635 return;
637 mMainloop.close(mContext, mStream);
638 mContext = nullptr;
639 mStream = nullptr;
643 void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept
645 /* FIXME: Update the device's UpdateSize (and/or BufferSize) using the new
646 * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
647 * property, which probably shouldn't change between device resets. But
648 * leaving it alone means ALC_REFRESH will be off.
650 mAttr = *(pa_stream_get_buffer_attr(stream));
651 TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
654 void PulsePlayback::streamStateCallback(pa_stream *stream) noexcept
656 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
658 ERR("Received stream failure!\n");
659 mDevice->handleDisconnect("Playback stream failure");
661 mMainloop.signal();
664 void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes) noexcept
666 do {
667 pa_free_cb_t free_func{nullptr};
668 auto buflen = static_cast<size_t>(-1);
669 void *buf{};
670 if(pa_stream_begin_write(stream, &buf, &buflen) || !buf) [[unlikely]]
672 buflen = nbytes;
673 buf = pa_xmalloc(buflen);
674 free_func = pa_xfree;
676 else
677 buflen = minz(buflen, nbytes);
678 nbytes -= buflen;
680 mDevice->renderSamples(buf, static_cast<uint>(buflen/mFrameSize), mSpec.channels);
682 int ret{pa_stream_write(stream, buf, buflen, free_func, 0, PA_SEEK_RELATIVE)};
683 if(ret != PA_OK) [[unlikely]]
684 ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
685 } while(nbytes > 0);
688 void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
690 struct ChannelMap {
691 DevFmtChannels fmt;
692 pa_channel_map map;
693 bool is_51rear;
695 static constexpr std::array<ChannelMap,8> chanmaps{{
696 { DevFmtX714, X714ChanMap, false },
697 { DevFmtX71, X71ChanMap, false },
698 { DevFmtX61, X61ChanMap, false },
699 { DevFmtX51, X51ChanMap, false },
700 { DevFmtX51, X51RearChanMap, true },
701 { DevFmtQuad, QuadChanMap, false },
702 { DevFmtStereo, StereoChanMap, false },
703 { DevFmtMono, MonoChanMap, false }
706 if(eol)
708 mMainloop.signal();
709 return;
712 auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
713 [info](const ChannelMap &chanmap) -> bool
714 { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
716 if(chaniter != chanmaps.cend())
718 if(!mDevice->Flags.test(ChannelsRequest))
719 mDevice->FmtChans = chaniter->fmt;
720 mIs51Rear = chaniter->is_51rear;
722 else
724 mIs51Rear = false;
725 char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
726 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
727 WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
730 if(info->active_port)
731 TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
732 mDevice->Flags.set(DirectEar, (info->active_port
733 && strcmp(info->active_port->name, "analog-output-headphones") == 0));
736 void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
738 if(eol)
740 mMainloop.signal();
741 return;
743 mDevice->DeviceName = info->description;
746 void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept
748 mDeviceName = pa_stream_get_device_name(stream);
749 TRACE("Stream moved to %s\n", mDeviceName->c_str());
753 void PulsePlayback::open(const char *name)
755 mMainloop = PulseMainloop::Create();
756 mMainloop.start();
758 const char *pulse_name{nullptr};
759 const char *dev_name{nullptr};
760 if(name)
762 if(PlaybackDevices.empty())
763 mMainloop.probePlaybackDevices();
765 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
766 [name](const DevMap &entry) -> bool { return entry.name == name; });
767 if(iter == PlaybackDevices.cend())
768 throw al::backend_exception{al::backend_error::NoDevice,
769 "Device name \"%s\" not found", name};
770 pulse_name = iter->device_name.c_str();
771 dev_name = iter->name.c_str();
774 MainloopUniqueLock plock{mMainloop};
775 mContext = plock.connectContext();
777 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
778 PA_STREAM_FIX_CHANNELS};
779 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true))
780 flags |= PA_STREAM_DONT_MOVE;
782 pa_sample_spec spec{};
783 spec.format = PA_SAMPLE_S16NE;
784 spec.rate = 44100;
785 spec.channels = 2;
787 if(!pulse_name)
789 static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT");
790 if(defname) pulse_name = defname->c_str();
792 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
793 mStream = plock.connectStream(pulse_name, mContext, flags, nullptr, &spec, nullptr,
794 BackendType::Playback);
796 pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
797 { return static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }, this);
798 mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
800 if(pulse_name) mDeviceName.emplace(pulse_name);
801 else mDeviceName.reset();
802 if(!dev_name)
804 auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
805 { return static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); };
806 pa_operation *op{pa_context_get_sink_info_by_name(mContext,
807 pa_stream_get_device_name(mStream), name_callback, this)};
808 plock.waitForOperation(op);
810 else
811 mDevice->DeviceName = dev_name;
814 bool PulsePlayback::reset()
816 MainloopUniqueLock plock{mMainloop};
817 const auto deviceName = mDeviceName ? mDeviceName->c_str() : nullptr;
819 if(mStream)
821 pa_stream_set_state_callback(mStream, nullptr, nullptr);
822 pa_stream_set_moved_callback(mStream, nullptr, nullptr);
823 pa_stream_set_write_callback(mStream, nullptr, nullptr);
824 pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
825 pa_stream_disconnect(mStream);
826 pa_stream_unref(mStream);
827 mStream = nullptr;
830 auto info_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
831 { return static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); };
832 pa_operation *op{pa_context_get_sink_info_by_name(mContext, deviceName, info_callback, this)};
833 plock.waitForOperation(op);
835 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
836 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
837 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true))
838 flags |= PA_STREAM_DONT_MOVE;
839 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", false))
841 /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
842 * reason. So if the user wants to adjust the overall device latency,
843 * we can't ask to get write signals as soon as minreq is reached.
845 flags &= ~PA_STREAM_EARLY_REQUESTS;
846 flags |= PA_STREAM_ADJUST_LATENCY;
848 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", false)
849 || !mDevice->Flags.test(FrequencyRequest))
850 flags |= PA_STREAM_FIX_RATE;
852 pa_channel_map chanmap{};
853 switch(mDevice->FmtChans)
855 case DevFmtMono:
856 chanmap = MonoChanMap;
857 break;
858 case DevFmtAmbi3D:
859 mDevice->FmtChans = DevFmtStereo;
860 /*fall-through*/
861 case DevFmtStereo:
862 chanmap = StereoChanMap;
863 break;
864 case DevFmtQuad:
865 chanmap = QuadChanMap;
866 break;
867 case DevFmtX51:
868 chanmap = (mIs51Rear ? X51RearChanMap : X51ChanMap);
869 break;
870 case DevFmtX61:
871 chanmap = X61ChanMap;
872 break;
873 case DevFmtX71:
874 case DevFmtX3D71:
875 chanmap = X71ChanMap;
876 break;
877 case DevFmtX714:
878 chanmap = X714ChanMap;
879 break;
881 setDefaultWFXChannelOrder();
883 switch(mDevice->FmtType)
885 case DevFmtByte:
886 mDevice->FmtType = DevFmtUByte;
887 /* fall-through */
888 case DevFmtUByte:
889 mSpec.format = PA_SAMPLE_U8;
890 break;
891 case DevFmtUShort:
892 mDevice->FmtType = DevFmtShort;
893 /* fall-through */
894 case DevFmtShort:
895 mSpec.format = PA_SAMPLE_S16NE;
896 break;
897 case DevFmtUInt:
898 mDevice->FmtType = DevFmtInt;
899 /* fall-through */
900 case DevFmtInt:
901 mSpec.format = PA_SAMPLE_S32NE;
902 break;
903 case DevFmtFloat:
904 mSpec.format = PA_SAMPLE_FLOAT32NE;
905 break;
907 mSpec.rate = mDevice->Frequency;
908 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
909 if(pa_sample_spec_valid(&mSpec) == 0)
910 throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample spec"};
912 const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
913 mAttr.maxlength = ~0u;
914 mAttr.tlength = mDevice->BufferSize * frame_size;
915 mAttr.prebuf = 0u;
916 mAttr.minreq = mDevice->UpdateSize * frame_size;
917 mAttr.fragsize = ~0u;
919 mStream = plock.connectStream(deviceName, mContext, flags, &mAttr, &mSpec, &chanmap,
920 BackendType::Playback);
922 pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
923 { return static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }, this);
924 pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
925 { return static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }, this);
927 mSpec = *(pa_stream_get_sample_spec(mStream));
928 mFrameSize = static_cast<uint>(pa_frame_size(&mSpec));
930 if(mDevice->Frequency != mSpec.rate)
932 /* Server updated our playback rate, so modify the buffer attribs
933 * accordingly.
935 const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
936 const auto perlen = static_cast<uint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0,
937 8192.0));
938 const auto buflen = static_cast<uint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2,
939 std::numeric_limits<int>::max()/mFrameSize));
941 mAttr.maxlength = ~0u;
942 mAttr.tlength = buflen * mFrameSize;
943 mAttr.prebuf = 0u;
944 mAttr.minreq = perlen * mFrameSize;
946 op = pa_stream_set_buffer_attr(mStream, &mAttr, &PulseMainloop::streamSuccessCallbackC,
947 &mMainloop);
948 plock.waitForOperation(op);
950 mDevice->Frequency = mSpec.rate;
953 auto attr_callback = [](pa_stream *stream, void *pdata) noexcept
954 { return static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); };
955 pa_stream_set_buffer_attr_callback(mStream, attr_callback, this);
956 bufferAttrCallback(mStream);
958 mDevice->BufferSize = mAttr.tlength / mFrameSize;
959 mDevice->UpdateSize = mAttr.minreq / mFrameSize;
961 return true;
964 void PulsePlayback::start()
966 MainloopUniqueLock plock{mMainloop};
968 /* Write some samples to fill the buffer before we start feeding it newly
969 * mixed samples.
971 if(size_t todo{pa_stream_writable_size(mStream)})
973 void *buf{pa_xmalloc(todo)};
974 mDevice->renderSamples(buf, static_cast<uint>(todo/mFrameSize), mSpec.channels);
975 pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE);
978 pa_stream_set_write_callback(mStream, [](pa_stream *stream, size_t nbytes, void *pdata)noexcept
979 { return static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }, this);
980 pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
981 &mMainloop)};
983 plock.waitForOperation(op);
986 void PulsePlayback::stop()
988 MainloopUniqueLock plock{mMainloop};
990 pa_operation *op{pa_stream_cork(mStream, 1, &PulseMainloop::streamSuccessCallbackC,
991 &mMainloop)};
992 plock.waitForOperation(op);
993 pa_stream_set_write_callback(mStream, nullptr, nullptr);
997 ClockLatency PulsePlayback::getClockLatency()
999 ClockLatency ret;
1000 pa_usec_t latency;
1001 int neg, err;
1004 MainloopUniqueLock plock{mMainloop};
1005 ret.ClockTime = GetDeviceClockTime(mDevice);
1006 err = pa_stream_get_latency(mStream, &latency, &neg);
1009 if(err != 0) [[unlikely]]
1011 /* If err = -PA_ERR_NODATA, it means we were called too soon after
1012 * starting the stream and no timing info has been received from the
1013 * server yet. Give a generic value since nothing better is available.
1015 if(err != -PA_ERR_NODATA)
1016 ERR("Failed to get stream latency: 0x%x\n", err);
1017 latency = mDevice->BufferSize - mDevice->UpdateSize;
1018 neg = 0;
1020 else if(neg) [[unlikely]]
1021 latency = 0;
1022 ret.Latency = std::chrono::microseconds{latency};
1024 return ret;
1028 struct PulseCapture final : public BackendBase {
1029 PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
1030 ~PulseCapture() override;
1032 void streamStateCallback(pa_stream *stream) noexcept;
1033 void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol) noexcept;
1034 void streamMovedCallback(pa_stream *stream) noexcept;
1036 void open(const char *name) override;
1037 void start() override;
1038 void stop() override;
1039 void captureSamples(al::byte *buffer, uint samples) override;
1040 uint availableSamples() override;
1041 ClockLatency getClockLatency() override;
1043 PulseMainloop mMainloop;
1045 al::optional<std::string> mDeviceName{al::nullopt};
1047 al::span<const al::byte> mCapBuffer;
1048 size_t mHoleLength{0};
1049 size_t mPacketLength{0};
1051 uint mLastReadable{0u};
1052 al::byte mSilentVal{};
1054 pa_buffer_attr mAttr{};
1055 pa_sample_spec mSpec{};
1057 pa_stream *mStream{nullptr};
1058 pa_context *mContext{nullptr};
1060 DEF_NEWDEL(PulseCapture)
1063 PulseCapture::~PulseCapture()
1065 if(!mContext)
1066 return;
1068 mMainloop.close(mContext, mStream);
1069 mContext = nullptr;
1070 mStream = nullptr;
1074 void PulseCapture::streamStateCallback(pa_stream *stream) noexcept
1076 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
1078 ERR("Received stream failure!\n");
1079 mDevice->handleDisconnect("Capture stream failure");
1081 mMainloop.signal();
1084 void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, int eol) noexcept
1086 if(eol)
1088 mMainloop.signal();
1089 return;
1091 mDevice->DeviceName = info->description;
1094 void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept
1096 mDeviceName = pa_stream_get_device_name(stream);
1097 TRACE("Stream moved to %s\n", mDeviceName->c_str());
1101 void PulseCapture::open(const char *name)
1103 if(!mMainloop)
1105 mMainloop = PulseMainloop::Create();
1106 mMainloop.start();
1109 const char *pulse_name{nullptr};
1110 if(name)
1112 if(CaptureDevices.empty())
1113 mMainloop.probeCaptureDevices();
1115 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1116 [name](const DevMap &entry) -> bool { return entry.name == name; });
1117 if(iter == CaptureDevices.cend())
1118 throw al::backend_exception{al::backend_error::NoDevice,
1119 "Device name \"%s\" not found", name};
1120 pulse_name = iter->device_name.c_str();
1121 mDevice->DeviceName = iter->name;
1124 MainloopUniqueLock plock{mMainloop};
1125 mContext = plock.connectContext();
1127 pa_channel_map chanmap{};
1128 switch(mDevice->FmtChans)
1130 case DevFmtMono:
1131 chanmap = MonoChanMap;
1132 break;
1133 case DevFmtStereo:
1134 chanmap = StereoChanMap;
1135 break;
1136 case DevFmtQuad:
1137 chanmap = QuadChanMap;
1138 break;
1139 case DevFmtX51:
1140 chanmap = X51ChanMap;
1141 break;
1142 case DevFmtX61:
1143 chanmap = X61ChanMap;
1144 break;
1145 case DevFmtX71:
1146 chanmap = X71ChanMap;
1147 break;
1148 case DevFmtX714:
1149 chanmap = X714ChanMap;
1150 break;
1151 case DevFmtX3D71:
1152 case DevFmtAmbi3D:
1153 throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
1154 DevFmtChannelsString(mDevice->FmtChans)};
1156 setDefaultWFXChannelOrder();
1158 switch(mDevice->FmtType)
1160 case DevFmtUByte:
1161 mSilentVal = al::byte(0x80);
1162 mSpec.format = PA_SAMPLE_U8;
1163 break;
1164 case DevFmtShort:
1165 mSpec.format = PA_SAMPLE_S16NE;
1166 break;
1167 case DevFmtInt:
1168 mSpec.format = PA_SAMPLE_S32NE;
1169 break;
1170 case DevFmtFloat:
1171 mSpec.format = PA_SAMPLE_FLOAT32NE;
1172 break;
1173 case DevFmtByte:
1174 case DevFmtUShort:
1175 case DevFmtUInt:
1176 throw al::backend_exception{al::backend_error::DeviceError,
1177 "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
1179 mSpec.rate = mDevice->Frequency;
1180 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
1181 if(pa_sample_spec_valid(&mSpec) == 0)
1182 throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"};
1184 const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
1185 const uint samples{maxu(mDevice->BufferSize, 100 * mDevice->Frequency / 1000)};
1186 mAttr.minreq = ~0u;
1187 mAttr.prebuf = ~0u;
1188 mAttr.maxlength = samples * frame_size;
1189 mAttr.tlength = ~0u;
1190 mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size;
1192 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
1193 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true))
1194 flags |= PA_STREAM_DONT_MOVE;
1196 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
1197 mStream = plock.connectStream(pulse_name, mContext, flags, &mAttr, &mSpec, &chanmap,
1198 BackendType::Capture);
1200 pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
1201 { return static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }, this);
1202 pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
1203 { return static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }, this);
1205 if(pulse_name) mDeviceName.emplace(pulse_name);
1206 else mDeviceName.reset();
1207 if(mDevice->DeviceName.empty())
1209 auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept
1210 { return static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); };
1211 pa_operation *op{pa_context_get_source_info_by_name(mContext,
1212 pa_stream_get_device_name(mStream), name_callback, this)};
1213 plock.waitForOperation(op);
1217 void PulseCapture::start()
1219 MainloopUniqueLock plock{mMainloop};
1220 pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
1221 &mMainloop)};
1222 plock.waitForOperation(op);
1225 void PulseCapture::stop()
1227 MainloopUniqueLock plock{mMainloop};
1228 pa_operation *op{pa_stream_cork(mStream, 1, &PulseMainloop::streamSuccessCallbackC,
1229 &mMainloop)};
1230 plock.waitForOperation(op);
1233 void PulseCapture::captureSamples(al::byte *buffer, uint samples)
1235 al::span<al::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)};
1237 /* Capture is done in fragment-sized chunks, so we loop until we get all
1238 * that's available.
1240 mLastReadable -= static_cast<uint>(dstbuf.size());
1241 while(!dstbuf.empty())
1243 if(mHoleLength > 0) [[unlikely]]
1245 const size_t rem{minz(dstbuf.size(), mHoleLength)};
1246 std::fill_n(dstbuf.begin(), rem, mSilentVal);
1247 dstbuf = dstbuf.subspan(rem);
1248 mHoleLength -= rem;
1250 continue;
1252 if(!mCapBuffer.empty())
1254 const size_t rem{minz(dstbuf.size(), mCapBuffer.size())};
1255 std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin());
1256 dstbuf = dstbuf.subspan(rem);
1257 mCapBuffer = mCapBuffer.subspan(rem);
1259 continue;
1262 if(!mDevice->Connected.load(std::memory_order_acquire)) [[unlikely]]
1263 break;
1265 MainloopUniqueLock plock{mMainloop};
1266 if(mPacketLength > 0)
1268 pa_stream_drop(mStream);
1269 mPacketLength = 0;
1272 const pa_stream_state_t state{pa_stream_get_state(mStream)};
1273 if(!PA_STREAM_IS_GOOD(state)) [[unlikely]]
1275 mDevice->handleDisconnect("Bad capture state: %u", state);
1276 break;
1279 const void *capbuf;
1280 size_t caplen;
1281 if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) [[unlikely]]
1283 mDevice->handleDisconnect("Failed retrieving capture samples: %s",
1284 pa_strerror(pa_context_errno(mContext)));
1285 break;
1287 plock.unlock();
1289 if(caplen == 0) break;
1290 if(!capbuf) [[unlikely]]
1291 mHoleLength = caplen;
1292 else
1293 mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
1294 mPacketLength = caplen;
1296 if(!dstbuf.empty())
1297 std::fill(dstbuf.begin(), dstbuf.end(), mSilentVal);
1300 uint PulseCapture::availableSamples()
1302 size_t readable{maxz(mCapBuffer.size(), mHoleLength)};
1304 if(mDevice->Connected.load(std::memory_order_acquire))
1306 MainloopUniqueLock plock{mMainloop};
1307 size_t got{pa_stream_readable_size(mStream)};
1308 if(static_cast<ssize_t>(got) < 0) [[unlikely]]
1310 const char *err{pa_strerror(static_cast<int>(got))};
1311 ERR("pa_stream_readable_size() failed: %s\n", err);
1312 mDevice->handleDisconnect("Failed getting readable size: %s", err);
1314 else
1316 /* "readable" is the number of bytes from the last packet that have
1317 * not yet been read by the caller. So add the stream's readable
1318 * size excluding the last packet (the stream size includes the
1319 * last packet until it's dropped).
1321 if(got > mPacketLength)
1322 readable += got - mPacketLength;
1326 /* Avoid uint overflow, and avoid decreasing the readable count. */
1327 readable = std::min<size_t>(readable, std::numeric_limits<uint>::max());
1328 mLastReadable = std::max(mLastReadable, static_cast<uint>(readable));
1329 return mLastReadable / static_cast<uint>(pa_frame_size(&mSpec));
1333 ClockLatency PulseCapture::getClockLatency()
1335 ClockLatency ret;
1336 pa_usec_t latency;
1337 int neg, err;
1340 MainloopUniqueLock plock{mMainloop};
1341 ret.ClockTime = GetDeviceClockTime(mDevice);
1342 err = pa_stream_get_latency(mStream, &latency, &neg);
1345 if(err != 0) [[unlikely]]
1347 ERR("Failed to get stream latency: 0x%x\n", err);
1348 latency = 0;
1349 neg = 0;
1351 else if(neg) [[unlikely]]
1352 latency = 0;
1353 ret.Latency = std::chrono::microseconds{latency};
1355 return ret;
1358 } // namespace
1361 bool PulseBackendFactory::init()
1363 #ifdef HAVE_DYNLOAD
1364 if(!pulse_handle)
1366 bool ret{true};
1367 std::string missing_funcs;
1369 #ifdef _WIN32
1370 #define PALIB "libpulse-0.dll"
1371 #elif defined(__APPLE__) && defined(__MACH__)
1372 #define PALIB "libpulse.0.dylib"
1373 #else
1374 #define PALIB "libpulse.so.0"
1375 #endif
1376 pulse_handle = LoadLib(PALIB);
1377 if(!pulse_handle)
1379 WARN("Failed to load %s\n", PALIB);
1380 return false;
1383 #define LOAD_FUNC(x) do { \
1384 p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
1385 if(!(p##x)) { \
1386 ret = false; \
1387 missing_funcs += "\n" #x; \
1389 } while(0)
1390 PULSE_FUNCS(LOAD_FUNC)
1391 #undef LOAD_FUNC
1393 if(!ret)
1395 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
1396 CloseLib(pulse_handle);
1397 pulse_handle = nullptr;
1398 return false;
1401 #endif /* HAVE_DYNLOAD */
1403 pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
1404 if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", false))
1405 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
1407 try {
1408 if(!gGlobalMainloop)
1410 gGlobalMainloop = PulseMainloop::Create();
1411 gGlobalMainloop.start();
1414 MainloopUniqueLock plock{gGlobalMainloop};
1415 pa_context *context{plock.connectContext()};
1416 pa_context_disconnect(context);
1417 pa_context_unref(context);
1418 return true;
1420 catch(...) {
1421 return false;
1425 bool PulseBackendFactory::querySupport(BackendType type)
1426 { return type == BackendType::Playback || type == BackendType::Capture; }
1428 std::string PulseBackendFactory::probe(BackendType type)
1430 std::string outnames;
1432 auto add_device = [&outnames](const DevMap &entry) -> void
1434 /* +1 to also append the null char (to ensure a null-separated list and
1435 * double-null terminated list).
1437 outnames.append(entry.name.c_str(), entry.name.length()+1);
1440 switch(type)
1442 case BackendType::Playback:
1443 gGlobalMainloop.probePlaybackDevices();
1444 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1445 break;
1447 case BackendType::Capture:
1448 gGlobalMainloop.probeCaptureDevices();
1449 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1450 break;
1453 return outnames;
1456 BackendPtr PulseBackendFactory::createBackend(DeviceBase *device, BackendType type)
1458 if(type == BackendType::Playback)
1459 return BackendPtr{new PulsePlayback{device}};
1460 if(type == BackendType::Capture)
1461 return BackendPtr{new PulseCapture{device}};
1462 return nullptr;
1465 BackendFactory &PulseBackendFactory::getFactory()
1467 static PulseBackendFactory factory{};
1468 return factory;