SDL_mixer: add back drmp3 as an alternative backend to minimp3

From 2049766f0bcb3cf6dceefa623de241a5f36cbf0b Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Mon, 10 Mar 2025 01:23:50 +0300
Subject: [PATCH] add back drmp3 as an alternative backend to minimp3

---
 Android.mk                                    |    5 +
 CMakeLists.txt                                |   13 +-
 VisualC/SDL_mixer.vcxproj                     |    2 +
 VisualC/SDL_mixer.vcxproj.filters             |    6 +
 Xcode/SDL_mixer.xcodeproj/project.pbxproj     |    8 +
 .../resources/CMake/SDL3_mixerConfig.cmake    |    1 +
 .../msvc/cmake/SDL3_mixerConfig.cmake.in      |    1 +
 build-scripts/release-info.json               |    3 +
 cmake/SDL3_mixerConfig.cmake.in               |    1 +
 src/codecs/dr_libs/dr_mp3.h                   | 4840 +++++++++++++++++
 src/codecs/music_drmp3.c                      |  294 +
 src/codecs/music_drmp3.h                      |   28 +
 src/music.c                                   |    4 +
 src/music.h                                   |    1 +
 14 files changed, 5205 insertions(+), 2 deletions(-)
 create mode 100644 src/codecs/dr_libs/dr_mp3.h
 create mode 100644 src/codecs/music_drmp3.c
 create mode 100644 src/codecs/music_drmp3.h

diff --git a/Android.mk b/Android.mk
index adae29c04..f6dad2e92 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,8 @@ VORBIS_LIBRARY_PATH := external/tremor
 
 # Enable this if you want to support loading MP3 music via MINIMP3
 SUPPORT_MP3_MINIMP3 ?= true
+# Enable this if you want to support loading MP3 music via dr_mp3
+SUPPORT_MP3_DRMP3 ?= false
 
 # Enable this if you want to support loading MP3 music via MPG123
 SUPPORT_MP3_MPG123 ?= false
@@ -132,6 +134,9 @@ endif
 ifeq ($(SUPPORT_MP3_MINIMP3),true)
     LOCAL_CFLAGS += -DMUSIC_MP3_MINIMP3
 endif
+ifeq ($(SUPPORT_MP3_DRMP3),true)
+    LOCAL_CFLAGS += -DMUSIC_MP3_DRMP3
+endif
 
 # This needs to be a shared library to comply with the LGPL license
 ifeq ($(SUPPORT_MP3_MPG123),true)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97686fc22..96d59ecc5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -149,12 +149,13 @@ endif()
 option(SDLMIXER_MP3 "Enable MP3 music" ON)
 
 cmake_dependent_option(SDLMIXER_MP3_MINIMP3 "Support loading MP3 music via minimp3" ON SDLMIXER_MP3 OFF)
+cmake_dependent_option(SDLMIXER_MP3_DRMP3 "Support loading MP3 music via dr_mp3" OFF SDLMIXER_MP3 OFF)
 
 cmake_dependent_option(SDLMIXER_MP3_MPG123 "Support loading MP3 music via MPG123" OFF SDLMIXER_MP3 OFF)
 cmake_dependent_option(SDLMIXER_MP3_MPG123_SHARED "Dynamically load mpg123" "${SDLMIXER_DEPS_SHARED}" SDLMIXER_MP3_MPG123 OFF)
 
-if(SDLMIXER_MP3 AND NOT (SDLMIXER_MP3_MINIMP3 OR SDLMIXER_MP3_MPG123))
-    message(FATAL_ERROR "MP3 support was enabled (SDLMIXER_MP3) but neither minimp3 (SDLMIXER_MP3_MINIMP3) or mpg123 (SDLMIXER_MP3_MPG123) were enabled.")
+if(SDLMIXER_MP3 AND NOT (SDLMIXER_MP3_MINIMP3 OR SDLMIXER_MP3_DRMP3 OR SDLMIXER_MP3_MPG123))
+    message(FATAL_ERROR "MP3 support was enabled (SDLMIXER_MP3) but neither drmp3 (SDLMIXER_MP3_DRMP3), minimp3 (SDLMIXER_MP3_MINIMP3) or mpg123 (SDLMIXER_MP3_MPG123) were enabled.")
 endif()
 
 option(SDLMIXER_MIDI "Enable MIDI music" ON)
@@ -251,6 +252,7 @@ add_library(${sdl3_mixer_target_name}
     src/codecs/load_sndfile.c
     src/codecs/mp3utils.c
     src/codecs/music_drflac.c
+    src/codecs/music_drmp3.c
     src/codecs/music_flac.c
     src/codecs/music_fluidsynth.c
     src/codecs/music_gme.c
@@ -832,6 +834,13 @@ if(SDLMIXER_MP3_MINIMP3)
     target_compile_definitions(${sdl3_mixer_target_name} PRIVATE MUSIC_MP3_MINIMP3)
 endif()
 
+list(APPEND SDLMIXER_BACKENDS MP3_DRMP3)
+set(SDLMIXER_MP3_DRMP3_ENABLED FALSE)
+if(SDLMIXER_MP3_DRMP3)
+    set(SDLMIXER_MP3_DRMP3_ENABLED TRUE)
+    target_compile_definitions(${sdl3_mixer_target_name} PRIVATE MUSIC_MP3_DRMP3)
+endif()
+
 list(APPEND SDLMIXER_BACKENDS MP3_MPG123)
 set(SDLMIXER_MP3_MPG123_ENABLED FALSE)
 if(SDLMIXER_MP3_MPG123)
diff --git a/VisualC/SDL_mixer.vcxproj b/VisualC/SDL_mixer.vcxproj
index 7317e5539..ca4f7a2fe 100644
--- a/VisualC/SDL_mixer.vcxproj
+++ b/VisualC/SDL_mixer.vcxproj
@@ -222,6 +222,7 @@
     <ClInclude Include="..\src\codecs\load_voc.h" />
     <ClInclude Include="..\src\codecs\mp3utils.h" />
     <ClInclude Include="..\src\codecs\music_drflac.h" />
+    <ClInclude Include="..\src\codecs\music_drmp3.h" />
     <ClInclude Include="..\src\codecs\music_flac.h" />
     <ClInclude Include="..\src\codecs\music_fluidsynth.h" />
     <ClInclude Include="..\src\codecs\music_gme.h" />
@@ -445,6 +446,7 @@
     <ClCompile Include="..\src\codecs\load_voc.c" />
     <ClCompile Include="..\src\codecs\mp3utils.c" />
     <ClCompile Include="..\src\codecs\music_drflac.c" />
+    <ClCompile Include="..\src\codecs\music_drmp3.c" />
     <ClCompile Include="..\src\codecs\music_flac.c" />
     <ClCompile Include="..\src\codecs\music_fluidsynth.c" />
     <ClCompile Include="..\src\codecs\music_gme.c" />
diff --git a/VisualC/SDL_mixer.vcxproj.filters b/VisualC/SDL_mixer.vcxproj.filters
index 024854f8d..97ab282c8 100644
--- a/VisualC/SDL_mixer.vcxproj.filters
+++ b/VisualC/SDL_mixer.vcxproj.filters
@@ -31,6 +31,9 @@
     <ClInclude Include="..\src\codecs\music_drflac.h">
       <Filter>Sources</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\codecs\music_drmp3.h">
+      <Filter>Sources</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\codecs\music_flac.h">
       <Filter>Sources</Filter>
     </ClInclude>
@@ -164,6 +167,9 @@
     <ClCompile Include="..\src\codecs\music_drflac.c">
       <Filter>Sources</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\codecs\music_drmp3.c">
+      <Filter>Sources</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\codecs\music_flac.c">
       <Filter>Sources</Filter>
     </ClCompile>
diff --git a/Xcode/SDL_mixer.xcodeproj/project.pbxproj b/Xcode/SDL_mixer.xcodeproj/project.pbxproj
index 3558ce332..1f2be8bc5 100644
--- a/Xcode/SDL_mixer.xcodeproj/project.pbxproj
+++ b/Xcode/SDL_mixer.xcodeproj/project.pbxproj
@@ -78,6 +78,8 @@
 		F307A5302B5473760012534B /* music_minimp3.h in Headers */ = {isa = PBXBuildFile; fileRef = F307A52E2B5473760012534B /* music_minimp3.h */; };
 		F3249B39285C448100DB9B5C /* CMake in Resources */ = {isa = PBXBuildFile; fileRef = F3249B36285C448100DB9B5C /* CMake */; };
 		F3412A412D4C950E00D6C2B7 /* SDL3.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3412A402D4C950E00D6C2B7 /* SDL3.framework */; };
+		F37A8D2D2838924900C38E95 /* music_drmp3.h in Headers */ = {isa = PBXBuildFile; fileRef = F37A8D2B2838924900C38E95 /* music_drmp3.h */; };
+		F37A8D2F2838924900C38E95 /* music_drmp3.c in Sources */ = {isa = PBXBuildFile; fileRef = F37A8D2C2838924900C38E95 /* music_drmp3.c */; };
 		F37A8D4A2838A23400C38E95 /* music_drflac.h in Headers */ = {isa = PBXBuildFile; fileRef = F37A8D412838A23400C38E95 /* music_drflac.h */; };
 		F37A8D502838A23400C38E95 /* music_drflac.c in Sources */ = {isa = PBXBuildFile; fileRef = F37A8D492838A23400C38E95 /* music_drflac.c */; };
 		F37A8DB52838AD1300C38E95 /* music_ogg_stb.c in Sources */ = {isa = PBXBuildFile; fileRef = F37A8DB42838AD1200C38E95 /* music_ogg_stb.c */; };
@@ -218,6 +220,8 @@
 		F307A52E2B5473760012534B /* music_minimp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = music_minimp3.h; sourceTree = "<group>"; };
 		F3249B36285C448100DB9B5C /* CMake */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CMake; sourceTree = "<group>"; };
 		F3412A402D4C950E00D6C2B7 /* SDL3.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL3.framework; path = macOS/SDL3.framework; sourceTree = "<group>"; };
+		F37A8D2B2838924900C38E95 /* music_drmp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = music_drmp3.h; sourceTree = "<group>"; };
+		F37A8D2C2838924900C38E95 /* music_drmp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = music_drmp3.c; sourceTree = "<group>"; };
 		F37A8D412838A23400C38E95 /* music_drflac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = music_drflac.h; sourceTree = "<group>"; };
 		F37A8D492838A23400C38E95 /* music_drflac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = music_drflac.c; sourceTree = "<group>"; };
 		F37A8DB42838AD1200C38E95 /* music_ogg_stb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = music_ogg_stb.c; sourceTree = "<group>"; };
@@ -322,6 +326,8 @@
 				639197F0239FE66700F1D8F8 /* mp3utils.h */,
 				F37A8D492838A23400C38E95 /* music_drflac.c */,
 				F37A8D412838A23400C38E95 /* music_drflac.h */,
+				F37A8D2C2838924900C38E95 /* music_drmp3.c */,
+				F37A8D2B2838924900C38E95 /* music_drmp3.h */,
 				AAE405D41F9607C100EDAF53 /* music_flac.c */,
 				AAE405C71F9607C000EDAF53 /* music_flac.h */,
 				AAE405C61F9607C000EDAF53 /* music_fluidsynth.c */,
@@ -459,6 +465,7 @@
 			files = (
 				AAE405E31F9607C300EDAF53 /* music_fluidsynth.h in Headers */,
 				AAE405F61F9607C300EDAF53 /* load_aiff.h in Headers */,
+				F37A8D2D2838924900C38E95 /* music_drmp3.h in Headers */,
 				F307A5302B5473760012534B /* music_minimp3.h in Headers */,
 				AAE405E71F9607C300EDAF53 /* load_voc.h in Headers */,
 				AAE405EC1F9607C300EDAF53 /* music_wav.h in Headers */,
@@ -669,6 +676,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				AAE405E21F9607C300EDAF53 /* load_aiff.c in Sources */,
+				F37A8D2F2838924900C38E95 /* music_drmp3.c in Sources */,
 				AAE405F01F9607C300EDAF53 /* load_voc.c in Sources */,
 				AAE405EA1F9607C300EDAF53 /* music_fluidsynth.c in Sources */,
 				AAE405FD1F9607C300EDAF53 /* music_ogg.c in Sources */,
diff --git a/Xcode/pkg-support/resources/CMake/SDL3_mixerConfig.cmake b/Xcode/pkg-support/resources/CMake/SDL3_mixerConfig.cmake
index 73e5c95b2..35c641c13 100644
--- a/Xcode/pkg-support/resources/CMake/SDL3_mixerConfig.cmake
+++ b/Xcode/pkg-support/resources/CMake/SDL3_mixerConfig.cmake
@@ -36,6 +36,7 @@ set(SDLMIXER_MOD_XMP_LITE          TRUE)
 
 set(SDLMIXER_MP3                   TRUE)
 set(SDLMIXER_MP3_MINIMP3           TRUE)
+set(SDLMIXER_MP3_DRMP3             FALSE)
 set(SDLMIXER_MP3_MPG123            FALSE)
 
 set(SDLMIXER_MIDI                  TRUE)
diff --git a/build-scripts/pkg-support/msvc/cmake/SDL3_mixerConfig.cmake.in b/build-scripts/pkg-support/msvc/cmake/SDL3_mixerConfig.cmake.in
index 969ed188d..736282dcf 100644
--- a/build-scripts/pkg-support/msvc/cmake/SDL3_mixerConfig.cmake.in
+++ b/build-scripts/pkg-support/msvc/cmake/SDL3_mixerConfig.cmake.in
@@ -35,6 +35,7 @@ set(SDLMIXER_MOD_XMP_LITE          FALSE)
 
 set(SDLMIXER_MP3                   TRUE)
 set(SDLMIXER_MP3_MINIMP3           TRUE)
+set(SDLMIXER_MP3_DRMP3             FALSE)
 set(SDLMIXER_MP3_MPG123            FALSE)
 
 set(SDLMIXER_MIDI                  TRUE)
diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json
index 8e71f91a0..ac6d6a79f 100644
--- a/build-scripts/release-info.json
+++ b/build-scripts/release-info.json
@@ -44,6 +44,7 @@
         "-DSDLMIXER_MOD_XMP=OFF",
         "-DSDLMIXER_MP3=ON",
         "-DSDLMIXER_MP3_MINIMP3=ON",
+        "-DSDLMIXER_MP3_DRMP3=OFF",
         "-DSDLMIXER_MP3_MPG123=OFF",
         "-DSDLMIXER_MIDI=ON",
         "-DSDLMIXER_MIDI_NATIVE=ON",
@@ -142,6 +143,7 @@
         "-DSDLMIXER_MOD_XMP=ON",
         "-DSDLMIXER_MP3=ON",
         "-DSDLMIXER_MP3_MINIMP3=ON",
+        "-DSDLMIXER_MP3_DRMP3=OFF",
         "-DSDLMIXER_MP3_MPG123=OFF",
         "-DSDLMIXER_MIDI=ON",
         "-DSDLMIXER_MIDI_NATIVE=ON",
@@ -231,6 +233,7 @@
         "-DSDLMIXER_MOD_XMP=OFF",
         "-DSDLMIXER_MP3=ON",
         "-DSDLMIXER_MP3_MINIMP3=ON",
+        "-DSDLMIXER_MP3_DRMP3=OFF",
         "-DSDLMIXER_MP3_MPG123=OFF",
         "-DSDLMIXER_MIDI=ON",
         "-DSDLMIXER_MIDI_NATIVE=ON",
diff --git a/cmake/SDL3_mixerConfig.cmake.in b/cmake/SDL3_mixerConfig.cmake.in
index 4e7e96236..5c5e5f11d 100644
--- a/cmake/SDL3_mixerConfig.cmake.in
+++ b/cmake/SDL3_mixerConfig.cmake.in
@@ -26,6 +26,7 @@ set(SDLMIXER_MOD_XMP_LITE          @SDLMIXER_MOD_XMP_ENABLED@)
 
 set(SDLMIXER_MP3                   @SDLMIXER_MP3_ENABLED@)
 set(SDLMIXER_MP3_MINIMP3           @SDLMIXER_MP3_MINIMP3_ENABLED@)
+set(SDLMIXER_MP3_DRMP3             @SDLMIXER_MP3_DRMP3_ENABLED@)
 set(SDLMIXER_MP3_MPG123            @SDLMIXER_MP3_MPG123_ENABLED@)
 
 set(SDLMIXER_MIDI                  @SDLMIXER_MIDI_ENABLED@)
diff --git a/src/codecs/dr_libs/dr_mp3.h b/src/codecs/dr_libs/dr_mp3.h
new file mode 100644
index 000000000..aee3cef9e
--- /dev/null
+++ b/src/codecs/dr_libs/dr_mp3.h
@@ -0,0 +1,4840 @@
+/*
+MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_mp3 - v0.6.40 - 2024-12-17
+
+David Reid - mackron@gmail.com
+
+GitHub: https://github.com/mackron/dr_libs
+
+Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for differences between minimp3 and dr_mp3.
+*/
+
+/*
+RELEASE NOTES - VERSION 0.6
+===========================
+Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been
+removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done
+yourself.
+
+
+Changes to Initialization
+-------------------------
+Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been
+removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed.
+
+    `drmp3_init()`
+    `drmp3_init_memory()`
+    `drmp3_init_file()`
+
+
+Miscellaneous Changes
+---------------------
+Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API.
+*/
+
+/*
+Introduction
+=============
+dr_mp3 is a single file library. To use it, do something like the following in one .c file.
+
+    ```c
+    #define DR_MP3_IMPLEMENTATION
+    #include "dr_mp3.h"
+    ```
+
+You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following:
+
+    ```c
+    drmp3 mp3;
+    if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
+        // Failed to open file
+    }
+
+    ...
+
+    drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames);
+    ```
+
+The drmp3 object is transparent so you can get access to the channel count and sample rate like so:
+
+    ```
+    drmp3_uint32 channels = mp3.channels;
+    drmp3_uint32 sampleRate = mp3.sampleRate;
+    ```
+
+The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek callbacks with
+`drmp3_init_memory()` and `drmp3_init()` respectively.
+
+You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request any number of PCM frames in each
+call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount.
+
+You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, `drmp3_open_memory_and_read_pcm_frames_f32()` and
+`drmp3_open_file_and_read_pcm_frames_f32()`.
+
+
+Build Options
+=============
+#define these options before including this file.
+
+#define DR_MP3_NO_STDIO
+  Disable drmp3_init_file(), etc.
+
+#define DR_MP3_NO_SIMD
+  Disable SIMD optimizations.
+*/
+
+#ifndef dr_mp3_h
+#define dr_mp3_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DRMP3_STRINGIFY(x)      #x
+#define DRMP3_XSTRINGIFY(x)     DRMP3_STRINGIFY(x)
+
+#define DRMP3_VERSION_MAJOR     0
+#define DRMP3_VERSION_MINOR     6
+#define DRMP3_VERSION_REVISION  40
+#define DRMP3_VERSION_STRING    DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
+
+#include <stddef.h> /* For size_t. */
+
+/* Sized Types */
+typedef   signed char           drmp3_int8;
+typedef unsigned char           drmp3_uint8;
+typedef   signed short          drmp3_int16;
+typedef unsigned short          drmp3_uint16;
+typedef   signed int            drmp3_int32;
+typedef unsigned int            drmp3_uint32;
+#if defined(_MSC_VER) && !defined(__clang__)
+    typedef   signed __int64    drmp3_int64;
+    typedef unsigned __int64    drmp3_uint64;
+#else
+    #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
+        #pragma GCC diagnostic push
+        #pragma GCC diagnostic ignored "-Wlong-long"
+        #if defined(__clang__)
+            #pragma GCC diagnostic ignored "-Wc++11-long-long"
+        #endif
+    #endif
+    typedef   signed long long  drmp3_int64;
+    typedef unsigned long long  drmp3_uint64;
+    #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
+        #pragma GCC diagnostic pop
+    #endif
+#endif
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__)
+    typedef drmp3_uint64        drmp3_uintptr;
+#else
+    typedef drmp3_uint32        drmp3_uintptr;
+#endif
+typedef drmp3_uint8             drmp3_bool8;
+typedef drmp3_uint32            drmp3_bool32;
+#define DRMP3_TRUE              1
+#define DRMP3_FALSE             0
+/* End Sized Types */
+
+/* Decorations */
+#if !defined(DRMP3_API)
+    #if defined(DRMP3_DLL)
+        #if defined(_WIN32)
+            #define DRMP3_DLL_IMPORT  __declspec(dllimport)
+            #define DRMP3_DLL_EXPORT  __declspec(dllexport)
+            #define DRMP3_DLL_PRIVATE static
+        #else
+            #if defined(__GNUC__) && __GNUC__ >= 4
+                #define DRMP3_DLL_IMPORT  __attribute__((visibility("default")))
+                #define DRMP3_DLL_EXPORT  __attribute__((visibility("default")))
+                #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
+            #else
+                #define DRMP3_DLL_IMPORT
+                #define DRMP3_DLL_EXPORT
+                #define DRMP3_DLL_PRIVATE static
+            #endif
+        #endif
+
+        #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
+            #define DRMP3_API  DRMP3_DLL_EXPORT
+        #else
+            #define DRMP3_API  DRMP3_DLL_IMPORT
+        #endif
+        #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
+    #else
+        #define DRMP3_API extern
+        #define DRMP3_PRIVATE static
+    #endif
+#endif
+/* End Decorations */
+
+/* Result Codes */
+typedef drmp3_int32 drmp3_result;
+#define DRMP3_SUCCESS                        0
+#define DRMP3_ERROR                         -1   /* A generic error. */
+#define DRMP3_INVALID_ARGS                  -2
+#define DRMP3_INVALID_OPERATION             -3
+#define DRMP3_OUT_OF_MEMORY                 -4
+#define DRMP3_OUT_OF_RANGE                  -5
+#define DRMP3_ACCESS_DENIED                 -6
+#define DRMP3_DOES_NOT_EXIST                -7
+#define DRMP3_ALREADY_EXISTS                -8
+#define DRMP3_TOO_MANY_OPEN_FILES           -9
+#define DRMP3_INVALID_FILE                  -10
+#define DRMP3_TOO_BIG                       -11
+#define DRMP3_PATH_TOO_LONG                 -12
+#define DRMP3_NAME_TOO_LONG                 -13
+#define DRMP3_NOT_DIRECTORY                 -14
+#define DRMP3_IS_DIRECTORY                  -15
+#define DRMP3_DIRECTORY_NOT_EMPTY           -16
+#define DRMP3_END_OF_FILE                   -17
+#define DRMP3_NO_SPACE                      -18
+#define DRMP3_BUSY                          -19
+#define DRMP3_IO_ERROR                      -20
+#define DRMP3_INTERRUPT                     -21
+#define DRMP3_UNAVAILABLE                   -22
+#define DRMP3_ALREADY_IN_USE                -23
+#define DRMP3_BAD_ADDRESS                   -24
+#define DRMP3_BAD_SEEK                      -25
+#define DRMP3_BAD_PIPE                      -26
+#define DRMP3_DEADLOCK                      -27
+#define DRMP3_TOO_MANY_LINKS                -28
+#define DRMP3_NOT_IMPLEMENTED               -29
+#define DRMP3_NO_MESSAGE                    -30
+#define DRMP3_BAD_MESSAGE                   -31
+#define DRMP3_NO_DATA_AVAILABLE             -32
+#define DRMP3_INVALID_DATA                  -33
+#define DRMP3_TIMEOUT                       -34
+#define DRMP3_NO_NETWORK                    -35
+#define DRMP3_NOT_UNIQUE                    -36
+#define DRMP3_NOT_SOCKET                    -37
+#define DRMP3_NO_ADDRESS                    -38
+#define DRMP3_BAD_PROTOCOL                  -39
+#define DRMP3_PROTOCOL_UNAVAILABLE          -40
+#define DRMP3_PROTOCOL_NOT_SUPPORTED        -41
+#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
+#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED  -43
+#define DRMP3_SOCKET_NOT_SUPPORTED          -44
+#define DRMP3_CONNECTION_RESET              -45
+#define DRMP3_ALREADY_CONNECTED             -46
+#define DRMP3_NOT_CONNECTED                 -47
+#define DRMP3_CONNECTION_REFUSED            -48
+#define DRMP3_NO_HOST                       -49
+#define DRMP3_IN_PROGRESS                   -50
+#define DRMP3_CANCELLED                     -51
+#define DRMP3_MEMORY_ALREADY_MAPPED         -52
+#define DRMP3_AT_END                        -53
+/* End Result Codes */
+
+#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME  1152
+#define DRMP3_MAX_SAMPLES_PER_FRAME         (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
+
+/* Inline */
+#ifdef _MSC_VER
+    #define DRMP3_INLINE __forceinline
+#elif defined(__GNUC__)
+    /*
+    I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
+    the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
+    case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
+    command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
+    I am using "__inline__" only when we're compiling in strict ANSI mode.
+    */
+    #if defined(__STRICT_ANSI__)
+        #define DRMP3_GNUC_INLINE_HINT __inline__
+    #else
+        #define DRMP3_GNUC_INLINE_HINT inline
+    #endif
+
+    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
+        #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline))
+    #else
+        #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT
+    #endif
+#elif defined(__WATCOMC__)
+    #define DRMP3_INLINE __inline
+#else
+    #define DRMP3_INLINE
+#endif
+/* End Inline */
+
+
+DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
+DRMP3_API const char* drmp3_version_string(void);
+
+
+/* Allocation Callbacks */
+typedef struct
+{
+    void* pUserData;
+    void* (* onMalloc)(size_t sz, void* pUserData);
+    void* (* onRealloc)(void* p, size_t sz, void* pUserData);
+    void  (* onFree)(void* p, void* pUserData);
+} drmp3_allocation_callbacks;
+/* End Allocation Callbacks */
+
+
+/*
+Low Level Push API
+==================
+*/
+typedef struct
+{
+    int frame_bytes, channels, hz, layer, bitrate_kbps;
+} drmp3dec_frame_info;
+
+typedef struct
+{
+    float mdct_overlap[2][9*32], qmf_state[15*2*32];
+    int reserv, free_format_bytes;
+    drmp3_uint8 header[4], reserv_buf[511];
+} drmp3dec;
+
+/* Initializes a low level decoder. */
+DRMP3_API void drmp3dec_init(drmp3dec *dec);
+
+/* Reads a frame from a low level decoder. */
+DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
+
+/* Helper for converting between f32 and s16. */
+DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
+
+
+
+/*
+Main API (Pull API)
+===================
+*/
+typedef enum
+{
+    drmp3_seek_origin_start,
+    drmp3_seek_origin_current
+} drmp3_seek_origin;
+
+typedef struct
+{
+    drmp3_uint64 seekPosInBytes;        /* Points to the first byte of an MP3 frame. */
+    drmp3_uint64 pcmFrameIndex;         /* The index of the PCM frame this seek point targets. */
+    drmp3_uint16 mp3FramesToDiscard;    /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */
+    drmp3_uint16 pcmFramesToDiscard;    /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */
+} drmp3_seek_point;
+
+/*
+Callback for when data is read. Return value is the number of bytes actually read.
+
+pUserData   [in]  The user data that was passed to drmp3_init(), drmp3_open() and family.
+pBufferOut  [out] The output buffer.
+bytesToRead [in]  The number of bytes to read.
+
+Returns the number of bytes actually read.
+
+A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+either the entire bytesToRead is filled or you have reached the end of the stream.
+*/
+typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+/*
+Callback for when data needs to be seeked.
+
+pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
+offset    [in] The number of bytes to move, relative to the origin. Will never be negative.
+origin    [in] The origin of the seek - the current position or the start of the stream.
+
+Returns whether or not the seek was successful.
+
+Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
+*/
+typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
+
+typedef struct
+{
+    drmp3_uint32 channels;
+    drmp3_uint32 sampleRate;
+} drmp3_config;
+
+typedef struct
+{
+    drmp3dec decoder;
+    drmp3_uint32 channels;
+    drmp3_uint32 sampleRate;
+    drmp3_read_proc onRead;
+    drmp3_seek_proc onSeek;
+    void* pUserData;
+    drmp3_allocation_callbacks allocationCallbacks;
+    drmp3_uint32 mp3FrameChannels;      /* The number of channels in the currently loaded MP3 frame. Internal use only. */
+    drmp3_uint32 mp3FrameSampleRate;    /* The sample rate of the currently loaded MP3 frame. Internal use only. */
+    drmp3_uint32 pcmFramesConsumedInMP3Frame;
+    drmp3_uint32 pcmFramesRemainingInMP3Frame;
+    drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];  /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */
+    drmp3_uint64 currentPCMFrame;       /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */
+    drmp3_uint64 streamCursor;          /* The current byte the decoder is sitting on in the raw stream. */
+    drmp3_seek_point* pSeekPoints;      /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */
+    drmp3_uint32 seekPointCount;        /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */
+    size_t dataSize;
+    size_t dataCapacity;
+    size_t dataConsumed;
+    drmp3_uint8* pData;
+    drmp3_bool32 atEnd : 1;
+    struct
+    {
+        const drmp3_uint8* pData;
+        size_t dataSize;
+        size_t currentReadPos;
+    } memory;   /* Only used for decoders that were opened against a block of memory. */
+} drmp3;
+
+/*
+Initializes an MP3 decoder.
+
+onRead    [in]           The function to call when data needs to be read from the client.
+onSeek    [in]           The function to call when the read position of the client data needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+
+Returns true if successful; false otherwise.
+
+Close the loader with drmp3_uninit().
+
+See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
+*/
+DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Initializes an MP3 decoder from a block of memory.
+
+This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+the lifetime of the drmp3 object.
+
+The buffer should contain the contents of the entire MP3 file.
+*/
+DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+#ifndef DR_MP3_NO_STDIO
+/*
+Initializes an MP3 decoder from a file.
+
+This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
+DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
+DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
+#endif
+
+/*
+Uninitializes an MP3 decoder.
+*/
+DRMP3_API void drmp3_uninit(drmp3* pMP3);
+
+/*
+Reads PCM frames as interleaved 32-bit IEEE floating point PCM.
+
+Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+*/
+DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
+
+/*
+Reads PCM frames as interleaved signed 16-bit integer PCM.
+
+Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+*/
+DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
+
+/*
+Seeks to a specific frame.
+
+Note that this is _not_ an MP3 frame, but rather a PCM frame.
+*/
+DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
+
+/*
+Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
+DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
+
+/*
+Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
+DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
+
+/*
+Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+
+This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient.
+*/
+DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
+
+/*
+Calculates the seekpoints based on PCM frames. This is slow.
+
+pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count.
+On output it contains the actual count. The reason for this design is that the client may request too many
+seekpoints, in w

(Patch may be truncated, please check the link at the top of this post.)