Skip to content

Commit

Permalink
Minimize locking from reading data from microphone when outputting th…
Browse files Browse the repository at this point in the history
…e audio.
  • Loading branch information
kus04e4ek committed Jun 13, 2024
1 parent 3101b48 commit de4b0f3
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 42 deletions.
5 changes: 3 additions & 2 deletions drivers/coreaudio/audio_driver_coreaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
ad->input_buffer_write(sample);
}
}
ad->input_buffer_end_write();
} else {
ERR_PRINT("AudioUnitRender failed, code: " + itos(result));
}
Expand Down Expand Up @@ -633,8 +634,8 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)

if (input) {
// Reset audio input to keep synchronization.
input_position = 0;
input_size = 0;
input_read = SizePosition(0, 0);
input_write = SizePosition(0, 0);
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions drivers/pulseaudio/audio_driver_pulseaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ Error AudioDriverPulseAudio::init_output_device() {
samples_out.resize(pa_buffer_size);

// Reset audio input to keep synchronization.
input_position = 0;
input_size = 0;
input_read = SizePosition(0, 0);
input_write = SizePosition(0, 0);

return OK;
}
Expand Down Expand Up @@ -546,6 +546,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
ad->input_buffer_write(sample);
}
}
ad->input_buffer_end_write();

read_bytes += bytes;
ret = pa_stream_drop(ad->pa_rec_str);
Expand Down
5 changes: 3 additions & 2 deletions drivers/wasapi/audio_driver_wasapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,8 @@ Error AudioDriverWASAPI::init_output_device(bool p_reinit) {
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
samples_in.resize(buffer_frames * channels);

input_position = 0;
input_size = 0;
input_read = SizePosition(0, 0);
input_write = SizePosition(0, 0);

print_verbose("WASAPI: detected " + itos(audio_output.channels) + " channels");
print_verbose("WASAPI: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
Expand Down Expand Up @@ -859,6 +859,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
ad->input_buffer_write(l);
ad->input_buffer_write(r);
}
ad->input_buffer_end_write();

read_frames += num_frames_available;

Expand Down
1 change: 1 addition & 0 deletions platform/android/audio_driver_opensl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf qu
input_buffer_write(sample);
input_buffer_write(sample); // call twice to convert to Stereo
}
input_buffer_end_write();
input_unlock();

SLresult res = (*recordBufferQueueItf)->Enqueue(recordBufferQueueItf, rec_buffer.ptrw(), rec_buffer.size() * sizeof(int16_t));
Expand Down
1 change: 1 addition & 0 deletions platform/web/audio_driver_web.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void AudioDriverWeb::_audio_driver_capture(int p_from, int p_samples) {
for (int i = read_pos; i < read_pos + to_read; i++) {
input_buffer_write(int32_t(input_rb[i] * 32768.f) * (1U << 16));
}
input_buffer_end_write();
}

Error AudioDriverWeb::init() {
Expand Down
53 changes: 32 additions & 21 deletions servers/audio/audio_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,54 +322,65 @@ AudioStreamMicrophone::AudioStreamMicrophone() {
}

int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
AudioDriver::get_singleton()->input_lock();

Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer();
unsigned int input_size = AudioDriver::get_singleton()->get_input_size();
int mix_rate = AudioDriver::get_singleton()->get_mix_rate();
const LocalVector<int32_t> &buf = AudioDriver::get_singleton()->get_input_buffer();
AudioDriver::SizePosition size_position = AudioDriver::get_singleton()->get_input_size_position();
unsigned int mix_rate = AudioDriver::get_singleton()->get_mix_rate();
unsigned int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1);
#ifdef DEBUG_ENABLED
unsigned int input_position = AudioDriver::get_singleton()->get_input_position();
#endif

int mixed_frames = p_frames;

if (playback_delay > input_size) {
if (playback_delay > size_position.size) {
for (int i = 0; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0.0f, 0.0f);
}
input_ofs = 0;
} else {
for (int i = 0; i < p_frames; i++) {
if (input_size > input_ofs && (int)input_ofs < buf.size()) {
bool was_locked = false;

int current_frame = 0;
for (; current_frame < p_frames; current_frame++) {
if (size_position.size > input_ofs && input_ofs < buf.size()) {
float l = (buf[input_ofs++] >> 16) / 32768.f;
if ((int)input_ofs >= buf.size()) {
if (input_ofs >= buf.size()) {
input_ofs = 0;
}
float r = (buf[input_ofs++] >> 16) / 32768.f;
if ((int)input_ofs >= buf.size()) {
if (input_ofs >= buf.size()) {
input_ofs = 0;
}

p_buffer[i] = AudioFrame(l, r);
p_buffer[current_frame] = AudioFrame(l, r);
} else {
if (mixed_frames == p_frames) {
mixed_frames = i;
if (!was_locked) {
// Wait while the other thread writes to the input buffer.
AudioDriver::get_singleton()->input_lock();
AudioDriver::get_singleton()->input_unlock();

size_position = AudioDriver::get_singleton()->get_input_size_position();
was_locked = true;

// Process the same frame on the next loop iteration.
current_frame--;
} else {
break;
}
p_buffer[i] = AudioFrame(0.0f, 0.0f);
}
}

mixed_frames = current_frame;
for (; current_frame < p_frames; current_frame++) {
p_buffer[current_frame] = AudioFrame(0.0f, 0.0f);
}
}

#ifdef DEBUG_ENABLED
if (mixed_frames != p_frames) {
ERR_PRINT(vformat("Buffer underrun: input_size = %d, input_ofs = %d, buf.size() = %d.", input_size, input_ofs, buf.size()));
} else if (input_ofs > input_position && (int)(input_ofs - input_position) < (p_frames * 2)) {
print_verbose(String(get_class_name()) + " buffer underrun: input_position=" + itos(input_position) + " input_ofs=" + itos(input_ofs) + " input_size=" + itos(input_size));
ERR_PRINT(vformat("Buffer underrun: size_position.size = %d, input_ofs = %d, buf.size() = %d.", size_position.size, input_ofs, buf.size()));
} else if (input_ofs > size_position.position && (int)(input_ofs - size_position.position) < (p_frames * 2)) {
print_verbose(String(get_class_name()) + " buffer underrun: size_position.position=" + itos(size_position.position) + " size_position.size=" + itos(size_position.size) + " input_ofs=" + itos(input_ofs));
}
#endif

AudioDriver::get_singleton()->input_unlock();
return mixed_frames;
}

Expand Down
22 changes: 13 additions & 9 deletions servers/audio_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,28 @@ double AudioDriver::get_time_to_next_mix() {
void AudioDriver::input_buffer_init(int driver_buffer_frames) {
const int input_buffer_channels = 2;
input_buffer.resize(driver_buffer_frames * input_buffer_channels * 4);
input_position = 0;
input_size = 0;
input_read = SizePosition(0, 0);
input_write = SizePosition(0, 0);
}

void AudioDriver::input_buffer_write(int32_t sample) {
if ((int)input_position < input_buffer.size()) {
input_buffer.write[input_position++] = sample;
if ((int)input_position >= input_buffer.size()) {
input_position = 0;
if (input_write.position < input_buffer.size()) {
input_buffer[input_write.position++] = sample;
if (input_write.position >= input_buffer.size()) {
input_write.position = 0;
}
if ((int)input_size < input_buffer.size()) {
input_size++;
if (input_write.size < input_buffer.size()) {
input_write.size++;
}
} else {
WARN_PRINT("input_buffer_write: Invalid input_position=" + itos(input_position) + " input_buffer.size()=" + itos(input_buffer.size()));
WARN_PRINT("input_buffer_write: Invalid input_write.position=" + itos(input_write.position) + " input_buffer.size()=" + itos(input_buffer.size()));
}
}

void AudioDriver::input_buffer_end_write() {
input_read = input_write;
}

int AudioDriver::_get_configured_mix_rate() {
StringName audio_driver_setting = "audio/driver/mix_rate";
int mix_rate = GLOBAL_GET(audio_driver_setting);
Expand Down
22 changes: 16 additions & 6 deletions servers/audio_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,26 @@ class AudioDriver {
SafeNumeric<uint64_t> prof_time;
#endif

public:
struct SizePosition {
unsigned int size;
unsigned int position;

SizePosition(unsigned int p_size = 0, unsigned int p_position = 0) noexcept :
size(p_size), position(p_position) {}
};

protected:
Vector<int32_t> input_buffer;
unsigned int input_position = 0;
unsigned int input_size = 0;
LocalVector<int32_t> input_buffer;
std::atomic<SizePosition> input_read;
SizePosition input_write;

void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true);
void update_mix_time(int p_frames);

void input_buffer_init(int driver_buffer_frames);
void input_buffer_write(int32_t sample);
void input_buffer_end_write();

int _get_configured_mix_rate();

Expand Down Expand Up @@ -123,9 +134,8 @@ class AudioDriver {
SpeakerMode get_speaker_mode_by_total_channels(int p_channels) const;
int get_total_channels_by_speaker_mode(SpeakerMode) const;

Vector<int32_t> get_input_buffer() { return input_buffer; }
unsigned int get_input_position() { return input_position; }
unsigned int get_input_size() { return input_size; }
const LocalVector<int32_t> &get_input_buffer() { return input_buffer; }
SizePosition get_input_size_position() { return input_read; }

#ifdef DEBUG_ENABLED
uint64_t get_profiling_time() const { return prof_time.get(); }
Expand Down

0 comments on commit de4b0f3

Please sign in to comment.