Avoid a divide-by-zero in UhjDecoder::decodeStereo
[openal-soft.git] / alc / backends / pulseaudio.cpp
blob67e0023463aa70c27df9ee68f9af77e723b29b38
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 <condition_variable>
32 #include <cstring>
33 #include <functional>
34 #include <limits>
35 #include <mutex>
36 #include <new>
37 #include <poll.h>
38 #include <stdint.h>
39 #include <stdlib.h>
40 #include <string>
41 #include <sys/types.h>
42 #include <thread>
43 #include <utility>
45 #include "albyte.h"
46 #include "alc/alconfig.h"
47 #include "almalloc.h"
48 #include "alnumeric.h"
49 #include "aloptional.h"
50 #include "alspan.h"
51 #include "core/devformat.h"
52 #include "core/device.h"
53 #include "core/helpers.h"
54 #include "core/logging.h"
55 #include "dynload.h"
56 #include "opthelpers.h"
57 #include "strutils.h"
58 #include "vector.h"
60 #include <pulse/pulseaudio.h>
63 namespace {
65 using uint = unsigned int;
67 #ifdef HAVE_DYNLOAD
68 #define PULSE_FUNCS(MAGIC) \
69 MAGIC(pa_mainloop_new); \
70 MAGIC(pa_mainloop_free); \
71 MAGIC(pa_mainloop_set_poll_func); \
72 MAGIC(pa_mainloop_run); \
73 MAGIC(pa_mainloop_quit); \
74 MAGIC(pa_mainloop_get_api); \
75 MAGIC(pa_context_new); \
76 MAGIC(pa_context_unref); \
77 MAGIC(pa_context_get_state); \
78 MAGIC(pa_context_disconnect); \
79 MAGIC(pa_context_set_state_callback); \
80 MAGIC(pa_context_errno); \
81 MAGIC(pa_context_connect); \
82 MAGIC(pa_context_get_server_info); \
83 MAGIC(pa_context_get_sink_info_by_name); \
84 MAGIC(pa_context_get_sink_info_list); \
85 MAGIC(pa_context_get_source_info_by_name); \
86 MAGIC(pa_context_get_source_info_list); \
87 MAGIC(pa_stream_new); \
88 MAGIC(pa_stream_unref); \
89 MAGIC(pa_stream_drop); \
90 MAGIC(pa_stream_get_state); \
91 MAGIC(pa_stream_peek); \
92 MAGIC(pa_stream_write); \
93 MAGIC(pa_stream_connect_record); \
94 MAGIC(pa_stream_connect_playback); \
95 MAGIC(pa_stream_readable_size); \
96 MAGIC(pa_stream_writable_size); \
97 MAGIC(pa_stream_is_corked); \
98 MAGIC(pa_stream_cork); \
99 MAGIC(pa_stream_is_suspended); \
100 MAGIC(pa_stream_get_device_name); \
101 MAGIC(pa_stream_get_latency); \
102 MAGIC(pa_stream_set_write_callback); \
103 MAGIC(pa_stream_set_buffer_attr); \
104 MAGIC(pa_stream_get_buffer_attr); \
105 MAGIC(pa_stream_get_sample_spec); \
106 MAGIC(pa_stream_get_time); \
107 MAGIC(pa_stream_set_read_callback); \
108 MAGIC(pa_stream_set_state_callback); \
109 MAGIC(pa_stream_set_moved_callback); \
110 MAGIC(pa_stream_set_underflow_callback); \
111 MAGIC(pa_stream_new_with_proplist); \
112 MAGIC(pa_stream_disconnect); \
113 MAGIC(pa_stream_set_buffer_attr_callback); \
114 MAGIC(pa_stream_begin_write); \
115 MAGIC(pa_channel_map_init_auto); \
116 MAGIC(pa_channel_map_parse); \
117 MAGIC(pa_channel_map_snprint); \
118 MAGIC(pa_channel_map_equal); \
119 MAGIC(pa_channel_map_superset); \
120 MAGIC(pa_channel_position_to_string); \
121 MAGIC(pa_operation_get_state); \
122 MAGIC(pa_operation_unref); \
123 MAGIC(pa_sample_spec_valid); \
124 MAGIC(pa_frame_size); \
125 MAGIC(pa_strerror); \
126 MAGIC(pa_path_get_filename); \
127 MAGIC(pa_get_binary_name); \
128 MAGIC(pa_xmalloc); \
129 MAGIC(pa_xfree);
131 void *pulse_handle;
132 #define MAKE_FUNC(x) decltype(x) * p##x
133 PULSE_FUNCS(MAKE_FUNC)
134 #undef MAKE_FUNC
136 #ifndef IN_IDE_PARSER
137 #define pa_mainloop_new ppa_mainloop_new
138 #define pa_mainloop_free ppa_mainloop_free
139 #define pa_mainloop_set_poll_func ppa_mainloop_set_poll_func
140 #define pa_mainloop_run ppa_mainloop_run
141 #define pa_mainloop_quit ppa_mainloop_quit
142 #define pa_mainloop_get_api ppa_mainloop_get_api
143 #define pa_context_new ppa_context_new
144 #define pa_context_unref ppa_context_unref
145 #define pa_context_get_state ppa_context_get_state
146 #define pa_context_disconnect ppa_context_disconnect
147 #define pa_context_set_state_callback ppa_context_set_state_callback
148 #define pa_context_errno ppa_context_errno
149 #define pa_context_connect ppa_context_connect
150 #define pa_context_get_server_info ppa_context_get_server_info
151 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
152 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
153 #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
154 #define pa_context_get_source_info_list ppa_context_get_source_info_list
155 #define pa_stream_new ppa_stream_new
156 #define pa_stream_unref ppa_stream_unref
157 #define pa_stream_disconnect ppa_stream_disconnect
158 #define pa_stream_drop ppa_stream_drop
159 #define pa_stream_set_write_callback ppa_stream_set_write_callback
160 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
161 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
162 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
163 #define pa_stream_get_time ppa_stream_get_time
164 #define pa_stream_set_read_callback ppa_stream_set_read_callback
165 #define pa_stream_set_state_callback ppa_stream_set_state_callback
166 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
167 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
168 #define pa_stream_connect_record ppa_stream_connect_record
169 #define pa_stream_connect_playback ppa_stream_connect_playback
170 #define pa_stream_readable_size ppa_stream_readable_size
171 #define pa_stream_writable_size ppa_stream_writable_size
172 #define pa_stream_is_corked ppa_stream_is_corked
173 #define pa_stream_cork ppa_stream_cork
174 #define pa_stream_is_suspended ppa_stream_is_suspended
175 #define pa_stream_get_device_name ppa_stream_get_device_name
176 #define pa_stream_get_latency ppa_stream_get_latency
177 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
178 #define pa_stream_begin_write ppa_stream_begin_write
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
240 /* *grumble* Don't use enums for bitflags. */
241 constexpr inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
242 { return pa_stream_flags_t(int(lhs) | int(rhs)); }
243 inline pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
245 lhs = lhs | rhs;
246 return lhs;
248 inline pa_stream_flags_t& operator&=(pa_stream_flags_t &lhs, int rhs)
250 lhs = pa_stream_flags_t(int(lhs) & rhs);
251 return lhs;
254 inline pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
256 lhs = pa_context_flags_t(int(lhs) | int(rhs));
257 return lhs;
261 struct DevMap {
262 std::string name;
263 std::string device_name;
266 bool checkName(const al::span<const DevMap> list, const std::string &name)
268 auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
269 return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
272 al::vector<DevMap> PlaybackDevices;
273 al::vector<DevMap> CaptureDevices;
276 /* Global flags and properties */
277 pa_context_flags_t pulse_ctx_flags;
279 class PulseMainloop {
280 std::thread mThread;
281 std::mutex mMutex;
282 std::condition_variable mCondVar;
283 pa_mainloop *mMainloop{nullptr};
285 static int poll(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) noexcept
287 auto plock = static_cast<std::unique_lock<std::mutex>*>(userdata);
288 plock->unlock();
289 int r{::poll(ufds, nfds, timeout)};
290 plock->lock();
291 return r;
294 int mainloop_proc()
296 SetRTPriority();
298 std::unique_lock<std::mutex> plock{mMutex};
299 mMainloop = pa_mainloop_new();
301 pa_mainloop_set_poll_func(mMainloop, poll, &plock);
302 mCondVar.notify_all();
304 int ret{};
305 pa_mainloop_run(mMainloop, &ret);
307 pa_mainloop_free(mMainloop);
308 mMainloop = nullptr;
310 return ret;
313 public:
314 ~PulseMainloop()
316 if(mThread.joinable())
319 std::lock_guard<std::mutex> _{mMutex};
320 pa_mainloop_quit(mMainloop, 0);
322 mThread.join();
326 std::unique_lock<std::mutex> getUniqueLock() { return std::unique_lock<std::mutex>{mMutex}; }
327 std::condition_variable &getCondVar() noexcept { return mCondVar; }
329 void contextStateCallback(pa_context *context) noexcept
331 pa_context_state_t state{pa_context_get_state(context)};
332 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
333 mCondVar.notify_all();
335 static void contextStateCallbackC(pa_context *context, void *pdata) noexcept
336 { static_cast<PulseMainloop*>(pdata)->contextStateCallback(context); }
338 void streamStateCallback(pa_stream *stream) noexcept
340 pa_stream_state_t state{pa_stream_get_state(stream)};
341 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
342 mCondVar.notify_all();
344 static void streamStateCallbackC(pa_stream *stream, void *pdata) noexcept
345 { static_cast<PulseMainloop*>(pdata)->streamStateCallback(stream); }
347 void streamSuccessCallback(pa_stream*, int) noexcept
348 { mCondVar.notify_all(); }
349 static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept
350 { static_cast<PulseMainloop*>(pdata)->streamSuccessCallback(stream, success); }
352 void waitForOperation(pa_operation *op, std::unique_lock<std::mutex> &plock)
354 if(op)
356 mCondVar.wait(plock,
357 [op]() -> bool { return pa_operation_get_state(op) != PA_OPERATION_RUNNING; });
358 pa_operation_unref(op);
362 pa_context *connectContext(std::unique_lock<std::mutex> &plock);
364 pa_stream *connectStream(const char *device_name, std::unique_lock<std::mutex> &plock,
365 pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
366 pa_channel_map *chanmap, BackendType type);
368 void close(pa_context *context, pa_stream *stream);
371 void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
373 if(eol)
375 mCondVar.notify_all();
376 return;
379 /* Skip this device is if it's already in the list. */
380 auto match_devname = [info](const DevMap &entry) -> bool
381 { return entry.device_name == info->name; };
382 if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_devname) != PlaybackDevices.cend())
383 return;
385 /* Make sure the display name (description) is unique. Append a number
386 * counter as needed.
388 int count{1};
389 std::string newname{info->description};
390 while(checkName(PlaybackDevices, newname))
392 newname = info->description;
393 newname += " #";
394 newname += std::to_string(++count);
396 PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
397 DevMap &newentry = PlaybackDevices.back();
399 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
401 static void deviceSinkCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
402 { static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(context, info, eol); }
404 void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) noexcept
406 if(eol)
408 mCondVar.notify_all();
409 return;
412 /* Skip this device is if it's already in the list. */
413 auto match_devname = [info](const DevMap &entry) -> bool
414 { return entry.device_name == info->name; };
415 if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_devname) != CaptureDevices.cend())
416 return;
418 /* Make sure the display name (description) is unique. Append a number
419 * counter as needed.
421 int count{1};
422 std::string newname{info->description};
423 while(checkName(CaptureDevices, newname))
425 newname = info->description;
426 newname += " #";
427 newname += std::to_string(++count);
429 CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
430 DevMap &newentry = CaptureDevices.back();
432 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
434 static void deviceSourceCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept
435 { static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(context, info, eol); }
437 void probePlaybackDevices();
438 void probeCaptureDevices();
442 pa_context *PulseMainloop::connectContext(std::unique_lock<std::mutex> &plock)
444 if(!mMainloop)
446 mThread = std::thread{std::mem_fn(&PulseMainloop::mainloop_proc), this};
447 mCondVar.wait(plock, [this]() noexcept { return mMainloop; });
450 pa_context *context{pa_context_new(pa_mainloop_get_api(mMainloop), nullptr)};
451 if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
452 "pa_context_new() failed"};
454 pa_context_set_state_callback(context, &contextStateCallbackC, this);
456 int err;
457 if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
459 pa_context_state_t state;
460 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
462 if(!PA_CONTEXT_IS_GOOD(state))
464 err = pa_context_errno(context);
465 if(err > 0) err = -err;
466 break;
469 mCondVar.wait(plock);
472 pa_context_set_state_callback(context, nullptr, nullptr);
474 if(err < 0)
476 pa_context_unref(context);
477 throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)",
478 pa_strerror(err)};
481 return context;
484 pa_stream *PulseMainloop::connectStream(const char *device_name,
485 std::unique_lock<std::mutex> &plock, pa_context *context, pa_stream_flags_t flags,
486 pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type)
488 const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
489 pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
490 if(!stream)
491 throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)",
492 pa_strerror(pa_context_errno(context))};
494 pa_stream_set_state_callback(stream, &streamStateCallbackC, this);
496 int err{(type==BackendType::Playback) ?
497 pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
498 pa_stream_connect_record(stream, device_name, attr, flags)};
499 if(err < 0)
501 pa_stream_unref(stream);
502 throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect (%s)",
503 stream_id, pa_strerror(err)};
506 pa_stream_state_t state;
507 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
509 if(!PA_STREAM_IS_GOOD(state))
511 err = pa_context_errno(context);
512 pa_stream_unref(stream);
513 throw al::backend_exception{al::backend_error::DeviceError,
514 "%s did not get ready (%s)", stream_id, pa_strerror(err)};
517 mCondVar.wait(plock);
519 pa_stream_set_state_callback(stream, nullptr, nullptr);
521 return stream;
524 void PulseMainloop::close(pa_context *context, pa_stream *stream)
526 std::lock_guard<std::mutex> _{mMutex};
527 if(stream)
529 pa_stream_set_state_callback(stream, nullptr, nullptr);
530 pa_stream_set_moved_callback(stream, nullptr, nullptr);
531 pa_stream_set_write_callback(stream, nullptr, nullptr);
532 pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
533 pa_stream_disconnect(stream);
534 pa_stream_unref(stream);
537 pa_context_disconnect(context);
538 pa_context_unref(context);
542 void PulseMainloop::probePlaybackDevices()
544 pa_context *context{};
545 pa_stream *stream{};
547 PlaybackDevices.clear();
548 try {
549 std::unique_lock<std::mutex> plock{mMutex};
551 context = connectContext(plock);
552 pa_operation *op{pa_context_get_sink_info_by_name(context, nullptr,
553 &deviceSinkCallbackC, this)};
554 waitForOperation(op, plock);
556 op = pa_context_get_sink_info_list(context, &deviceSinkCallbackC, this);
557 waitForOperation(op, plock);
559 pa_context_disconnect(context);
560 pa_context_unref(context);
561 context = nullptr;
563 catch(std::exception &e) {
564 ERR("Error enumerating devices: %s\n", e.what());
565 if(context) close(context, stream);
569 void PulseMainloop::probeCaptureDevices()
571 pa_context *context{};
572 pa_stream *stream{};
574 CaptureDevices.clear();
575 try {
576 std::unique_lock<std::mutex> plock{mMutex};
578 context = connectContext(plock);
579 pa_operation *op{pa_context_get_source_info_by_name(context, nullptr,
580 &deviceSourceCallbackC, this)};
581 waitForOperation(op, plock);
583 op = pa_context_get_source_info_list(context, &deviceSourceCallbackC, this);
584 waitForOperation(op, plock);
586 pa_context_disconnect(context);
587 pa_context_unref(context);
588 context = nullptr;
590 catch(std::exception &e) {
591 ERR("Error enumerating devices: %s\n", e.what());
592 if(context) close(context, stream);
597 /* Used for initial connection test and enumeration. */
598 PulseMainloop gGlobalMainloop;
601 struct PulsePlayback final : public BackendBase {
602 PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
603 ~PulsePlayback() override;
605 void bufferAttrCallback(pa_stream *stream) noexcept;
606 static void bufferAttrCallbackC(pa_stream *stream, void *pdata) noexcept
607 { static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); }
609 void streamStateCallback(pa_stream *stream) noexcept;
610 static void streamStateCallbackC(pa_stream *stream, void *pdata) noexcept
611 { static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }
613 void streamWriteCallback(pa_stream *stream, size_t nbytes) noexcept;
614 static void streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata) noexcept
615 { static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }
617 void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept;
618 static void sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
619 { static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }
621 void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept;
622 static void sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
623 { static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }
625 void streamMovedCallback(pa_stream *stream) noexcept;
626 static void streamMovedCallbackC(pa_stream *stream, void *pdata) noexcept
627 { static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }
629 void open(const char *name) override;
630 bool reset() override;
631 void start() override;
632 void stop() override;
633 ClockLatency getClockLatency() override;
635 PulseMainloop mMainloop;
637 al::optional<std::string> mDeviceName{al::nullopt};
639 bool mIs51Rear{false};
640 pa_buffer_attr mAttr;
641 pa_sample_spec mSpec;
643 pa_stream *mStream{nullptr};
644 pa_context *mContext{nullptr};
646 uint mFrameSize{0u};
648 DEF_NEWDEL(PulsePlayback)
651 PulsePlayback::~PulsePlayback()
653 if(!mContext)
654 return;
656 mMainloop.close(mContext, mStream);
657 mContext = nullptr;
658 mStream = nullptr;
662 void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept
664 /* FIXME: Update the device's UpdateSize (and/or BufferSize) using the new
665 * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
666 * property, which probably shouldn't change between device resets. But
667 * leaving it alone means ALC_REFRESH will be off.
669 mAttr = *(pa_stream_get_buffer_attr(stream));
670 TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
673 void PulsePlayback::streamStateCallback(pa_stream *stream) noexcept
675 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
677 ERR("Received stream failure!\n");
678 mDevice->handleDisconnect("Playback stream failure");
680 mMainloop.getCondVar().notify_all();
683 void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes) noexcept
685 do {
686 pa_free_cb_t free_func{nullptr};
687 auto buflen = static_cast<size_t>(-1);
688 void *buf;
689 if UNLIKELY(pa_stream_begin_write(stream, &buf, &buflen) || !buf)
691 buflen = nbytes;
692 buf = pa_xmalloc(buflen);
693 free_func = pa_xfree;
695 else
696 buflen = minz(buflen, nbytes);
697 nbytes -= buflen;
699 mDevice->renderSamples(buf, static_cast<uint>(buflen/mFrameSize), mSpec.channels);
701 int ret{pa_stream_write(stream, buf, buflen, free_func, 0, PA_SEEK_RELATIVE)};
702 if UNLIKELY(ret != PA_OK)
703 ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
704 } while(nbytes > 0);
707 void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
709 struct ChannelMap {
710 DevFmtChannels fmt;
711 pa_channel_map map;
712 bool is_51rear;
714 static constexpr std::array<ChannelMap,7> chanmaps{{
715 { DevFmtX71, X71ChanMap, false },
716 { DevFmtX61, X61ChanMap, false },
717 { DevFmtX51, X51ChanMap, false },
718 { DevFmtX51, X51RearChanMap, true },
719 { DevFmtQuad, QuadChanMap, false },
720 { DevFmtStereo, StereoChanMap, false },
721 { DevFmtMono, MonoChanMap, false }
724 if(eol)
726 mMainloop.getCondVar().notify_all();
727 return;
730 auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
731 [info](const ChannelMap &chanmap) -> bool
732 { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
734 if(chaniter != chanmaps.cend())
736 if(!mDevice->Flags.test(ChannelsRequest))
737 mDevice->FmtChans = chaniter->fmt;
738 mIs51Rear = chaniter->is_51rear;
740 else
742 mIs51Rear = false;
743 char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
744 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
745 WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
748 if(info->active_port)
749 TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
750 mDevice->Flags.set(DirectEar, (info->active_port
751 && strcmp(info->active_port->name, "analog-output-headphones") == 0));
754 void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
756 if(eol)
758 mMainloop.getCondVar().notify_all();
759 return;
761 mDevice->DeviceName = info->description;
764 void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept
766 mDeviceName = pa_stream_get_device_name(stream);
767 TRACE("Stream moved to %s\n", mDeviceName->c_str());
771 void PulsePlayback::open(const char *name)
773 const char *pulse_name{nullptr};
774 const char *dev_name{nullptr};
776 if(name)
778 if(PlaybackDevices.empty())
779 mMainloop.probePlaybackDevices();
781 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
782 [name](const DevMap &entry) -> bool { return entry.name == name; });
783 if(iter == PlaybackDevices.cend())
784 throw al::backend_exception{al::backend_error::NoDevice,
785 "Device name \"%s\" not found", name};
786 pulse_name = iter->device_name.c_str();
787 dev_name = iter->name.c_str();
790 auto plock = mMainloop.getUniqueLock();
791 if(!mContext)
792 mContext = mMainloop.connectContext(plock);
794 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
795 PA_STREAM_FIX_CHANNELS};
796 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
797 flags |= PA_STREAM_DONT_MOVE;
799 pa_sample_spec spec{};
800 spec.format = PA_SAMPLE_S16NE;
801 spec.rate = 44100;
802 spec.channels = 2;
804 if(!pulse_name)
806 static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT");
807 if(defname) pulse_name = defname->c_str();
809 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
810 pa_stream *stream{mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec,
811 nullptr, BackendType::Playback)};
812 if(mStream)
814 pa_stream_set_state_callback(mStream, nullptr, nullptr);
815 pa_stream_set_moved_callback(mStream, nullptr, nullptr);
816 pa_stream_set_write_callback(mStream, nullptr, nullptr);
817 pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
818 pa_stream_disconnect(mStream);
819 pa_stream_unref(mStream);
821 mStream = stream;
823 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
824 mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
826 mDeviceName = pulse_name ? al::make_optional<std::string>(pulse_name) : al::nullopt;
827 if(!dev_name)
829 pa_operation *op{pa_context_get_sink_info_by_name(mContext,
830 pa_stream_get_device_name(mStream), &PulsePlayback::sinkNameCallbackC, this)};
831 mMainloop.waitForOperation(op, plock);
833 else
834 mDevice->DeviceName = dev_name;
837 bool PulsePlayback::reset()
839 auto plock = mMainloop.getUniqueLock();
840 const auto deviceName = mDeviceName ? mDeviceName->c_str() : nullptr;
842 if(mStream)
844 pa_stream_set_state_callback(mStream, nullptr, nullptr);
845 pa_stream_set_moved_callback(mStream, nullptr, nullptr);
846 pa_stream_set_write_callback(mStream, nullptr, nullptr);
847 pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
848 pa_stream_disconnect(mStream);
849 pa_stream_unref(mStream);
850 mStream = nullptr;
853 pa_operation *op{pa_context_get_sink_info_by_name(mContext, deviceName,
854 &PulsePlayback::sinkInfoCallbackC, this)};
855 mMainloop.waitForOperation(op, plock);
857 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
858 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
859 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
860 flags |= PA_STREAM_DONT_MOVE;
861 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
863 /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
864 * reason. So if the user wants to adjust the overall device latency,
865 * we can't ask to get write signals as soon as minreq is reached.
867 flags &= ~PA_STREAM_EARLY_REQUESTS;
868 flags |= PA_STREAM_ADJUST_LATENCY;
870 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0)
871 || !mDevice->Flags.test(FrequencyRequest))
872 flags |= PA_STREAM_FIX_RATE;
874 pa_channel_map chanmap{};
875 switch(mDevice->FmtChans)
877 case DevFmtMono:
878 chanmap = MonoChanMap;
879 break;
880 case DevFmtAmbi3D:
881 mDevice->FmtChans = DevFmtStereo;
882 /*fall-through*/
883 case DevFmtStereo:
884 chanmap = StereoChanMap;
885 break;
886 case DevFmtQuad:
887 chanmap = QuadChanMap;
888 break;
889 case DevFmtX51:
890 chanmap = (mIs51Rear ? X51RearChanMap : X51ChanMap);
891 break;
892 case DevFmtX61:
893 chanmap = X61ChanMap;
894 break;
895 case DevFmtX71:
896 chanmap = X71ChanMap;
897 break;
899 setDefaultWFXChannelOrder();
901 switch(mDevice->FmtType)
903 case DevFmtByte:
904 mDevice->FmtType = DevFmtUByte;
905 /* fall-through */
906 case DevFmtUByte:
907 mSpec.format = PA_SAMPLE_U8;
908 break;
909 case DevFmtUShort:
910 mDevice->FmtType = DevFmtShort;
911 /* fall-through */
912 case DevFmtShort:
913 mSpec.format = PA_SAMPLE_S16NE;
914 break;
915 case DevFmtUInt:
916 mDevice->FmtType = DevFmtInt;
917 /* fall-through */
918 case DevFmtInt:
919 mSpec.format = PA_SAMPLE_S32NE;
920 break;
921 case DevFmtFloat:
922 mSpec.format = PA_SAMPLE_FLOAT32NE;
923 break;
925 mSpec.rate = mDevice->Frequency;
926 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
927 if(pa_sample_spec_valid(&mSpec) == 0)
928 throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample spec"};
930 const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
931 mAttr.maxlength = ~0u;
932 mAttr.tlength = mDevice->BufferSize * frame_size;
933 mAttr.prebuf = 0u;
934 mAttr.minreq = mDevice->UpdateSize * frame_size;
935 mAttr.fragsize = ~0u;
937 mStream = mMainloop.connectStream(deviceName, plock, mContext, flags, &mAttr, &mSpec,
938 &chanmap, BackendType::Playback);
940 pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
941 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
943 mSpec = *(pa_stream_get_sample_spec(mStream));
944 mFrameSize = static_cast<uint>(pa_frame_size(&mSpec));
946 if(mDevice->Frequency != mSpec.rate)
948 /* Server updated our playback rate, so modify the buffer attribs
949 * accordingly.
951 const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
952 const auto perlen = static_cast<uint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0,
953 8192.0));
954 const auto buflen = static_cast<uint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2,
955 std::numeric_limits<int>::max()/mFrameSize));
957 mAttr.maxlength = ~0u;
958 mAttr.tlength = buflen * mFrameSize;
959 mAttr.prebuf = 0u;
960 mAttr.minreq = perlen * mFrameSize;
962 op = pa_stream_set_buffer_attr(mStream, &mAttr, &PulseMainloop::streamSuccessCallbackC,
963 &mMainloop);
964 mMainloop.waitForOperation(op, plock);
966 mDevice->Frequency = mSpec.rate;
969 pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
970 bufferAttrCallback(mStream);
972 mDevice->BufferSize = mAttr.tlength / mFrameSize;
973 mDevice->UpdateSize = mAttr.minreq / mFrameSize;
975 return true;
978 void PulsePlayback::start()
980 auto plock = mMainloop.getUniqueLock();
982 /* Write some (silent) samples to fill the buffer before we start feeding
983 * it newly mixed samples.
985 if(size_t todo{pa_stream_writable_size(mStream)})
987 void *buf{pa_xmalloc(todo)};
988 switch(mSpec.format)
990 case PA_SAMPLE_U8:
991 std::fill_n(static_cast<uint8_t*>(buf), todo, 0x80);
992 break;
993 case PA_SAMPLE_ALAW:
994 std::fill_n(static_cast<uint8_t*>(buf), todo, 0xD5);
995 break;
996 case PA_SAMPLE_ULAW:
997 std::fill_n(static_cast<uint8_t*>(buf), todo, 0x7f);
998 break;
999 default:
1000 std::fill_n(static_cast<uint8_t*>(buf), todo, 0x00);
1001 break;
1003 pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE);
1006 pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
1007 pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
1008 &mMainloop)};
1010 mMainloop.waitForOperation(op, plock);
1013 void PulsePlayback::stop()
1015 auto plock = mMainloop.getUniqueLock();
1017 pa_operation *op{pa_stream_cork(mStream, 1, &PulseMainloop::streamSuccessCallbackC,
1018 &mMainloop)};
1019 mMainloop.waitForOperation(op, plock);
1020 pa_stream_set_write_callback(mStream, nullptr, nullptr);
1024 ClockLatency PulsePlayback::getClockLatency()
1026 ClockLatency ret;
1027 pa_usec_t latency;
1028 int neg, err;
1031 auto plock = mMainloop.getUniqueLock();
1032 ret.ClockTime = GetDeviceClockTime(mDevice);
1033 err = pa_stream_get_latency(mStream, &latency, &neg);
1036 if UNLIKELY(err != 0)
1038 /* If err = -PA_ERR_NODATA, it means we were called too soon after
1039 * starting the stream and no timing info has been received from the
1040 * server yet. Give a generic value since nothing better is available.
1042 if(err != -PA_ERR_NODATA)
1043 ERR("Failed to get stream latency: 0x%x\n", err);
1044 latency = mDevice->BufferSize - mDevice->UpdateSize;
1045 neg = 0;
1047 else if UNLIKELY(neg)
1048 latency = 0;
1049 ret.Latency = std::chrono::microseconds{latency};
1051 return ret;
1055 struct PulseCapture final : public BackendBase {
1056 PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
1057 ~PulseCapture() override;
1059 void streamStateCallback(pa_stream *stream) noexcept;
1060 static void streamStateCallbackC(pa_stream *stream, void *pdata) noexcept
1061 { static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }
1063 void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol) noexcept;
1064 static void sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept
1065 { static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }
1067 void streamMovedCallback(pa_stream *stream) noexcept;
1068 static void streamMovedCallbackC(pa_stream *stream, void *pdata) noexcept
1069 { static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }
1071 void open(const char *name) override;
1072 void start() override;
1073 void stop() override;
1074 void captureSamples(al::byte *buffer, uint samples) override;
1075 uint availableSamples() override;
1076 ClockLatency getClockLatency() override;
1078 PulseMainloop mMainloop;
1080 al::optional<std::string> mDeviceName{al::nullopt};
1082 uint mLastReadable{0u};
1083 al::byte mSilentVal{};
1085 al::span<const al::byte> mCapBuffer;
1086 ssize_t mCapLen{0};
1088 pa_buffer_attr mAttr{};
1089 pa_sample_spec mSpec{};
1091 pa_stream *mStream{nullptr};
1092 pa_context *mContext{nullptr};
1094 DEF_NEWDEL(PulseCapture)
1097 PulseCapture::~PulseCapture()
1099 if(!mContext)
1100 return;
1102 mMainloop.close(mContext, mStream);
1103 mContext = nullptr;
1104 mStream = nullptr;
1108 void PulseCapture::streamStateCallback(pa_stream *stream) noexcept
1110 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
1112 ERR("Received stream failure!\n");
1113 mDevice->handleDisconnect("Capture stream failure");
1115 mMainloop.getCondVar().notify_all();
1118 void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, int eol) noexcept
1120 if(eol)
1122 mMainloop.getCondVar().notify_all();
1123 return;
1125 mDevice->DeviceName = info->description;
1128 void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept
1130 mDeviceName = pa_stream_get_device_name(stream);
1131 TRACE("Stream moved to %s\n", mDeviceName->c_str());
1135 void PulseCapture::open(const char *name)
1137 const char *pulse_name{nullptr};
1138 if(name)
1140 if(CaptureDevices.empty())
1141 mMainloop.probeCaptureDevices();
1143 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1144 [name](const DevMap &entry) -> bool { return entry.name == name; });
1145 if(iter == CaptureDevices.cend())
1146 throw al::backend_exception{al::backend_error::NoDevice,
1147 "Device name \"%s\" not found", name};
1148 pulse_name = iter->device_name.c_str();
1149 mDevice->DeviceName = iter->name;
1152 auto plock = mMainloop.getUniqueLock();
1153 mContext = mMainloop.connectContext(plock);
1155 pa_channel_map chanmap{};
1156 switch(mDevice->FmtChans)
1158 case DevFmtMono:
1159 chanmap = MonoChanMap;
1160 break;
1161 case DevFmtStereo:
1162 chanmap = StereoChanMap;
1163 break;
1164 case DevFmtQuad:
1165 chanmap = QuadChanMap;
1166 break;
1167 case DevFmtX51:
1168 chanmap = X51ChanMap;
1169 break;
1170 case DevFmtX61:
1171 chanmap = X61ChanMap;
1172 break;
1173 case DevFmtX71:
1174 chanmap = X71ChanMap;
1175 break;
1176 case DevFmtAmbi3D:
1177 throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
1178 DevFmtChannelsString(mDevice->FmtChans)};
1180 setDefaultWFXChannelOrder();
1182 switch(mDevice->FmtType)
1184 case DevFmtUByte:
1185 mSilentVal = al::byte(0x80);
1186 mSpec.format = PA_SAMPLE_U8;
1187 break;
1188 case DevFmtShort:
1189 mSpec.format = PA_SAMPLE_S16NE;
1190 break;
1191 case DevFmtInt:
1192 mSpec.format = PA_SAMPLE_S32NE;
1193 break;
1194 case DevFmtFloat:
1195 mSpec.format = PA_SAMPLE_FLOAT32NE;
1196 break;
1197 case DevFmtByte:
1198 case DevFmtUShort:
1199 case DevFmtUInt:
1200 throw al::backend_exception{al::backend_error::DeviceError,
1201 "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
1203 mSpec.rate = mDevice->Frequency;
1204 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
1205 if(pa_sample_spec_valid(&mSpec) == 0)
1206 throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"};
1208 const auto frame_size = static_cast<uint>(pa_frame_size(&mSpec));
1209 const uint samples{maxu(mDevice->BufferSize, 100 * mDevice->Frequency / 1000)};
1210 mAttr.minreq = ~0u;
1211 mAttr.prebuf = ~0u;
1212 mAttr.maxlength = samples * frame_size;
1213 mAttr.tlength = ~0u;
1214 mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size;
1216 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
1217 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
1218 flags |= PA_STREAM_DONT_MOVE;
1220 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
1221 mStream = mMainloop.connectStream(pulse_name, plock, mContext, flags, &mAttr, &mSpec, &chanmap,
1222 BackendType::Capture);
1224 pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
1225 pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
1227 mDeviceName = pulse_name ? al::make_optional<std::string>(pulse_name) : al::nullopt;
1228 if(mDevice->DeviceName.empty())
1230 pa_operation *op{pa_context_get_source_info_by_name(mContext,
1231 pa_stream_get_device_name(mStream), &PulseCapture::sourceNameCallbackC, this)};
1232 mMainloop.waitForOperation(op, plock);
1236 void PulseCapture::start()
1238 auto plock = mMainloop.getUniqueLock();
1239 pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
1240 &mMainloop)};
1241 mMainloop.waitForOperation(op, plock);
1244 void PulseCapture::stop()
1246 auto plock = mMainloop.getUniqueLock();
1247 pa_operation *op{pa_stream_cork(mStream, 1, &PulseMainloop::streamSuccessCallbackC,
1248 &mMainloop)};
1249 mMainloop.waitForOperation(op, plock);
1252 void PulseCapture::captureSamples(al::byte *buffer, uint samples)
1254 al::span<al::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)};
1256 /* Capture is done in fragment-sized chunks, so we loop until we get all
1257 * that's available */
1258 mLastReadable -= static_cast<uint>(dstbuf.size());
1259 while(!dstbuf.empty())
1261 if(!mCapBuffer.empty())
1263 const size_t rem{minz(dstbuf.size(), mCapBuffer.size())};
1264 if UNLIKELY(mCapLen < 0)
1265 std::fill_n(dstbuf.begin(), rem, mSilentVal);
1266 else
1267 std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin());
1268 dstbuf = dstbuf.subspan(rem);
1269 mCapBuffer = mCapBuffer.subspan(rem);
1271 continue;
1274 if UNLIKELY(!mDevice->Connected.load(std::memory_order_acquire))
1275 break;
1277 auto plock = mMainloop.getUniqueLock();
1278 if(mCapLen != 0)
1280 pa_stream_drop(mStream);
1281 mCapBuffer = {};
1282 mCapLen = 0;
1284 const pa_stream_state_t state{pa_stream_get_state(mStream)};
1285 if UNLIKELY(!PA_STREAM_IS_GOOD(state))
1287 mDevice->handleDisconnect("Bad capture state: %u", state);
1288 break;
1290 const void *capbuf;
1291 size_t caplen;
1292 if UNLIKELY(pa_stream_peek(mStream, &capbuf, &caplen) < 0)
1294 mDevice->handleDisconnect("Failed retrieving capture samples: %s",
1295 pa_strerror(pa_context_errno(mContext)));
1296 break;
1298 plock.unlock();
1300 if(caplen == 0) break;
1301 if UNLIKELY(!capbuf)
1302 mCapLen = -static_cast<ssize_t>(caplen);
1303 else
1304 mCapLen = static_cast<ssize_t>(caplen);
1305 mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
1307 if(!dstbuf.empty())
1308 std::fill(dstbuf.begin(), dstbuf.end(), mSilentVal);
1311 uint PulseCapture::availableSamples()
1313 size_t readable{mCapBuffer.size()};
1315 if(mDevice->Connected.load(std::memory_order_acquire))
1317 auto plock = mMainloop.getUniqueLock();
1318 size_t got{pa_stream_readable_size(mStream)};
1319 if UNLIKELY(static_cast<ssize_t>(got) < 0)
1321 const char *err{pa_strerror(static_cast<int>(got))};
1322 ERR("pa_stream_readable_size() failed: %s\n", err);
1323 mDevice->handleDisconnect("Failed getting readable size: %s", err);
1325 else
1327 const auto caplen = static_cast<size_t>(std::abs(mCapLen));
1328 if(got > caplen) readable += got - caplen;
1332 readable = std::min<size_t>(readable, std::numeric_limits<uint>::max());
1333 mLastReadable = std::max(mLastReadable, static_cast<uint>(readable));
1334 return mLastReadable / static_cast<uint>(pa_frame_size(&mSpec));
1338 ClockLatency PulseCapture::getClockLatency()
1340 ClockLatency ret;
1341 pa_usec_t latency;
1342 int neg, err;
1345 auto plock = mMainloop.getUniqueLock();
1346 ret.ClockTime = GetDeviceClockTime(mDevice);
1347 err = pa_stream_get_latency(mStream, &latency, &neg);
1350 if UNLIKELY(err != 0)
1352 ERR("Failed to get stream latency: 0x%x\n", err);
1353 latency = 0;
1354 neg = 0;
1356 else if UNLIKELY(neg)
1357 latency = 0;
1358 ret.Latency = std::chrono::microseconds{latency};
1360 return ret;
1363 } // namespace
1366 bool PulseBackendFactory::init()
1368 #ifdef HAVE_DYNLOAD
1369 if(!pulse_handle)
1371 bool ret{true};
1372 std::string missing_funcs;
1374 #ifdef _WIN32
1375 #define PALIB "libpulse-0.dll"
1376 #elif defined(__APPLE__) && defined(__MACH__)
1377 #define PALIB "libpulse.0.dylib"
1378 #else
1379 #define PALIB "libpulse.so.0"
1380 #endif
1381 pulse_handle = LoadLib(PALIB);
1382 if(!pulse_handle)
1384 WARN("Failed to load %s\n", PALIB);
1385 return false;
1388 #define LOAD_FUNC(x) do { \
1389 p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
1390 if(!(p##x)) { \
1391 ret = false; \
1392 missing_funcs += "\n" #x; \
1394 } while(0)
1395 PULSE_FUNCS(LOAD_FUNC)
1396 #undef LOAD_FUNC
1398 if(!ret)
1400 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
1401 CloseLib(pulse_handle);
1402 pulse_handle = nullptr;
1403 return false;
1406 #endif /* HAVE_DYNLOAD */
1408 pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
1409 if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
1410 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
1412 try {
1413 auto plock = gGlobalMainloop.getUniqueLock();
1414 pa_context *context{gGlobalMainloop.connectContext(plock)};
1415 pa_context_disconnect(context);
1416 pa_context_unref(context);
1417 return true;
1419 catch(...) {
1420 return false;
1424 bool PulseBackendFactory::querySupport(BackendType type)
1425 { return type == BackendType::Playback || type == BackendType::Capture; }
1427 std::string PulseBackendFactory::probe(BackendType type)
1429 std::string outnames;
1431 auto add_device = [&outnames](const DevMap &entry) -> void
1433 /* +1 to also append the null char (to ensure a null-separated list and
1434 * double-null terminated list).
1436 outnames.append(entry.name.c_str(), entry.name.length()+1);
1439 switch(type)
1441 case BackendType::Playback:
1442 gGlobalMainloop.probePlaybackDevices();
1443 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1444 break;
1446 case BackendType::Capture:
1447 gGlobalMainloop.probeCaptureDevices();
1448 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1449 break;
1452 return outnames;
1455 BackendPtr PulseBackendFactory::createBackend(DeviceBase *device, BackendType type)
1457 if(type == BackendType::Playback)
1458 return BackendPtr{new PulsePlayback{device}};
1459 if(type == BackendType::Capture)
1460 return BackendPtr{new PulseCapture{device}};
1461 return nullptr;
1464 BackendFactory &PulseBackendFactory::getFactory()
1466 static PulseBackendFactory factory{};
1467 return factory;