SDL_mixer: vorbis, stb: Deal with Vorbis data with "deferred" samples at start (06a09)

From 06a0911c11c78ebb3de4af19df7f04f24adccafb Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 4 Jun 2026 11:47:26 -0400
Subject: [PATCH] vorbis, stb: Deal with Vorbis data with "deferred" samples at
 start

It appears that some Ogg Vorbis files might have something at startup that
stb_vorbis wants to reject before it gets to the actual audio--I am vague on
the details of what this would be--but this can cause it to return zero while
it flushes through these.

Check if a given call to stb_vorbis_get_samples_float_interleaved started with
some pending "deferred" samples, and if it returns zero in this case, keep
trying until either we get real audio data or we hit EOF with nothing still
reported as deferred.

Arguably, this is a bug in stb_vorbis (the docs say that this call should
return zero for EOF, so it should probably be doing this loop to flush out
deferred samples itself), but it's easy enough to work around it in our code
for now.

(Manual port of fix for https://github.com/icculus/SDL_sound/issues/110)

(cherry picked from commit 6fb7bcafb109b0170c09e0f0ac55bc1e0a018c3c)
---
 src/decoder_stb_vorbis.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/decoder_stb_vorbis.c b/src/decoder_stb_vorbis.c
index b69a7794..eea7dc09 100644
--- a/src/decoder_stb_vorbis.c
+++ b/src/decoder_stb_vorbis.c
@@ -224,7 +224,13 @@ static bool SDLCALL STBVORBIS_decode(void *track_userdata, SDL_AudioStream *stre
 
     float **pcm_channels = NULL;
     int num_channels = 0;
-    int amount = stb_vorbis_get_frame_float(tdata->vorbis, &num_channels, &pcm_channels);
+    int amount, has_deferred;
+
+    do {
+        has_deferred = tdata->vorbis->discard_samples_deferred > 0;
+        amount = stb_vorbis_get_frame_float(tdata->vorbis, &num_channels, &pcm_channels);
+    } while ((amount == 0) && has_deferred);  /* if it's still flushing out garbage at the start of the stream, keep trying. */
+
     if (amount <= 0) {
         return false;  // EOF
     }