Remove redundant void argument list in function def
[openal-soft.git] / Alc / backends / pulseaudio.cpp
blobbf9528133b6d8492e3ddc6ef36dd4f1085050870
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 <cstring>
28 #include <array>
29 #include <string>
30 #include <vector>
31 #include <atomic>
32 #include <thread>
33 #include <algorithm>
35 #include "alMain.h"
36 #include "alu.h"
37 #include "alconfig.h"
38 #include "compat.h"
40 #include <pulse/pulseaudio.h>
43 namespace {
45 #ifdef HAVE_DYNLOAD
46 void *pa_handle;
47 #define MAKE_FUNC(x) decltype(x) * p##x
48 MAKE_FUNC(pa_context_unref);
49 MAKE_FUNC(pa_sample_spec_valid);
50 MAKE_FUNC(pa_frame_size);
51 MAKE_FUNC(pa_stream_drop);
52 MAKE_FUNC(pa_strerror);
53 MAKE_FUNC(pa_context_get_state);
54 MAKE_FUNC(pa_stream_get_state);
55 MAKE_FUNC(pa_threaded_mainloop_signal);
56 MAKE_FUNC(pa_stream_peek);
57 MAKE_FUNC(pa_threaded_mainloop_wait);
58 MAKE_FUNC(pa_threaded_mainloop_unlock);
59 MAKE_FUNC(pa_threaded_mainloop_in_thread);
60 MAKE_FUNC(pa_context_new);
61 MAKE_FUNC(pa_threaded_mainloop_stop);
62 MAKE_FUNC(pa_context_disconnect);
63 MAKE_FUNC(pa_threaded_mainloop_start);
64 MAKE_FUNC(pa_threaded_mainloop_get_api);
65 MAKE_FUNC(pa_context_set_state_callback);
66 MAKE_FUNC(pa_stream_write);
67 MAKE_FUNC(pa_xfree);
68 MAKE_FUNC(pa_stream_connect_record);
69 MAKE_FUNC(pa_stream_connect_playback);
70 MAKE_FUNC(pa_stream_readable_size);
71 MAKE_FUNC(pa_stream_writable_size);
72 MAKE_FUNC(pa_stream_is_corked);
73 MAKE_FUNC(pa_stream_cork);
74 MAKE_FUNC(pa_stream_is_suspended);
75 MAKE_FUNC(pa_stream_get_device_name);
76 MAKE_FUNC(pa_stream_get_latency);
77 MAKE_FUNC(pa_path_get_filename);
78 MAKE_FUNC(pa_get_binary_name);
79 MAKE_FUNC(pa_threaded_mainloop_free);
80 MAKE_FUNC(pa_context_errno);
81 MAKE_FUNC(pa_xmalloc);
82 MAKE_FUNC(pa_stream_unref);
83 MAKE_FUNC(pa_threaded_mainloop_accept);
84 MAKE_FUNC(pa_stream_set_write_callback);
85 MAKE_FUNC(pa_threaded_mainloop_new);
86 MAKE_FUNC(pa_context_connect);
87 MAKE_FUNC(pa_stream_set_buffer_attr);
88 MAKE_FUNC(pa_stream_get_buffer_attr);
89 MAKE_FUNC(pa_stream_get_sample_spec);
90 MAKE_FUNC(pa_stream_get_time);
91 MAKE_FUNC(pa_stream_set_read_callback);
92 MAKE_FUNC(pa_stream_set_state_callback);
93 MAKE_FUNC(pa_stream_set_moved_callback);
94 MAKE_FUNC(pa_stream_set_underflow_callback);
95 MAKE_FUNC(pa_stream_new_with_proplist);
96 MAKE_FUNC(pa_stream_disconnect);
97 MAKE_FUNC(pa_threaded_mainloop_lock);
98 MAKE_FUNC(pa_channel_map_init_auto);
99 MAKE_FUNC(pa_channel_map_parse);
100 MAKE_FUNC(pa_channel_map_snprint);
101 MAKE_FUNC(pa_channel_map_equal);
102 MAKE_FUNC(pa_context_get_server_info);
103 MAKE_FUNC(pa_context_get_sink_info_by_name);
104 MAKE_FUNC(pa_context_get_sink_info_list);
105 MAKE_FUNC(pa_context_get_source_info_by_name);
106 MAKE_FUNC(pa_context_get_source_info_list);
107 MAKE_FUNC(pa_operation_get_state);
108 MAKE_FUNC(pa_operation_unref);
109 MAKE_FUNC(pa_proplist_new);
110 MAKE_FUNC(pa_proplist_free);
111 MAKE_FUNC(pa_proplist_set);
112 MAKE_FUNC(pa_channel_map_superset);
113 MAKE_FUNC(pa_stream_set_buffer_attr_callback);
114 MAKE_FUNC(pa_stream_begin_write);
115 #undef MAKE_FUNC
117 #ifndef IN_IDE_PARSER
118 #define pa_context_unref ppa_context_unref
119 #define pa_sample_spec_valid ppa_sample_spec_valid
120 #define pa_frame_size ppa_frame_size
121 #define pa_stream_drop ppa_stream_drop
122 #define pa_strerror ppa_strerror
123 #define pa_context_get_state ppa_context_get_state
124 #define pa_stream_get_state ppa_stream_get_state
125 #define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal
126 #define pa_stream_peek ppa_stream_peek
127 #define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait
128 #define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock
129 #define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread
130 #define pa_context_new ppa_context_new
131 #define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop
132 #define pa_context_disconnect ppa_context_disconnect
133 #define pa_threaded_mainloop_start ppa_threaded_mainloop_start
134 #define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api
135 #define pa_context_set_state_callback ppa_context_set_state_callback
136 #define pa_stream_write ppa_stream_write
137 #define pa_xfree ppa_xfree
138 #define pa_stream_connect_record ppa_stream_connect_record
139 #define pa_stream_connect_playback ppa_stream_connect_playback
140 #define pa_stream_readable_size ppa_stream_readable_size
141 #define pa_stream_writable_size ppa_stream_writable_size
142 #define pa_stream_is_corked ppa_stream_is_corked
143 #define pa_stream_cork ppa_stream_cork
144 #define pa_stream_is_suspended ppa_stream_is_suspended
145 #define pa_stream_get_device_name ppa_stream_get_device_name
146 #define pa_stream_get_latency ppa_stream_get_latency
147 #define pa_path_get_filename ppa_path_get_filename
148 #define pa_get_binary_name ppa_get_binary_name
149 #define pa_threaded_mainloop_free ppa_threaded_mainloop_free
150 #define pa_context_errno ppa_context_errno
151 #define pa_xmalloc ppa_xmalloc
152 #define pa_stream_unref ppa_stream_unref
153 #define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept
154 #define pa_stream_set_write_callback ppa_stream_set_write_callback
155 #define pa_threaded_mainloop_new ppa_threaded_mainloop_new
156 #define pa_context_connect ppa_context_connect
157 #define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
158 #define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
159 #define pa_stream_get_sample_spec ppa_stream_get_sample_spec
160 #define pa_stream_get_time ppa_stream_get_time
161 #define pa_stream_set_read_callback ppa_stream_set_read_callback
162 #define pa_stream_set_state_callback ppa_stream_set_state_callback
163 #define pa_stream_set_moved_callback ppa_stream_set_moved_callback
164 #define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
165 #define pa_stream_new_with_proplist ppa_stream_new_with_proplist
166 #define pa_stream_disconnect ppa_stream_disconnect
167 #define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock
168 #define pa_channel_map_init_auto ppa_channel_map_init_auto
169 #define pa_channel_map_parse ppa_channel_map_parse
170 #define pa_channel_map_snprint ppa_channel_map_snprint
171 #define pa_channel_map_equal ppa_channel_map_equal
172 #define pa_context_get_server_info ppa_context_get_server_info
173 #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
174 #define pa_context_get_sink_info_list ppa_context_get_sink_info_list
175 #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
176 #define pa_context_get_source_info_list ppa_context_get_source_info_list
177 #define pa_operation_get_state ppa_operation_get_state
178 #define pa_operation_unref ppa_operation_unref
179 #define pa_proplist_new ppa_proplist_new
180 #define pa_proplist_free ppa_proplist_free
181 #define pa_proplist_set ppa_proplist_set
182 #define pa_channel_map_superset ppa_channel_map_superset
183 #define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
184 #define pa_stream_begin_write ppa_stream_begin_write
185 #endif /* IN_IDE_PARSER */
187 #endif
189 ALCboolean pulse_load()
191 ALCboolean ret{ALC_TRUE};
192 #ifdef HAVE_DYNLOAD
193 if(!pa_handle)
195 std::string missing_funcs;
197 #ifdef _WIN32
198 #define PALIB "libpulse-0.dll"
199 #elif defined(__APPLE__) && defined(__MACH__)
200 #define PALIB "libpulse.0.dylib"
201 #else
202 #define PALIB "libpulse.so.0"
203 #endif
204 pa_handle = LoadLib(PALIB);
205 if(!pa_handle)
207 WARN("Failed to load %s\n", PALIB);
208 return ALC_FALSE;
211 #define LOAD_FUNC(x) do { \
212 p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pa_handle, #x)); \
213 if(!(p##x)) { \
214 ret = ALC_FALSE; \
215 missing_funcs += "\n" #x; \
217 } while(0)
218 LOAD_FUNC(pa_context_unref);
219 LOAD_FUNC(pa_sample_spec_valid);
220 LOAD_FUNC(pa_stream_drop);
221 LOAD_FUNC(pa_frame_size);
222 LOAD_FUNC(pa_strerror);
223 LOAD_FUNC(pa_context_get_state);
224 LOAD_FUNC(pa_stream_get_state);
225 LOAD_FUNC(pa_threaded_mainloop_signal);
226 LOAD_FUNC(pa_stream_peek);
227 LOAD_FUNC(pa_threaded_mainloop_wait);
228 LOAD_FUNC(pa_threaded_mainloop_unlock);
229 LOAD_FUNC(pa_threaded_mainloop_in_thread);
230 LOAD_FUNC(pa_context_new);
231 LOAD_FUNC(pa_threaded_mainloop_stop);
232 LOAD_FUNC(pa_context_disconnect);
233 LOAD_FUNC(pa_threaded_mainloop_start);
234 LOAD_FUNC(pa_threaded_mainloop_get_api);
235 LOAD_FUNC(pa_context_set_state_callback);
236 LOAD_FUNC(pa_stream_write);
237 LOAD_FUNC(pa_xfree);
238 LOAD_FUNC(pa_stream_connect_record);
239 LOAD_FUNC(pa_stream_connect_playback);
240 LOAD_FUNC(pa_stream_readable_size);
241 LOAD_FUNC(pa_stream_writable_size);
242 LOAD_FUNC(pa_stream_is_corked);
243 LOAD_FUNC(pa_stream_cork);
244 LOAD_FUNC(pa_stream_is_suspended);
245 LOAD_FUNC(pa_stream_get_device_name);
246 LOAD_FUNC(pa_stream_get_latency);
247 LOAD_FUNC(pa_path_get_filename);
248 LOAD_FUNC(pa_get_binary_name);
249 LOAD_FUNC(pa_threaded_mainloop_free);
250 LOAD_FUNC(pa_context_errno);
251 LOAD_FUNC(pa_xmalloc);
252 LOAD_FUNC(pa_stream_unref);
253 LOAD_FUNC(pa_threaded_mainloop_accept);
254 LOAD_FUNC(pa_stream_set_write_callback);
255 LOAD_FUNC(pa_threaded_mainloop_new);
256 LOAD_FUNC(pa_context_connect);
257 LOAD_FUNC(pa_stream_set_buffer_attr);
258 LOAD_FUNC(pa_stream_get_buffer_attr);
259 LOAD_FUNC(pa_stream_get_sample_spec);
260 LOAD_FUNC(pa_stream_get_time);
261 LOAD_FUNC(pa_stream_set_read_callback);
262 LOAD_FUNC(pa_stream_set_state_callback);
263 LOAD_FUNC(pa_stream_set_moved_callback);
264 LOAD_FUNC(pa_stream_set_underflow_callback);
265 LOAD_FUNC(pa_stream_new_with_proplist);
266 LOAD_FUNC(pa_stream_disconnect);
267 LOAD_FUNC(pa_threaded_mainloop_lock);
268 LOAD_FUNC(pa_channel_map_init_auto);
269 LOAD_FUNC(pa_channel_map_parse);
270 LOAD_FUNC(pa_channel_map_snprint);
271 LOAD_FUNC(pa_channel_map_equal);
272 LOAD_FUNC(pa_context_get_server_info);
273 LOAD_FUNC(pa_context_get_sink_info_by_name);
274 LOAD_FUNC(pa_context_get_sink_info_list);
275 LOAD_FUNC(pa_context_get_source_info_by_name);
276 LOAD_FUNC(pa_context_get_source_info_list);
277 LOAD_FUNC(pa_operation_get_state);
278 LOAD_FUNC(pa_operation_unref);
279 LOAD_FUNC(pa_proplist_new);
280 LOAD_FUNC(pa_proplist_free);
281 LOAD_FUNC(pa_proplist_set);
282 LOAD_FUNC(pa_channel_map_superset);
283 LOAD_FUNC(pa_stream_set_buffer_attr_callback);
284 LOAD_FUNC(pa_stream_begin_write);
285 #undef LOAD_FUNC
287 if(ret == ALC_FALSE)
289 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
290 CloseLib(pa_handle);
291 pa_handle = nullptr;
294 #endif /* HAVE_DYNLOAD */
295 return ret;
299 /* *grumble* Don't use enums for bitflags. */
300 inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
301 { return pa_stream_flags_t(int(lhs) | int(rhs)); }
303 inline pa_stream_flags_t operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs)
305 lhs = pa_stream_flags_t(int(lhs) | int(rhs));
306 return lhs;
309 inline pa_context_flags_t operator|=(pa_context_flags_t &lhs, pa_context_flags_t rhs)
311 lhs = pa_context_flags_t(int(lhs) | int(rhs));
312 return lhs;
316 class palock_guard {
317 pa_threaded_mainloop *mLoop;
319 public:
320 explicit palock_guard(pa_threaded_mainloop *loop) : mLoop(loop)
321 { pa_threaded_mainloop_lock(mLoop); }
322 ~palock_guard() { pa_threaded_mainloop_unlock(mLoop); }
324 palock_guard(const palock_guard&) = delete;
325 palock_guard& operator=(const palock_guard&) = delete;
328 class unique_palock {
329 pa_threaded_mainloop *mLoop{nullptr};
330 bool mLocked{false};
332 public:
333 unique_palock() noexcept = default;
334 explicit unique_palock(pa_threaded_mainloop *loop) : mLoop(loop)
336 pa_threaded_mainloop_lock(mLoop);
337 mLocked = true;
339 unique_palock(unique_palock&& rhs) : mLoop(rhs.mLoop), mLocked(rhs.mLocked)
340 { rhs.mLoop = nullptr; rhs.mLocked = false; }
341 ~unique_palock() { if(mLocked) pa_threaded_mainloop_unlock(mLoop); }
343 unique_palock& operator=(const unique_palock&) = delete;
344 unique_palock& operator=(unique_palock&& rhs)
346 if(mLocked)
347 pa_threaded_mainloop_unlock(mLoop);
348 mLoop = rhs.mLoop; rhs.mLoop = nullptr;
349 mLocked = rhs.mLocked; rhs.mLocked = false;
350 return *this;
353 void lock()
355 pa_threaded_mainloop_lock(mLoop);
356 mLocked = true;
358 void unlock()
360 mLocked = false;
361 pa_threaded_mainloop_unlock(mLoop);
366 /* Global flags and properties */
367 pa_context_flags_t pulse_ctx_flags;
368 pa_proplist *prop_filter;
371 /* PulseAudio Event Callbacks */
372 void context_state_callback(pa_context *context, void *pdata)
374 auto loop = static_cast<pa_threaded_mainloop*>(pdata);
375 pa_context_state_t state{pa_context_get_state(context)};
376 if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
377 pa_threaded_mainloop_signal(loop, 0);
380 void stream_state_callback(pa_stream *stream, void *pdata)
382 auto loop = static_cast<pa_threaded_mainloop*>(pdata);
383 pa_stream_state_t state{pa_stream_get_state(stream)};
384 if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
385 pa_threaded_mainloop_signal(loop, 0);
388 void stream_success_callback(pa_stream *UNUSED(stream), int UNUSED(success), void *pdata)
390 auto loop = static_cast<pa_threaded_mainloop*>(pdata);
391 pa_threaded_mainloop_signal(loop, 0);
394 void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
396 if(op)
398 while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
399 pa_threaded_mainloop_wait(loop);
400 pa_operation_unref(op);
405 pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
407 const char *name{"OpenAL Soft"};
409 const PathNamePair &binname = GetProcBinary();
410 if(!binname.fname.empty())
411 name = binname.fname.c_str();
413 pa_context *context{pa_context_new(pa_threaded_mainloop_get_api(loop), name)};
414 if(!context)
416 ERR("pa_context_new() failed\n");
417 return nullptr;
420 pa_context_set_state_callback(context, context_state_callback, loop);
422 int err;
423 if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
425 pa_context_state_t state;
426 while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
428 if(!PA_CONTEXT_IS_GOOD(state))
430 err = pa_context_errno(context);
431 if(err > 0) err = -err;
432 break;
435 pa_threaded_mainloop_wait(loop);
438 pa_context_set_state_callback(context, nullptr, nullptr);
440 if(err < 0)
442 if(!silent)
443 ERR("Context did not connect: %s\n", pa_strerror(err));
444 pa_context_unref(context);
445 context = nullptr;
448 return context;
452 using MainloopContextPair = std::pair<pa_threaded_mainloop*,pa_context*>;
453 MainloopContextPair pulse_open(void(*state_cb)(pa_context*,void*), void *ptr)
455 pa_threaded_mainloop *loop{pa_threaded_mainloop_new()};
456 if(UNLIKELY(!loop))
458 ERR("pa_threaded_mainloop_new() failed!\n");
459 return {nullptr, nullptr};
461 if(UNLIKELY(pa_threaded_mainloop_start(loop) < 0))
463 ERR("pa_threaded_mainloop_start() failed\n");
464 pa_threaded_mainloop_free(loop);
465 return {nullptr, nullptr};
468 unique_palock palock{loop};
469 pa_context *context{connect_context(loop, AL_FALSE)};
470 if(UNLIKELY(!context))
472 palock = unique_palock{};
473 pa_threaded_mainloop_stop(loop);
474 pa_threaded_mainloop_free(loop);
475 return {nullptr, nullptr};
477 pa_context_set_state_callback(context, state_cb, ptr);
478 return {loop, context};
481 void pulse_close(pa_threaded_mainloop *loop, pa_context *context, pa_stream *stream)
483 { palock_guard _{loop};
484 if(stream)
486 pa_stream_set_state_callback(stream, nullptr, nullptr);
487 pa_stream_set_moved_callback(stream, nullptr, nullptr);
488 pa_stream_set_write_callback(stream, nullptr, nullptr);
489 pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
490 pa_stream_disconnect(stream);
491 pa_stream_unref(stream);
494 pa_context_disconnect(context);
495 pa_context_unref(context);
498 pa_threaded_mainloop_stop(loop);
499 pa_threaded_mainloop_free(loop);
503 struct DevMap {
504 std::string name;
505 std::string device_name;
507 template<typename StrT0, typename StrT1>
508 DevMap(StrT0&& name_, StrT1&& devname_)
509 : name{std::forward<StrT0>(name_)}, device_name{std::forward<StrT1>(devname_)}
513 bool checkName(const al::vector<DevMap> &list, const std::string &name)
515 return std::find_if(list.cbegin(), list.cend(),
516 [&name](const DevMap &entry) -> bool
517 { return entry.name == name; }
518 ) != list.cend();
521 al::vector<DevMap> PlaybackDevices;
522 al::vector<DevMap> CaptureDevices;
525 pa_stream *pulse_connect_stream(const char *device_name, pa_threaded_mainloop *loop,
526 pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
527 pa_channel_map *chanmap, BackendType type)
529 pa_stream *stream{pa_stream_new_with_proplist(context,
530 (type==BackendType::Playback) ? "Playback Stream" : "Capture Stream", spec, chanmap,
531 prop_filter)};
532 if(!stream)
534 ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
535 return nullptr;
538 pa_stream_set_state_callback(stream, stream_state_callback, loop);
540 int err{(type==BackendType::Playback) ?
541 pa_stream_connect_playback(stream, device_name, attr, flags, nullptr, nullptr) :
542 pa_stream_connect_record(stream, device_name, attr, flags)};
543 if(err < 0)
545 ERR("Stream did not connect: %s\n", pa_strerror(err));
546 pa_stream_unref(stream);
547 return nullptr;
550 pa_stream_state_t state;
551 while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
553 if(!PA_STREAM_IS_GOOD(state))
555 ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
556 pa_stream_unref(stream);
557 return nullptr;
560 pa_threaded_mainloop_wait(loop);
562 pa_stream_set_state_callback(stream, nullptr, nullptr);
564 return stream;
568 void device_sink_callback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
570 auto loop = static_cast<pa_threaded_mainloop*>(pdata);
572 if(eol)
574 pa_threaded_mainloop_signal(loop, 0);
575 return;
578 /* Skip this device is if it's already in the list. */
579 if(std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
580 [info](const DevMap &entry) -> bool
581 { return entry.device_name == info->name; }
582 ) != PlaybackDevices.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(PlaybackDevices, newname))
592 newname = info->description;
593 newname += " #";
594 newname += std::to_string(++count);
596 PlaybackDevices.emplace_back(std::move(newname), info->name);
597 DevMap &newentry = PlaybackDevices.back();
599 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
602 void probePlaybackDevices()
604 PlaybackDevices.clear();
606 pa_threaded_mainloop *loop{pa_threaded_mainloop_new()};
607 if(loop && pa_threaded_mainloop_start(loop) >= 0)
609 unique_palock palock{loop};
611 pa_context *context{connect_context(loop, AL_FALSE)};
612 if(context)
614 pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
615 PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
617 pa_sample_spec spec;
618 spec.format = PA_SAMPLE_S16NE;
619 spec.rate = 44100;
620 spec.channels = 2;
622 pa_stream *stream{pulse_connect_stream(nullptr, loop, context, flags, nullptr, &spec,
623 nullptr, BackendType::Playback)};
624 if(stream)
626 pa_operation *op{pa_context_get_sink_info_by_name(context,
627 pa_stream_get_device_name(stream), device_sink_callback, loop)};
628 wait_for_operation(op, loop);
630 pa_stream_disconnect(stream);
631 pa_stream_unref(stream);
632 stream = nullptr;
635 pa_operation *op{pa_context_get_sink_info_list(context,
636 device_sink_callback, loop)};
637 wait_for_operation(op, loop);
639 pa_context_disconnect(context);
640 pa_context_unref(context);
642 palock = unique_palock{};
643 pa_threaded_mainloop_stop(loop);
645 if(loop)
646 pa_threaded_mainloop_free(loop);
650 void device_source_callback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata)
652 auto loop = static_cast<pa_threaded_mainloop*>(pdata);
654 if(eol)
656 pa_threaded_mainloop_signal(loop, 0);
657 return;
660 /* Skip this device is if it's already in the list. */
661 if(std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
662 [info](const DevMap &entry) -> bool
663 { return entry.device_name == info->name; }
664 ) != CaptureDevices.cend())
665 return;
667 /* Make sure the display name (description) is unique. Append a number
668 * counter as needed.
670 int count{1};
671 std::string newname{info->description};
672 while(checkName(CaptureDevices, newname))
674 newname = info->description;
675 newname += " #";
676 newname += std::to_string(++count);
678 CaptureDevices.emplace_back(std::move(newname), info->name);
679 DevMap &newentry = CaptureDevices.back();
681 TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str());
684 void probeCaptureDevices()
686 CaptureDevices.clear();
688 pa_threaded_mainloop *loop{pa_threaded_mainloop_new()};
689 if(loop && pa_threaded_mainloop_start(loop) >= 0)
691 unique_palock palock{loop};
693 pa_context *context{connect_context(loop, AL_FALSE)};
694 if(context)
696 pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
697 PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
699 pa_sample_spec spec;
700 spec.format = PA_SAMPLE_S16NE;
701 spec.rate = 44100;
702 spec.channels = 1;
704 pa_stream *stream{pulse_connect_stream(nullptr, loop, context, flags, nullptr, &spec,
705 nullptr, BackendType::Capture)};
706 if(stream)
708 pa_operation *op{pa_context_get_source_info_by_name(context,
709 pa_stream_get_device_name(stream), device_source_callback, loop)};
710 wait_for_operation(op, loop);
712 pa_stream_disconnect(stream);
713 pa_stream_unref(stream);
714 stream = nullptr;
717 pa_operation *op{pa_context_get_source_info_list(context,
718 device_source_callback, loop)};
719 wait_for_operation(op, loop);
721 pa_context_disconnect(context);
722 pa_context_unref(context);
724 palock.unlock();
725 pa_threaded_mainloop_stop(loop);
727 if(loop)
728 pa_threaded_mainloop_free(loop);
732 struct PulsePlayback final : public BackendBase {
733 PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
734 ~PulsePlayback() override;
736 static void bufferAttrCallbackC(pa_stream *stream, void *pdata);
737 void bufferAttrCallback(pa_stream *stream);
739 static void contextStateCallbackC(pa_context *context, void *pdata);
740 void contextStateCallback(pa_context *context);
742 static void streamStateCallbackC(pa_stream *stream, void *pdata);
743 void streamStateCallback(pa_stream *stream);
745 static void streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata);
746 void streamWriteCallback(pa_stream *stream, size_t nbytes);
748 static void sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
749 void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol);
751 static void sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
752 void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol);
754 static void streamMovedCallbackC(pa_stream *stream, void *pdata);
755 void streamMovedCallback(pa_stream *stream);
757 ALCenum open(const ALCchar *name) override;
758 ALCboolean reset() override;
759 ALCboolean start() override;
760 void stop() override;
761 ClockLatency getClockLatency() override;
762 void lock() override;
763 void unlock() override;
765 std::string mDeviceName;
767 pa_buffer_attr mAttr;
768 pa_sample_spec mSpec;
770 pa_threaded_mainloop *mLoop{nullptr};
772 pa_stream *mStream{nullptr};
773 pa_context *mContext{nullptr};
775 ALuint mBufferSize{0u};
776 ALuint mFrameSize{0u};
778 static constexpr inline const char *CurrentPrefix() noexcept { return "PulsePlayback::"; }
779 DEF_NEWDEL(PulsePlayback)
782 PulsePlayback::~PulsePlayback()
784 if(!mLoop)
785 return;
787 pulse_close(mLoop, mContext, mStream);
788 mLoop = nullptr;
789 mContext = nullptr;
790 mStream = nullptr;
794 void PulsePlayback::bufferAttrCallbackC(pa_stream *stream, void *pdata)
795 { static_cast<PulsePlayback*>(pdata)->bufferAttrCallback(stream); }
797 void PulsePlayback::bufferAttrCallback(pa_stream *stream)
799 /* FIXME: Update the device's UpdateSize (and/or NumUpdates) using the new
800 * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
801 * property, which probably shouldn't change between device resets. But
802 * leaving it alone means ALC_REFRESH will be off.
804 mAttr = *(pa_stream_get_buffer_attr(stream));
805 TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf);
807 const ALuint num_periods{(mAttr.tlength + mAttr.minreq/2u) / mAttr.minreq};
808 mBufferSize = maxu(num_periods, 2u) * mAttr.minreq;
811 void PulsePlayback::contextStateCallbackC(pa_context *context, void *pdata)
812 { static_cast<PulsePlayback*>(pdata)->contextStateCallback(context); }
814 void PulsePlayback::contextStateCallback(pa_context *context)
816 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
818 ERR("Received context failure!\n");
819 aluHandleDisconnect(mDevice, "Playback state failure");
821 pa_threaded_mainloop_signal(mLoop, 0);
824 void PulsePlayback::streamStateCallbackC(pa_stream *stream, void *pdata)
825 { static_cast<PulsePlayback*>(pdata)->streamStateCallback(stream); }
827 void PulsePlayback::streamStateCallback(pa_stream *stream)
829 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
831 ERR("Received stream failure!\n");
832 aluHandleDisconnect(mDevice, "Playback stream failure");
834 pa_threaded_mainloop_signal(mLoop, 0);
837 void PulsePlayback::streamWriteCallbackC(pa_stream *stream, size_t nbytes, void *pdata)
838 { static_cast<PulsePlayback*>(pdata)->streamWriteCallback(stream, nbytes); }
840 void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes)
842 /* Round down to the nearest period/minreq multiple if doing more than 1. */
843 if(nbytes > mAttr.minreq)
844 nbytes -= nbytes%mAttr.minreq;
846 void *buf{pa_xmalloc(nbytes)};
847 aluMixData(mDevice, buf, nbytes/mFrameSize);
849 int ret{pa_stream_write(stream, buf, nbytes, pa_xfree, 0, PA_SEEK_RELATIVE)};
850 if(UNLIKELY(ret != PA_OK))
851 ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
854 void PulsePlayback::sinkInfoCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
855 { static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); }
857 void PulsePlayback::sinkInfoCallback(pa_context* UNUSED(context), const pa_sink_info *info, int eol)
859 struct ChannelMap {
860 DevFmtChannels chans;
861 pa_channel_map map;
863 static constexpr std::array<ChannelMap,7> chanmaps{{
864 { DevFmtX71, { 8, {
865 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
866 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
867 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
868 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
869 } } },
870 { DevFmtX61, { 7, {
871 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
872 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
873 PA_CHANNEL_POSITION_REAR_CENTER,
874 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
875 } } },
876 { DevFmtX51, { 6, {
877 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
878 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
879 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
880 } } },
881 { DevFmtX51Rear, { 6, {
882 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
883 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
884 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
885 } } },
886 { DevFmtQuad, { 4, {
887 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
888 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
889 } } },
890 { DevFmtStereo, { 2, {
891 PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT
892 } } },
893 { DevFmtMono, { 1, {PA_CHANNEL_POSITION_MONO} } }
896 if(eol)
898 pa_threaded_mainloop_signal(mLoop, 0);
899 return;
902 auto chanmap = std::find_if(chanmaps.cbegin(), chanmaps.cend(),
903 [info](const ChannelMap &chanmap) -> bool
904 { return pa_channel_map_superset(&info->channel_map, &chanmap.map); }
906 if(chanmap != chanmaps.cend())
908 if(!(mDevice->Flags&DEVICE_CHANNELS_REQUEST))
909 mDevice->FmtChans = chanmap->chans;
911 else
913 char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
914 pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
915 WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
918 if(info->active_port)
919 TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
920 mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
921 info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0);
924 void PulsePlayback::sinkNameCallbackC(pa_context *context, const pa_sink_info *info, int eol, void *pdata)
925 { static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); }
927 void PulsePlayback::sinkNameCallback(pa_context* UNUSED(context), const pa_sink_info *info, int eol)
929 if(eol)
931 pa_threaded_mainloop_signal(mLoop, 0);
932 return;
934 mDevice->DeviceName = info->description;
937 void PulsePlayback::streamMovedCallbackC(pa_stream *stream, void *pdata)
938 { static_cast<PulsePlayback*>(pdata)->streamMovedCallback(stream); }
940 void PulsePlayback::streamMovedCallback(pa_stream *stream)
942 mDeviceName = pa_stream_get_device_name(stream);
943 TRACE("Stream moved to %s\n", mDeviceName.c_str());
947 ALCenum PulsePlayback::open(const ALCchar *name)
949 const char *pulse_name{nullptr};
950 const char *dev_name{nullptr};
952 if(name)
954 if(PlaybackDevices.empty())
955 probePlaybackDevices();
957 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
958 [name](const DevMap &entry) -> bool
959 { return entry.name == name; }
961 if(iter == PlaybackDevices.cend())
962 return ALC_INVALID_VALUE;
963 pulse_name = iter->device_name.c_str();
964 dev_name = iter->name.c_str();
967 std::tie(mLoop, mContext) = pulse_open(&PulsePlayback::contextStateCallbackC, this);
968 if(!mLoop) return ALC_INVALID_VALUE;
970 unique_palock palock{mLoop};
972 pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
973 PA_STREAM_FIX_CHANNELS};
974 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 0))
975 flags |= PA_STREAM_DONT_MOVE;
977 pa_sample_spec spec{};
978 spec.format = PA_SAMPLE_S16NE;
979 spec.rate = 44100;
980 spec.channels = 2;
982 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
983 if(!pulse_name)
985 pulse_name = getenv("ALSOFT_PULSE_DEFAULT");
986 if(pulse_name && !pulse_name[0]) pulse_name = nullptr;
988 mStream = pulse_connect_stream(pulse_name, mLoop, mContext, flags, nullptr, &spec, nullptr,
989 BackendType::Playback);
990 if(!mStream)
992 palock = unique_palock{};
993 pulse_close(mLoop, mContext, mStream);
994 mLoop = nullptr;
995 mContext = nullptr;
996 return ALC_INVALID_VALUE;
998 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
999 mFrameSize = pa_frame_size(pa_stream_get_sample_spec(mStream));
1001 mDeviceName = pa_stream_get_device_name(mStream);
1002 if(!dev_name)
1004 pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
1005 &PulsePlayback::sinkNameCallbackC, this)};
1006 wait_for_operation(op, mLoop);
1008 else
1009 mDevice->DeviceName = dev_name;
1011 return ALC_NO_ERROR;
1014 ALCboolean PulsePlayback::reset()
1016 unique_palock palock{mLoop};
1018 if(mStream)
1020 pa_stream_set_state_callback(mStream, nullptr, nullptr);
1021 pa_stream_set_moved_callback(mStream, nullptr, nullptr);
1022 pa_stream_set_write_callback(mStream, nullptr, nullptr);
1023 pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
1024 pa_stream_disconnect(mStream);
1025 pa_stream_unref(mStream);
1026 mStream = nullptr;
1029 pa_operation *op{pa_context_get_sink_info_by_name(mContext, mDeviceName.c_str(),
1030 &PulsePlayback::sinkInfoCallbackC, this)};
1031 wait_for_operation(op, mLoop);
1033 pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
1034 PA_STREAM_AUTO_TIMING_UPDATE};
1035 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 0))
1036 flags |= PA_STREAM_DONT_MOVE;
1037 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", 0))
1038 flags |= PA_STREAM_ADJUST_LATENCY;
1039 if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", 0) ||
1040 !(mDevice->Flags&DEVICE_FREQUENCY_REQUEST))
1041 flags |= PA_STREAM_FIX_RATE;
1043 switch(mDevice->FmtType)
1045 case DevFmtByte:
1046 mDevice->FmtType = DevFmtUByte;
1047 /* fall-through */
1048 case DevFmtUByte:
1049 mSpec.format = PA_SAMPLE_U8;
1050 break;
1051 case DevFmtUShort:
1052 mDevice->FmtType = DevFmtShort;
1053 /* fall-through */
1054 case DevFmtShort:
1055 mSpec.format = PA_SAMPLE_S16NE;
1056 break;
1057 case DevFmtUInt:
1058 mDevice->FmtType = DevFmtInt;
1059 /* fall-through */
1060 case DevFmtInt:
1061 mSpec.format = PA_SAMPLE_S32NE;
1062 break;
1063 case DevFmtFloat:
1064 mSpec.format = PA_SAMPLE_FLOAT32NE;
1065 break;
1067 mSpec.rate = mDevice->Frequency;
1068 mSpec.channels = mDevice->channelsFromFmt();
1070 if(pa_sample_spec_valid(&mSpec) == 0)
1072 ERR("Invalid sample format\n");
1073 return ALC_FALSE;
1076 const char *mapname{nullptr};
1077 pa_channel_map chanmap;
1078 switch(mDevice->FmtChans)
1080 case DevFmtMono:
1081 mapname = "mono";
1082 break;
1083 case DevFmtAmbi3D:
1084 mDevice->FmtChans = DevFmtStereo;
1085 /*fall-through*/
1086 case DevFmtStereo:
1087 mapname = "front-left,front-right";
1088 break;
1089 case DevFmtQuad:
1090 mapname = "front-left,front-right,rear-left,rear-right";
1091 break;
1092 case DevFmtX51:
1093 mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
1094 break;
1095 case DevFmtX51Rear:
1096 mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
1097 break;
1098 case DevFmtX61:
1099 mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
1100 break;
1101 case DevFmtX71:
1102 mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
1103 break;
1105 if(!pa_channel_map_parse(&chanmap, mapname))
1107 ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
1108 return ALC_FALSE;
1110 SetDefaultWFXChannelOrder(mDevice);
1112 size_t period_size{mDevice->UpdateSize * pa_frame_size(&mSpec)};
1113 mAttr.maxlength = -1;
1114 mAttr.tlength = period_size * maxu(mDevice->NumUpdates, 2);
1115 mAttr.prebuf = 0;
1116 mAttr.minreq = period_size;
1117 mAttr.fragsize = -1;
1119 mStream = pulse_connect_stream(mDeviceName.c_str(), mLoop, mContext, flags, &mAttr, &mSpec,
1120 &chanmap, BackendType::Playback);
1121 if(!mStream) return ALC_FALSE;
1123 pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
1124 pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
1126 mSpec = *(pa_stream_get_sample_spec(mStream));
1127 mFrameSize = pa_frame_size(&mSpec);
1129 if(mDevice->Frequency != mSpec.rate)
1131 /* Server updated our playback rate, so modify the buffer attribs
1132 * accordingly. */
1133 mDevice->NumUpdates = static_cast<ALuint>(clampd(
1134 static_cast<ALdouble>(mSpec.rate)/mDevice->Frequency*mDevice->NumUpdates + 0.5, 2.0, 16.0));
1136 period_size = mDevice->UpdateSize * mFrameSize;
1137 mAttr.maxlength = -1;
1138 mAttr.tlength = period_size * maxu(mDevice->NumUpdates, 2);
1139 mAttr.prebuf = 0;
1140 mAttr.minreq = period_size;
1142 op = pa_stream_set_buffer_attr(mStream, &mAttr, stream_success_callback, mLoop);
1143 wait_for_operation(op, mLoop);
1145 mDevice->Frequency = mSpec.rate;
1148 pa_stream_set_buffer_attr_callback(mStream, &PulsePlayback::bufferAttrCallbackC, this);
1149 bufferAttrCallback(mStream);
1151 mDevice->NumUpdates = clampu((mAttr.tlength + mAttr.minreq/2u) / mAttr.minreq, 2u, 16u);
1152 mDevice->UpdateSize = mAttr.minreq / mFrameSize;
1154 /* HACK: prebuf should be 0 as that's what we set it to. However on some
1155 * systems it comes back as non-0, so we have to make sure the device will
1156 * write enough audio to start playback. The lack of manual start control
1157 * may have unintended consequences, but it's better than not starting at
1158 * all.
1160 if(mAttr.prebuf != 0)
1162 ALuint len{mAttr.prebuf / mFrameSize};
1163 if(len <= mDevice->UpdateSize*mDevice->NumUpdates)
1164 ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
1165 len, mAttr.prebuf, mDevice->UpdateSize*mDevice->NumUpdates);
1166 else
1168 ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
1169 len, mAttr.prebuf, mDevice->UpdateSize*mDevice->NumUpdates);
1170 mDevice->NumUpdates = (len+mDevice->UpdateSize-1) / mDevice->UpdateSize;
1174 return ALC_TRUE;
1177 ALCboolean PulsePlayback::start()
1179 unique_palock palock{mLoop};
1181 pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
1182 pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, mLoop)};
1183 wait_for_operation(op, mLoop);
1185 return ALC_TRUE;
1188 void PulsePlayback::stop()
1190 unique_palock palock{mLoop};
1192 pa_stream_set_write_callback(mStream, nullptr, nullptr);
1193 pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, mLoop)};
1194 wait_for_operation(op, mLoop);
1198 ClockLatency PulsePlayback::getClockLatency()
1200 ClockLatency ret;
1201 pa_usec_t latency;
1202 int neg, err;
1204 { palock_guard _{mLoop};
1205 ret.ClockTime = GetDeviceClockTime(mDevice);
1206 err = pa_stream_get_latency(mStream, &latency, &neg);
1209 if(UNLIKELY(err != 0))
1211 /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
1212 * after starting the stream and no timing info has been received from
1213 * the server yet. Should we wait, possibly stalling the app, or give a
1214 * dummy value? Either way, it shouldn't be 0. */
1215 if(err != -PA_ERR_NODATA)
1216 ERR("Failed to get stream latency: 0x%x\n", err);
1217 latency = 0;
1218 neg = 0;
1220 else if(UNLIKELY(neg))
1221 latency = 0;
1222 ret.Latency = std::chrono::microseconds{latency};
1224 return ret;
1228 void PulsePlayback::lock()
1229 { pa_threaded_mainloop_lock(mLoop); }
1231 void PulsePlayback::unlock()
1232 { pa_threaded_mainloop_unlock(mLoop); }
1235 struct PulseCapture final : public BackendBase {
1236 PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
1237 ~PulseCapture() override;
1239 static void contextStateCallbackC(pa_context *context, void *pdata);
1240 void contextStateCallback(pa_context *context);
1242 static void streamStateCallbackC(pa_stream *stream, void *pdata);
1243 void streamStateCallback(pa_stream *stream);
1245 static void sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata);
1246 void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol);
1248 static void streamMovedCallbackC(pa_stream *stream, void *pdata);
1249 void streamMovedCallback(pa_stream *stream);
1251 ALCenum open(const ALCchar *name) override;
1252 ALCboolean start() override;
1253 void stop() override;
1254 ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
1255 ALCuint availableSamples() override;
1256 ClockLatency getClockLatency() override;
1257 void lock() override;
1258 void unlock() override;
1260 std::string mDeviceName;
1262 const void *mCapStore{nullptr};
1263 size_t mCapLen{0u};
1264 size_t mCapRemain{0u};
1266 ALCuint mLastReadable{0u};
1268 pa_buffer_attr mAttr{};
1269 pa_sample_spec mSpec{};
1271 pa_threaded_mainloop *mLoop{nullptr};
1273 pa_stream *mStream{nullptr};
1274 pa_context *mContext{nullptr};
1276 static constexpr inline const char *CurrentPrefix() noexcept { return "PulseCapture::"; }
1277 DEF_NEWDEL(PulseCapture)
1280 PulseCapture::~PulseCapture()
1282 if(!mLoop)
1283 return;
1284 pulse_close(mLoop, mContext, mStream);
1285 mLoop = nullptr;
1286 mContext = nullptr;
1287 mStream = nullptr;
1290 void PulseCapture::contextStateCallbackC(pa_context *context, void *pdata)
1291 { static_cast<PulseCapture*>(pdata)->contextStateCallback(context); }
1293 void PulseCapture::contextStateCallback(pa_context *context)
1295 if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
1297 ERR("Received context failure!\n");
1298 aluHandleDisconnect(mDevice, "Capture state failure");
1300 pa_threaded_mainloop_signal(mLoop, 0);
1303 void PulseCapture::streamStateCallbackC(pa_stream *stream, void *pdata)
1304 { static_cast<PulseCapture*>(pdata)->streamStateCallback(stream); }
1306 void PulseCapture::streamStateCallback(pa_stream *stream)
1308 if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
1310 ERR("Received stream failure!\n");
1311 aluHandleDisconnect(mDevice, "Capture stream failure");
1313 pa_threaded_mainloop_signal(mLoop, 0);
1316 void PulseCapture::sourceNameCallbackC(pa_context *context, const pa_source_info *info, int eol, void *pdata)
1317 { static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); }
1319 void PulseCapture::sourceNameCallback(pa_context* UNUSED(context), const pa_source_info *info, int eol)
1321 if(eol)
1323 pa_threaded_mainloop_signal(mLoop, 0);
1324 return;
1326 mDevice->DeviceName = info->description;
1329 void PulseCapture::streamMovedCallbackC(pa_stream *stream, void *pdata)
1330 { static_cast<PulseCapture*>(pdata)->streamMovedCallback(stream); }
1332 void PulseCapture::streamMovedCallback(pa_stream *stream)
1334 mDeviceName = pa_stream_get_device_name(stream);
1335 TRACE("Stream moved to %s\n", mDeviceName.c_str());
1339 ALCenum PulseCapture::open(const ALCchar *name)
1341 const char *pulse_name{nullptr};
1342 if(name)
1344 if(CaptureDevices.empty())
1345 probeCaptureDevices();
1347 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1348 [name](const DevMap &entry) -> bool
1349 { return entry.name == name; }
1351 if(iter == CaptureDevices.cend())
1352 return ALC_INVALID_VALUE;
1353 pulse_name = iter->device_name.c_str();
1354 mDevice->DeviceName = iter->name;
1357 std::tie(mLoop, mContext) = pulse_open(&PulseCapture::contextStateCallbackC, this);
1358 if(!mLoop) return ALC_INVALID_VALUE;
1360 unique_palock palock{mLoop};
1362 switch(mDevice->FmtType)
1364 case DevFmtUByte:
1365 mSpec.format = PA_SAMPLE_U8;
1366 break;
1367 case DevFmtShort:
1368 mSpec.format = PA_SAMPLE_S16NE;
1369 break;
1370 case DevFmtInt:
1371 mSpec.format = PA_SAMPLE_S32NE;
1372 break;
1373 case DevFmtFloat:
1374 mSpec.format = PA_SAMPLE_FLOAT32NE;
1375 break;
1376 case DevFmtByte:
1377 case DevFmtUShort:
1378 case DevFmtUInt:
1379 ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
1380 return ALC_INVALID_VALUE;
1383 const char *mapname{nullptr};
1384 pa_channel_map chanmap;
1385 switch(mDevice->FmtChans)
1387 case DevFmtMono:
1388 mapname = "mono";
1389 break;
1390 case DevFmtStereo:
1391 mapname = "front-left,front-right";
1392 break;
1393 case DevFmtQuad:
1394 mapname = "front-left,front-right,rear-left,rear-right";
1395 break;
1396 case DevFmtX51:
1397 mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
1398 break;
1399 case DevFmtX51Rear:
1400 mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
1401 break;
1402 case DevFmtX61:
1403 mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
1404 break;
1405 case DevFmtX71:
1406 mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
1407 break;
1408 case DevFmtAmbi3D:
1409 ERR("%s capture samples not supported\n", DevFmtChannelsString(mDevice->FmtChans));
1410 return ALC_INVALID_VALUE;
1412 if(!pa_channel_map_parse(&chanmap, mapname))
1414 ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
1415 return ALC_INVALID_VALUE;
1418 mSpec.rate = mDevice->Frequency;
1419 mSpec.channels = mDevice->channelsFromFmt();
1421 if(pa_sample_spec_valid(&mSpec) == 0)
1423 ERR("Invalid sample format\n");
1424 return ALC_INVALID_VALUE;
1427 if(!pa_channel_map_init_auto(&chanmap, mSpec.channels, PA_CHANNEL_MAP_WAVEEX))
1429 ERR("Couldn't build map for channel count (%d)!\n", mSpec.channels);
1430 return ALC_INVALID_VALUE;
1433 ALuint samples{mDevice->UpdateSize * mDevice->NumUpdates};
1434 samples = maxu(samples, 100 * mDevice->Frequency / 1000);
1436 mAttr.minreq = -1;
1437 mAttr.prebuf = -1;
1438 mAttr.maxlength = samples * pa_frame_size(&mSpec);
1439 mAttr.tlength = -1;
1440 mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * pa_frame_size(&mSpec);
1442 pa_stream_flags_t flags{PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY};
1443 if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 0))
1444 flags |= PA_STREAM_DONT_MOVE;
1446 TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
1447 mStream = pulse_connect_stream(pulse_name, mLoop, mContext, flags, &mAttr, &mSpec, &chanmap,
1448 BackendType::Capture);
1449 if(!mStream) return ALC_INVALID_VALUE;
1451 pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
1452 pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
1454 mDeviceName = pa_stream_get_device_name(mStream);
1455 if(mDevice->DeviceName.empty())
1457 pa_operation *op{pa_context_get_source_info_by_name(mContext, mDeviceName.c_str(),
1458 &PulseCapture::sourceNameCallbackC, this)};
1459 wait_for_operation(op, mLoop);
1462 return ALC_NO_ERROR;
1465 ALCboolean PulseCapture::start()
1467 palock_guard _{mLoop};
1468 pa_operation *op{pa_stream_cork(mStream, 0, stream_success_callback, mLoop)};
1469 wait_for_operation(op, mLoop);
1470 return ALC_TRUE;
1473 void PulseCapture::stop()
1475 palock_guard _{mLoop};
1476 pa_operation *op{pa_stream_cork(mStream, 1, stream_success_callback, mLoop)};
1477 wait_for_operation(op, mLoop);
1480 ALCenum PulseCapture::captureSamples(ALCvoid *buffer, ALCuint samples)
1482 ALCuint todo{samples * static_cast<ALCuint>(pa_frame_size(&mSpec))};
1484 /* Capture is done in fragment-sized chunks, so we loop until we get all
1485 * that's available */
1486 mLastReadable -= todo;
1487 unique_palock palock{mLoop};
1488 while(todo > 0)
1490 size_t rem{todo};
1492 if(mCapLen == 0)
1494 pa_stream_state_t state{pa_stream_get_state(mStream)};
1495 if(!PA_STREAM_IS_GOOD(state))
1497 aluHandleDisconnect(mDevice, "Bad capture state: %u", state);
1498 return ALC_INVALID_DEVICE;
1500 if(pa_stream_peek(mStream, &mCapStore, &mCapLen) < 0)
1502 ERR("pa_stream_peek() failed: %s\n",
1503 pa_strerror(pa_context_errno(mContext)));
1504 aluHandleDisconnect(mDevice, "Failed retrieving capture samples: %s",
1505 pa_strerror(pa_context_errno(mContext)));
1506 return ALC_INVALID_DEVICE;
1508 mCapRemain = mCapLen;
1510 rem = minz(rem, mCapRemain);
1512 memcpy(buffer, mCapStore, rem);
1514 buffer = static_cast<ALbyte*>(buffer) + rem;
1515 todo -= rem;
1517 mCapStore = reinterpret_cast<const ALbyte*>(mCapStore) + rem;
1518 mCapRemain -= rem;
1519 if(mCapRemain == 0)
1521 pa_stream_drop(mStream);
1522 mCapLen = 0;
1525 palock.unlock();
1526 if(todo > 0)
1527 memset(buffer, ((mDevice->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
1529 return ALC_NO_ERROR;
1532 ALCuint PulseCapture::availableSamples()
1534 size_t readable{mCapRemain};
1536 if(mDevice->Connected.load(std::memory_order_acquire))
1538 palock_guard _{mLoop};
1539 size_t got{pa_stream_readable_size(mStream)};
1540 if(static_cast<ssize_t>(got) < 0)
1542 ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
1543 aluHandleDisconnect(mDevice, "Failed getting readable size: %s", pa_strerror(got));
1545 else if(got > mCapLen)
1546 readable += got - mCapLen;
1549 if(mLastReadable < readable)
1550 mLastReadable = readable;
1551 return mLastReadable / pa_frame_size(&mSpec);
1555 ClockLatency PulseCapture::getClockLatency()
1557 ClockLatency ret;
1558 pa_usec_t latency;
1559 int neg, err;
1561 { palock_guard _{mLoop};
1562 ret.ClockTime = GetDeviceClockTime(mDevice);
1563 err = pa_stream_get_latency(mStream, &latency, &neg);
1566 if(UNLIKELY(err != 0))
1568 ERR("Failed to get stream latency: 0x%x\n", err);
1569 latency = 0;
1570 neg = 0;
1572 else if(UNLIKELY(neg))
1573 latency = 0;
1574 ret.Latency = std::chrono::microseconds{latency};
1576 return ret;
1580 void PulseCapture::lock()
1581 { pa_threaded_mainloop_lock(mLoop); }
1583 void PulseCapture::unlock()
1584 { pa_threaded_mainloop_unlock(mLoop); }
1586 } // namespace
1589 bool PulseBackendFactory::init()
1591 bool ret{false};
1593 if(pulse_load())
1595 pulse_ctx_flags = PA_CONTEXT_NOFLAGS;
1596 if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
1597 pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
1599 pa_threaded_mainloop *loop{pa_threaded_mainloop_new()};
1600 if(loop && pa_threaded_mainloop_start(loop) >= 0)
1602 unique_palock palock{loop};
1603 pa_context *context{connect_context(loop, AL_TRUE)};
1604 if(context)
1606 ret = true;
1608 /* Some libraries (Phonon, Qt) set some pulseaudio properties
1609 * through environment variables, which causes all streams in
1610 * the process to inherit them. This attempts to filter those
1611 * properties out by setting them to 0-length data. */
1612 prop_filter = pa_proplist_new();
1613 pa_proplist_set(prop_filter, PA_PROP_MEDIA_ROLE, nullptr, 0);
1614 pa_proplist_set(prop_filter, "phonon.streamid", nullptr, 0);
1616 pa_context_disconnect(context);
1617 pa_context_unref(context);
1619 palock.unlock();
1620 pa_threaded_mainloop_stop(loop);
1622 if(loop)
1623 pa_threaded_mainloop_free(loop);
1626 return ret;
1629 void PulseBackendFactory::deinit()
1631 PlaybackDevices.clear();
1632 CaptureDevices.clear();
1634 if(prop_filter)
1635 pa_proplist_free(prop_filter);
1636 prop_filter = nullptr;
1638 /* PulseAudio doesn't like being CloseLib'd sometimes */
1641 bool PulseBackendFactory::querySupport(BackendType type)
1642 { return type == BackendType::Playback || type == BackendType::Capture; }
1644 void PulseBackendFactory::probe(DevProbe type, std::string *outnames)
1646 auto add_device = [outnames](const DevMap &entry) -> void
1648 /* +1 to also append the null char (to ensure a null-separated list and
1649 * double-null terminated list).
1651 outnames->append(entry.name.c_str(), entry.name.length()+1);
1653 switch(type)
1655 case ALL_DEVICE_PROBE:
1656 probePlaybackDevices();
1657 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1658 break;
1660 case CAPTURE_DEVICE_PROBE:
1661 probeCaptureDevices();
1662 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1663 break;
1667 BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
1669 if(type == BackendType::Playback)
1670 return BackendPtr{new PulsePlayback{device}};
1671 if(type == BackendType::Capture)
1672 return BackendPtr{new PulseCapture{device}};
1673 return nullptr;
1676 BackendFactory &PulseBackendFactory::getFactory()
1678 static PulseBackendFactory factory{};
1679 return factory;