SDL_mixer: api: MIX_UntagTrack(track, NULL) will now untag everything on `track`.

From e1c207577196df9808a4536311089d1be929fb57 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 29 Dec 2025 13:55:20 -0500
Subject: [PATCH] api: MIX_UntagTrack(track, NULL) will now untag everything on
 `track`.

Reference Issue #759.
---
 include/SDL3_mixer/SDL_mixer.h |  4 +++-
 src/SDL_mixer.c                | 24 ++++++++++++++++--------
 2 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/include/SDL3_mixer/SDL_mixer.h b/include/SDL3_mixer/SDL_mixer.h
index 64049b6dc..0acab79f1 100644
--- a/include/SDL3_mixer/SDL_mixer.h
+++ b/include/SDL3_mixer/SDL_mixer.h
@@ -1244,8 +1244,10 @@ extern SDL_DECLSPEC bool SDLCALL MIX_TagTrack(MIX_Track *track, const char *tag)
  * It's legal to remove a tag that the track doesn't have; this function
  * doesn't report errors, so this simply does nothing.
  *
+ * Specifying a NULL tag will remove all tags on a track.
+ *
  * \param track the track from which to remove a tag.
- * \param tag the tag to remove.
+ * \param tag the tag to remove, or NULL to remove all current tags.
  *
  * \threadsafety It is safe to call this function from any thread.
  *
diff --git a/src/SDL_mixer.c b/src/SDL_mixer.c
index 2501c037d..637bc03a4 100644
--- a/src/SDL_mixer.c
+++ b/src/SDL_mixer.c
@@ -1821,19 +1821,27 @@ bool MIX_TagTrack(MIX_Track *track, const char *tag)
 
 void MIX_UntagTrack(MIX_Track *track, const char *tag)
 {
-    if (!CheckTrackTagParam(track, tag)) {
+    if (!CheckTrackParam(track)) {
         return;  // do nothing.
     }
 
     const SDL_PropertiesID tags = track->tags;
-
-    SDL_LockProperties(tags);
-    if (SDL_GetBooleanProperty(tags, tag, false)) {  // if tag isn't there, nothing to do.
-        SDL_assert(SDL_GetPointerProperty(track->mixer->track_tags, tag, NULL) != NULL);  // shouldn't be NULL, there's definitely a track with this tag!
-        RemoveTrackFromMixerTagList(track->mixer, track, tag);
-        SDL_SetBooleanProperty(tags, tag, false);
+    if (!tag) {  // untag everything on the track.
+        SDL_EnumerateProperties(tags, RemoveTrackFromAllMixerTagLists, track);
+        const SDL_PropertiesID new_tags = SDL_CreateProperties();
+        LockTrack(track);
+        track->tags = new_tags;
+        UnlockTrack(track);
+        SDL_DestroyProperties(tags);  // just nuke all the tags and start over.
+    } else {
+        SDL_LockProperties(tags);
+        if (SDL_GetBooleanProperty(tags, tag, false)) {  // if tag isn't there, nothing to do.
+            SDL_assert(SDL_GetPointerProperty(track->mixer->track_tags, tag, NULL) != NULL);  // shouldn't be NULL, there's definitely a track with this tag!
+            RemoveTrackFromMixerTagList(track->mixer, track, tag);
+            SDL_SetBooleanProperty(tags, tag, false);
+        }
+        SDL_UnlockProperties(tags);
     }
-    SDL_UnlockProperties(tags);
 }
 
 bool MIX_SetTrackPlaybackPosition(MIX_Track *track, Sint64 frames)