Modified Microphone implementation to handle only one device at a time (WIP)
This commit is contained in:
parent
76fd9d215c
commit
061358d838
22 changed files with 556 additions and 1018 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue