1 | /**************************************************************************/ |
2 | /* audio_driver_pulseaudio.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "audio_driver_pulseaudio.h" |
32 | |
33 | #ifdef PULSEAUDIO_ENABLED |
34 | |
35 | #include "core/config/project_settings.h" |
36 | #include "core/os/os.h" |
37 | #include "core/version.h" |
38 | |
39 | #ifdef ALSAMIDI_ENABLED |
40 | #ifdef SOWRAP_ENABLED |
41 | #include "drivers/alsa/asound-so_wrap.h" |
42 | #else |
43 | #include <alsa/asoundlib.h> |
44 | #endif |
45 | #endif |
46 | |
47 | void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { |
48 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
49 | |
50 | switch (pa_context_get_state(c)) { |
51 | case PA_CONTEXT_TERMINATED: |
52 | print_verbose("PulseAudio: context terminated" ); |
53 | ad->pa_ready = -1; |
54 | break; |
55 | case PA_CONTEXT_FAILED: |
56 | print_verbose("PulseAudio: context failed" ); |
57 | ad->pa_ready = -1; |
58 | break; |
59 | case PA_CONTEXT_READY: |
60 | print_verbose("PulseAudio: context ready" ); |
61 | ad->pa_ready = 1; |
62 | break; |
63 | default: |
64 | print_verbose("PulseAudio: context other" ); |
65 | // TODO: Check if we want to handle some of the other |
66 | // PA context states like PA_CONTEXT_UNCONNECTED. |
67 | break; |
68 | } |
69 | } |
70 | |
71 | void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { |
72 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
73 | |
74 | // If eol is set to a positive number, you're at the end of the list |
75 | if (eol > 0) { |
76 | return; |
77 | } |
78 | |
79 | // If eol is set to a negative number there's an error. |
80 | if (eol < 0) { |
81 | ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c)))); |
82 | ad->pa_status--; |
83 | return; |
84 | } |
85 | |
86 | ad->pa_map = l->channel_map; |
87 | ad->pa_status++; |
88 | } |
89 | |
90 | void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { |
91 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
92 | |
93 | // If eol is set to a positive number, you're at the end of the list |
94 | if (eol > 0) { |
95 | return; |
96 | } |
97 | |
98 | // If eol is set to a negative number there's an error. |
99 | if (eol < 0) { |
100 | ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c)))); |
101 | ad->pa_status--; |
102 | return; |
103 | } |
104 | |
105 | ad->pa_rec_map = l->channel_map; |
106 | ad->pa_status++; |
107 | } |
108 | |
109 | void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { |
110 | ERR_FAIL_NULL_MSG(i, "PulseAudio server info is null." ); |
111 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
112 | |
113 | ad->default_input_device = i->default_source_name; |
114 | ad->default_output_device = i->default_sink_name; |
115 | ad->pa_status++; |
116 | } |
117 | |
118 | Error AudioDriverPulseAudio::detect_channels(bool input) { |
119 | pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map); |
120 | |
121 | String device = input ? input_device_name : output_device_name; |
122 | if (device == "Default" ) { |
123 | // Get the default output device name |
124 | pa_status = 0; |
125 | pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); |
126 | if (pa_op) { |
127 | while (pa_status == 0) { |
128 | int ret = pa_mainloop_iterate(pa_ml, 1, nullptr); |
129 | if (ret < 0) { |
130 | ERR_PRINT("pa_mainloop_iterate error" ); |
131 | } |
132 | } |
133 | |
134 | pa_operation_unref(pa_op); |
135 | } else { |
136 | ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); |
137 | return FAILED; |
138 | } |
139 | } |
140 | |
141 | char dev[1024]; |
142 | if (device == "Default" ) { |
143 | strcpy(dev, input ? default_input_device.utf8().get_data() : default_output_device.utf8().get_data()); |
144 | } else { |
145 | strcpy(dev, device.utf8().get_data()); |
146 | } |
147 | print_verbose("PulseAudio: Detecting channels for device: " + String(dev)); |
148 | |
149 | // Now using the device name get the amount of channels |
150 | pa_status = 0; |
151 | pa_operation *pa_op; |
152 | if (input) { |
153 | pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); |
154 | } else { |
155 | pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); |
156 | } |
157 | |
158 | if (pa_op) { |
159 | while (pa_status == 0) { |
160 | int ret = pa_mainloop_iterate(pa_ml, 1, nullptr); |
161 | if (ret < 0) { |
162 | ERR_PRINT("pa_mainloop_iterate error" ); |
163 | } |
164 | } |
165 | |
166 | pa_operation_unref(pa_op); |
167 | |
168 | if (pa_status == -1) { |
169 | return FAILED; |
170 | } |
171 | } else { |
172 | if (input) { |
173 | ERR_PRINT("pa_context_get_source_info_by_name error" ); |
174 | } else { |
175 | ERR_PRINT("pa_context_get_sink_info_by_name error" ); |
176 | } |
177 | } |
178 | |
179 | return OK; |
180 | } |
181 | |
182 | Error AudioDriverPulseAudio::init_output_device() { |
183 | // If there is a specified output device, check that it is really present |
184 | if (output_device_name != "Default" ) { |
185 | PackedStringArray list = get_output_device_list(); |
186 | if (list.find(output_device_name) == -1) { |
187 | output_device_name = "Default" ; |
188 | new_output_device = "Default" ; |
189 | } |
190 | } |
191 | |
192 | // Detect the amount of channels PulseAudio is using |
193 | // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal, |
194 | // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels |
195 | // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1) |
196 | Error err = detect_channels(); |
197 | if (err != OK) { |
198 | // This most likely means there are no sinks. |
199 | ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels" ); |
200 | return err; |
201 | } |
202 | |
203 | switch (pa_map.channels) { |
204 | case 1: // Mono |
205 | case 3: // Surround 2.1 |
206 | case 5: // Surround 5.0 |
207 | case 7: // Surround 7.0 |
208 | channels = pa_map.channels + 1; |
209 | break; |
210 | |
211 | case 2: // Stereo |
212 | case 4: // Surround 4.0 |
213 | case 6: // Surround 5.1 |
214 | case 8: // Surround 7.1 |
215 | channels = pa_map.channels; |
216 | break; |
217 | |
218 | default: |
219 | WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels)); |
220 | pa_channel_map_init_stereo(&pa_map); |
221 | channels = 2; |
222 | break; |
223 | } |
224 | |
225 | int tmp_latency = Engine::get_singleton()->get_audio_output_latency(); |
226 | buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000); |
227 | pa_buffer_size = buffer_frames * pa_map.channels; |
228 | |
229 | print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels" ); |
230 | print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms" ); |
231 | |
232 | pa_sample_spec spec; |
233 | spec.format = PA_SAMPLE_S16LE; |
234 | spec.channels = pa_map.channels; |
235 | spec.rate = mix_rate; |
236 | pa_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; |
237 | pa_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; |
238 | pa_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; |
239 | pa_map.map[3] = PA_CHANNEL_POSITION_LFE; |
240 | pa_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT; |
241 | pa_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; |
242 | pa_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; |
243 | pa_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; |
244 | |
245 | pa_str = pa_stream_new(pa_ctx, "Sound" , &spec, &pa_map); |
246 | if (pa_str == nullptr) { |
247 | ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); |
248 | ERR_FAIL_V(ERR_CANT_OPEN); |
249 | } |
250 | |
251 | pa_buffer_attr attr; |
252 | // set to appropriate buffer length (in bytes) from global settings |
253 | // Note: PulseAudio defaults to 4 fragments, which means that the actual |
254 | // latency is tlength / fragments. It seems that the PulseAudio has no way |
255 | // to get the fragments number so we're hardcoding this to the default of 4 |
256 | const int fragments = 4; |
257 | attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments; |
258 | // set them to be automatically chosen |
259 | attr.prebuf = (uint32_t)-1; |
260 | attr.maxlength = (uint32_t)-1; |
261 | attr.minreq = (uint32_t)-1; |
262 | |
263 | const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data(); |
264 | pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); |
265 | int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr); |
266 | ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); |
267 | |
268 | samples_in.resize(buffer_frames * channels); |
269 | samples_out.resize(pa_buffer_size); |
270 | |
271 | // Reset audio input to keep synchronization. |
272 | input_position = 0; |
273 | input_size = 0; |
274 | |
275 | return OK; |
276 | } |
277 | |
278 | Error AudioDriverPulseAudio::init() { |
279 | #ifdef SOWRAP_ENABLED |
280 | #ifdef DEBUG_ENABLED |
281 | int dylibloader_verbose = 1; |
282 | #else |
283 | int dylibloader_verbose = 0; |
284 | #endif |
285 | #ifdef ALSAMIDI_ENABLED |
286 | // If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well |
287 | initialize_asound(dylibloader_verbose); |
288 | #endif |
289 | if (initialize_pulse(dylibloader_verbose)) { |
290 | return ERR_CANT_OPEN; |
291 | } |
292 | #endif |
293 | bool ver_ok = false; |
294 | String version = String::utf8(pa_get_library_version()); |
295 | Vector<String> ver_parts = version.split("." ); |
296 | if (ver_parts.size() >= 2) { |
297 | ver_ok = (ver_parts[0].to_int() >= 8); // 8.0.0 |
298 | } |
299 | print_verbose(vformat("PulseAudio %s detected." , version)); |
300 | if (!ver_ok) { |
301 | print_verbose("Unsupported PulseAudio library version!" ); |
302 | return ERR_CANT_OPEN; |
303 | } |
304 | |
305 | active.clear(); |
306 | exit_thread.clear(); |
307 | |
308 | mix_rate = _get_configured_mix_rate(); |
309 | |
310 | pa_ml = pa_mainloop_new(); |
311 | ERR_FAIL_NULL_V(pa_ml, ERR_CANT_OPEN); |
312 | |
313 | String context_name; |
314 | if (Engine::get_singleton()->is_editor_hint()) { |
315 | context_name = VERSION_NAME " Editor" ; |
316 | } else { |
317 | context_name = GLOBAL_GET("application/config/name" ); |
318 | if (context_name.is_empty()) { |
319 | context_name = VERSION_NAME " Project" ; |
320 | } |
321 | } |
322 | |
323 | pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr()); |
324 | ERR_FAIL_NULL_V(pa_ctx, ERR_CANT_OPEN); |
325 | |
326 | pa_ready = 0; |
327 | pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this); |
328 | |
329 | int ret = pa_context_connect(pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr); |
330 | if (ret < 0) { |
331 | if (pa_ctx) { |
332 | pa_context_unref(pa_ctx); |
333 | pa_ctx = nullptr; |
334 | } |
335 | |
336 | if (pa_ml) { |
337 | pa_mainloop_free(pa_ml); |
338 | pa_ml = nullptr; |
339 | } |
340 | |
341 | return ERR_CANT_OPEN; |
342 | } |
343 | |
344 | while (pa_ready == 0) { |
345 | ret = pa_mainloop_iterate(pa_ml, 1, nullptr); |
346 | if (ret < 0) { |
347 | ERR_PRINT("pa_mainloop_iterate error" ); |
348 | } |
349 | } |
350 | |
351 | if (pa_ready < 0) { |
352 | if (pa_ctx) { |
353 | pa_context_disconnect(pa_ctx); |
354 | pa_context_unref(pa_ctx); |
355 | pa_ctx = nullptr; |
356 | } |
357 | |
358 | if (pa_ml) { |
359 | pa_mainloop_free(pa_ml); |
360 | pa_ml = nullptr; |
361 | } |
362 | |
363 | return ERR_CANT_OPEN; |
364 | } |
365 | |
366 | init_output_device(); |
367 | thread.start(AudioDriverPulseAudio::thread_func, this); |
368 | |
369 | return OK; |
370 | } |
371 | |
372 | float AudioDriverPulseAudio::get_latency() { |
373 | lock(); |
374 | |
375 | pa_usec_t pa_lat = 0; |
376 | if (pa_stream_get_state(pa_str) == PA_STREAM_READY) { |
377 | int negative = 0; |
378 | |
379 | if (pa_stream_get_latency(pa_str, &pa_lat, &negative) >= 0) { |
380 | if (negative) { |
381 | pa_lat = 0; |
382 | } |
383 | } |
384 | } |
385 | |
386 | if (pa_lat > 0) { |
387 | latency = double(pa_lat) / 1000000.0; |
388 | } |
389 | |
390 | unlock(); |
391 | return latency; |
392 | } |
393 | |
394 | void AudioDriverPulseAudio::thread_func(void *p_udata) { |
395 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(p_udata); |
396 | unsigned int write_ofs = 0; |
397 | size_t avail_bytes = 0; |
398 | uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec(); |
399 | |
400 | while (!ad->exit_thread.is_set()) { |
401 | size_t read_bytes = 0; |
402 | size_t written_bytes = 0; |
403 | |
404 | if (avail_bytes == 0) { |
405 | ad->lock(); |
406 | ad->start_counting_ticks(); |
407 | |
408 | if (!ad->active.is_set()) { |
409 | ad->samples_out.fill(0); |
410 | } else { |
411 | ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); |
412 | |
413 | int16_t *out_ptr = ad->samples_out.ptrw(); |
414 | |
415 | if (ad->channels == ad->pa_map.channels) { |
416 | for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { |
417 | out_ptr[i] = ad->samples_in[i] >> 16; |
418 | } |
419 | } else { |
420 | // Uneven amount of channels |
421 | unsigned int in_idx = 0; |
422 | unsigned int out_idx = 0; |
423 | |
424 | for (unsigned int i = 0; i < ad->buffer_frames; i++) { |
425 | for (int j = 0; j < ad->pa_map.channels - 1; j++) { |
426 | out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16; |
427 | } |
428 | uint32_t l = ad->samples_in[in_idx++] >> 16; |
429 | uint32_t r = ad->samples_in[in_idx++] >> 16; |
430 | out_ptr[out_idx++] = (l + r) / 2; |
431 | } |
432 | } |
433 | } |
434 | |
435 | avail_bytes = ad->pa_buffer_size * sizeof(int16_t); |
436 | write_ofs = 0; |
437 | ad->stop_counting_ticks(); |
438 | ad->unlock(); |
439 | } |
440 | |
441 | ad->lock(); |
442 | ad->start_counting_ticks(); |
443 | |
444 | int ret; |
445 | do { |
446 | ret = pa_mainloop_iterate(ad->pa_ml, 0, nullptr); |
447 | } while (ret > 0); |
448 | |
449 | if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { |
450 | size_t bytes = pa_stream_writable_size(ad->pa_str); |
451 | if (bytes > 0) { |
452 | size_t bytes_to_write = MIN(bytes, avail_bytes); |
453 | const void *ptr = ad->samples_out.ptr(); |
454 | ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, nullptr, 0LL, PA_SEEK_RELATIVE); |
455 | if (ret != 0) { |
456 | ERR_PRINT("PulseAudio: pa_stream_write error: " + String(pa_strerror(ret))); |
457 | } else { |
458 | avail_bytes -= bytes_to_write; |
459 | write_ofs += bytes_to_write; |
460 | written_bytes += bytes_to_write; |
461 | } |
462 | } |
463 | } |
464 | |
465 | // User selected a new output device, finish the current one so we'll init the new output device |
466 | if (ad->output_device_name != ad->new_output_device) { |
467 | ad->output_device_name = ad->new_output_device; |
468 | ad->finish_output_device(); |
469 | |
470 | Error err = ad->init_output_device(); |
471 | if (err != OK) { |
472 | ERR_PRINT("PulseAudio: init_output_device error" ); |
473 | ad->output_device_name = "Default" ; |
474 | ad->new_output_device = "Default" ; |
475 | |
476 | err = ad->init_output_device(); |
477 | if (err != OK) { |
478 | ad->active.clear(); |
479 | ad->exit_thread.set(); |
480 | break; |
481 | } |
482 | } |
483 | |
484 | avail_bytes = 0; |
485 | write_ofs = 0; |
486 | } |
487 | |
488 | // If we're using the default output device, check that the current output device is still the default |
489 | if (ad->output_device_name == "Default" ) { |
490 | uint64_t msec = OS::get_singleton()->get_ticks_msec(); |
491 | if (msec > (default_device_msec + 1000)) { |
492 | String old_default_device = ad->default_output_device; |
493 | |
494 | default_device_msec = msec; |
495 | |
496 | ad->pa_status = 0; |
497 | pa_operation *pa_op = pa_context_get_server_info(ad->pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)ad); |
498 | if (pa_op) { |
499 | while (ad->pa_status == 0) { |
500 | ret = pa_mainloop_iterate(ad->pa_ml, 1, nullptr); |
501 | if (ret < 0) { |
502 | ERR_PRINT("pa_mainloop_iterate error" ); |
503 | } |
504 | } |
505 | |
506 | pa_operation_unref(pa_op); |
507 | } else { |
508 | ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx)))); |
509 | } |
510 | |
511 | if (old_default_device != ad->default_output_device) { |
512 | ad->finish_output_device(); |
513 | |
514 | Error err = ad->init_output_device(); |
515 | if (err != OK) { |
516 | ERR_PRINT("PulseAudio: init_output_device error" ); |
517 | ad->active.clear(); |
518 | ad->exit_thread.set(); |
519 | break; |
520 | } |
521 | |
522 | avail_bytes = 0; |
523 | write_ofs = 0; |
524 | } |
525 | } |
526 | } |
527 | |
528 | if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) { |
529 | size_t bytes = pa_stream_readable_size(ad->pa_rec_str); |
530 | if (bytes > 0) { |
531 | const void *ptr = nullptr; |
532 | size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t); |
533 | |
534 | bytes = MIN(bytes, maxbytes); |
535 | ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes); |
536 | if (ret != 0) { |
537 | ERR_PRINT("pa_stream_peek error" ); |
538 | } else { |
539 | int16_t *srcptr = (int16_t *)ptr; |
540 | for (size_t i = bytes >> 1; i > 0; i--) { |
541 | int32_t sample = int32_t(*srcptr++) << 16; |
542 | ad->input_buffer_write(sample); |
543 | |
544 | if (ad->pa_rec_map.channels == 1) { |
545 | // In case input device is single channel convert it to Stereo |
546 | ad->input_buffer_write(sample); |
547 | } |
548 | } |
549 | |
550 | read_bytes += bytes; |
551 | ret = pa_stream_drop(ad->pa_rec_str); |
552 | if (ret != 0) { |
553 | ERR_PRINT("pa_stream_drop error" ); |
554 | } |
555 | } |
556 | } |
557 | |
558 | // User selected a new input device, finish the current one so we'll init the new input device |
559 | if (ad->input_device_name != ad->new_input_device) { |
560 | ad->input_device_name = ad->new_input_device; |
561 | ad->finish_input_device(); |
562 | |
563 | Error err = ad->init_input_device(); |
564 | if (err != OK) { |
565 | ERR_PRINT("PulseAudio: init_input_device error" ); |
566 | ad->input_device_name = "Default" ; |
567 | ad->new_input_device = "Default" ; |
568 | |
569 | err = ad->init_input_device(); |
570 | if (err != OK) { |
571 | ad->active.clear(); |
572 | ad->exit_thread.set(); |
573 | break; |
574 | } |
575 | } |
576 | } |
577 | } |
578 | |
579 | ad->stop_counting_ticks(); |
580 | ad->unlock(); |
581 | |
582 | // Let the thread rest a while if we haven't read or write anything |
583 | if (written_bytes == 0 && read_bytes == 0) { |
584 | OS::get_singleton()->delay_usec(1000); |
585 | } |
586 | } |
587 | } |
588 | |
589 | void AudioDriverPulseAudio::start() { |
590 | active.set(); |
591 | } |
592 | |
593 | int AudioDriverPulseAudio::get_mix_rate() const { |
594 | return mix_rate; |
595 | } |
596 | |
597 | AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { |
598 | return get_speaker_mode_by_total_channels(channels); |
599 | } |
600 | |
601 | void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { |
602 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
603 | |
604 | // If eol is set to a positive number, you're at the end of the list |
605 | if (eol > 0) { |
606 | return; |
607 | } |
608 | |
609 | ad->pa_devices.push_back(l->name); |
610 | ad->pa_status++; |
611 | } |
612 | |
613 | PackedStringArray AudioDriverPulseAudio::get_output_device_list() { |
614 | pa_devices.clear(); |
615 | pa_devices.push_back("Default" ); |
616 | |
617 | if (pa_ctx == nullptr) { |
618 | return pa_devices; |
619 | } |
620 | |
621 | lock(); |
622 | |
623 | // Get the output device list |
624 | pa_status = 0; |
625 | pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this); |
626 | if (pa_op) { |
627 | while (pa_status == 0) { |
628 | int ret = pa_mainloop_iterate(pa_ml, 1, nullptr); |
629 | if (ret < 0) { |
630 | ERR_PRINT("pa_mainloop_iterate error" ); |
631 | } |
632 | } |
633 | |
634 | pa_operation_unref(pa_op); |
635 | } else { |
636 | ERR_PRINT("pa_context_get_server_info error" ); |
637 | } |
638 | |
639 | unlock(); |
640 | |
641 | return pa_devices; |
642 | } |
643 | |
644 | String AudioDriverPulseAudio::get_output_device() { |
645 | return output_device_name; |
646 | } |
647 | |
648 | void AudioDriverPulseAudio::set_output_device(const String &p_name) { |
649 | lock(); |
650 | new_output_device = p_name; |
651 | unlock(); |
652 | } |
653 | |
654 | void AudioDriverPulseAudio::lock() { |
655 | mutex.lock(); |
656 | } |
657 | |
658 | void AudioDriverPulseAudio::unlock() { |
659 | mutex.unlock(); |
660 | } |
661 | |
662 | void AudioDriverPulseAudio::finish_output_device() { |
663 | if (pa_str) { |
664 | pa_stream_disconnect(pa_str); |
665 | pa_stream_unref(pa_str); |
666 | pa_str = nullptr; |
667 | } |
668 | } |
669 | |
670 | void AudioDriverPulseAudio::finish() { |
671 | if (!thread.is_started()) { |
672 | return; |
673 | } |
674 | |
675 | exit_thread.set(); |
676 | if (thread.is_started()) { |
677 | thread.wait_to_finish(); |
678 | } |
679 | |
680 | finish_output_device(); |
681 | |
682 | if (pa_ctx) { |
683 | pa_context_disconnect(pa_ctx); |
684 | pa_context_unref(pa_ctx); |
685 | pa_ctx = nullptr; |
686 | } |
687 | |
688 | if (pa_ml) { |
689 | pa_mainloop_free(pa_ml); |
690 | pa_ml = nullptr; |
691 | } |
692 | } |
693 | |
694 | Error AudioDriverPulseAudio::init_input_device() { |
695 | // If there is a specified input device, check that it is really present |
696 | if (input_device_name != "Default" ) { |
697 | PackedStringArray list = get_input_device_list(); |
698 | if (list.find(input_device_name) == -1) { |
699 | input_device_name = "Default" ; |
700 | new_input_device = "Default" ; |
701 | } |
702 | } |
703 | |
704 | detect_channels(true); |
705 | switch (pa_rec_map.channels) { |
706 | case 1: // Mono |
707 | case 2: // Stereo |
708 | break; |
709 | |
710 | default: |
711 | WARN_PRINT("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels)); |
712 | pa_channel_map_init_stereo(&pa_rec_map); |
713 | break; |
714 | } |
715 | |
716 | print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels" ); |
717 | |
718 | pa_sample_spec spec; |
719 | |
720 | spec.format = PA_SAMPLE_S16LE; |
721 | spec.channels = pa_rec_map.channels; |
722 | spec.rate = mix_rate; |
723 | |
724 | int input_latency = 30; |
725 | int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000); |
726 | int input_buffer_size = input_buffer_frames * spec.channels; |
727 | |
728 | pa_buffer_attr attr; |
729 | attr.fragsize = input_buffer_size * sizeof(int16_t); |
730 | |
731 | pa_rec_str = pa_stream_new(pa_ctx, "Record" , &spec, &pa_rec_map); |
732 | if (pa_rec_str == nullptr) { |
733 | ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); |
734 | ERR_FAIL_V(ERR_CANT_OPEN); |
735 | } |
736 | |
737 | const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data(); |
738 | pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); |
739 | int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags); |
740 | if (error_code < 0) { |
741 | ERR_PRINT("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code))); |
742 | ERR_FAIL_V(ERR_CANT_OPEN); |
743 | } |
744 | |
745 | input_buffer_init(input_buffer_frames); |
746 | |
747 | print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels" ); |
748 | print_verbose("PulseAudio: input buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms" ); |
749 | |
750 | return OK; |
751 | } |
752 | |
753 | void AudioDriverPulseAudio::finish_input_device() { |
754 | if (pa_rec_str) { |
755 | int ret = pa_stream_disconnect(pa_rec_str); |
756 | if (ret != 0) { |
757 | ERR_PRINT("PulseAudio: pa_stream_disconnect error: " + String(pa_strerror(ret))); |
758 | } |
759 | pa_stream_unref(pa_rec_str); |
760 | pa_rec_str = nullptr; |
761 | } |
762 | } |
763 | |
764 | Error AudioDriverPulseAudio::input_start() { |
765 | lock(); |
766 | Error err = init_input_device(); |
767 | unlock(); |
768 | |
769 | return err; |
770 | } |
771 | |
772 | Error AudioDriverPulseAudio::input_stop() { |
773 | lock(); |
774 | finish_input_device(); |
775 | unlock(); |
776 | |
777 | return OK; |
778 | } |
779 | |
780 | void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { |
781 | AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); |
782 | |
783 | // If eol is set to a positive number, you're at the end of the list |
784 | if (eol > 0) { |
785 | return; |
786 | } |
787 | |
788 | if (l->monitor_of_sink == PA_INVALID_INDEX) { |
789 | ad->pa_rec_devices.push_back(l->name); |
790 | } |
791 | |
792 | ad->pa_status++; |
793 | } |
794 | |
795 | PackedStringArray AudioDriverPulseAudio::get_input_device_list() { |
796 | pa_rec_devices.clear(); |
797 | pa_rec_devices.push_back("Default" ); |
798 | |
799 | if (pa_ctx == nullptr) { |
800 | return pa_rec_devices; |
801 | } |
802 | |
803 | lock(); |
804 | |
805 | // Get the device list |
806 | pa_status = 0; |
807 | pa_operation *pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, (void *)this); |
808 | if (pa_op) { |
809 | while (pa_status == 0) { |
810 | int ret = pa_mainloop_iterate(pa_ml, 1, nullptr); |
811 | if (ret < 0) { |
812 | ERR_PRINT("pa_mainloop_iterate error" ); |
813 | } |
814 | } |
815 | |
816 | pa_operation_unref(pa_op); |
817 | } else { |
818 | ERR_PRINT("pa_context_get_server_info error" ); |
819 | } |
820 | |
821 | unlock(); |
822 | |
823 | return pa_rec_devices; |
824 | } |
825 | |
826 | String AudioDriverPulseAudio::get_input_device() { |
827 | lock(); |
828 | String name = input_device_name; |
829 | unlock(); |
830 | |
831 | return name; |
832 | } |
833 | |
834 | void AudioDriverPulseAudio::set_input_device(const String &p_name) { |
835 | lock(); |
836 | new_input_device = p_name; |
837 | unlock(); |
838 | } |
839 | |
840 | AudioDriverPulseAudio::AudioDriverPulseAudio() { |
841 | samples_in.clear(); |
842 | samples_out.clear(); |
843 | } |
844 | |
845 | #endif // PULSEAUDIO_ENABLED |
846 | |