From f8359a74d394ecaf7c81fc312333b8ad6239471a Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Mon, 29 Nov 2021 15:04:57 +0000 Subject: [PATCH 1/2] Send audio samples to frontend with correct pacing --- libretro/libretro.c | 86 ++++++++++++++++++++++++++++++++++++++++++++- sound.c | 54 +++++++++++++++------------- sound.h | 3 +- 3 files changed, 116 insertions(+), 27 deletions(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index cf2d2e2..0c02e05 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -54,6 +54,19 @@ static int translation_caches_inited = 0; // Usually 59.72750057 Hz, unless GBC_RATE is overclocked (for 60FPS) #define GBA_FPS ((float) GBC_BASE_RATE) / (308 * 228 * 4) +/* An alpha factor of 1/180 is *somewhat* equivalent + * to calculating the average for the last 180 + * frames, or 3 seconds of runtime... */ +#define SAMPLES_PER_FRAME_MOVING_AVG_ALPHA (1.0f / 180.0f) +static s16 *audio_sample_buffer = NULL; +static u32 audio_sample_buffer_size = 0; +static float audio_samples_per_frame_avg = 0.0f; + +/* Workaround for a RetroArch audio driver + * limitation: a maximum of 1024 frames + * can be written per call of audio_batch_cb() */ +#define AUDIO_BATCH_FRAMES_MAX 1024 + /* Maximum number of consecutive frames that * can be skipped */ #define FRAMESKIP_MAX 30 @@ -72,6 +85,7 @@ static bios_type selected_bios = auto_detect; static retro_log_printf_t log_cb; static retro_video_refresh_t video_cb; +static retro_audio_sample_batch_t audio_batch_cb; static retro_input_poll_t input_poll_cb; static retro_environment_t environ_cb; @@ -367,6 +381,56 @@ void set_fastforward_override(bool fastforward) environ_cb(RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE, &ff_override); } +static void audio_run(void) +{ + u32 samples_available = sound_samples_available(); + + if (samples_available > 0) + { + s16 *audio_buffer_ptr; + u32 samples_to_read; + u32 samples_produced; + + /* Update 'running average' of number of + * samples per frame. + * Note that this is not a true running + * average, but just a leaky-integrator/ + * exponential moving average, used because + * it is simple and fast (i.e. requires no + * window of samples). */ + audio_samples_per_frame_avg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samples_available) + + ((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audio_samples_per_frame_avg); + + samples_to_read = (u32)(audio_samples_per_frame_avg + 0.5f); + + /* Resize audio output buffer, if required */ + if (audio_sample_buffer_size < (samples_to_read * 2)) + { + audio_sample_buffer_size = (samples_to_read * 2); + audio_sample_buffer = (s16*)realloc(audio_sample_buffer, + audio_sample_buffer_size * sizeof(s16)); + } + + samples_produced = sound_read_samples(audio_sample_buffer, samples_to_read); + + /* Workaround for a RetroArch audio driver + * limitation: a maximum of 1024 frames + * can be written per call of audio_batch_cb(), + * so we have to send samples in chunks */ + audio_buffer_ptr = audio_sample_buffer; + while (samples_produced > 0) + { + u32 samples_to_write = (samples_produced > AUDIO_BATCH_FRAMES_MAX) ? + AUDIO_BATCH_FRAMES_MAX : samples_produced; + + audio_batch_cb(audio_buffer_ptr, samples_to_write); + + samples_produced -= samples_to_write; + audio_buffer_ptr += samples_to_write << 1; + } + } +} + static void video_run(void) { u16 *gba_screen_pixels_buf = gba_screen_pixels; @@ -457,6 +521,11 @@ void retro_get_system_av_info(struct retro_system_av_info* info) void retro_init(void) { + u32 audio_samples_per_frame = (u32)((float)(GBA_SOUND_FREQUENCY) / (float)(GBA_FPS)); + audio_sample_buffer_size = audio_samples_per_frame * 2; + audio_sample_buffer = (s16*)malloc(audio_sample_buffer_size * sizeof(s16)); + audio_samples_per_frame_avg = (float)audio_samples_per_frame; + #if defined(HAVE_DYNAREC) #if defined(MMAP_JIT_CACHE) rom_translation_cache = map_jit_block(ROM_TRANSLATION_CACHE_SIZE + RAM_TRANSLATION_CACHE_SIZE); @@ -593,6 +662,13 @@ void retro_deinit(void) video_post_process = NULL; post_process_cc = false; post_process_mix = false; + + if (audio_sample_buffer) + free(audio_sample_buffer); + + audio_sample_buffer = NULL; + audio_sample_buffer_size = 0; + audio_samples_per_frame_avg = 0.0f; } static retro_time_t retro_perf_dummy_get_time_usec() { return 0; } @@ -636,6 +712,14 @@ void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } + +void retro_set_audio_sample(retro_audio_sample_t 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) { input_poll_cb = cb; @@ -1159,7 +1243,7 @@ void retro_run(void) execute_arm(execute_cycles); } - render_audio(); + audio_run(); video_run(); if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) diff --git a/sound.c b/sound.c index 87c8dc8..e50bf0a 100644 --- a/sound.c +++ b/sound.c @@ -749,34 +749,38 @@ unsigned sound_write_savestate(u8 *dst) return (unsigned int)(dst - startp); } - -#include "libretro.h" - -static retro_audio_sample_batch_t audio_batch_cb; -void retro_set_audio_sample(retro_audio_sample_t cb) { } -void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; } - -void render_audio(void) +u32 sound_samples_available(void) { - static s16 stream_base[512]; - s16 *source; + u32 num_samples = (gbc_sound_buffer_index - sound_buffer_base) & BUFFER_SIZE_MASK; + num_samples = (num_samples > 512) ? (num_samples - 512) : 0; + return (num_samples >> 1); +} + +u32 sound_read_samples(s16 *out, u32 count) +{ + u32 samples_available = sound_samples_available(); u32 i; - while (((gbc_sound_buffer_index - sound_buffer_base) & BUFFER_SIZE_MASK) > 512) + if (count > samples_available) + count = samples_available; + + for(i = 0; i < (count << 1); i++) { - source = (s16 *)(sound_buffer + sound_buffer_base); - for(i = 0; i < 512; i++) - { - s32 current_sample = source[i]; - if(current_sample > 2047) - current_sample = 2047; - if(current_sample < -2048) - current_sample = -2048; - stream_base[i] = current_sample * 16; - source[i] = 0; - } - audio_batch_cb(stream_base, 256); - sound_buffer_base += 512; - sound_buffer_base &= BUFFER_SIZE_MASK; + u32 source_index = (sound_buffer_base + i) & BUFFER_SIZE_MASK; + s32 current_sample = sound_buffer[source_index]; + + sound_buffer[source_index] = 0; + + if(current_sample > 2047) + current_sample = 2047; + if(current_sample < -2048) + current_sample = -2048; + + out[i] = current_sample * 16; } + + sound_buffer_base += (count << 1); + sound_buffer_base &= BUFFER_SIZE_MASK; + + return count; } diff --git a/sound.h b/sound.h index 539bbd9..1bdcf59 100644 --- a/sound.h +++ b/sound.h @@ -108,7 +108,8 @@ void init_sound(int need_reset); unsigned sound_write_savestate(u8 *dst); bool sound_read_savestate(const u8 *src); -void render_audio(void); +u32 sound_samples_available(void); +u32 sound_read_samples(s16 *out, u32 count); void reset_sound(void); From 20bd7c111b3271abd894b7a43eb3d4a5b21922c1 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Mon, 29 Nov 2021 17:45:06 +0000 Subject: [PATCH 2/2] Calculate number of audio samples to read per frame deterministically --- libretro/libretro.c | 92 +++++++++++++++++++-------------------------- sound.c | 29 +++++++------- sound.h | 3 +- 3 files changed, 55 insertions(+), 69 deletions(-) diff --git a/libretro/libretro.c b/libretro/libretro.c index 0c02e05..a908dec 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -54,13 +54,9 @@ static int translation_caches_inited = 0; // Usually 59.72750057 Hz, unless GBC_RATE is overclocked (for 60FPS) #define GBA_FPS ((float) GBC_BASE_RATE) / (308 * 228 * 4) -/* An alpha factor of 1/180 is *somewhat* equivalent - * to calculating the average for the last 180 - * frames, or 3 seconds of runtime... */ -#define SAMPLES_PER_FRAME_MOVING_AVG_ALPHA (1.0f / 180.0f) -static s16 *audio_sample_buffer = NULL; -static u32 audio_sample_buffer_size = 0; -static float audio_samples_per_frame_avg = 0.0f; +static s16 *audio_sample_buffer = NULL; +static float audio_samples_per_frame = 0.0f; +static float audio_samples_accumulator = 0.0f; /* Workaround for a RetroArch audio driver * limitation: a maximum of 1024 frames @@ -383,51 +379,40 @@ void set_fastforward_override(bool fastforward) static void audio_run(void) { - u32 samples_available = sound_samples_available(); + s16 *audio_buffer_ptr; + u32 samples_to_read; + u32 samples_produced; - if (samples_available > 0) + /* audio_samples_per_frame is decimal; + * get integer component */ + samples_to_read = (u32)audio_samples_per_frame; + + /* Account for fractional component */ + audio_samples_accumulator += audio_samples_per_frame - + (float)samples_to_read; + + if (audio_samples_accumulator >= 1.0f) { - s16 *audio_buffer_ptr; - u32 samples_to_read; - u32 samples_produced; + samples_to_read++; + audio_samples_accumulator -= 1.0f; + } - /* Update 'running average' of number of - * samples per frame. - * Note that this is not a true running - * average, but just a leaky-integrator/ - * exponential moving average, used because - * it is simple and fast (i.e. requires no - * window of samples). */ - audio_samples_per_frame_avg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samples_available) + - ((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audio_samples_per_frame_avg); + samples_produced = sound_read_samples(audio_sample_buffer, samples_to_read); - samples_to_read = (u32)(audio_samples_per_frame_avg + 0.5f); + /* Workaround for a RetroArch audio driver + * limitation: a maximum of 1024 frames + * can be written per call of audio_batch_cb(), + * so we have to send samples in chunks */ + audio_buffer_ptr = audio_sample_buffer; + while (samples_produced > 0) + { + u32 samples_to_write = (samples_produced > AUDIO_BATCH_FRAMES_MAX) ? + AUDIO_BATCH_FRAMES_MAX : samples_produced; - /* Resize audio output buffer, if required */ - if (audio_sample_buffer_size < (samples_to_read * 2)) - { - audio_sample_buffer_size = (samples_to_read * 2); - audio_sample_buffer = (s16*)realloc(audio_sample_buffer, - audio_sample_buffer_size * sizeof(s16)); - } + audio_batch_cb(audio_buffer_ptr, samples_to_write); - samples_produced = sound_read_samples(audio_sample_buffer, samples_to_read); - - /* Workaround for a RetroArch audio driver - * limitation: a maximum of 1024 frames - * can be written per call of audio_batch_cb(), - * so we have to send samples in chunks */ - audio_buffer_ptr = audio_sample_buffer; - while (samples_produced > 0) - { - u32 samples_to_write = (samples_produced > AUDIO_BATCH_FRAMES_MAX) ? - AUDIO_BATCH_FRAMES_MAX : samples_produced; - - audio_batch_cb(audio_buffer_ptr, samples_to_write); - - samples_produced -= samples_to_write; - audio_buffer_ptr += samples_to_write << 1; - } + samples_produced -= samples_to_write; + audio_buffer_ptr += samples_to_write << 1; } } @@ -521,10 +506,11 @@ void retro_get_system_av_info(struct retro_system_av_info* info) void retro_init(void) { - u32 audio_samples_per_frame = (u32)((float)(GBA_SOUND_FREQUENCY) / (float)(GBA_FPS)); - audio_sample_buffer_size = audio_samples_per_frame * 2; - audio_sample_buffer = (s16*)malloc(audio_sample_buffer_size * sizeof(s16)); - audio_samples_per_frame_avg = (float)audio_samples_per_frame; + u32 audio_sample_buffer_size; + audio_samples_per_frame = (float)(GBA_SOUND_FREQUENCY) / (float)(GBA_FPS); + audio_samples_accumulator = 0.0f; + audio_sample_buffer_size = ((u32)audio_samples_per_frame + 1) * 2; + audio_sample_buffer = (s16*)malloc(audio_sample_buffer_size * sizeof(s16)); #if defined(HAVE_DYNAREC) #if defined(MMAP_JIT_CACHE) @@ -666,9 +652,9 @@ void retro_deinit(void) if (audio_sample_buffer) free(audio_sample_buffer); - audio_sample_buffer = NULL; - audio_sample_buffer_size = 0; - audio_samples_per_frame_avg = 0.0f; + audio_sample_buffer = NULL; + audio_samples_per_frame = 0.0f; + audio_samples_accumulator = 0.0f; } static retro_time_t retro_perf_dummy_get_time_usec() { return 0; } diff --git a/sound.c b/sound.c index e50bf0a..a99c9b2 100644 --- a/sound.c +++ b/sound.c @@ -749,22 +749,22 @@ unsigned sound_write_savestate(u8 *dst) return (unsigned int)(dst - startp); } -u32 sound_samples_available(void) +u32 sound_read_samples(s16 *out, u32 frames) { - u32 num_samples = (gbc_sound_buffer_index - sound_buffer_base) & BUFFER_SIZE_MASK; - num_samples = (num_samples > 512) ? (num_samples - 512) : 0; - return (num_samples >> 1); -} - -u32 sound_read_samples(s16 *out, u32 count) -{ - u32 samples_available = sound_samples_available(); u32 i; + u32 samples_to_read = frames << 1; + /* Get total number of samples in the buffer */ + u32 samples_available = (gbc_sound_buffer_index - sound_buffer_base) & BUFFER_SIZE_MASK; + /* The last 512 samples are 'in use', and cannot + * be read out yet */ + samples_available = (samples_available > 512) ? (samples_available - 512) : 0; + /* Available sample count must be an even number */ + samples_available = (samples_available >> 1) << 1; - if (count > samples_available) - count = samples_available; + if (samples_to_read > samples_available) + samples_to_read = samples_available; - for(i = 0; i < (count << 1); i++) + for(i = 0; i < samples_to_read; i++) { u32 source_index = (sound_buffer_base + i) & BUFFER_SIZE_MASK; s32 current_sample = sound_buffer[source_index]; @@ -779,8 +779,9 @@ u32 sound_read_samples(s16 *out, u32 count) out[i] = current_sample * 16; } - sound_buffer_base += (count << 1); + sound_buffer_base += samples_to_read; sound_buffer_base &= BUFFER_SIZE_MASK; - return count; + /* Function returns number of frames read */ + return (samples_to_read >> 1); } diff --git a/sound.h b/sound.h index 1bdcf59..28076dc 100644 --- a/sound.h +++ b/sound.h @@ -108,8 +108,7 @@ void init_sound(int need_reset); unsigned sound_write_savestate(u8 *dst); bool sound_read_savestate(const u8 *src); -u32 sound_samples_available(void); -u32 sound_read_samples(s16 *out, u32 count); +u32 sound_read_samples(s16 *out, u32 frames); void reset_sound(void);