Switch Linux/Dolphin MP3 decode from minimp3 to libmad

minimp3's sliding-window frame confirmation could spuriously discard real
frames near the tail of a not-yet-full window, losing ~5.5% of a real
VBR file's content even at a tuned 256KB window (~0.3% residual) -
audible as the whole track finishing early with stutters at each drop.

libmad's mad_stream/mad_frame/mad_synth API reports "need more data"
(MAD_ERROR_BUFLEN) and "genuinely bad data" separately rather than
overloading one return value, which was the actual ambiguity minimp3
couldn't resolve. Verified against the same real file with a 32KB window
(vs minimp3's 256KB): frame count and duration match the Xing header
exactly. Confirmed building for Linux, GameCube, and Wii (the latter two
via the project's real devkitPPC/libogc Docker toolchain) - libmad ships
inside libogc itself on Dolphin, no separate fetch needed there.

libmad is GPL-licensed, unlike the rest of this MIT project - a
deliberate tradeoff, noted at each site that pulls it in.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-09-02 09:47:15 -05:00
co-authored by Claude Sonnet 5
parent 3ee0a53688
commit fd5bcfae90
11 changed files with 256 additions and 151 deletions
+32
View File
@@ -0,0 +1,32 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# NOTE: libmad itself is GPL-licensed (see https://www.underbit.com/products/mad/),
# not MIT like the rest of this project. Any binary linking it must comply
# with the GPL - a deliberate tradeoff accepted for MP3 decode correctness
# (see audiostreammp3decodersw.c's own comment on why minimp3 wasn't enough).
find_path(LIBMAD_INCLUDE_DIR NAMES mad.h)
mark_as_advanced(LIBMAD_INCLUDE_DIR)
find_library(LIBMAD_LIBRARY NAMES mad)
mark_as_advanced(LIBMAD_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
libmad
REQUIRED_VARS
LIBMAD_LIBRARY
LIBMAD_INCLUDE_DIR
)
if(libmad_FOUND AND NOT TARGET libmad::mad)
add_library(libmad::mad UNKNOWN IMPORTED)
set_target_properties(libmad::mad
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${LIBMAD_INCLUDE_DIR}
IMPORTED_LOCATION ${LIBMAD_LIBRARY}
)
endif()
-28
View File
@@ -1,28 +0,0 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# minimp3 has no tagged releases - pinned to a specific commit instead of a
# branch for reproducibility.
include(FetchContent)
FetchContent_Declare(
minimp3
GIT_REPOSITORY https://github.com/lieff/minimp3.git
GIT_TAG ea99364f61c14656440e8d77e9c233ccf3124633
)
FetchContent_MakeAvailable(minimp3)
set(MINIMP3_INCLUDE_DIR "${minimp3_SOURCE_DIR}")
set(MINIMP3_HEADER "${minimp3_SOURCE_DIR}/minimp3.h")
if(EXISTS "${MINIMP3_HEADER}")
add_library(minimp3 INTERFACE)
target_include_directories(minimp3 INTERFACE "${MINIMP3_INCLUDE_DIR}")
set(minimp3_FOUND TRUE)
else()
set(minimp3_FOUND FALSE)
endif()
mark_as_advanced(MINIMP3_INCLUDE_DIR MINIMP3_HEADER)
+1
View File
@@ -62,6 +62,7 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
z
lzma
ansnd
mad
)
if(DUSK_DOLPHIN_BUILD_TYPE STREQUAL "ISO")