summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Dubé <159546+serprex@users.noreply.github.com>2026-07-10 20:24:11 +0000
committerGitHub <noreply@github.com>2026-07-10 20:24:11 +0000
commitdb41e1b5f84ce8cf35635dac68ad818cbf70c453 (patch)
treea9b3e516f8e960b7dec9b9c4d32752ae5f84de04
parent49edb25c4501e25f4bdb6836a020952919c950d0 (diff)
Fix audio regression playing audio too slowly (#6894)
Dither to 32000Khz
-rw-r--r--soh/soh/OTRGlobals.cpp31
1 files changed, 19 insertions, 12 deletions
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index b77a8341d..4079ba687 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -1020,13 +1020,24 @@ std::unordered_map<std::string, ExtensionEntry> ExtensionCache;
void OTRAudio_Thread() {
#define SAMPLES_HIGH 560
+#define SAMPLES_MID 544
#define SAMPLES_LOW 528
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1)
#define NUM_AUDIO_CHANNELS 2
- // Single producer routine used by both the wake-driven and pre-buffer
- // loops. Captures the per-iteration sample count from the caller.
- auto produce_and_play = [&](u32 num_audio_samples) {
+ // The sequencer advances a fixed slice of musical time per engine update
+ // (tempoInternalToExternal in audio_heap.c assumes 60 updates/sec), so with
+ // production paced by backend buffer fill the sample count must average
+ // exactly 32000/60 = 533.33 per update or tempo drifts.
+ // Two thirds 528 one third 544 gives 533.33.
+ int32_t sample_debt_thirds = 0;
+
+ // Single producer routine used by both wake-driven and pre-buffer loops.
+ // Picks per-iteration sample count itself, then produces and plays it.
+ auto produce_next_batch = [&]() {
+ u32 num_audio_samples = sample_debt_thirds > 0 ? SAMPLES_MID : SAMPLES_LOW;
+ sample_debt_thirds += (1600 - 3 * (int32_t)num_audio_samples) * AUDIO_FRAMES_PER_UPDATE;
+
const u32 total_frames = num_audio_samples * AUDIO_FRAMES_PER_UPDATE;
const u32 total_samples = total_frames * NUM_AUDIO_CHANNELS;
@@ -1077,18 +1088,16 @@ void OTRAudio_Thread() {
{
std::unique_lock<std::mutex> Lock(audio.mutex);
- int samples_left = AudioPlayer_Buffered();
- u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
// Producer guard (banteg/Shipwright#6594): skip advancing the audio
- // engine if the backend ring cannot accept the smallest next burst.
+ // engine if the backend ring cannot accept the largest next burst.
// Generating PCM that DoPlay() would refuse creates a discontinuity
// audible as a click. The pre-buffer loop below will catch up once
// the backend drains enough.
- if (AudioPlayer_Buffered() + SAMPLES_LOW * AUDIO_FRAMES_PER_UPDATE > AudioPlayer_GetDesiredBuffered()) {
+ if (AudioPlayer_Buffered() + SAMPLES_MID * AUDIO_FRAMES_PER_UPDATE > AudioPlayer_GetDesiredBuffered()) {
audio.processing = false;
} else {
- produce_and_play(num_audio_samples);
+ produce_next_batch();
audio.processing = false;
}
}
@@ -1099,12 +1108,10 @@ void OTRAudio_Thread() {
// The producer guard (same as above) prevents advancing the audio engine
// when the backend ring is already at capacity.
while (audio.running && AudioPlayer_Buffered() < AudioPlayer_GetDesiredBuffered()) {
- if (AudioPlayer_Buffered() + SAMPLES_LOW * AUDIO_FRAMES_PER_UPDATE > AudioPlayer_GetDesiredBuffered()) {
+ if (AudioPlayer_Buffered() + SAMPLES_MID * AUDIO_FRAMES_PER_UPDATE > AudioPlayer_GetDesiredBuffered()) {
break;
}
- int samples_left = AudioPlayer_Buffered();
- u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
- produce_and_play(num_audio_samples);
+ produce_next_batch();
}
}
}