Reduce some indentation
[openal-soft.git] / alc / backends / pulseaudio.cpp
blob45e28aca608972680a4c3904278b0279320866c2
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 "backends/pulseaudio.h"
26 #include <poll.h>
27 #include <cstring>
29 #include <array>
30 #include <string>
31 #include <vector>
32 #include <atomic>
33 #include <thread>
34 #include <algorithm>
35 #include <condition_variable>
37 #include "alcmain.h"
38 #include "alu.h"
39 #include "alconfig.h"
40 #include "alexcpt.h"
41 #include "compat.h"
42 #include "dynload.h"
43 #include "strutils.h"
45 #include <pulse/pulseaudio.h>
48 namespace {
50 #ifdef HAVE_DYNLOAD
51 #define PULSE_FUNCS(MAGIC) \
52 MAGIC(pa_mainloop_new); \
53 MAGIC(pa_mainloop_free); \
54 MAGIC(pa_mainloop_set_poll_func); \
55 MAGIC(pa_mainloop_run); \
56 MAGIC(pa_mainloop_get_api); \
57 MAGIC(pa_context_new); \
58 MAGIC(pa_context_unref); \
59 MAGIC(pa_context_get_state); \
60 MAGIC(pa_context_disconnect); \
61 MAGIC(pa_context_set_state_callback); \
62 MAGIC(pa_context_errno); \
63 MAGIC(pa_context_connect); \
64 MAGIC(pa_context_get_server_info); \
65 MAGIC(pa_context_get_sink_info_by_name); \
66 MAGIC(pa_context_get_sink_info_list); \
67 MAGIC(pa_context_get_source_info_by_name); \
68 MAGIC(pa_context_get_source_info_list); \
69 MAGIC(pa_stream_new); \
70 MAGIC(pa_stream_unref); \
71 MAGIC(pa_stream_drop); \
72 MAGIC(pa_stream_get_state); \
73 MAGIC(pa_stream_peek); \
74 MAGIC(pa_stream_write); \
75 MAGIC(pa_stream_connect_record); \
76 MAGIC(pa_stream_connect_playback); \
77 MAGIC(pa_stream_readable_size); \
78 MAGIC(pa_stream_writable_size); \
79 MAGIC(pa_stream_is_corked); \
80 MAGIC(pa_stream_cork); \
81 MAGIC(pa_stream_is_suspended); \
82 MAGIC(pa_stream_get_device_name); \
83 MAGIC(pa_stream_get_latency); \
84 MAGIC(pa_stream_set_write_callback); \
85 MAGIC(pa_stream_set_buffer_attr); \
86 MAGIC(pa_stream_get_buffer_attr); \
87 MAGIC(pa_stream_get_sample_spec); \
88 MAGIC(pa_stream_get_time); \
89 MAGIC(pa_stream_set_read_callback); \
90 MAGIC(pa_stream_set_state_callback); \
91 MAGIC(pa_stream_set_moved_callback); \
92 MAGIC(pa_stream_set_underflow_callback); \
93 MAGIC(pa_stream_new_with_proplist); \
94 MAGIC(pa_stream_disconnect); \
95 MAGIC(pa_stream_set_buffer_attr_callback); \
96 MAGIC(pa_stream_begin_write); \
97 MAGIC(pa_channel_map_init_auto); \
98 MAGIC(pa_channel_map_parse); \
99 MAGIC(pa_channel_map_snprint); \
100 MAGIC(pa_channel_map_equal); \
101 MAGIC(pa_channel_map_superset); \
102 MAGIC(pa_operation_get_state); \
103 MAGIC(pa_operation_unref); \
104 MAGIC(pa_sample_spec_valid); \
105 MAGIC(pa_frame_size); \
106 MAGIC(pa_strerror); \
107 MAGIC(pa_path_get_filename); \
108 MAGIC(pa_get_binary_name); \
109 MAGIC(pa_xmalloc); \
110 MAGIC(pa_xfree);
112 void *pulse_handle;
113 #define MAKE_FUNC(x) decltype(x) * p##x
114 PULSE_FUNCS(MAKE_FUNC)
115 #undef MAKE_FUNC
117 #ifndef IN_IDE_PARSER
118 #define pa_mainloop_new ppa_mainloop_new
119 #define pa_mainloop_free ppa_mainloop_free
120 #define pa_mainloop_set_poll_func ppa_mainloop_set_poll_func
121 #define pa_mainloop_run ppa_mainloop_run
122 #define pa_mainloop_get_api ppa_mainloop_get_api
123 #define pa_context_new ppa_context_new
124 #define pa_context_unref ppa_context_unref
125 #define pa_context_get_state ppa_context_get_state
126 #define pa_context_disconnect ppa_context_disconnect
127 #define pa_context_set_state_callback ppa_context_set_state_callback
128 #define pa_context_errno ppa_context_errno
129 #define pa_context_connect ppa_context_connect
130 #define pa_context_get_server_info ppa_context_get_server_info
131 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
132 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
133 #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
134 #define pa_context_get_source_info_list ppa_context_get_source_info_list
135 #define pa_stream_new ppa_stream_new
136 #define pa_stream_unref ppa_stream_unref
137 #define pa_stream_disconnect ppa_stream_disconnect
138 #define pa_stream_drop ppa_stream_drop
139 #define pa_stream_set_write_callback ppa_stream_set_write_callback
140 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
141 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
142 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
143 #define pa_stream_get_time ppa_stream_get_time
144 #define pa_stream_set_read_callback ppa_stream_set_read_callback
145 #define pa_stream_set_state_callback ppa_stream_set_state_callback
146 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
147 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
148 #define pa_stream_connect_record ppa_stream_connect_record
149 #define pa_stream_connect_playback ppa_stream_connect_playback
150 #define pa_stream_readable_size ppa_stream_readable_size
151 #define pa_stream_writable_size ppa_stream_writable_size
152 #define pa_stream_is_corked ppa_stream_is_corked
153 #define pa_stream_cork ppa_stream_cork
154 #define pa_stream_is_suspended ppa_stream_is_suspended
155 #define pa_stream_get_device_name ppa_stream_get_device_name
156 #define pa_stream_get_latency ppa_stream_get_latency
157 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
158 #define pa_stream_begin_write ppa_stream_begin_write*/
159 #define pa_channel_map_init_auto ppa_channel_map_init_auto
160 #define pa_channel_map_parse ppa_channel_map_parse
161 #define pa_channel_map_snprint ppa_channel_map_snprint
162 #define pa_channel_map_equal ppa_channel_map_equal
163 #define pa_channel_map_superset ppa_channel_map_superset
164 #define pa_operation_get_state ppa_operation_get_state
165 #define pa_operation_unref ppa_operation_unref
166 #define pa_sample_spec_valid ppa_sample_spec_valid
167 #define pa_frame_size ppa_frame_size
168 #define pa_strerror ppa_strerror
169 #define pa_stream_get_state ppa_stream_get_state
170 #define pa_stream_peek ppa_stream_peek
171 #define pa_stream_write ppa_stream_write
172 #define pa_xfree ppa_xfree
173 #define pa_path_get_filename ppa_path_get_filename
174 #define pa_get_binary_name ppa_get_binary_name
175 #define pa_xmalloc ppa_xmalloc
176 #endif /* IN_IDE_PARSER */
178 #endif
181 constexpr pa_channel_map MonoChanMap{
182 1, {PA_CHANNEL_POSITION_MONO}
183 }, StereoChanMap{
184 2, {PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT}
185 }, QuadChanMap{
186 4, {
187 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
188 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
190 }, X51ChanMap{
191 6, {
192 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
193 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
194 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
196 }, X51RearChanMap{
197 6, {
198 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
199 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
200 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
202 }, X61ChanMap{
203 7, {
204 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
205 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
206 PA_CHANNEL_POSITION_REAR_CENTER,
207 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
209 }, X71ChanMap{
210 8, {
211 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
212 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
213 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
214 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
218 size_t ChannelFromPulse(pa_channel_position_t chan)
220 switch(chan)
222 case PA_CHANNEL_POSITION_INVALID: break;
223 case PA_CHANNEL_POSITION_MONO: return FrontCenter;
224 case PA_CHANNEL_POSITION_FRONT_LEFT: return FrontLeft;
225 case PA_CHANNEL_POSITION_FRONT_RIGHT: return FrontRight;
226 case PA_CHANNEL_POSITION_FRONT_CENTER: return FrontCenter;
227 case PA_CHANNEL_POSITION_REAR_CENTER: return BackCenter;
228 case PA_CHANNEL_POSITION_REAR_LEFT: return BackLeft;
229 case PA_CHANNEL_POSITION_REAR_RIGHT: return BackRight;
230 case PA_CHANNEL_POSITION_LFE: return LFE;
231 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: break;
232 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: break;
233 case PA_CHANNEL_POSITION_SIDE_LEFT: return SideLeft;
234 case PA_CHANNEL_POSITION_SIDE_RIGHT: return SideRight;
235 case PA_CHANNEL_POSITION_AUX0: return Aux0;
236 case PA_CHANNEL_POSITION_AUX1: return Aux1;
237 case PA_CHANNEL_POSITION_AUX2: return Aux2;
238 case PA_CHANNEL_POSITION_AUX3: return Aux3;
239 case PA_CHANNEL_POSITION_AUX4: return Aux4;
240 case PA_CHANNEL_POSITION_AUX5: return Aux5;
241 case PA_CHANNEL_POSITION_AUX6: return Aux6;
242 case PA_CHANNEL_POSITION_AUX7: return Aux7;
243 case PA_CHANNEL_POSITION_AUX8: return Aux8;
244 case PA_CHANNEL_POSITION_AUX9: return Aux9;
245 case PA_CHANNEL_POSITION_AUX10: return Aux10;
246 case PA_CHANNEL_POSITION_AUX11: return Aux11;
247 case PA_CHANNEL_POSITION_AUX12: return Aux12;
248 case PA_CHANNEL_POSITION_AUX13: return Aux13;
249 case PA_CHANNEL_POSITION_AUX14: return Aux14;
250 case PA_CHANNEL_POSITION_AUX15: return Aux15;
251 case PA_CHANNEL_POSITION_AUX16: break;
252 case PA_CHANNEL_POSITION_AUX17: break;
253 case PA_CHANNEL_POSITION_AUX18: break;
254 case PA_CHANNEL_POSITION_AUX19: break;
255 case PA_CHANNEL_POSITION_AUX20: break;
256 case PA_CHANNEL_POSITION_AUX21: break;
257 case PA_CHANNEL_POSITION_AUX22: break;
258 case PA_CHANNEL_POSITION_AUX23: break;
259 case PA_CHANNEL_POSITION_AUX24: break;
260 case PA_CHANNEL_POSITION_AUX25: break;
261 case PA_CHANNEL_POSITION_AUX26: break;
262 case PA_CHANNEL_POSITION_AUX27: break;
263 case PA_CHANNEL_POSITION_AUX28: break;
264 case PA_CHANNEL_POSITION_AUX29: break;
265 case PA_CHANNEL_POSITION_AUX30: break;
266 case PA_CHANNEL_POSITION_AUX31: break;
267 case PA_CHANNEL_POSITION_TOP_CENTER: break;
268 case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return UpperFrontLeft;
269 case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return UpperFrontRight;
270 case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: break;
271 case PA_CHANNEL_POSITION_TOP_REAR_LEFT: return UpperBackLeft;
272 case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return UpperBackRight;
273 case PA_CHANNEL_POSITION_TOP_REAR_CENTER: break;
274 case PA_CHANNEL_POSITION_MAX: break;
276 throw al::backend_exception{ALC_INVALID_VALUE, "Unexpected channel enum %d", chan};
279 void SetChannelOrderFromMap(ALCdevice *device, const pa_channel_map &chanmap)
281 device->RealOut.ChannelIndex.fill(INVALID_CHANNEL_INDEX);
282 for(ALuint i{0};i < chanmap.channels;++i)
283 device->RealOut.ChannelIndex[ChannelFromPulse(chanmap.map[i])] = i;
287 /* *grumble* Don't use enums for bitflags. */
288 inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
289 { return pa_stream_flags_t(int(lhs) | int(rhs)); }
290 inline pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
292 lhs = pa_stream_flags_t(int(lhs) | int(rhs));
293 return lhs;
295 inline pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
297 lhs = pa_context_flags_t(int(lhs) | int(rhs));
298 return lhs;
301 inline pa_stream_flags_t& operator&=(pa_stream_flags_t &lhs, int rhs)
303 lhs = pa_stream_flags_t(int(lhs) & rhs);
304 return lhs;
308 /* Global flags and properties */
309 pa_context_flags_t pulse_ctx_flags;
311 pa_mainloop *pulse_mainloop{nullptr};
313 std::mutex pulse_lock;
314 std::condition_variable pulse_condvar;
316 int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
318 auto plock = static_cast<std::unique_lock<std::mutex>*>(userdata);
319 plock->unlock();
320 int r{poll(ufds, nfds, timeout)};
321 plock->lock();
322 return r;
325 int pulse_mainloop_thread()
327 SetRTPriority();
329 std::unique_lock<std::mutex> plock{pulse_lock};
330 pulse_mainloop = pa_mainloop_new();
332 pa_mainloop_set_poll_func(pulse_mainloop, pulse_poll_func, &plock);
333 pulse_condvar.notify_all();
335 int ret{};
336 pa_mainloop_run(pulse_mainloop, &ret);
338 pa_mainloop_free(pulse_mainloop);
339 pulse_mainloop = nullptr;
341 return ret;
345 /* PulseAudio Event Callbacks */
346 void context_state_callback(pa_context *context, void* /*pdata*/)
348 pa_context_state_t state{pa_context_get_state(context)};
349 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
350 pulse_condvar.notify_all();
353 void stream_state_callback(pa_stream *stream, void* /*pdata*/)
355 pa_stream_state_t state{pa_stream_get_state(stream)};
356 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
357 pulse_condvar.notify_all();
360 void stream_success_callback(pa_stream* /*stream*/, int /*success*/, void* /*pdata*/)
362 pulse_condvar.notify_all();
365 void wait_for_operation(pa_operation *op, std::unique_lock<std::mutex> &plock)
367 if(op)
369 while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
370 pulse_condvar.wait(plock);
371 pa_operation_unref(op);
376 pa_context *connect_context(std::unique_lock<std::mutex> &plock)
378 const char *name{"OpenAL Soft"};
380 const PathNamePair &binname = GetProcBinary();
381 if(!binname.fname.empty())
382 name = binname.fname.c_str();
384 if UNLIKELY(!pulse_mainloop)
386 std::thread{pulse_mainloop_thread}.detach();
387 while(!pulse_mainloop)
388 pulse_condvar.wait(plock);
391 pa_context *context{pa_context_new(pa_mainloop_get_api(pulse_mainloop), name)};
392 if(!context) throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_context_new() failed"};
394 pa_context_set_state_callback(context, context_state_callback, nullptr);
396 int err;
397 if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
399 pa_context_state_t state;
400 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
402 if(!PA_CONTEXT_IS_GOOD(state))
404 err = pa_context_errno(context);
405 if(err > 0) err = -err;
406 break;
409 pulse_condvar.wait(plock);
412 pa_context_set_state_callback(context, nullptr, nullptr);
414 if(err < 0)
416 pa_context_unref(context);
417 throw al::backend_exception{ALC_INVALID_VALUE, "Context did not connect (%s)",
418 pa_strerror(err)};
421 return context;
425 void pulse_close(pa_context *context, pa_stream *stream)
427 std::lock_guard<std::mutex> _{pulse_lock};
428 if(stream)
430 pa_stream_set_state_callback(stream, nullptr, nullptr);
431 pa_stream_set_moved_callback(stream, nullptr, nullptr);
432 pa_stream_set_write_callback(stream, nullptr, nullptr);
433 pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
434 pa_stream_disconnect(stream);
435 pa_stream_unref(stream);
438 pa_context_disconnect(context);
439 pa_context_unref(context);
443 struct DevMap {
444 std::string name;
445 std::string device_name;
448 bool checkName(const al::vector<DevMap> &list, const std::string &name)
450 auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
451 return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
454 al::vector<DevMap> PlaybackDevices;
455 al::vector<DevMap> CaptureDevices;
458 pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::mutex> &plock,
459 pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
460 pa_channel_map *chanmap, BackendType type)
462 const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
463 pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
464 if(!stream)
465 throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_stream_new() failed (%s)",
466 pa_strerror(pa_context_errno(context))};
468 pa_stream_set_state_callback(stream, stream_state_callback, nullptr);
470 int err{(type==BackendType::Playback) ?
471 pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
472 pa_stream_connect_record(stream, device_name, attr, flags)};
473 if(err < 0)
475 pa_stream_unref(stream);
476 throw al::backend_exception{ALC_INVALID_VALUE, "%s did not connect (%s)", stream_id,
477 pa_strerror(err)};
480 pa_stream_state_t state;
481 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
483 if(!PA_STREAM_IS_GOOD(state))
485 err = pa_context_errno(context);
486 pa_stream_unref(stream);
487 throw al::backend_exception{ALC_INVALID_VALUE, "%s did not get ready (%s)", stream_id,
488 pa_strerror(err)};
491 pulse_condvar.wait(plock);
493 pa_stream_set_state_callback(stream, nullptr, nullptr);
495 return stream;
499 void device_sink_callback(pa_context*, const pa_sink_info *info, int eol, void*)
501 if(eol)
503 pulse_condvar.notify_all();
504 return;
507 /* Skip this device is if it's already in the list. */
508 if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
509 [info](const DevMap &entry) -> bool
510 { return entry.device_name == info->name; }
511 ) != PlaybackDevices.cend())
512 return;
514 /* Make sure the display name (description) is unique. Append a number
515 * counter as needed.
517 int count{1};
518 std::string newname{info->description};
519 while(checkName(PlaybackDevices, newname))
521 newname = info->description;
522 newname += " #";
523 newname += std::to_string(++count);
525 PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name});
526 DevMap &newentry = PlaybackDevices.back();
528 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
531 void probePlaybackDevices()
533 PlaybackDevices.clear();
535 try {
536 std::unique_lock<std::mutex> plock{pulse_lock};
538 pa_context *context{connect_context(plock)};
540 const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
541 PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
543 pa_sample_spec spec{};
544 spec.format = PA_SAMPLE_S16NE;
545 spec.rate = 44100;
546 spec.channels = 2;
548 pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec,
549 nullptr, BackendType::Playback)};
550 pa_operation *op{pa_context_get_sink_info_by_name(context,
551 pa_stream_get_device_name(stream), device_sink_callback, nullptr)};
552 wait_for_operation(op, plock);
554 pa_stream_disconnect(stream);
555 pa_stream_unref(stream);
556 stream = nullptr;
558 op = pa_context_get_sink_info_list(context, device_sink_callback, nullptr);
559 wait_for_operation(op, plock);
561 pa_context_disconnect(context);
562 pa_context_unref(context);
564 catch(std::exception &e) {
565 ERR("Error enumerating devices: %s\n", e.what());
570 void device_source_callback(pa_context*, const pa_source_info *info, int eol, void*)
572 if(eol)
574 pulse_condvar.notify_all();
575 return;
578 /* Skip this device is if it's already in the list. */
579 if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
580 [info](const DevMap &entry) -> bool
581 { return entry.device_name == info->name; }
582 ) != CaptureDevices.cend())
583 return;
585 /* Make sure the display name (description) is unique. Append a number
586 * counter as needed.
588 int count{1};
589 std::string newname{info->description};
590 while(checkName(CaptureDevices, newname))
592 newname = info->description;
593 newname += " #";
594 newname += std::to_string(++count);
596 CaptureDevices.emplace_back(DevMap{std::move(newname), info->name});
597 DevMap &newentry = CaptureDevices.back();
599 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
602 void probeCaptureDevices()
604 CaptureDevices.clear();
606 try {
607 std::unique_lock<std::mutex> plock{pulse_lock};
609 pa_context *context{connect_context(plock)};
611 const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
612 PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
614 pa_sample_spec spec{};
615 spec.format = PA_SAMPLE_S16NE;
616 spec.rate = 44100;
617 spec.channels = 1;
619 pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
620 BackendType::Capture)};
621 pa_operation *op{pa_context_get_source_info_by_name(context,
622 pa_stream_get_device_name(stream), device_source_callback, nullptr)};
623 wait_for_operation(op, plock);
625 pa_stream_disconnect(stream);
626 pa_stream_unref(stream);
627 stream = nullptr;
629 op = pa_context_get_source_info_list(context, device_source_callback, nullptr);
630 wait_for_operation(op, plock);
632 pa_context_disconnect(context);
633 pa_context_unref(context);
635 catch(std::exception &e) {
636 ERR("Error enumerating devices: %s\n", e.what());
641 struct PulsePlayback final : public BackendBase {
642 PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
643 ~PulsePlayback() override;
645 static void bufferAttrCallbackC(pa_stream *stream, void *pdata);
646 void bufferAttrCallback(pa_stream *stream);
648 static void contextStateCallbackC(pa_context *context, void *pdata);
649 void contextStateCallback(pa_context *context);
651 static void streamStateCallbackC(pa_stream *stream, void *pdata);
652 void streamStateCallback(pa_stream *stream);
654 static void streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata);
655 void streamWriteCallback(pa_stream *stream, size_t nbytes);
657 static void sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
658 void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol);
660 static void sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
661 void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol);
663 static void streamMovedCallbackC(pa_stream *stream, void *pdata);
664 void streamMovedCallback(pa_stream *stream);
666 void open(const ALCchar *name) override;
667 bool reset() override;
668 bool start() override;
669 void stop() override;
670 ClockLatency getClockLatency() override;
671 void lock() override;
672 void unlock() override;
674 std::string mDeviceName;
676 pa_buffer_attr mAttr;
677 pa_sample_spec mSpec;
679 pa_stream *mStream{nullptr};
680 pa_context *mContext{nullptr};
682 ALuint mFrameSize{0u};
684 DEF_NEWDEL(PulsePlayback)
687 PulsePlayback::~PulsePlayback()
689 if(!mContext)
690 return;
692 pulse_close(mContext, mStream);
693 mContext = nullptr;
694 mStream = nullptr;
698 void PulsePlayback::bufferAttrCallbackC(pa_stream *stream, void *pdata)
699 { static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); }
701 void PulsePlayback::bufferAttrCallback(pa_stream *stream)
703 /* FIXME: Update the device's UpdateSize (and/or BufferSize) using the new
704 * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
705 * property, which probably shouldn't change between device resets. But
706 * leaving it alone means ALC_REFRESH will be off.
708 mAttr = *(pa_stream_get_buffer_attr(stream));
709 TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
712 void PulsePlayback::contextStateCallbackC(pa_context *context, void *pdata)
713 { static_cast<PulsePlayback*>(pdata)->contextStateCallback(context); }
715 void PulsePlayback::contextStateCallback(pa_context *context)
717 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
719 ERR("Received context failure!\n");
720 aluHandleDisconnect(mDevice, "Playback state failure");
722 pulse_condvar.notify_all();
725 void PulsePlayback::streamStateCallbackC(pa_stream *stream, void *pdata)
726 { static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }
728 void PulsePlayback::streamStateCallback(pa_stream *stream)
730 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
732 ERR("Received stream failure!\n");
733 aluHandleDisconnect(mDevice, "Playback stream failure");
735 pulse_condvar.notify_all();
738 void PulsePlayback::streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata)
739 { static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }
741 void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes)
743 void *buf{pa_xmalloc(nbytes)};
744 aluMixData(mDevice, buf, static_cast<ALuint>(nbytes/mFrameSize));
746 int ret{pa_stream_write(stream, buf, nbytes, pa_xfree, 0, PA_SEEK_RELATIVE)};
747 if UNLIKELY(ret != PA_OK)
748 ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
751 void PulsePlayback::sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
752 { static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }
754 void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int eol)
756 struct ChannelMap {
757 DevFmtChannels fmt;
758 pa_channel_map map;
760 static constexpr std::array<ChannelMap,7> chanmaps{{
761 { DevFmtX71, X71ChanMap },
762 { DevFmtX61, X61ChanMap },
763 { DevFmtX51, X51ChanMap },
764 { DevFmtX51Rear, X51RearChanMap },
765 { DevFmtQuad, QuadChanMap },
766 { DevFmtStereo, StereoChanMap },
767 { DevFmtMono, MonoChanMap }
770 if(eol)
772 pulse_condvar.notify_all();
773 return;
776 auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
777 [info](const ChannelMap &chanmap) -> bool
778 { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
780 if(chaniter != chanmaps.cend())
782 if(!mDevice->Flags.get<ChannelsRequest>())
783 mDevice->FmtChans = chaniter->fmt;
785 else
787 char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
788 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
789 WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
792 if(info->active_port)
793 TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
794 mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
795 info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0);
798 void PulsePlayback::sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
799 { static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }
801 void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol)
803 if(eol)
805 pulse_condvar.notify_all();
806 return;
808 mDevice->DeviceName = info->description;
811 void PulsePlayback::streamMovedCallbackC(pa_stream *stream, void *pdata)
812 { static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }
814 void PulsePlayback::streamMovedCallback(pa_stream *stream)
816 mDeviceName = pa_stream_get_device_name(stream);
817 TRACE("Stream moved to %s\n", mDeviceName.c_str());
821 void PulsePlayback::open(const ALCchar *name)
823 const char *pulse_name{nullptr};
824 const char *dev_name{nullptr};
826 if(name)
828 if(PlaybackDevices.empty())
829 probePlaybackDevices();
831 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
832 [name](const DevMap &entry) -> bool
833 { return entry.name == name; }
835 if(iter == PlaybackDevices.cend())
836 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
837 pulse_name = iter->device_name.c_str();
838 dev_name = iter->name.c_str();
841 std::unique_lock<std::mutex> plock{pulse_lock};
843 mContext = connect_context(plock);
844 pa_context_set_state_callback(mContext, &PulsePlayback::contextStateCallbackC, this);
846 pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS};
847 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
848 flags |= PA_STREAM_DONT_MOVE;
850 pa_sample_spec spec{};
851 spec.format = PA_SAMPLE_S16NE;
852 spec.rate = 44100;
853 spec.channels = 2;
855 if(!pulse_name)
857 static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT");
858 if(defname) pulse_name = defname->c_str();
860 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
861 mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
862 BackendType::Playback);
864 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
865 mFrameSize = static_cast<ALuint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
867 mDeviceName = pa_stream_get_device_name(mStream);
868 if(!dev_name)
870 pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
871 &PulsePlayback::sinkNameCallbackC, this)};
872 wait_for_operation(op, plock);
874 else
875 mDevice->DeviceName = dev_name;
878 bool PulsePlayback::reset()
880 std::unique_lock<std::mutex> plock{pulse_lock};
882 if(mStream)
884 pa_stream_set_state_callback(mStream, nullptr, nullptr);
885 pa_stream_set_moved_callback(mStream, nullptr, nullptr);
886 pa_stream_set_write_callback(mStream, nullptr, nullptr);
887 pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
888 pa_stream_disconnect(mStream);
889 pa_stream_unref(mStream);
890 mStream = nullptr;
893 pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
894 &PulsePlayback::sinkInfoCallbackC, this)};
895 wait_for_operation(op, plock);
897 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
898 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS};
899 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
900 flags |= PA_STREAM_DONT_MOVE;
901 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
903 /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some
904 * reason. So if the user wants to adjust the overall device latency,
905 * we can't ask to get write signals as soon as minreq is reached.
907 flags &= ~PA_STREAM_EARLY_REQUESTS;
908 flags |= PA_STREAM_ADJUST_LATENCY;
910 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
911 !mDevice->Flags.get<FrequencyRequest>())
912 flags |= PA_STREAM_FIX_RATE;
914 pa_channel_map chanmap{};
915 switch(mDevice->FmtChans)
917 case DevFmtMono:
918 chanmap = MonoChanMap;
919 break;
920 case DevFmtAmbi3D:
921 mDevice->FmtChans = DevFmtStereo;
922 /*fall-through*/
923 case DevFmtStereo:
924 chanmap = StereoChanMap;
925 break;
926 case DevFmtQuad:
927 chanmap = QuadChanMap;
928 break;
929 case DevFmtX51:
930 chanmap = X51ChanMap;
931 break;
932 case DevFmtX51Rear:
933 chanmap = X51RearChanMap;
934 break;
935 case DevFmtX61:
936 chanmap = X61ChanMap;
937 break;
938 case DevFmtX71:
939 chanmap = X71ChanMap;
940 break;
942 SetChannelOrderFromMap(mDevice, chanmap);
944 switch(mDevice->FmtType)
946 case DevFmtByte:
947 mDevice->FmtType = DevFmtUByte;
948 /* fall-through */
949 case DevFmtUByte:
950 mSpec.format = PA_SAMPLE_U8;
951 break;
952 case DevFmtUShort:
953 mDevice->FmtType = DevFmtShort;
954 /* fall-through */
955 case DevFmtShort:
956 mSpec.format = PA_SAMPLE_S16NE;
957 break;
958 case DevFmtUInt:
959 mDevice->FmtType = DevFmtInt;
960 /* fall-through */
961 case DevFmtInt:
962 mSpec.format = PA_SAMPLE_S32NE;
963 break;
964 case DevFmtFloat:
965 mSpec.format = PA_SAMPLE_FLOAT32NE;
966 break;
968 mSpec.rate = mDevice->Frequency;
969 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
970 if(pa_sample_spec_valid(&mSpec) == 0)
971 throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample spec"};
973 const ALuint frame_size{static_cast<ALuint>(pa_frame_size(&mSpec))};
974 mAttr.maxlength = ~0u;
975 mAttr.tlength = mDevice->BufferSize * frame_size;
976 mAttr.prebuf = 0u;
977 mAttr.minreq = mDevice->UpdateSize * frame_size;
978 mAttr.fragsize = ~0u;
980 mStream = pulse_connect_stream(mDeviceName.c_str(), plock, mContext, flags, &mAttr, &mSpec,
981 &chanmap, BackendType::Playback);
983 pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
984 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
986 mSpec = *(pa_stream_get_sample_spec(mStream));
987 mFrameSize = static_cast<ALuint>(pa_frame_size(&mSpec));
989 if(mDevice->Frequency != mSpec.rate)
991 /* Server updated our playback rate, so modify the buffer attribs
992 * accordingly.
994 const auto scale = static_cast<double>(mSpec.rate) / mDevice->Frequency;
995 const ALuint perlen{static_cast<ALuint>(clampd(scale*mDevice->UpdateSize + 0.5, 64.0,
996 8192.0))};
997 const ALuint buflen{static_cast<ALuint>(clampd(scale*mDevice->BufferSize + 0.5, perlen*2,
998 std::numeric_limits<int>::max()/mFrameSize))};
1000 mAttr.maxlength = ~0u;
1001 mAttr.tlength = buflen * mFrameSize;
1002 mAttr.prebuf = 0u;
1003 mAttr.minreq = perlen * mFrameSize;
1005 op = pa_stream_set_buffer_attr(mStream, &mAttr, stream_success_callback, nullptr);
1006 wait_for_operation(op, plock);
1008 mDevice->Frequency = mSpec.rate;
1011 pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
1012 bufferAttrCallback(mStream);
1014 mDevice->BufferSize = mAttr.tlength / mFrameSize;
1015 mDevice->UpdateSize = mAttr.minreq / mFrameSize;
1017 /* HACK: prebuf should be 0 as that's what we set it to. However on some
1018 * systems it comes back as non-0, so we have to make sure the device will
1019 * write enough audio to start playback. The lack of manual start control
1020 * may have unintended consequences, but it's better than not starting at
1021 * all.
1023 if(mAttr.prebuf != 0)
1025 ALuint len{mAttr.prebuf / mFrameSize};
1026 if(len <= mDevice->BufferSize)
1027 ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
1028 len, mAttr.prebuf, mDevice->BufferSize);
1031 return true;
1034 bool PulsePlayback::start()
1036 std::unique_lock<std::mutex> plock{pulse_lock};
1038 pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
1039 pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
1040 wait_for_operation(op, plock);
1042 return true;
1045 void PulsePlayback::stop()
1047 std::unique_lock<std::mutex> plock{pulse_lock};
1049 pa_stream_set_write_callback(mStream, nullptr, nullptr);
1050 pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
1051 wait_for_operation(op, plock);
1055 ClockLatency PulsePlayback::getClockLatency()
1057 ClockLatency ret;
1058 pa_usec_t latency;
1059 int neg, err;
1061 { std::lock_guard<std::mutex> _{pulse_lock};
1062 ret.ClockTime = GetDeviceClockTime(mDevice);
1063 err = pa_stream_get_latency(mStream, &latency, &neg);
1066 if UNLIKELY(err != 0)
1068 /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
1069 * after starting the stream and no timing info has been received from
1070 * the server yet. Should we wait, possibly stalling the app, or give a
1071 * dummy value? Either way, it shouldn't be 0. */
1072 if(err != -PA_ERR_NODATA)
1073 ERR("Failed to get stream latency: 0x%x\n", err);
1074 latency = 0;
1075 neg = 0;
1077 else if UNLIKELY(neg)
1078 latency = 0;
1079 ret.Latency = std::chrono::microseconds{latency};
1081 return ret;
1085 void PulsePlayback::lock()
1086 { pulse_lock.lock(); }
1088 void PulsePlayback::unlock()
1089 { pulse_lock.unlock(); }
1092 struct PulseCapture final : public BackendBase {
1093 PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
1094 ~PulseCapture() override;
1096 static void contextStateCallbackC(pa_context *context, void *pdata);
1097 void contextStateCallback(pa_context *context);
1099 static void streamStateCallbackC(pa_stream *stream, void *pdata);
1100 void streamStateCallback(pa_stream *stream);
1102 static void sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata);
1103 void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol);
1105 static void streamMovedCallbackC(pa_stream *stream, void *pdata);
1106 void streamMovedCallback(pa_stream *stream);
1108 void open(const ALCchar *name) override;
1109 bool start() override;
1110 void stop() override;
1111 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
1112 ALCuint availableSamples() override;
1113 ClockLatency getClockLatency() override;
1114 void lock() override;
1115 void unlock() override;
1117 std::string mDeviceName;
1119 ALCuint mLastReadable{0u};
1120 al::byte mSilentVal{};
1122 al::span<const al::byte> mCapBuffer;
1123 ssize_t mCapLen{0};
1125 pa_buffer_attr mAttr{};
1126 pa_sample_spec mSpec{};
1128 pa_stream *mStream{nullptr};
1129 pa_context *mContext{nullptr};
1131 DEF_NEWDEL(PulseCapture)
1134 PulseCapture::~PulseCapture()
1136 if(!mContext)
1137 return;
1139 pulse_close(mContext, mStream);
1140 mContext = nullptr;
1141 mStream = nullptr;
1144 void PulseCapture::contextStateCallbackC(pa_context *context, void *pdata)
1145 { static_cast<PulseCapture*>(pdata)->contextStateCallback(context); }
1147 void PulseCapture::contextStateCallback(pa_context *context)
1149 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
1151 ERR("Received context failure!\n");
1152 aluHandleDisconnect(mDevice, "Capture state failure");
1154 pulse_condvar.notify_all();
1157 void PulseCapture::streamStateCallbackC(pa_stream *stream, void *pdata)
1158 { static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }
1160 void PulseCapture::streamStateCallback(pa_stream *stream)
1162 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
1164 ERR("Received stream failure!\n");
1165 aluHandleDisconnect(mDevice, "Capture stream failure");
1167 pulse_condvar.notify_all();
1170 void PulseCapture::sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata)
1171 { static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }
1173 void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, int eol)
1175 if(eol)
1177 pulse_condvar.notify_all();
1178 return;
1180 mDevice->DeviceName = info->description;
1183 void PulseCapture::streamMovedCallbackC(pa_stream *stream, void *pdata)
1184 { static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }
1186 void PulseCapture::streamMovedCallback(pa_stream *stream)
1188 mDeviceName = pa_stream_get_device_name(stream);
1189 TRACE("Stream moved to %s\n", mDeviceName.c_str());
1193 void PulseCapture::open(const ALCchar *name)
1195 const char *pulse_name{nullptr};
1196 if(name)
1198 if(CaptureDevices.empty())
1199 probeCaptureDevices();
1201 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1202 [name](const DevMap &entry) -> bool
1203 { return entry.name == name; }
1205 if(iter == CaptureDevices.cend())
1206 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
1207 pulse_name = iter->device_name.c_str();
1208 mDevice->DeviceName = iter->name;
1211 std::unique_lock<std::mutex> plock{pulse_lock};
1213 mContext = connect_context(plock);
1214 pa_context_set_state_callback(mContext, &PulseCapture::contextStateCallbackC, this);
1216 pa_channel_map chanmap{};
1217 switch(mDevice->FmtChans)
1219 case DevFmtMono:
1220 chanmap = MonoChanMap;
1221 break;
1222 case DevFmtStereo:
1223 chanmap = StereoChanMap;
1224 break;
1225 case DevFmtQuad:
1226 chanmap = QuadChanMap;
1227 break;
1228 case DevFmtX51:
1229 chanmap = X51ChanMap;
1230 break;
1231 case DevFmtX51Rear:
1232 chanmap = X51RearChanMap;
1233 break;
1234 case DevFmtX61:
1235 chanmap = X61ChanMap;
1236 break;
1237 case DevFmtX71:
1238 chanmap = X71ChanMap;
1239 break;
1240 case DevFmtAmbi3D:
1241 throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
1242 DevFmtChannelsString(mDevice->FmtChans)};
1244 SetChannelOrderFromMap(mDevice, chanmap);
1246 switch(mDevice->FmtType)
1248 case DevFmtUByte:
1249 mSilentVal = al::byte(0x80);
1250 mSpec.format = PA_SAMPLE_U8;
1251 break;
1252 case DevFmtShort:
1253 mSpec.format = PA_SAMPLE_S16NE;
1254 break;
1255 case DevFmtInt:
1256 mSpec.format = PA_SAMPLE_S32NE;
1257 break;
1258 case DevFmtFloat:
1259 mSpec.format = PA_SAMPLE_FLOAT32NE;
1260 break;
1261 case DevFmtByte:
1262 case DevFmtUShort:
1263 case DevFmtUInt:
1264 throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
1265 DevFmtTypeString(mDevice->FmtType)};
1267 mSpec.rate = mDevice->Frequency;
1268 mSpec.channels = static_cast<uint8_t>(mDevice->channelsFromFmt());
1269 if(pa_sample_spec_valid(&mSpec) == 0)
1270 throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample format"};
1272 const ALuint frame_size{static_cast<ALuint>(pa_frame_size(&mSpec))};
1273 const ALuint samples{maxu(mDevice->BufferSize, 100 * mDevice->Frequency / 1000)};
1274 mAttr.minreq = ~0u;
1275 mAttr.prebuf = ~0u;
1276 mAttr.maxlength = samples * frame_size;
1277 mAttr.tlength = ~0u;
1278 mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size;
1280 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY};
1281 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
1282 flags |= PA_STREAM_DONT_MOVE;
1284 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
1285 mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, &mAttr, &mSpec, &chanmap,
1286 BackendType::Capture);
1288 pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
1289 pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
1291 mDeviceName = pa_stream_get_device_name(mStream);
1292 if(mDevice->DeviceName.empty())
1294 pa_operation *op{pa_context_get_source_info_by_name(mContext, mDeviceName.c_str(),
1295 &PulseCapture::sourceNameCallbackC, this)};
1296 wait_for_operation(op, plock);
1300 bool PulseCapture::start()
1302 std::unique_lock<std::mutex> plock{pulse_lock};
1303 pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, nullptr)};
1304 wait_for_operation(op, plock);
1305 return true;
1308 void PulseCapture::stop()
1310 std::unique_lock<std::mutex> plock{pulse_lock};
1311 pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, nullptr)};
1312 wait_for_operation(op, plock);
1315 ALCenum PulseCapture::captureSamples(al::byte *buffer, ALCuint samples)
1317 al::span<al::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)};
1319 /* Capture is done in fragment-sized chunks, so we loop until we get all
1320 * that's available */
1321 mLastReadable -= static_cast<ALCuint>(dstbuf.size());
1322 while(!dstbuf.empty())
1324 if(!mCapBuffer.empty())
1326 const size_t rem{minz(dstbuf.size(), mCapBuffer.size())};
1327 if UNLIKELY(mCapLen < 0)
1328 std::fill_n(dstbuf.begin(), rem, mSilentVal);
1329 else
1330 std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin());
1331 dstbuf = dstbuf.subspan(rem);
1332 mCapBuffer = mCapBuffer.subspan(rem);
1334 continue;
1337 if UNLIKELY(!mDevice->Connected.load(std::memory_order_acquire))
1338 break;
1340 std::unique_lock<std::mutex> plock{pulse_lock};
1341 if(mCapLen != 0)
1343 pa_stream_drop(mStream);
1344 mCapBuffer = {};
1345 mCapLen = 0;
1347 const pa_stream_state_t state{pa_stream_get_state(mStream)};
1348 if UNLIKELY(!PA_STREAM_IS_GOOD(state))
1350 aluHandleDisconnect(mDevice, "Bad capture state: %u", state);
1351 break;
1353 const void *capbuf;
1354 size_t caplen;
1355 if UNLIKELY(pa_stream_peek(mStream, &capbuf, &caplen) < 0)
1357 aluHandleDisconnect(mDevice, "Failed retrieving capture samples: %s",
1358 pa_strerror(pa_context_errno(mContext)));
1359 break;
1361 plock.unlock();
1363 if(caplen == 0) break;
1364 if UNLIKELY(!capbuf)
1365 mCapLen = -static_cast<ssize_t>(caplen);
1366 else
1367 mCapLen = static_cast<ssize_t>(caplen);
1368 mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
1370 if(!dstbuf.empty())
1371 std::fill(dstbuf.begin(), dstbuf.end(), mSilentVal);
1373 return ALC_NO_ERROR;
1376 ALCuint PulseCapture::availableSamples()
1378 size_t readable{mCapBuffer.size()};
1380 if(mDevice->Connected.load(std::memory_order_acquire))
1382 std::lock_guard<std::mutex> _{pulse_lock};
1383 size_t got{pa_stream_readable_size(mStream)};
1384 if(static_cast<ssize_t>(got) < 0)
1386 const char *err{pa_strerror(static_cast<int>(got))};
1387 ERR("pa_stream_readable_size() failed: %s\n", err);
1388 aluHandleDisconnect(mDevice, "Failed getting readable size: %s", err);
1390 else
1392 const auto caplen = static_cast<size_t>(std::abs(mCapLen));
1393 if(got > caplen) readable += got - caplen;
1397 readable = std::min<size_t>(readable, std::numeric_limits<ALCuint>::max());
1398 mLastReadable = std::max(mLastReadable, static_cast<ALCuint>(readable));
1399 return mLastReadable / static_cast<ALCuint>(pa_frame_size(&mSpec));
1403 ClockLatency PulseCapture::getClockLatency()
1405 ClockLatency ret;
1406 pa_usec_t latency;
1407 int neg, err;
1409 { std::lock_guard<std::mutex> _{pulse_lock};
1410 ret.ClockTime = GetDeviceClockTime(mDevice);
1411 err = pa_stream_get_latency(mStream, &latency, &neg);
1414 if UNLIKELY(err != 0)
1416 ERR("Failed to get stream latency: 0x%x\n", err);
1417 latency = 0;
1418 neg = 0;
1420 else if UNLIKELY(neg)
1421 latency = 0;
1422 ret.Latency = std::chrono::microseconds{latency};
1424 return ret;
1428 void PulseCapture::lock()
1429 { pulse_lock.lock(); }
1431 void PulseCapture::unlock()
1432 { pulse_lock.unlock(); }
1434 } // namespace
1437 bool PulseBackendFactory::init()
1439 #ifdef HAVE_DYNLOAD
1440 if(!pulse_handle)
1442 bool ret{true};
1443 std::string missing_funcs;
1445 #ifdef _WIN32
1446 #define PALIB "libpulse-0.dll"
1447 #elif defined(__APPLE__) && defined(__MACH__)
1448 #define PALIB "libpulse.0.dylib"
1449 #else
1450 #define PALIB "libpulse.so.0"
1451 #endif
1452 pulse_handle = LoadLib(PALIB);
1453 if(!pulse_handle)
1455 WARN("Failed to load %s\n", PALIB);
1456 return false;
1459 #define LOAD_FUNC(x) do { \
1460 p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
1461 if(!(p##x)) { \
1462 ret = false; \
1463 missing_funcs += "\n" #x; \
1465 } while(0)
1466 PULSE_FUNCS(LOAD_FUNC)
1467 #undef LOAD_FUNC
1469 if(!ret)
1471 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
1472 CloseLib(pulse_handle);
1473 pulse_handle = nullptr;
1474 return false;
1477 #endif /* HAVE_DYNLOAD */
1479 pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
1480 if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
1481 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
1483 try {
1484 std::unique_lock<std::mutex> plock{pulse_lock};
1485 pa_context *context{connect_context(plock)};
1486 pa_context_disconnect(context);
1487 pa_context_unref(context);
1488 return true;
1490 catch(...) {
1491 return false;
1495 bool PulseBackendFactory::querySupport(BackendType type)
1496 { return type == BackendType::Playback || type == BackendType::Capture; }
1498 void PulseBackendFactory::probe(DevProbe type, std::string *outnames)
1500 auto add_device = [outnames](const DevMap &entry) -> void
1502 /* +1 to also append the null char (to ensure a null-separated list and
1503 * double-null terminated list).
1505 outnames->append(entry.name.c_str(), entry.name.length()+1);
1507 switch(type)
1509 case DevProbe::Playback:
1510 probePlaybackDevices();
1511 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1512 break;
1514 case DevProbe::Capture:
1515 probeCaptureDevices();
1516 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1517 break;
1521 BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
1523 if(type == BackendType::Playback)
1524 return BackendPtr{new PulsePlayback{device}};
1525 if(type == BackendType::Capture)
1526 return BackendPtr{new PulseCapture{device}};
1527 return nullptr;
1530 BackendFactory &PulseBackendFactory::getFactory()
1532 static PulseBackendFactory factory{};
1533 return factory;