Modified Microphone implementation to handle only one device at a time (WIP)

This commit is contained in:
Marcelo Fernandez 2018-07-03 22:08:43 -03:00 committed by Saracen
parent 76fd9d215c
commit 061358d838
22 changed files with 556 additions and 1018 deletions

View file

@ -287,74 +287,71 @@ float AudioDriverPulseAudio::get_latency() {
void AudioDriverPulseAudio::thread_func(void *p_udata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
unsigned int write_ofs = 0;
size_t avail_bytes = 0;
while (!ad->exit_thread) {
size_t read_bytes = 0;
size_t written_bytes = 0;
if (avail_bytes == 0) {
ad->lock();
ad->start_counting_ticks();
if (!ad->active) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out.write[i] = 0;
}
} else {
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out.write[i] = ad->samples_in[i] >> 16;
}
} else {
// Uneven amount of channels
unsigned int in_idx = 0;
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++) {
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;
}
}
}
avail_bytes = ad->pa_buffer_size * sizeof(int16_t);
write_ofs = 0;
ad->stop_counting_ticks();
ad->unlock();
}
ad->lock();
ad->start_counting_ticks();
if (!ad->active) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out.write[i] = 0;
}
} else {
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out.write[i] = ad->samples_in[i] >> 16;
}
} else {
// Uneven amount of channels
unsigned int in_idx = 0;
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++) {
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;
}
}
}
int error_code;
int byte_size = ad->pa_buffer_size * sizeof(int16_t);
int ret;
do {
ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
} while (ret > 0);
if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {
const void *ptr = ad->samples_out.ptr();
while (byte_size > 0) {
size_t bytes = pa_stream_writable_size(ad->pa_str);
if (bytes > 0) {
if (bytes > byte_size) {
bytes = byte_size;
}
ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE);
if (ret >= 0) {
byte_size -= bytes;
ptr = (const char *)ptr + bytes;
}
if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {
size_t bytes = pa_stream_writable_size(ad->pa_str);
if (bytes > 0) {
size_t bytes_to_write = MIN(bytes, avail_bytes);
const void *ptr = ad->samples_out.ptr();
ret = pa_stream_write(ad->pa_str, ptr + write_ofs, bytes_to_write, NULL, 0LL, PA_SEEK_RELATIVE);
if (ret != 0) {
ERR_PRINT("pa_stream_write error");
} else {
ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
if (ret == 0) {
// If pa_mainloop_iterate returns 0 sleep for 1 msec to wait
// for the stream to be able to process more bytes
ad->stop_counting_ticks();
ad->unlock();
OS::get_singleton()->delay_usec(1000);
ad->lock();
ad->start_counting_ticks();
}
avail_bytes -= bytes_to_write;
write_ofs += bytes_to_write;
written_bytes += bytes_to_write;
}
}
}
@ -379,8 +376,41 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
}
}
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->audio_input_buffer.size() * sizeof(int16_t);
bytes = MIN(bytes, maxbytes);
ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes);
if (ret != 0) {
ERR_PRINT("pa_stream_peek error");
} else {
int16_t *srcptr = (int16_t *)ptr;
for (size_t i = bytes >> 1; i > 0; i--) {
ad->audio_input_buffer.write[ad->audio_input_position++] = int32_t(*srcptr++) << 16;
if (ad->audio_input_position >= ad->audio_input_buffer.size()) {
ad->audio_input_position = 0;
}
}
read_bytes += bytes;
ret = pa_stream_drop(ad->pa_rec_str);
if (ret != 0) {
ERR_PRINT("pa_stream_drop error");
}
}
}
}
ad->stop_counting_ticks();
ad->unlock();
// Let the thread rest a while if we haven't read or write anything
if (written_bytes == 0 && read_bytes == 0) {
OS::get_singleton()->delay_usec(1000);
}
}
ad->thread_exited = true;
@ -510,26 +540,60 @@ void AudioDriverPulseAudio::finish() {
thread = NULL;
}
bool AudioDriverPulseAudio::capture_device_start(StringName p_name) {
Error AudioDriverPulseAudio::capture_start() {
return false;
Error err = OK;
lock();
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
spec.channels = 2;
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;
pa_buffer_attr attr;
attr.fragsize = buffer_size * sizeof(int16_t);
pa_channel_map pa_rec_map;
pa_channel_map_init_stereo(&pa_rec_map);
pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);
if (pa_rec_str == NULL) {
ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
ERR_FAIL_V(ERR_CANT_OPEN);
}
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
int error_code = pa_stream_connect_record(pa_rec_str, NULL, &attr, flags);
if (error_code < 0) {
ERR_PRINTS("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code)));
err = ERR_CANT_OPEN;
}
audio_input_buffer.resize(input_buffer_frames * 8);
for (int i = 0; i < audio_input_buffer.size(); i++) {
audio_input_buffer.write[i] = 0;
}
audio_input_position = 0;
unlock();
return err;
}
bool AudioDriverPulseAudio::capture_device_stop(StringName p_name) {
Error AudioDriverPulseAudio::capture_stop() {
if (pa_rec_str) {
pa_stream_disconnect(pa_rec_str);
pa_stream_unref(pa_rec_str);
pa_rec_str = NULL;
}
return false;
}
PoolStringArray AudioDriverPulseAudio::capture_device_get_names() {
PoolStringArray names;
return names;
}
StringName AudioDriverPulseAudio::capture_device_get_default_name() {
return "";
return OK;
}
AudioDriverPulseAudio::AudioDriverPulseAudio() {
@ -537,6 +601,7 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
pa_ml = NULL;
pa_ctx = NULL;
pa_str = NULL;
pa_rec_str = NULL;
mutex = NULL;
thread = NULL;
@ -550,6 +615,7 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
mix_rate = 0;
buffer_frames = 0;
input_buffer_frames = 0;
pa_buffer_size = 0;
channels = 0;
pa_ready = 0;

View file

@ -47,6 +47,7 @@ class AudioDriverPulseAudio : public AudioDriver {
pa_mainloop *pa_ml;
pa_context *pa_ctx;
pa_stream *pa_str;
pa_stream *pa_rec_str;
pa_channel_map pa_map;
String device_name;
@ -58,6 +59,7 @@ 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;
@ -98,13 +100,11 @@ public:
virtual void unlock();
virtual void finish();
virtual bool capture_device_start(StringName p_name);
virtual bool capture_device_stop(StringName p_name);
virtual PoolStringArray capture_device_get_names();
virtual StringName capture_device_get_default_name();
virtual float get_latency();
virtual Error capture_start();
virtual Error capture_stop();
AudioDriverPulseAudio();
~AudioDriverPulseAudio();
};