SDL_mixer: fluidsynth: Brute-force seeking.

From 0533c396c7fb972f22737e305628ea7f76396e0b Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 14 Jan 2026 13:40:29 -0500
Subject: [PATCH] fluidsynth: Brute-force seeking.

This sucks, and is slow, but it works. We might revisit this later.

Fixes #519.
---
 src/decoder_fluidsynth.c | 42 +++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/src/decoder_fluidsynth.c b/src/decoder_fluidsynth.c
index d5bb502e..75457b57 100644
--- a/src/decoder_fluidsynth.c
+++ b/src/decoder_fluidsynth.c
@@ -90,6 +90,7 @@ typedef struct FLUIDSYNTH_TrackData
     fluid_synth_t *synth;
     fluid_settings_t *settings;
     fluid_player_t *player;
+    Uint64 current_frame;
     int freq;
 } FLUIDSYNTH_TrackData;
 
@@ -384,30 +385,45 @@ static bool SDLCALL FLUIDSYNTH_decode(void *track_userdata, SDL_AudioStream *str
     }
 
     SDL_PutAudioStreamData(stream, samples, sizeof (samples));
+    tdata->current_frame += SDL_arraysize(samples) / 2;
     return true;
 }
 
 static bool SDLCALL FLUIDSYNTH_seek(void *track_userdata, Uint64 frame)
 {
-#if (FLUIDSYNTH_VERSION_MAJOR < 2)
-    return SDL_Unsupported();
-#else
     FLUIDSYNTH_TrackData *tdata = (FLUIDSYNTH_TrackData *) track_userdata;
-    Sint64 ticks = MIX_FramesToMS(tdata->freq, frame);
-    if (ticks == -1) {
-        ticks = 0;
+
+    // This is expensive, but it's not trivial to seek to a specific frame in fluidsynth for various reasons.
+    //  (see some explanations in https://github.com/libsdl-org/SDL_mixer/issues/519)
+    if (tdata->current_frame > frame) {
+        if (fluidsynth.fluid_player_seek(tdata->player, 0) != FLUID_OK) {
+            return SDL_SetError("Couldn't rewind MIDI track");
+        }
+        tdata->current_frame = 0;
+
+        if (fluidsynth.fluid_player_get_status(tdata->player) != FLUID_PLAYER_PLAYING) {
+            if (fluidsynth.fluid_player_play(tdata->player) != FLUID_OK) {
+                return SDL_SetError("Failed to restart FluidSynth player");
+            }
+        }
     }
 
-    // !!! FIXME: docs say this will fail if a seek was requested and then a second seek happens before we play more of the midi file, since the first seek will still be in progress.
-    bool result = (fluidsynth.fluid_player_seek(tdata->player, (int)ticks) == FLUID_OK);
+    Uint64 remaining_frames = frame - tdata->current_frame;
+    while (remaining_frames > 0) {
+        if (fluidsynth.fluid_player_get_status(tdata->player) != FLUID_PLAYER_PLAYING) {
+            return SDL_SetError("Seek past end of MIDI file");
+        }
 
-    if (result && fluidsynth.fluid_player_get_status(tdata->player) != FLUID_PLAYER_PLAYING) {
-        /* start playing if player is done */
-        result = (fluidsynth.fluid_player_play(tdata->player) == FLUID_OK);
+        float samples[512];
+        const Uint64 write_frames = SDL_min((Uint64) (SDL_arraysize(samples) / 2), remaining_frames);
+        if (fluidsynth.fluid_synth_write_float(tdata->synth, (int) write_frames, samples, 0, 2, samples, 1, 2) != FLUID_OK) {
+            return SDL_SetError("Seek past end of MIDI file");  // maybe EOF...?
+        }
+        tdata->current_frame += write_frames;
+        remaining_frames -= write_frames;
     }
 
-    return result;
-#endif
+    return true;
 }
 
 static void SDLCALL FLUIDSYNTH_quit_track(void *track_userdata)