summaryrefslogtreecommitdiff
path: root/drivers/pulseaudio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pulseaudio')
-rw-r--r--drivers/pulseaudio/SCsub2
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp156
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h5
3 files changed, 105 insertions, 58 deletions
diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub
index ee39fd2631..28b315ae66 100644
--- a/drivers/pulseaudio/SCsub
+++ b/drivers/pulseaudio/SCsub
@@ -3,5 +3,3 @@
Import('env')
env.add_source_files(env.drivers_sources, "*.cpp")
-
-Export('env')
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 7578fbc0a0..7ba2175652 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -43,10 +43,13 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
ad->pa_ready = -1;
break;
-
case PA_CONTEXT_READY:
ad->pa_ready = 1;
break;
+ default:
+ // TODO: Check if we want to handle some of the other
+ // PA context states like PA_CONTEXT_UNCONNECTED.
+ break;
}
}
@@ -188,6 +191,14 @@ Error AudioDriverPulseAudio::init_device() {
spec.format = PA_SAMPLE_S16LE;
spec.channels = pa_map.channels;
spec.rate = mix_rate;
+ pa_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ pa_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ pa_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ pa_map.map[3] = PA_CHANNEL_POSITION_LFE;
+ pa_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+ pa_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ pa_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ pa_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
if (pa_str == NULL) {
@@ -216,8 +227,8 @@ Error AudioDriverPulseAudio::init_device() {
samples_out.resize(pa_buffer_size);
// Reset audio input to keep synchronisation.
- input_position = 0;
- input_size = 0;
+ capture_position = 0;
+ capture_size = 0;
return OK;
}
@@ -255,7 +266,10 @@ Error AudioDriverPulseAudio::init() {
}
while (pa_ready == 0) {
- pa_mainloop_iterate(pa_ml, 1, NULL);
+ ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
}
if (pa_ready < 0) {
@@ -313,6 +327,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
unsigned int write_ofs = 0;
size_t avail_bytes = 0;
+ uint32_t default_device_msec = OS::get_singleton()->get_ticks_msec();
while (!ad->exit_thread) {
@@ -340,12 +355,12 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
unsigned int out_idx = 0;
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
- for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) {
+ for (int j = 0; j < ad->pa_map.channels - 1; j++) {
ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
- uint32_t l = ad->samples_in[in_idx++];
- uint32_t r = ad->samples_in[in_idx++];
- ad->samples_out.write[out_idx++] = ((l >> 1) + (r >> 1)) >> 16;
+ uint32_t l = ad->samples_in[in_idx++] >> 16;
+ uint32_t r = ad->samples_in[in_idx++] >> 16;
+ ad->samples_out.write[out_idx++] = (l + r) / 2;
}
}
}
@@ -371,7 +386,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
const void *ptr = ad->samples_out.ptr();
ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, NULL, 0LL, PA_SEEK_RELATIVE);
if (ret != 0) {
- ERR_PRINT("pa_stream_write error");
+ ERR_PRINTS("PulseAudio: pa_stream_write error: " + String(pa_strerror(ret)));
} else {
avail_bytes -= bytes_to_write;
write_ofs += bytes_to_write;
@@ -398,13 +413,57 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
break;
}
}
+
+ avail_bytes = 0;
+ write_ofs = 0;
+ }
+
+ // If we're using the default device check that the current device is still the default
+ if (ad->device_name == "Default") {
+
+ uint32_t msec = OS::get_singleton()->get_ticks_msec();
+ if (msec > (default_device_msec + 1000)) {
+ String old_default_device = ad->default_device;
+
+ default_device_msec = msec;
+
+ ad->pa_status = 0;
+ pa_operation *pa_op = pa_context_get_server_info(ad->pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)ad);
+ if (pa_op) {
+ while (ad->pa_status == 0) {
+ ret = pa_mainloop_iterate(ad->pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
+ }
+
+ pa_operation_unref(pa_op);
+ } else {
+ ERR_PRINT("pa_context_get_server_info error");
+ }
+
+ if (old_default_device != ad->default_device) {
+ ad->finish_device();
+
+ Error err = ad->init_device();
+ if (err != OK) {
+ ERR_PRINT("PulseAudio: init_device error");
+ ad->active = false;
+ ad->exit_thread = true;
+ break;
+ }
+
+ avail_bytes = 0;
+ write_ofs = 0;
+ }
+ }
}
if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) {
size_t bytes = pa_stream_readable_size(ad->pa_rec_str);
if (bytes > 0) {
const void *ptr = NULL;
- size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t);
+ size_t maxbytes = ad->capture_buffer.size() * sizeof(int16_t);
bytes = MIN(bytes, maxbytes);
ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes);
@@ -414,11 +473,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
int16_t *srcptr = (int16_t *)ptr;
for (size_t i = bytes >> 1; i > 0; i--) {
int32_t sample = int32_t(*srcptr++) << 16;
- ad->input_buffer_write(sample);
+ ad->capture_buffer_write(sample);
if (ad->pa_rec_map.channels == 1) {
- // In case input device is single channel convert it to Stereo
- ad->input_buffer_write(sample);
+ // In case capture device is single channel convert it to Stereo
+ ad->capture_buffer_write(sample);
}
}
@@ -605,25 +664,23 @@ Error AudioDriverPulseAudio::capture_init_device() {
break;
default:
- WARN_PRINTS("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels));
+ WARN_PRINTS("PulseAudio: Unsupported number of capture channels: " + itos(pa_rec_map.channels));
pa_channel_map_init_stereo(&pa_rec_map);
break;
}
- print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
-
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
spec.channels = pa_rec_map.channels;
spec.rate = mix_rate;
- int latency = 30;
- input_buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
- int buffer_size = input_buffer_frames * spec.channels;
+ int input_latency = 30;
+ int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000);
+ int input_buffer_size = input_buffer_frames * spec.channels;
pa_buffer_attr attr;
- attr.fragsize = buffer_size * sizeof(int16_t);
+ attr.fragsize = input_buffer_size * sizeof(int16_t);
pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);
if (pa_rec_str == NULL) {
@@ -639,9 +696,10 @@ Error AudioDriverPulseAudio::capture_init_device() {
ERR_FAIL_V(ERR_CANT_OPEN);
}
- input_buffer.resize(input_buffer_frames * 8);
- input_position = 0;
- input_size = 0;
+ capture_buffer_init(input_buffer_frames);
+
+ print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " capture channels");
+ print_verbose("PulseAudio: capture buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms");
return OK;
}
@@ -738,36 +796,28 @@ String AudioDriverPulseAudio::capture_get_device() {
return name;
}
-AudioDriverPulseAudio::AudioDriverPulseAudio() {
-
- pa_ml = NULL;
- pa_ctx = NULL;
- pa_str = NULL;
- pa_rec_str = NULL;
-
- mutex = NULL;
- thread = NULL;
-
- device_name = "Default";
- new_device = "Default";
- default_device = "";
-
+AudioDriverPulseAudio::AudioDriverPulseAudio() :
+ thread(NULL),
+ mutex(NULL),
+ pa_ml(NULL),
+ pa_ctx(NULL),
+ pa_str(NULL),
+ pa_rec_str(NULL),
+ device_name("Default"),
+ new_device("Default"),
+ default_device(""),
+ mix_rate(0),
+ buffer_frames(0),
+ pa_buffer_size(0),
+ channels(0),
+ pa_ready(0),
+ pa_status(0),
+ active(false),
+ thread_exited(false),
+ exit_thread(false),
+ latency(0) {
samples_in.clear();
samples_out.clear();
-
- mix_rate = 0;
- buffer_frames = 0;
- input_buffer_frames = 0;
- pa_buffer_size = 0;
- channels = 0;
- pa_ready = 0;
- pa_status = 0;
-
- active = false;
- thread_exited = false;
- exit_thread = false;
-
- latency = 0;
}
AudioDriverPulseAudio::~AudioDriverPulseAudio() {
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index f8358a452b..caa09d6020 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -64,7 +64,6 @@ class AudioDriverPulseAudio : public AudioDriver {
unsigned int mix_rate;
unsigned int buffer_frames;
- unsigned int input_buffer_frames;
unsigned int pa_buffer_size;
int channels;
int pa_ready;