Path: blob/master/drivers/pulseaudio/audio_driver_pulseaudio.cpp
9973 views
/**************************************************************************/1/* audio_driver_pulseaudio.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "audio_driver_pulseaudio.h"3132#ifdef PULSEAUDIO_ENABLED3334#include "core/config/project_settings.h"35#include "core/os/os.h"36#include "core/version.h"3738#ifdef ALSAMIDI_ENABLED39#ifdef SOWRAP_ENABLED40#include "drivers/alsa/asound-so_wrap.h"41#else42#include <alsa/asoundlib.h>43#endif44#endif4546void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {47AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);4849switch (pa_context_get_state(c)) {50case PA_CONTEXT_TERMINATED:51print_verbose("PulseAudio: context terminated");52ad->pa_ready = -1;53break;54case PA_CONTEXT_FAILED:55print_verbose("PulseAudio: context failed");56ad->pa_ready = -1;57break;58case PA_CONTEXT_READY:59print_verbose("PulseAudio: context ready");60ad->pa_ready = 1;61break;62default:63print_verbose("PulseAudio: context other");64// TODO: Check if we want to handle some of the other65// PA context states like PA_CONTEXT_UNCONNECTED.66break;67}68}6970void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {71AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);7273// If eol is set to a positive number, you're at the end of the list74if (eol > 0) {75return;76}7778// If eol is set to a negative number there's an error.79if (eol < 0) {80ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));81ad->pa_status--;82return;83}8485ad->pa_map = l->channel_map;86ad->pa_status++;87}8889void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {90AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);9192// If eol is set to a positive number, you're at the end of the list93if (eol > 0) {94return;95}9697// If eol is set to a negative number there's an error.98if (eol < 0) {99ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));100ad->pa_status--;101return;102}103104ad->pa_rec_map = l->channel_map;105ad->pa_status++;106}107108void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {109ERR_FAIL_NULL_MSG(i, "PulseAudio server info is null.");110AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);111112ad->default_input_device = i->default_source_name;113ad->default_output_device = i->default_sink_name;114ad->pa_status++;115}116117Error AudioDriverPulseAudio::detect_channels(bool input) {118pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map);119120String device = input ? input_device_name : output_device_name;121if (device == "Default") {122// Get the default output device name123pa_status = 0;124pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this);125if (pa_op) {126while (pa_status == 0) {127int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);128if (ret < 0) {129ERR_PRINT("pa_mainloop_iterate error");130}131}132133pa_operation_unref(pa_op);134} else {135ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(pa_ctx))));136return FAILED;137}138}139140if (device == "Default") {141device = input ? default_input_device : default_output_device;142}143print_verbose("PulseAudio: Detecting channels for device: " + device);144145CharString device_utf8 = device.utf8();146147// Now using the device name get the amount of channels148pa_status = 0;149pa_operation *pa_op;150if (input) {151pa_op = pa_context_get_source_info_by_name(pa_ctx, device_utf8.get_data(), &AudioDriverPulseAudio::pa_source_info_cb, (void *)this);152} else {153pa_op = pa_context_get_sink_info_by_name(pa_ctx, device_utf8.get_data(), &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this);154}155156if (pa_op) {157while (pa_status == 0) {158int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);159if (ret < 0) {160ERR_PRINT("pa_mainloop_iterate error");161}162}163164pa_operation_unref(pa_op);165166if (pa_status == -1) {167return FAILED;168}169} else {170if (input) {171ERR_PRINT("pa_context_get_source_info_by_name error");172} else {173ERR_PRINT("pa_context_get_sink_info_by_name error");174}175}176177return OK;178}179180Error AudioDriverPulseAudio::init_output_device() {181// If there is a specified output device, check that it is really present182if (output_device_name != "Default") {183PackedStringArray list = get_output_device_list();184if (!list.has(output_device_name)) {185output_device_name = "Default";186new_output_device = "Default";187}188}189190// Detect the amount of channels PulseAudio is using191// Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,192// if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels193// will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)194Error err = detect_channels();195if (err != OK) {196// This most likely means there are no sinks.197ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels");198return err;199}200201switch (pa_map.channels) {202case 1: // Mono203case 3: // Surround 2.1204case 5: // Surround 5.0205case 7: // Surround 7.0206channels = pa_map.channels + 1;207break;208209case 2: // Stereo210case 4: // Surround 4.0211case 6: // Surround 5.1212case 8: // Surround 7.1213channels = pa_map.channels;214break;215216default:217WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));218pa_channel_map_init_stereo(&pa_map);219channels = 2;220break;221}222223int tmp_latency = Engine::get_singleton()->get_audio_output_latency();224buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000);225pa_buffer_size = buffer_frames * pa_map.channels;226227print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");228print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");229230pa_sample_spec spec;231spec.format = PA_SAMPLE_S16LE;232spec.channels = pa_map.channels;233spec.rate = mix_rate;234pa_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;235pa_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;236pa_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;237pa_map.map[3] = PA_CHANNEL_POSITION_LFE;238pa_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;239pa_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;240pa_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;241pa_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;242243pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);244if (pa_str == nullptr) {245ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));246ERR_FAIL_V(ERR_CANT_OPEN);247}248249pa_buffer_attr attr;250// set to appropriate buffer length (in bytes) from global settings251// Note: PulseAudio defaults to 4 fragments, which means that the actual252// latency is tlength / fragments. It seems that the PulseAudio has no way253// to get the fragments number so we're hardcoding this to the default of 4254const int fragments = 4;255attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments;256// set them to be automatically chosen257attr.prebuf = (uint32_t)-1;258attr.maxlength = (uint32_t)-1;259attr.minreq = (uint32_t)-1;260261const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data();262pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);263int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr);264ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN);265266samples_in.resize(buffer_frames * channels);267samples_out.resize(pa_buffer_size);268269// Reset audio input to keep synchronization.270input_position = 0;271input_size = 0;272273return OK;274}275276Error AudioDriverPulseAudio::init() {277#ifdef SOWRAP_ENABLED278#ifdef DEBUG_ENABLED279int dylibloader_verbose = 1;280#else281int dylibloader_verbose = 0;282#endif283#ifdef ALSAMIDI_ENABLED284// If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well285initialize_asound(dylibloader_verbose);286#endif287if (initialize_pulse(dylibloader_verbose)) {288return ERR_CANT_OPEN;289}290#endif291bool ver_ok = false;292String version = String::utf8(pa_get_library_version());293Vector<String> ver_parts = version.split(".");294if (ver_parts.size() >= 2) {295ver_ok = (ver_parts[0].to_int() >= 8); // 8.0.0296}297print_verbose(vformat("PulseAudio %s detected.", version));298if (!ver_ok) {299print_verbose("Unsupported PulseAudio library version!");300return ERR_CANT_OPEN;301}302303active.clear();304exit_thread.clear();305306mix_rate = _get_configured_mix_rate();307308pa_ml = pa_mainloop_new();309ERR_FAIL_NULL_V(pa_ml, ERR_CANT_OPEN);310311String context_name;312if (Engine::get_singleton()->is_editor_hint()) {313context_name = GODOT_VERSION_NAME " Editor";314} else {315context_name = GLOBAL_GET("application/config/name");316if (context_name.is_empty()) {317context_name = GODOT_VERSION_NAME " Project";318}319}320321pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr());322ERR_FAIL_NULL_V(pa_ctx, ERR_CANT_OPEN);323324pa_ready = 0;325pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this);326327int ret = pa_context_connect(pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);328if (ret < 0) {329if (pa_ctx) {330pa_context_unref(pa_ctx);331pa_ctx = nullptr;332}333334if (pa_ml) {335pa_mainloop_free(pa_ml);336pa_ml = nullptr;337}338339return ERR_CANT_OPEN;340}341342while (pa_ready == 0) {343ret = pa_mainloop_iterate(pa_ml, 1, nullptr);344if (ret < 0) {345ERR_PRINT("pa_mainloop_iterate error");346}347}348349if (pa_ready < 0) {350if (pa_ctx) {351pa_context_disconnect(pa_ctx);352pa_context_unref(pa_ctx);353pa_ctx = nullptr;354}355356if (pa_ml) {357pa_mainloop_free(pa_ml);358pa_ml = nullptr;359}360361return ERR_CANT_OPEN;362}363364init_output_device();365thread.start(AudioDriverPulseAudio::thread_func, this);366367return OK;368}369370float AudioDriverPulseAudio::get_latency() {371lock();372373pa_usec_t pa_lat = 0;374if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {375int negative = 0;376377if (pa_stream_get_latency(pa_str, &pa_lat, &negative) >= 0) {378if (negative) {379pa_lat = 0;380}381}382}383384if (pa_lat > 0) {385latency = double(pa_lat) / 1000000.0;386}387388unlock();389return latency;390}391392void AudioDriverPulseAudio::thread_func(void *p_udata) {393AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(p_udata);394unsigned int write_ofs = 0;395size_t avail_bytes = 0;396uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();397398while (!ad->exit_thread.is_set()) {399size_t read_bytes = 0;400size_t written_bytes = 0;401402if (avail_bytes == 0) {403ad->lock();404ad->start_counting_ticks();405406if (!ad->active.is_set()) {407ad->samples_out.fill(0);408} else {409ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());410411int16_t *out_ptr = ad->samples_out.ptrw();412413if (ad->channels == ad->pa_map.channels) {414for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {415out_ptr[i] = ad->samples_in[i] >> 16;416}417} else {418// Uneven amount of channels419unsigned int in_idx = 0;420unsigned int out_idx = 0;421422for (unsigned int i = 0; i < ad->buffer_frames; i++) {423for (int j = 0; j < ad->pa_map.channels - 1; j++) {424out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16;425}426uint32_t l = ad->samples_in[in_idx++] >> 16;427uint32_t r = ad->samples_in[in_idx++] >> 16;428out_ptr[out_idx++] = (l + r) / 2;429}430}431}432433avail_bytes = ad->pa_buffer_size * sizeof(int16_t);434write_ofs = 0;435ad->stop_counting_ticks();436ad->unlock();437}438439ad->lock();440ad->start_counting_ticks();441442int ret;443do {444ret = pa_mainloop_iterate(ad->pa_ml, 0, nullptr);445} while (ret > 0);446447if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {448size_t bytes = pa_stream_writable_size(ad->pa_str);449if (bytes > 0) {450size_t bytes_to_write = MIN(bytes, avail_bytes);451const void *ptr = ad->samples_out.ptr();452ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, nullptr, 0LL, PA_SEEK_RELATIVE);453if (ret != 0) {454ERR_PRINT("PulseAudio: pa_stream_write error: " + String(pa_strerror(ret)));455} else {456avail_bytes -= bytes_to_write;457write_ofs += bytes_to_write;458written_bytes += bytes_to_write;459}460}461}462463// User selected a new output device, finish the current one so we'll init the new output device464if (ad->output_device_name != ad->new_output_device) {465ad->output_device_name = ad->new_output_device;466ad->finish_output_device();467468Error err = ad->init_output_device();469if (err != OK) {470ERR_PRINT("PulseAudio: init_output_device error");471ad->output_device_name = "Default";472ad->new_output_device = "Default";473474err = ad->init_output_device();475if (err != OK) {476ad->active.clear();477ad->exit_thread.set();478break;479}480}481482avail_bytes = 0;483write_ofs = 0;484}485486// If we're using the default output device, check that the current output device is still the default487if (ad->output_device_name == "Default") {488uint64_t msec = OS::get_singleton()->get_ticks_msec();489if (msec > (default_device_msec + 1000)) {490String old_default_device = ad->default_output_device;491492default_device_msec = msec;493494ad->pa_status = 0;495pa_operation *pa_op = pa_context_get_server_info(ad->pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)ad);496if (pa_op) {497while (ad->pa_status == 0) {498ret = pa_mainloop_iterate(ad->pa_ml, 1, nullptr);499if (ret < 0) {500ERR_PRINT("pa_mainloop_iterate error");501}502}503504pa_operation_unref(pa_op);505} else {506ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx))));507}508509if (old_default_device != ad->default_output_device) {510ad->finish_output_device();511512Error err = ad->init_output_device();513if (err != OK) {514ERR_PRINT("PulseAudio: init_output_device error");515ad->active.clear();516ad->exit_thread.set();517break;518}519520avail_bytes = 0;521write_ofs = 0;522}523}524}525526if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) {527size_t bytes = pa_stream_readable_size(ad->pa_rec_str);528if (bytes > 0) {529const void *ptr = nullptr;530size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t);531532bytes = MIN(bytes, maxbytes);533ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes);534if (ret != 0) {535ERR_PRINT("pa_stream_peek error");536} else {537int16_t *srcptr = (int16_t *)ptr;538for (size_t i = bytes >> 1; i > 0; i--) {539int32_t sample = int32_t(*srcptr++) << 16;540ad->input_buffer_write(sample);541542if (ad->pa_rec_map.channels == 1) {543// In case input device is single channel convert it to Stereo544ad->input_buffer_write(sample);545}546}547548read_bytes += bytes;549ret = pa_stream_drop(ad->pa_rec_str);550if (ret != 0) {551ERR_PRINT("pa_stream_drop error");552}553}554}555556// User selected a new input device, finish the current one so we'll init the new input device557if (ad->input_device_name != ad->new_input_device) {558ad->input_device_name = ad->new_input_device;559ad->finish_input_device();560561Error err = ad->init_input_device();562if (err != OK) {563ERR_PRINT("PulseAudio: init_input_device error");564ad->input_device_name = "Default";565ad->new_input_device = "Default";566567err = ad->init_input_device();568if (err != OK) {569ad->active.clear();570ad->exit_thread.set();571break;572}573}574}575}576577ad->stop_counting_ticks();578ad->unlock();579580// Let the thread rest a while if we haven't read or write anything581if (written_bytes == 0 && read_bytes == 0) {582OS::get_singleton()->delay_usec(1000);583}584}585}586587void AudioDriverPulseAudio::start() {588active.set();589}590591int AudioDriverPulseAudio::get_mix_rate() const {592return mix_rate;593}594595AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const {596return get_speaker_mode_by_total_channels(channels);597}598599void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {600AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);601602// If eol is set to a positive number, you're at the end of the list603if (eol > 0) {604return;605}606607ad->pa_devices.push_back(l->name);608ad->pa_status++;609}610611PackedStringArray AudioDriverPulseAudio::get_output_device_list() {612pa_devices.clear();613pa_devices.push_back("Default");614615if (pa_ctx == nullptr) {616return pa_devices;617}618619lock();620621// Get the output device list622pa_status = 0;623pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this);624if (pa_op) {625while (pa_status == 0) {626int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);627if (ret < 0) {628ERR_PRINT("pa_mainloop_iterate error");629}630}631632pa_operation_unref(pa_op);633} else {634ERR_PRINT("pa_context_get_server_info error");635}636637unlock();638639return pa_devices;640}641642String AudioDriverPulseAudio::get_output_device() {643return output_device_name;644}645646void AudioDriverPulseAudio::set_output_device(const String &p_name) {647lock();648new_output_device = p_name;649unlock();650}651652void AudioDriverPulseAudio::lock() {653mutex.lock();654}655656void AudioDriverPulseAudio::unlock() {657mutex.unlock();658}659660void AudioDriverPulseAudio::finish_output_device() {661if (pa_str) {662pa_stream_disconnect(pa_str);663pa_stream_unref(pa_str);664pa_str = nullptr;665}666}667668void AudioDriverPulseAudio::finish() {669if (!thread.is_started()) {670return;671}672673exit_thread.set();674if (thread.is_started()) {675thread.wait_to_finish();676}677678finish_output_device();679680if (pa_ctx) {681pa_context_disconnect(pa_ctx);682pa_context_unref(pa_ctx);683pa_ctx = nullptr;684}685686if (pa_ml) {687pa_mainloop_free(pa_ml);688pa_ml = nullptr;689}690}691692Error AudioDriverPulseAudio::init_input_device() {693// If there is a specified input device, check that it is really present694if (input_device_name != "Default") {695PackedStringArray list = get_input_device_list();696if (!list.has(input_device_name)) {697input_device_name = "Default";698new_input_device = "Default";699}700}701702detect_channels(true);703switch (pa_rec_map.channels) {704case 1: // Mono705case 2: // Stereo706break;707708default:709WARN_PRINT("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels));710pa_channel_map_init_stereo(&pa_rec_map);711break;712}713714print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");715716pa_sample_spec spec;717718spec.format = PA_SAMPLE_S16LE;719spec.channels = pa_rec_map.channels;720spec.rate = mix_rate;721722int input_latency = 30;723int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000);724int input_buffer_size = input_buffer_frames * spec.channels;725726pa_buffer_attr attr = {};727attr.maxlength = (uint32_t)-1;728attr.fragsize = input_buffer_size * sizeof(int16_t);729730pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);731if (pa_rec_str == nullptr) {732ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));733ERR_FAIL_V(ERR_CANT_OPEN);734}735736const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data();737pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);738int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags);739if (error_code < 0) {740ERR_PRINT("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code)));741ERR_FAIL_V(ERR_CANT_OPEN);742}743744input_buffer_init(input_buffer_frames);745746print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");747print_verbose("PulseAudio: input buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms");748749return OK;750}751752void AudioDriverPulseAudio::finish_input_device() {753if (pa_rec_str) {754int ret = pa_stream_disconnect(pa_rec_str);755if (ret != 0) {756ERR_PRINT("PulseAudio: pa_stream_disconnect error: " + String(pa_strerror(ret)));757}758pa_stream_unref(pa_rec_str);759pa_rec_str = nullptr;760}761}762763Error AudioDriverPulseAudio::input_start() {764lock();765Error err = init_input_device();766unlock();767768return err;769}770771Error AudioDriverPulseAudio::input_stop() {772lock();773finish_input_device();774unlock();775776return OK;777}778779void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {780AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);781782// If eol is set to a positive number, you're at the end of the list783if (eol > 0) {784return;785}786787if (l->monitor_of_sink == PA_INVALID_INDEX) {788ad->pa_rec_devices.push_back(l->name);789}790791ad->pa_status++;792}793794PackedStringArray AudioDriverPulseAudio::get_input_device_list() {795pa_rec_devices.clear();796pa_rec_devices.push_back("Default");797798if (pa_ctx == nullptr) {799return pa_rec_devices;800}801802lock();803804// Get the device list805pa_status = 0;806pa_operation *pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, (void *)this);807if (pa_op) {808while (pa_status == 0) {809int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);810if (ret < 0) {811ERR_PRINT("pa_mainloop_iterate error");812}813}814815pa_operation_unref(pa_op);816} else {817ERR_PRINT("pa_context_get_server_info error");818}819820unlock();821822return pa_rec_devices;823}824825String AudioDriverPulseAudio::get_input_device() {826lock();827String name = input_device_name;828unlock();829830return name;831}832833void AudioDriverPulseAudio::set_input_device(const String &p_name) {834lock();835new_input_device = p_name;836unlock();837}838839AudioDriverPulseAudio::AudioDriverPulseAudio() {840samples_in.clear();841samples_out.clear();842}843844#endif // PULSEAUDIO_ENABLED845846847