From 9e8f918b274c2b0ccea584099bf9ac6abb7c98fe Mon Sep 17 00:00:00 2001 From: Nikos Chantziaras Date: Fri, 1 Apr 2022 03:40:18 +0300 Subject: [PATCH] Libretro: Batch upload audio to frontend after video upload Uploading audio at the end of retro_run() with retro_audio_sample_batch_t instead of using retro_audio_sample_t to submit samples as they are generated has peformance benefits when using low audio latency settings. Frontends that implement audio sync and DRC (like RetroArch) will block for a long time when submitting audio. This can cause video frame presentation to be delayed too long. Low audio latency settings make it worse, as DRC and audio sync is performed more often, creating a bottleneck. Simply accumulating all generated audio samples and uploading them at the end with retro_audio_sample_batch_t improves things condiderably. --- libretro/libretro.c | 70 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index f793eb4..c67d3f2 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -86,7 +86,7 @@ static struct retro_log_callback logging; static retro_log_printf_t log_cb; static retro_video_refresh_t video_cb; -static retro_audio_sample_t audio_sample_cb; +static retro_audio_sample_batch_t audio_batch_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; @@ -101,7 +101,11 @@ static bool geometry_updated = false; static bool link_cable_emulation = false; /*static bool infrared_emulation = false;*/ -signed short soundbuf[1024 * 2]; +static struct { + int16_t *data; + int32_t size; + int32_t capacity; +} output_audio_buffer = {NULL, 0, 0}; char retro_system_directory[4096]; char retro_save_directory[4096]; @@ -174,12 +178,59 @@ static void rumble_callback(GB_gameboy_t *gb, double amplitude) } } +static void ensure_output_audio_buffer_capacity(int32_t capacity) +{ + if (capacity <= output_audio_buffer.capacity) { + return; + } + output_audio_buffer.data = realloc( + output_audio_buffer.data, capacity * sizeof(*output_audio_buffer.data)); + output_audio_buffer.capacity = capacity; + log_cb(RETRO_LOG_DEBUG, "Output audio buffer capacity set to %d\n", capacity); +} + +static void init_output_audio_buffer(int32_t capacity) +{ + output_audio_buffer.data = NULL; + output_audio_buffer.size = 0; + output_audio_buffer.capacity = 0; + ensure_output_audio_buffer_capacity(capacity); +} + +static void free_output_audio_buffer() +{ + free(output_audio_buffer.data); + output_audio_buffer.data = NULL; + output_audio_buffer.size = 0; + output_audio_buffer.capacity = 0; +} + +static void upload_output_audio_buffer() +{ + int32_t remaining_frames = output_audio_buffer.size / 2; + int16_t *buf_pos = output_audio_buffer.data; + + while (remaining_frames > 0) { + size_t uploaded_frames = audio_batch_cb(buf_pos, remaining_frames); + buf_pos += uploaded_frames * 2; + remaining_frames -= uploaded_frames; + } + output_audio_buffer.size = 0; +} + static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { - if ((audio_out == GB_1 && gb == &gameboy[0]) || - (audio_out == GB_2 && gb == &gameboy[1])) { - audio_sample_cb(sample->left, sample->right); + if (!(audio_out == GB_1 && gb == &gameboy[0]) && + !(audio_out == GB_2 && gb == &gameboy[1])) { + return; } + + if (output_audio_buffer.capacity - output_audio_buffer.size < 2) { + ensure_output_audio_buffer_capacity(output_audio_buffer.capacity * 1.5); + } + + output_audio_buffer.data[output_audio_buffer.size++] = sample->left; + output_audio_buffer.data[output_audio_buffer.size++] = sample->right; } static void vblank1(GB_gameboy_t *gb) @@ -1044,6 +1095,8 @@ void retro_init(void) if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) { libretro_supports_bitmasks = true; } + + init_output_audio_buffer(16384); } void retro_deinit(void) @@ -1053,6 +1106,8 @@ void retro_deinit(void) frame_buf = NULL; frame_buf_copy = NULL; + free_output_audio_buffer(); + libretro_supports_bitmasks = false; } @@ -1121,11 +1176,12 @@ void retro_set_environment(retro_environment_t cb) void retro_set_audio_sample(retro_audio_sample_t cb) { - audio_sample_cb = cb; + (void)cb; } void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { + audio_batch_cb = cb; } void retro_set_input_poll(retro_input_poll_t cb) @@ -1236,7 +1292,7 @@ void retro_run(void) GB_get_screen_width(&gameboy[0]) * sizeof(uint32_t)); } - + upload_output_audio_buffer(); initialized = true; }