SDL_mixer: api: Added MIX_[SG]etMasterFrequencyRatio().

From 5c168f7f2eae33cdcbd39a958640004a874bf14c Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 16 Jan 2026 13:49:07 -0500
Subject: [PATCH] api: Added MIX_[SG]etMasterFrequencyRatio().

Fixes #801.
---
 include/SDL3_mixer/SDL_mixer.h | 50 ++++++++++++++++++++++++++++++++++
 src/SDL_mixer.c                | 26 +++++++++++++++---
 src/SDL_mixer.exports          |  2 ++
 src/SDL_mixer.sym              |  2 ++
 4 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/include/SDL3_mixer/SDL_mixer.h b/include/SDL3_mixer/SDL_mixer.h
index dabae855..f4a6c3e1 100644
--- a/include/SDL3_mixer/SDL_mixer.h
+++ b/include/SDL3_mixer/SDL_mixer.h
@@ -2266,6 +2266,56 @@ extern SDL_DECLSPEC bool SDLCALL MIX_SetTagGain(MIX_Mixer *mixer, const char *ta
 
 /* frequency ratio ... */
 
+/**
+ * Set a mixer's master frequency ratio.
+ *
+ * Each mixer has a master frequency ratio, that affects the entire mix. This
+ * can cause the final output to change speed and pitch. A value greater than
+ * 1.0f will play the audio faster, and at a higher pitch. A value less than
+ * 1.0f will play the audio slower, and at a lower pitch. 1.0f is normal
+ * speed.
+ *
+ * Each track _also_ has a frequency ratio; it will be applied when mixing
+ * that track's audio regardless of the master setting. The master setting
+ * affects the final output after all mixing has been completed.
+ *
+ * A mixer's master frequency ratio defaults to 1.0f.
+ *
+ * This value can be changed at any time to adjust the future mix.
+ *
+ * \param mixer the mixer to adjust.
+ * \param ratio the frequency ratio. Must be between 0.01f and 100.0f.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL_mixer 3.0.0.
+ *
+ * \sa MIX_GetMasterFrequencyRatio
+ * \sa MIX_SetTrackFrequencyRatio
+ */
+extern SDL_DECLSPEC bool SDLCALL MIX_SetMasterFrequencyRatio(MIX_Mixer *mixer, float ratio);
+
+/**
+ * Get a mixer's master frequency ratio.
+ *
+ * This returns the last value set through MIX_SetMasterFrequencyRatio(), or
+ * 1.0f if no value has ever been explicitly set.
+ *
+ * \param mixer the mixer to query.
+ * \returns the mixer's current master frequency ratio.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL_mixer 3.0.0.
+ *
+ * \sa MIX_SetMasterFrequencyRatio
+ * \sa MIX_GetTrackFrequencyRatio
+ */
+extern SDL_DECLSPEC float SDLCALL MIX_GetMasterFrequencyRatio(MIX_Mixer *mixer);
+
+
 /**
  * Change the frequency ratio of a track.
  *
diff --git a/src/SDL_mixer.c b/src/SDL_mixer.c
index 5e235340..34402845 100644
--- a/src/SDL_mixer.c
+++ b/src/SDL_mixer.c
@@ -2613,12 +2613,29 @@ bool MIX_SetTagGain(MIX_Mixer *mixer, const char *tag, float gain)
     return true;
 }
 
-static bool SetTrackFrequencyRatio(MIX_Track *track, float ratio)
+bool MIX_SetMasterFrequencyRatio(MIX_Mixer *mixer, float ratio)
 {
-    // don't have to LockTrack, as SDL_SetAudioStreamFrequencyRatio will do that.
+    if (!CheckMixerParam(mixer)) {
+        return false;
+    }
+
+    ratio = SDL_clamp(ratio, 0.01f, 100.0f);   // !!! FIXME: this clamps, but should it fail instead?
+
+    // don't have to LockMixer, as SDL_SetAudioStreamFrequencyRatio will do that.
+    return SDL_SetAudioStreamFrequencyRatio(mixer->output_stream, ratio);
+}
+
+float MIX_GetMasterFrequencyRatio(MIX_Mixer *mixer)
+{
+    if (!CheckMixerParam(mixer)) {
+        return 0.0f;
+    }
+
+    // don't have to LockMixer, as SDL_GetAudioStreamFrequencyRatio will do that.
     //LockTrack(track);
-    const bool retval = SDL_SetAudioStreamFrequencyRatio(track->output_stream, ratio);
+    const float retval = SDL_GetAudioStreamFrequencyRatio(mixer->output_stream);
     //UnlockTrack(track);
+
     return retval;
 }
 
@@ -2630,7 +2647,8 @@ bool MIX_SetTrackFrequencyRatio(MIX_Track *track, float ratio)
 
     ratio = SDL_clamp(ratio, 0.01f, 100.0f);   // !!! FIXME: this clamps, but should it fail instead?
 
-    return SetTrackFrequencyRatio(track, ratio);
+    // don't have to LockTrack, as SDL_SetAudioStreamFrequencyRatio will do that.
+    return SDL_SetAudioStreamFrequencyRatio(track->output_stream, ratio);
 }
 
 float MIX_GetTrackFrequencyRatio(MIX_Track *track)
diff --git a/src/SDL_mixer.exports b/src/SDL_mixer.exports
index 1c92e31b..500c568f 100644
--- a/src/SDL_mixer.exports
+++ b/src/SDL_mixer.exports
@@ -88,4 +88,6 @@ _MIX_SetTrackLoops
 _MIX_GetTrackFadeFrames
 _MIX_GetTrackTags
 _MIX_GetTaggedTracks
+_MIX_SetMasterFrequencyRatio
+_MIX_GetMasterFrequencyRatio
 # extra symbols go here (don't modify this line)
diff --git a/src/SDL_mixer.sym b/src/SDL_mixer.sym
index 9f8d6759..3f2229f4 100644
--- a/src/SDL_mixer.sym
+++ b/src/SDL_mixer.sym
@@ -89,6 +89,8 @@ SDL3_mixer_0.0.0 {
     MIX_GetTrackFadeFrames;
     MIX_GetTrackTags;
     MIX_GetTaggedTracks;
+    MIX_SetMasterFrequencyRatio;
+    MIX_GetMasterFrequencyRatio;
     # extra symbols go here (don't modify this line)
   local: *;
 };