[Scummvm-git-logs] scummvm master -> 606f9dace5ffe9ae133cf9b9a1b40b9a50a9fef9
spleen1981
noreply at scummvm.org
Sun Jul 26 23:53:45 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
606f9dace5 LIBRETRO: Pace audio generation against wall time, not frame count (#7742)
Commit: 606f9dace5ffe9ae133cf9b9a1b40b9a50a9fef9
https://github.com/scummvm/scummvm/commit/606f9dace5ffe9ae133cf9b9a1b40b9a50a9fef9
Author: AlbertoVelazquez (50219414+AlbertoVelazquez at users.noreply.github.com)
Date: 2026-07-27T01:53:41+02:00
Commit Message:
LIBRETRO: Pace audio generation against wall time, not frame count (#7742)
audio_run() emits a fixed sample_rate/frame_rate samples per call. As
it runs once per retro_run(), the output rate follows the achieved
call rate rather than real time: a frontend running below frame_rate
underproduces and the audio buffer underruns (crackle), while one
running above overproduces. Video is already frame-locked; audio was
not.
Derive the per-call count from the wall-clock time elapsed since the
previous call (cpu_features_get_time_usec()), so average production
stays at sample_rate regardless of the retro_run() cadence. Bound the
batch to the mix buffer size so a long stall (content load,
save-state) cannot request a multi-second batch. The fractional-sample
remainder is preserved, and a vsync-locked frontend gets the same
per-frame count as before.
Changed paths:
backends/platform/libretro/src/libretro-core.cpp
diff --git a/backends/platform/libretro/src/libretro-core.cpp b/backends/platform/libretro/src/libretro-core.cpp
index 57ceb5ba30f..9627e4870e7 100644
--- a/backends/platform/libretro/src/libretro-core.cpp
+++ b/backends/platform/libretro/src/libretro-core.cpp
@@ -38,6 +38,8 @@
#include <libgen.h>
#endif
+#include <features/features_cpu.h> // cpu_features_get_time_usec()
+
/**
* Include base/internal_version.h to allow access to SCUMMVM_VERSION.
* @see retro_get_system_info()
@@ -97,6 +99,7 @@ static float frame_rate = 0;
static uint16 sample_rate = 0;
static float audio_samples_per_frame = 0.0f; // length in samples per frame
static float audio_samples_accumulator = 0.0f;
+static retro_time_t audio_last_time_usec = 0; // timestamp of the previous audio_run()
static int16 *audio_sample_buffer = NULL; // pointer to output buffer
@@ -191,6 +194,7 @@ static void log_scummvm_exit_code(void) {
static void audio_buffer_init(uint16 sample_rate, uint16 frame_rate) {
audio_samples_accumulator = 0.0f;
+ audio_last_time_usec = cpu_features_get_time_usec();
audio_samples_per_frame = (float)sample_rate / (float)frame_rate;
uint32 audio_sample_buffer_size = ((uint32)retro_setting_get_audio_samples_buffer_size()) * 2 * sizeof(int16);
audio_sample_buffer = audio_sample_buffer ? (int16 *)realloc(audio_sample_buffer, audio_sample_buffer_size) : (int16 *)malloc(audio_sample_buffer_size);
@@ -205,19 +209,33 @@ static void audio_run(void) {
int16 *audio_buffer_ptr;
uint32 samples_to_read;
uint32 samples_produced;
+ uint16 samples_buffer_size = retro_setting_get_audio_samples_buffer_size();
+
+ /* Pace audio against elapsed wall time instead of a fixed sample_rate/frame_rate
+ * batch, so output stays at sample_rate even when the frontend runs retro_run()
+ * above or below frame_rate (a slow frontend would otherwise underrun the audio
+ * buffer). Video stays frame-locked. */
+ retro_time_t now_usec = cpu_features_get_time_usec();
+ float samples_target = (float)sample_rate * (float)(now_usec - audio_last_time_usec) / 1000000.0f;
+ audio_last_time_usec = now_usec;
- /* Audio_samples_per_frame is decimal;
- * get integer component */
- samples_to_read = (uint32)audio_samples_per_frame;
+ /* Samples_target is decimal; get integer component */
+ samples_to_read = (uint32)samples_target;
/* Account for fractional component */
- audio_samples_accumulator += audio_samples_per_frame - (float)samples_to_read;
+ audio_samples_accumulator += samples_target - (float)samples_to_read;
if (audio_samples_accumulator >= 1.0f) {
samples_to_read++;
audio_samples_accumulator -= 1.0f;
}
+ /* Bound the batch to the mix buffer: after a long stall (content load,
+ * save-state) the elapsed interval would otherwise request a multi-second
+ * batch and overrun audio_sample_buffer. */
+ if (samples_to_read > samples_buffer_size)
+ samples_to_read = samples_buffer_size;
+
samples_produced = ((Audio::MixerImpl *)g_system->getMixer())->mixCallback((byte *) audio_sample_buffer, samples_to_read * 2 * sizeof(int16));
/* Workaround as currently there's no way to detect silence-only buffers from the mixer */
More information about the Scummvm-git-logs
mailing list