SDL2_mixer-2.8.0/0000755000076500000240000000000014553251273012440 5ustar valvestaffSDL2_mixer-2.8.0/configure.ac0000644000076500000240000012742314553251241014732 0ustar valvestaffdnl Process this file with autoconf to produce a configure script. dnl Set various version strings - taken gratefully from the GTk sources # See release_checklist.md m4_define([MAJOR_VERSION_MACRO], [2]) m4_define([MINOR_VERSION_MACRO], [8]) m4_define([MICRO_VERSION_MACRO], [0]) AC_INIT([SDL2_mixer], [MAJOR_VERSION_MACRO.MINOR_VERSION_MACRO.MICRO_VERSION_MACRO], [https://github.com/libsdl-org/SDL_mixer/issues], [SDL2_mixer]) AC_CONFIG_SRCDIR([src/mixer.c]) AC_CONFIG_AUX_DIR(build-scripts) AC_SUBST([MAJOR_VERSION], MAJOR_VERSION_MACRO) AC_SUBST([MINOR_VERSION], MINOR_VERSION_MACRO) AC_SUBST([MICRO_VERSION], MICRO_VERSION_MACRO) BINARY_AGE=`expr $MINOR_VERSION \* 100 + $MICRO_VERSION` AS_CASE(["$MINOR_VERSION"], [*@<:@02468@:>@], dnl Stable branch, 2.6.1 -> libSDL2_mixer-2.0.so.0.600.1 [INTERFACE_AGE="$MICRO_VERSION"], [*], dnl Development branch, 2.5.1 -> libSDL2_mixer-2.0.so.0.501.0 [INTERFACE_AGE=0]) AC_SUBST(MAJOR_VERSION) AC_SUBST(MINOR_VERSION) AC_SUBST(MICRO_VERSION) AC_SUBST(INTERFACE_AGE) AC_SUBST(BINARY_AGE) # Automake defines VERSION to be the same as PACKAGE_VERSION, but we're # not using Automake in SDL_mixer, so we need to set up the @VERSION@ # substitution for SDL2_mixer.pc ourselves AC_SUBST([VERSION], [$PACKAGE_VERSION]) dnl libtool versioning LT_INIT([win32-dll]) # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_mixer-2.0.so.0. # TODO: in SDL 3, stop using -release, which will simplify it to libSDL3_mixer.so.0 LT_RELEASE=2.0 # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. LT_MAJOR=0 LT_REVISION=$INTERFACE_AGE LT_AGE=`expr $BINARY_AGE - $INTERFACE_AGE` LT_CURRENT=`expr $LT_MAJOR + $LT_AGE` LT_EXTRA="" m4_pattern_allow([^LT_]) AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_EXTRA) dnl Detect the canonical build and host environments dnl AC_CANONICAL_HOST dnl Check for tools AC_PROG_AWK AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_PROG_FGREP AC_PROG_MAKE_SET LT_PROG_RC PKG_PROG_PKG_CONFIG if [ test -z "$AWK" ]; then AC_MSG_ERROR([*** awk not found, aborting]) fi AC_CHECK_PROGS([SORT], [gsort sort], [false]) AS_IF([! "$SORT" -V /dev/null], [AC_MSG_WARN([sort(1) that supports the -V option is required to find dynamic libraries])]) dnl Set up the compiler and linker flags case "$host" in *-*-cygwin*) # We build SDL on cygwin without the UNIX emulation layer BASE_CFLAGS="-I/usr/include/mingw" BASE_LDFLAGS="" have_no_cygwin=no AC_MSG_CHECKING(for GCC -mno-cygwin option) save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS -mno-cygwin" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [have_no_cygwin=yes],[]) AC_MSG_RESULT($have_no_cygwin) CFLAGS="$save_CFLAGS" if test x$have_no_cygwin = xyes; then BASE_CFLAGS="$BASE_CFLAGS -mno-cygwin" BASE_LDFLAGS="-mno-cygwin" fi ;; *-*-os2*) # disable static builds on os/2 enable_static=no # -DBUILD_SDL is needed for DECLSPEC BASE_CFLAGS="-DBUILD_SDL" BASE_LDFLAGS="" # OS/2 does not support a DLL name longer than 8 characters. LT_EXTRA="-os2dllname SDL2mix" ;; *) BASE_CFLAGS="-D_GNU_SOURCE=1" BASE_LDFLAGS="" ;; esac BUILD_CFLAGS="$CFLAGS $CPPFLAGS -I$srcdir/include -I$srcdir/src -I$srcdir/src/codecs" EXTRA_CFLAGS="$INCLUDE $BASE_CFLAGS" BUILD_LDFLAGS="$LDFLAGS" EXTRA_LDFLAGS="$BASE_LDFLAGS" MIXER_LIBS= ## These are common directories to find software packages #for path in /usr/freeware /usr/pkg /usr/local; do # if test -d $path/include; then # EXTRA_CFLAGS="$EXTRA_CFLAGS -I$path/include" # fi # if test -d $path/lib; then # EXTRA_LDFLAGS="$EXTRA_LDFLAGS -L$path/lib" # fi #done CPPFLAGS="$CPPFLAGS $EXTRA_CFLAGS" CFLAGS="$CFLAGS $EXTRA_CFLAGS" LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS" dnl See whether we can use gcc style dependency tracking AC_ARG_ENABLE(dependency-tracking, [AS_HELP_STRING([--enable-dependency-tracking], [Use gcc -MMD -MT dependency tracking [default=yes]])], , enable_dependency_tracking=yes) if test x$enable_dependency_tracking = xyes; then have_gcc_mmd_mt=no AC_MSG_CHECKING(for GCC -MMD -MT option) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(__GNUC__) || __GNUC__ < 3 #error Dependency tracking requires GCC 3.0 or newer #endif ]],[])], [have_gcc_mmd_mt=yes],[]) AC_MSG_RESULT($have_gcc_mmd_mt) if test x$have_gcc_mmd_mt = xyes; then DEPENDENCY_TRACKING_OPTIONS="-MMD -MT \$@" fi fi case "$host" in *-*-cygwin* | *-*-mingw*) VERSION_SOURCES="$srcdir/version.rc" EXE=".exe" if test "$build" != "$host"; then # cross-compiling # Default cross-compile location ac_default_prefix=/usr/local/cross-tools/$host else # Look for the location of the tools and install there if test "$BUILD_PREFIX" != ""; then ac_default_prefix=$BUILD_PREFIX elif test "$MINGW_PREFIX" != ""; then ac_default_prefix=$MINGW_PREFIX fi fi ;; *-*-os2*) EXE=".exe" ;; *) EXE="" ;; esac # Standard C sources SOURCES=`ls $srcdir/src/*.c $srcdir/src/codecs/*.c | fgrep -v playwave.c | fgrep -v playmus.c` dnl set this to use on systems that use lib64 instead of lib base_bindir=`echo \${bindir} | sed 's/.*\/\(.*\)/\1/; q'` base_libdir=`echo \${libdir} | sed 's/.*\/\(.*\)/\1/; q'` CheckNoUndef() { AC_MSG_CHECKING(for linker option --no-undefined) have_no_undefined=no case "${host_os}" in dnl Skip this on platforms where it is just simply busted openbsd*) ;; darwin*) have_no_undefined="-Wl,-undefined,error" BUILD_LDFLAGS="$BUILD_LDFLAGS -Wl,-undefined,error" ;; *) save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--no-undefined" AC_LINK_IFELSE([AC_LANG_PROGRAM],[have_no_undefined=yes BUILD_LDFLAGS="$BUILD_LDFLAGS -Wl,--no-undefined"]) LDFLAGS="$save_LDFLAGS" ;; esac AC_MSG_RESULT($have_no_undefined) } dnl See if GCC's -Wall is supported. CheckWarnAll() { AC_MSG_CHECKING(for GCC -Wall option) have_gcc_Wall=no save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS -Wall" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int x = 0;])], [have_gcc_Wall=yes]) AC_MSG_RESULT($have_gcc_Wall) CFLAGS="$save_CFLAGS" if test x$have_gcc_Wall = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -Wall" dnl Haiku headers use multicharacter constants all over the place. Ignore these warnings when using -Wall. AC_MSG_CHECKING(for necessary GCC -Wno-multichar option) need_gcc_Wno_multichar=no case "$host" in *-*-haiku*) need_gcc_Wno_multichar=yes ;; esac AC_MSG_RESULT($need_gcc_Wno_multichar) if test x$need_gcc_Wno_multichar = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -Wno-multichar" fi fi } dnl See if GCC's -fvisibility=hidden is supported (gcc4 and later, usually). CheckVisibilityHidden() { AC_MSG_CHECKING(for GCC -fvisibility=hidden option) have_gcc_fvisibility=no case "$host" in *-*-cygwin* | *-*-mingw* | *-*-os2*) AC_MSG_RESULT([ignored for $host_os]) return ;; esac visibility_CFLAGS="-fvisibility=hidden" save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS $visibility_CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error SDL only uses visibility attributes in GCC 4 or newer #endif ]],[])], [have_gcc_fvisibility=yes],[]) AC_MSG_RESULT($have_gcc_fvisibility) CFLAGS="$save_CFLAGS" if test x$have_gcc_fvisibility = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS $visibility_CFLAGS" fi } dnl Function to find a library in the compiler search path find_lib() { gcc_bin_path=[`$CC -print-search-dirs 2>/dev/null | $FGREP programs: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`] gcc_lib_path=[`$CC -print-search-dirs 2>/dev/null | $FGREP libraries: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`] env_lib_path=[`echo $LIBS $LDFLAGS $* | sed 's/-L[ ]*//g'`] env_bin_path=`echo $env_lib_path | sed 's,/lib,/bin,'` if test "$cross_compiling" = yes; then host_lib_path="" else host_lib_path="$ac_default_prefix/$base_libdir $ac_default_prefix/$base_bindir /usr/$base_libdir /usr/local/$base_libdir" fi for path in $env_bin_path $env_lib_path $gcc_bin_path $gcc_lib_path $host_lib_path; do lib=[`ls -- $path/$1 2>/dev/null | sed 's,.*/,,' | $SORT -V -r | $AWK 'BEGIN{FS="."}{ print NF, $0 }' | $SORT -n -s | sed 's,[0-9]* ,,' | head -1`] if test x$lib != x; then echo $lib return fi done } dnl Check for SDL SDL_VERSION=2.0.9 AM_PATH_SDL2($SDL_VERSION, :, AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) ) CFLAGS="$CFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" EXTRA_CFLAGS="$EXTRA_CFLAGS $SDL_CFLAGS" dnl For use in static assertions EXTRA_CFLAGS="$EXTRA_CFLAGS -DSDL_BUILD_MAJOR_VERSION=$MAJOR_VERSION -DSDL_BUILD_MINOR_VERSION=$MINOR_VERSION -DSDL_BUILD_MICRO_VERSION=$MICRO_VERSION" dnl check for GCC warning options CheckWarnAll dnl check for GCC visibility attributes CheckVisibilityHidden dnl Check for math library AC_CHECK_LIB(m, pow, [LIBM="-lm"]) AC_CHECK_HEADERS([signal.h], [EXTRA_CFLAGS="$EXTRA_CFLAGS -DHAVE_SIGNAL_H"]) AC_CHECK_FUNCS(setbuf, [EXTRA_CFLAGS="$EXTRA_CFLAGS -DHAVE_SETBUF"]) dnl Check command-line options SDL2MIXER_CMD=0 AC_ARG_ENABLE([music-cmd], [AS_HELP_STRING([--enable-music-cmd], [support an external music player [default=yes]])], [], [enable_music_cmd=detect]) if test "x$enable_music_cmd" != xno; then AC_CHECK_FUNCS([fork vfork]) if test "x$ac_cv_func_fork" = "xyes"; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DHAVE_FORK" elif test "x$ac_cv_func_vfork" = "xyes"; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DHAVE_VFORK" elif test "x$enable_music_cmd" = "xyes"; then AC_MSG_ERROR([external music player not available on your platform]) else enable_music_cmd=no fi if test "x$enable_music_cmd" != xno; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_CMD" SDL2MIXER_CMD=1 fi fi SDL2MIXER_WAVE=0 AC_ARG_ENABLE([music-wave], [AS_HELP_STRING([--enable-music-wave], [enable streaming WAVE music [default=yes]])], [], [enable_music_wave=yes]) if test x$enable_music_wave = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_WAV" SDL2MIXER_WAVE=1 fi AC_ARG_ENABLE([music-mod], [AS_HELP_STRING([--enable-music-mod], [enable MOD music [default=yes]])], [], [enable_music_mod=yes]) SDL2MIXER_MOD_MODPLUG=0 AC_ARG_ENABLE([music-mod-modplug], [AS_HELP_STRING([--enable-music-mod-modplug], [enable MOD music via modplug [default=no]])], [], [enable_music_mod_modplug=no]) AC_ARG_ENABLE([music-mod-modplug-shared], [AS_HELP_STRING([--enable-music-mod-modplug-shared], [dynamically load modplug library [default=yes]])], [], [enable_music_mod_modplug_shared=yes]) if test x$enable_music_mod = xyes -a x$enable_music_mod_modplug = xyes; then PKG_CHECK_MODULES([MODPLUG], [libmodplug >= 0.8.8], [dnl have_libmodplug_hdr=yes have_libmodplug_lib=yes have_libmodplug_pc=yes ], [dnl AC_CHECK_HEADER([libmodplug/modplug.h], [have_libmodplug_hdr=yes]) AC_CHECK_LIB([modplug], [ModPlug_Load], [have_libmodplug_lib=yes;MODPLUG_LIBS="-lmodplug"]) ]) if test x$have_libmodplug_hdr = xyes -a x$have_libmodplug_lib = xyes; then have_libmodplug=yes case "$host" in *-*-darwin*) modplug_lib=[`find_lib "libmodplug.[0-9]*.dylib"`] if test x$modplug_lib = x; then modplug_lib=[`find_lib libmodplug.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) modplug_lib=[`find_lib "libmodplug*.dll"`] ;; *) modplug_lib=[`find_lib "libmodplug[0-9]*.so.*"`] if test x$modplug_lib = x; then modplug_lib=[`find_lib "libmodplug.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MOD_MODPLUG $MODPLUG_CFLAGS" if test x$enable_music_mod_modplug_shared = xyes && test x$modplug_lib != x; then echo "-- dynamic libmodplug -> $modplug_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DMODPLUG_DYNAMIC=\\\"$modplug_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $MODPLUG_LIBS" if test x$have_libmodplug_pc = xyes; then PC_REQUIRES="$PC_REQUIRES libmodplug" else PC_LIBS="$PC_LIBS $MODPLUG_LIBS" fi fi SDL2MIXER_MOD_MODPLUG=1 else AC_MSG_WARN([*** Unable to find ModPlug library (http://modplug-xmms.sourceforge.net/)]) fi fi SDL2MIXER_MOD_XMP=0 SDL2MIXER_MOD_XMP_LITE=0 AC_ARG_ENABLE([music-mod-xmp], [AS_HELP_STRING([--enable-music-mod-xmp], [enable MOD music via libxmp [default=yes]])], [], [enable_music_mod_xmp=yes]) AC_ARG_ENABLE([music-mod-xmp-lite], [AS_HELP_STRING([--enable-music-mod-xmp-lite], [use libxmp-lite instead of libxmp [default=no]])], [], [enable_music_mod_xmp_lite=no]) AC_ARG_ENABLE([music-mod-xmp-shared], [AS_HELP_STRING([--enable-music-mod-xmp-shared], [dynamically load xmp library [default=yes]])], [], [enable_music_mod_xmp_shared=yes]) if test x$enable_music_mod = xyes -a x$enable_music_mod_xmp = xyes; then xmplib=xmp if test x$enable_music_mod_xmp_lite = xyes; then xmplib=xmp-lite fi PKG_CHECK_MODULES([XMP], [lib$xmplib >= 4.2], [dnl have_libxmp_hdr=yes have_libxmp_lib=yes have_libxmp_pc=yes ], [dnl AC_CHECK_HEADER([xmp.h], [have_libxmp_hdr=yes]) AC_CHECK_LIB([xmp], [xmp_load_module_from_memory], [have_libxmp_lib=yes;XMP_LIBS="-l$xmplib"]) ]) if test x$have_libxmp_hdr = xyes -a x$have_libxmp_lib = xyes; then have_libxmp=yes case "$host" in *-*-darwin*) xmp_lib=[`find_lib "lib$xmplib.[0-9]*.dylib"`] if test x$xmp_lib = x; then xmp_lib=[`find_lib lib$xmplib.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) xmp_lib=[`find_lib "lib$xmplib*.dll"`] ;; *) xmp_lib=[`find_lib "lib$xmplib[0-9]*.so.*"`] if test x$xmp_lib = x; then xmp_lib=[`find_lib "lib$xmplib.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MOD_XMP $XMP_CFLAGS" if test x$enable_music_mod_xmp_shared = xyes && test x$xmp_lib != x; then echo "-- dynamic libxmp -> $xmp_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DXMP_DYNAMIC=\\\"$xmp_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $XMP_LIBS" if test x$have_libxmp_pc = xyes; then PC_REQUIRES="$PC_REQUIRES lib$xmplib" else PC_LIBS="$PC_LIBS $XMP_LIBS" fi fi SDL2MIXER_MOD_XMP=1 if test x$enable_music_mod_xmp_lite = xyes; then SDL2MIXER_MOD_XMP_LITE=1 fi else AC_MSG_WARN([*** Unable to find xmp library (http://xmp.sourceforge.net/)]) fi fi if test x$have_libmodplug != xyes -a x$have_libxmp != xyes; then AC_MSG_WARN([MOD support disabled]) fi SDL2MIXER_MIDI_FLUIDSYNTH=0 SDL2MIXER_MIDI_NATIVE=0 SDL2MIXER_MIDI_TIMIDITY=0 AC_ARG_ENABLE([music-midi], [AS_HELP_STRING([--enable-music-midi], [enable MIDI music [default=yes]])], [], [enable_music_midi=yes]) if test x$enable_music_midi = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MID" AC_ARG_ENABLE([music-midi-timidity], [AS_HELP_STRING([--enable-music-midi-timidity], [enable timidity MIDI output [default=yes]])], [], [enable_music_midi_timidity=yes]) if test x$enable_music_midi_timidity = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MID_TIMIDITY -I\$(srcdir)/src/codecs/timidity" SOURCES="$SOURCES $srcdir/src/codecs/timidity/*.c" timidity_cfg=no AC_ARG_WITH([timidity-cfg], [AS_HELP_STRING([--with-timidity-cfg=FILE],[Specify full path to timidity.cfg])], timidity_cfg="$withval", []) if test x$timidity_cfg != xyes -a x$timidity_cfg != xno; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DTIMIDITY_CFG=\\\"$timidity_cfg\\\"" fi SDL2MIXER_MIDI_TIMIDITY=1 fi AC_ARG_ENABLE([music-midi-native], [AS_HELP_STRING([--enable-music-midi-native], [enable native MIDI music output [default=yes]])], [], [enable_music_midi_native=yes]) if test x$enable_music_midi_native = xyes; then use_music_midi_native=no case "$host" in *-*-cygwin* | *-*-mingw*) use_music_midi_native=yes MIXER_LIBS="$MIXER_LIBS -lwinmm" PC_LIBS="$PC_LIBS -lwinmm" ;; *-*-darwin*) use_music_midi_native=yes MIXER_LIBS="$MIXER_LIBS -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices" PC_LIBS="$PC_LIBS -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices" ;; *-*-haiku*) use_music_midi_native=yes_cpp MIXER_LIBS="$MIXER_LIBS -lmidi" PC_LIBS="$PC_LIBS -lmidi" ;; esac if test x$use_music_midi_native = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MID_NATIVE -I\$(srcdir)/src/codecs/native_midi" SOURCES="$SOURCES $srcdir/src/codecs/native_midi/*.c" SDL2MIXER_MIDI_NATIVE=1 elif test x$use_music_midi_native = xyes_cpp; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MID_NATIVE -I\$(srcdir)/src/codecs/native_midi" SOURCES="$SOURCES $srcdir/src/codecs/native_midi/*.c" SOURCES_CXX="$SOURCES_CXX $srcdir/src/codecs/native_midi/*.cpp" SDL2MIXER_MIDI_NATIVE=1 fi fi AC_ARG_ENABLE([music-midi-fluidsynth], [AS_HELP_STRING([--enable-music-midi-fluidsynth], [enable FluidSynth MIDI output [default=yes]])], [], [enable_music_midi_fluidsynth=yes]) AC_ARG_ENABLE([music-midi-fluidsynth-shared], [AS_HELP_STRING([--enable-music-midi-fluidsynth-shared], [dynamically load FluidSynth library [default=yes]])], [], [enable_music_midi_fluidsynth_shared=yes]) if test x$enable_music_midi_fluidsynth = xyes; then PKG_CHECK_MODULES([FLUIDSYNTH], [fluidsynth], [dnl have_fluidsynth_hdr=yes have_fluidsynth_lib=yes have_fluidsynth_pc=yes ], [dnl AC_CHECK_HEADER([fluidsynth.h], [have_fluidsynth_hdr=yes]) AC_CHECK_LIB([fluidsynth], [fluid_player_add_mem], [have_fluidsynth_lib=yes;FLUIDSYNTH_LIBS="-lfluidsynth"]) ]) if test x$have_fluidsynth_hdr = xyes -a x$have_fluidsynth_lib = xyes; then have_fluidsynth=yes case "$host" in *-*-darwin*) fluidsynth_lib=[`find_lib "libfluidsynth.[0-9]*.dylib"`] if test x$fluidsynth_lib = x; then fluidsynth_lib=[`find_lib libfluidsynth.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) fluidsynth_lib=[`find_lib "fluidsynth*.dll"`] if test x$fluidsynth_lib = x; then fluidsynth_lib=[`find_lib "libfluidsynth*.dll"`] fi ;; *) fluidsynth_lib=[`find_lib "libfluidsynth[0-9]*.so.*"`] if test x$fluidsynth_lib = x; then fluidsynth_lib=[`find_lib "libfluidsynth.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MID_FLUIDSYNTH $FLUIDSYNTH_CFLAGS" if test x$enable_music_midi_fluidsynth_shared = xyes && test x$fluidsynth_lib != x; then echo "-- dynamic libfluidsynth -> $fluidsynth_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DFLUIDSYNTH_DYNAMIC=\\\"$fluidsynth_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $FLUIDSYNTH_LIBS" if test x$have_fluidsynth_pc = xyes; then PC_REQUIRES="$PC_REQUIRES fluidsynth" else PC_LIBS="$PC_LIBS $FLUIDSYNTH_LIBS" fi fi SDL2MIXER_MIDI_FLUIDSYNTH=1 else AC_MSG_WARN([*** Unable to find FluidSynth library (http://www.fluidsynth.org/)]) AC_MSG_WARN([FluidSynth support disabled]) fi fi fi if test x$enable_music_midi_timidity != xyes -a \ x$use_music_midi_native != xyes -a x$use_music_midi_native != xyes_cpp -a \ x$have_fluidsynth != xyes; then AC_MSG_WARN([MIDI support disabled]) fi SDL2MIXER_GME=0 AC_ARG_ENABLE([music-gme], [AS_HELP_STRING([--enable-music-gme], [enable Game Music Emu support [default=yes]])], [], [enable_music_gme=yes]) AC_ARG_ENABLE([music-gme-shared], [AS_HELP_STRING([--enable-music-gme-shared], [dynamically load libgme library [default=yes]])], [], [enable_music_gme_shared=yes]) if test x$enable_music_gme = xyes; then PKG_CHECK_MODULES([GME], [libgme], [dnl have_gme_hdr=yes have_gme_lib=yes have_gme_pc=yes ], [dnl AC_CHECK_HEADER([gme/gme.h], [have_gme_hdr=yes]) AC_CHECK_LIB([gme], [gme_open_data], [have_gme_lib=yes;GME_LIBS="-lgme"]) ]) if test x$have_gme_hdr = xyes -a x$have_gme_lib = xyes; then have_gme=yes case "$host" in *-*-darwin*) gme_lib=[`find_lib "libgme.[0-9]*.dylib"`] if test x$gme_lib = x; then gme_lib=[`find_lib libgme.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) gme_lib=[`find_lib "libgme*.dll"`] ;; *) gme_lib=[`find_lib "libgme.so.*"`] ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_GME $GME_CFLAGS" if test x$enable_music_gme_shared = xyes && test x$gme_lib != x; then echo "-- dynamic libgme -> $gme_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DGME_DYNAMIC=\\\"$gme_lib\\\"" else EXTRA_LDFLAGS="$EXTRA_LDFLAGS $GME_LIBS" if test x$have_gme_pc = xyes; then PC_REQUIRES="$PC_REQUIRES libgme" else PC_LIBS="$PC_LIBS $GME_LIBS" fi fi SDL2MIXER_GME=1 else AC_MSG_WARN([*** Unable to find GME library (https://github.com/libgme/game-music-emu)]) AC_MSG_WARN([Game Music Emu support disabled.]) fi fi AC_ARG_ENABLE([music-ogg], [AS_HELP_STRING([--enable-music-ogg], [enable Ogg Vorbis music [default=yes]])], [], [enable_music_ogg=yes]) SDL2MIXER_VORBIS_STB=0 AC_ARG_ENABLE(music-ogg-stb, [AS_HELP_STRING([--enable-music-ogg-stb], [enable OGG Vorbis music via stb_vorbis [default=yes]])], [], enable_music_ogg_stb=yes) if test x$enable_music_ogg = xyes -a x$enable_music_ogg_stb = xyes; then have_stb_vorbis=yes EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_OGG -DOGG_USE_STB" SDL2MIXER_VORBIS_STB=1 fi SDL2MIXER_VORBIS_VORBISFILE=0 AC_ARG_ENABLE(music-ogg-vorbis, [AS_HELP_STRING([--enable-music-ogg-vorbis], [enable OGG Vorbis music via libvorbis [default=no]])], [], enable_music_ogg_vorbis=no) AC_ARG_ENABLE([music-ogg-vorbis-shared], [AS_HELP_STRING([--enable-music-ogg-vorbis-shared], [dynamically load libvorbis library [default=yes]])], [], [enable_music_ogg_vorbis_shared=yes]) if test x$enable_music_ogg = xyes -a x$enable_music_ogg_vorbis = xyes; then LIBS_SAVED="$LIBS" PKG_CHECK_MODULES([VORBIS], [vorbisfile], [dnl have_ogg_hdr=yes have_ogg_lib=yes have_ogg_pc=yes ], [dnl AC_CHECK_HEADER([vorbis/vorbisfile.h], [have_ogg_hdr=yes]) AC_CHECK_LIB([vorbisfile], [ov_open_callbacks], [have_ogg_lib=yes;VORBIS_LIBS="-lvorbisfile -lvorbis -logg -lm"], [], [-lvorbis -logg -lm]) ]) LIBS="$LIBS_SAVED" if test x$have_ogg_hdr = xyes -a x$have_ogg_lib = xyes; then have_vorbis=yes case "$host" in *-*-darwin*) ogg_lib=[`find_lib "libvorbisfile.[0-9]*.dylib"`] if test x$ogg_lib = x; then ogg_lib=[`find_lib libvorbisfile.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) ogg_lib=[`find_lib "libvorbisfile*.dll"`] ;; *) ogg_lib=[`find_lib "libvorbisfile[0-9]*.so.*"`] if test x$ogg_lib = x; then ogg_lib=[`find_lib "libvorbisfile.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_OGG $VORBIS_CFLAGS" if test x$enable_music_ogg_vorbis_shared = xyes && test x$ogg_lib != x; then echo "-- dynamic libvorbisfile -> $ogg_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DOGG_DYNAMIC=\\\"$ogg_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $VORBIS_LIBS" if test x$have_ogg_pc = xyes; then PC_REQUIRES="$PC_REQUIRES vorbisfile" else PC_LIBS="$PC_LIBS $VORBIS_LIBS" fi fi SDL2MIXER_VORBIS_VORBISFILE=1 else AC_MSG_WARN([*** Unable to find Ogg Vorbis library (http://www.xiph.org/)]) fi fi SDL2MIXER_VORBIS_TREMOR=0 AC_ARG_ENABLE(music-ogg-tremor, [AS_HELP_STRING([--enable-music-ogg-tremor], [enable OGG Vorbis music via Tremor [default=no]])], [], enable_music_ogg_tremor=no) AC_ARG_ENABLE([music-ogg-tremor-shared], [AS_HELP_STRING([--enable-music-ogg-tremor-shared], [dynamically load Tremor library [default=yes]])], [], [enable_music_ogg_tremor_shared=yes]) if test x$enable_music_ogg = xyes -a x$enable_music_ogg_tremor = xyes; then LIBS_SAVED="$LIBS" PKG_CHECK_MODULES([TREMOR], [vorbisidec], [dnl have_tremor_hdr=yes have_tremor_lib=yes have_tremor_pc=yes ], [dnl AC_CHECK_HEADER([tremor/ivorbisfile.h], [have_tremor_hdr=yes]) AC_CHECK_LIB([vorbisidec], [ov_open_callbacks], [have_tremor_lib=yes;TREMOR_LIBS="-lvorbisidec -logg"], [], [-logg]) ]) LIBS="$LIBS_SAVED" if test x$have_tremor_hdr = xyes -a x$have_tremor_lib = xyes; then have_tremor=yes case "$host" in *-*-darwin*) ogg_lib=[`find_lib "libvorbisidec.[0-9]*.dylib"`] if test x$ogg_lib = x; then ogg_lib=[`find_lib libvorbisidec.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) ogg_lib=[`find_lib "vorbisidec*.dll"`] if test x$ogg_lib = x; then ogg_lib=[`find_lib "libvorbisidec*.dll"`] fi ;; *) ogg_lib=[`find_lib "libvorbisidec[0-9]*.so.*"`] if test x$ogg_lib = x; then ogg_lib=[`find_lib "libvorbisidec.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_OGG -DOGG_USE_TREMOR $TREMOR_CFLAGS" if test x$enable_music_ogg_tremor_shared = xyes && test x$ogg_lib != x; then echo "-- dynamic libvorbisidec -> $ogg_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DOGG_DYNAMIC=\\\"$ogg_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $TREMOR_LIBS" if test x$have_tremor_pc = xyes; then PC_REQUIRES="$PC_REQUIRES vorbisidec" else PC_LIBS="$PC_LIBS $TREMOR_LIBS" fi fi SDL2MIXER_VORBIS_TREMOR=1 else AC_MSG_WARN([*** Unable to find Ogg Vorbis Tremor library (http://www.xiph.org/)]) fi fi if test x$enable_music_ogg = xyes -a \ x$have_stb_vorbis != xyes -a x$have_vorbis != xyes -a x$have_tremor != xyes; then AC_MSG_WARN([Ogg Vorbis support disabled]) fi SDL2MIXER_FLAC_DRFLAC=0 AC_ARG_ENABLE([music-flac], [AS_HELP_STRING([--enable-music-flac], [enable FLAC music [default=yes]])], [], [enable_music_flac=yes]) AC_ARG_ENABLE(music-flac-drflac, [AS_HELP_STRING([--enable-music-flac-drflac], [enable FLAC music via dr_flac [default=yes]])], [], [enable_music_flac_drflac=yes]) if test x$enable_music_flac = xyes -a x$enable_music_flac_drflac = xyes; then have_drflac=yes EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_FLAC_DRFLAC" SDL2MIXER_FLAC_DRFLAC=1 fi SDL2MIXER_FLAC_LIBFLAC=0 AC_ARG_ENABLE(music-flac-libflac, [AS_HELP_STRING([--enable-music-flac-libflac], [enable FLAC music via libFLAC [default=no]])], [], [enable_music_flac_libflac=no]) AC_ARG_ENABLE([music-flac-libflac-shared], [AS_HELP_STRING([--enable-music-flac-libflac-shared], [dynamically load FLAC library [default=yes]])], [], [enable_music_flac_libflac_shared=yes]) if test x$enable_music_flac = xyes -a x$enable_music_flac_libflac = xyes; then libflac_ver=8 PKG_CHECK_MODULES([FLAC], [flac], [dnl have_flac_hdr=yes have_flac_lib=yes have_flac_pc=yes ], [dnl AC_CHECK_HEADER([FLAC/stream_decoder.h], [have_flac_hdr=yes]) AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_new], [have_flac_lib=yes;FLAC_LIBS="-lFLAC"]) ]) if test x$have_flac_hdr = xyes -a x$have_flac_lib = xyes; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $FLAC_CFLAGS" AC_MSG_CHECKING([for libflac so-name version >= $libflac_ver]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include "FLAC/export.h" #include "stdio.h" ]], [[ #if defined(FLAC_API_VERSION_CURRENT) && (FLAC_API_VERSION_CURRENT >= $libflac_ver) #else #error "old-flac" #endif ]])], [have_flac_ver=yes],[have_flac_ver=no]) CFLAGS="$save_CFLAGS" AC_MSG_RESULT($have_flac_ver) fi if test x$have_flac_ver = xyes; then have_libflac=yes case "$host" in *-*-darwin*) flac_lib=[`find_lib "libFLAC.[0-9]*.dylib"`] if test x$flac_lib = x; then flac_lib=[`find_lib libFLAC.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) flac_lib=[`find_lib "libFLAC*.dll"`] ;; *) flac_lib=[`find_lib "libFLAC[0-9]*.so.*"`] if test x$flac_lib = x; then flac_lib=[`find_lib "libFLAC.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_FLAC_LIBFLAC $FLAC_CFLAGS" if test x$enable_music_flac_libflac_shared = xyes && test x$flac_lib != x; then echo "-- dynamic libFLAC -> $flac_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DFLAC_DYNAMIC=\\\"$flac_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $FLAC_LIBS" if test x$have_flac_pc = xyes; then PC_REQUIRES="$PC_REQUIRES flac" else PC_LIBS="$PC_LIBS $FLAC_LIBS" fi fi SDL2MIXER_FLAC_LIBFLAC=1 else AC_MSG_WARN([*** Unable to find FLAC library (https://xiph.org/flac/)]) fi fi if test x$enable_music_flac = xyes -a \ x$have_drflac != xyes -a x$have_libflac != xyes; then AC_MSG_WARN([FLAC support disabled]) fi AC_ARG_ENABLE(music-mp3, [AS_HELP_STRING([--enable-music-mp3], [enable MP3 music [default=yes]])], [], enable_music_mp3=yes) SDL2MIXER_MP3_MINIMP3=0 AC_ARG_ENABLE(music-mp3-minimp3, [AS_HELP_STRING([--enable-music-mp3-minimp3], [enable MP3 music via minimp3 [default=yes]])], [], [enable_music_mp3_minimp3=yes]) if test x$enable_music_mp3 = xyes -a x$enable_music_mp3_minimp3 = xyes; then have_minimp3=yes EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MINIMP3" SDL2MIXER_MP3_MINIMP3=1 fi SDL2MIXER_MP3_MPG123=0 AC_ARG_ENABLE(music-mp3-mpg123, [AS_HELP_STRING([--enable-music-mp3-mpg123], [enable MP3 music via mpg123 [default=no]])], [], [enable_music_mp3_mpg123=no]) AC_ARG_ENABLE([music-mp3-mpg123-shared], [AS_HELP_STRING([--enable-music-mp3-mpg123-shared], [dynamically load mpg123 library [default=yes]])], [], [enable_music_mp3_mpg123_shared=yes]) if test x$enable_music_mp3 = xyes -a x$enable_music_mp3_mpg123 = xyes; then PKG_CHECK_MODULES([MPG123], [libmpg123], [dnl have_mpg123_hdr=yes have_mpg123_lib=yes have_mpg123_pc=yes ], [dnl AC_CHECK_HEADER([mpg123.h], [have_mpg123_hdr=yes]) AC_CHECK_LIB([mpg123], [mpg123_replace_reader_handle], [have_mpg123_lib=yes;MPG123_LIBS="-lmpg123"]) ]) if test x$have_mpg123_hdr = xyes -a x$have_mpg123_lib = xyes; then have_libmpg123=yes case "$host" in *-*-darwin*) mpg123_lib=[`find_lib "libmpg123.[0-9]*.dylib"`] if test x$mpg123_lib = x; then mpg123_lib=[`find_lib libmpg123.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) mpg123_lib=[`find_lib "libmpg123*.dll"`] ;; *) mpg123_lib=[`find_lib "libmpg123.so.*"`] ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_MP3_MPG123 $MPG123_CFLAGS" if test x$enable_music_mp3_mpg123_shared = xyes && test x$mpg123_lib != x; then echo "-- dynamic libmpg123 -> $mpg123_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DMPG123_DYNAMIC=\\\"$mpg123_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $MPG123_LIBS" if test x$have_mpg123_pc = xyes; then PC_REQUIRES="$PC_REQUIRES libmpg123" else PC_LIBS="$PC_LIBS $MPG123_LIBS" fi fi SDL2MIXER_MP3_MPG123=1 else AC_MSG_WARN([*** Unable to find mpg123 library (https://www.mpg123.de)]) fi fi if test x$enable_music_mp3 = xyes -a x$have_minimp3 != xyes -a x$have_libmpg123 != xyes; then AC_MSG_WARN([MP3 support disabled]) fi SDL2MIXER_OPUS=0 AC_ARG_ENABLE([music-opus], [AS_HELP_STRING([--enable-music-opus], [enable Opus music [default=yes]])], [], [enable_music_opus=yes]) AC_ARG_ENABLE([music-opus-shared], [AS_HELP_STRING([--enable-music-opus-shared], [dynamically load opusfile library [default=yes]])], [], [enable_music_opus_shared=yes]) if test x$enable_music_opus = xyes; then LIBS_SAVED="$LIBS" PKG_CHECK_MODULES([OPUSFILE], [opusfile >= 0.2], [dnl have_opusfile_hdr=yes have_opusfile_lib=yes have_opusfile_pc=yes ], [dnl AC_CHECK_HEADER([opus/opusfile.h], [have_opusfile_hdr=yes]) AC_CHECK_LIB([opusfile], [op_open_callbacks], [have_opusfile_lib=yes;OPUSFILE_LIBS="-lopusfile -lopus"], [], [-lopus -logg -lm]) ]) LIBS="$LIBS_SAVED" if test x$have_opusfile_hdr = xyes -a x$have_opusfile_lib = xyes; then have_opusfile=yes case "$host" in *-*-darwin*) opusfile_lib=[`find_lib "libopusfile.[0-9]*.dylib"`] if test x$opusfile_lib = x; then opusfile_lib=[`find_lib libopusfile.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) opusfile_lib=[`find_lib "libopusfile*.dll"`] ;; *) opusfile_lib=[`find_lib "libopusfile[0-9]*.so.*"`] if test x$opusfile_lib = x; then opusfile_lib=[`find_lib "libopusfile.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_OPUS $OPUSFILE_CFLAGS" if test x$enable_music_opus_shared = xyes && test x$opusfile_lib != x; then echo "-- dynamic opusfile -> $opusfile_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DOPUS_DYNAMIC=\\\"$opusfile_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $OPUSFILE_LIBS" if test x$have_opusfile_pc = xyes; then PC_REQUIRES="$PC_REQUIRES opusfile" else PC_LIBS="$PC_LIBS $OPUSFILE_LIBS" fi fi SDL2MIXER_OPUS=1 else AC_MSG_WARN([*** Unable to find opusfile library (http://opus-codec.org/)]) AC_MSG_WARN([Opus support disabled]) fi fi SDL2MIXER_WAVPACK=0 AC_ARG_ENABLE([music-wavpack], [AS_HELP_STRING([--enable-music-wavpack], [enable WavPack music [default=yes]])], [], [enable_music_wavpack=yes]) AC_ARG_ENABLE([music-wavpack-shared], [AS_HELP_STRING([--enable-music-wavpack-shared], [dynamically load WavPack library [default=yes]])], [], [enable_music_wavpack_shared=yes]) AC_ARG_ENABLE([music-wavpack-dsd], [AS_HELP_STRING([--enable-music-wavpack-dsd], [enable WavPack DSD music support [default=no]])], [], [enable_music_wavpack_dsd=no]) if test x$enable_music_wavpack = xyes; then LIBS_SAVED="$LIBS" PKG_CHECK_MODULES([WAVPACK], [wavpack >= 4.0], [dnl have_wavpack_hdr=yes have_wavpack_lib=yes have_wavpack_pc=yes ], [dnl AC_CHECK_HEADER([wavpack.h], [have_wavpack_hdr=yes;have_wavpack_hdr2=yes]) AC_CHECK_HEADER([wavpack/wavpack.h], [have_wavpack_hdr=yes]) AC_CHECK_LIB([wavpack], [WavpackOpenFileInputEx], [have_wavpack_lib=yes;WAVPACK_LIBS="-lwavpack"], [], [-lm]) ]) if test x$have_wavpack_pc = xyes; then CFLAGS_SAVED="$CFLAGS" CFLAGS="$CFLAGS $WAVPACK_CFLAGS" AC_CHECK_HEADER([wavpack.h], [have_wavpack_hdr2=yes]) CFLAGS="$CFLAGS_SAVED" fi LIBS="$LIBS_SAVED" if test x$have_wavpack_hdr = xyes -a x$have_wavpack_lib = xyes; then have_wavpack=yes case "$host" in *-*-darwin*) wavpack_lib=[`find_lib "libwavpack.[0-9]*.dylib"`] if test x$wavpack_lib = x; then wavpack_lib=[`find_lib libwavpack.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) wavpack_lib=[`find_lib "libwavpack*.dll"`] if test x$wavpack_lib = x; then wavpack_lib=[`find_lib wavpackdll.dll`] fi ;; *) wavpack_lib=[`find_lib "libwavpack[0-9]*.so.*"`] if test x$wavpack_lib = x; then wavpack_lib=[`find_lib "libwavpack.so.*"`] fi ;; esac EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_WAVPACK $WAVPACK_CFLAGS" if test x$enable_music_wavpack_dsd = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DMUSIC_WAVPACK_DSD" fi if test x$have_wavpack_hdr2 = xyes; then EXTRA_CFLAGS="$EXTRA_CFLAGS -DHAVE_WAVPACK_H" fi if test x$enable_music_wavpack_shared = xyes && test x$wavpack_lib != x; then echo "-- dynamic wavpack -> $wavpack_lib" EXTRA_CFLAGS="$EXTRA_CFLAGS -DWAVPACK_DYNAMIC=\\\"$wavpack_lib\\\"" else MIXER_LIBS="$MIXER_LIBS $WAVPACK_LIBS" if test x$have_wavpack_pc = xyes; then PC_REQUIRES="$PC_REQUIRES wavpack" else PC_LIBS="$PC_LIBS $WAVPACK_LIBS" fi fi SDL2MIXER_WAVPACK=1 else AC_MSG_WARN([*** Unable to find WavPack library (http://www.wavpack.com)]) AC_MSG_WARN([WavPack support disabled]) fi fi dnl check for LD --no-undefined option CheckNoUndef MIXER_LIBS="$MIXER_LIBS $LIBM" OBJECTS=`echo $SOURCES` DEPENDS=`echo $SOURCES` OBJECTS=`echo "$OBJECTS" | sed 's,[[^ ]]*/\([[^ ]]*\)\.c,$(objects)/\1.lo,g'` DEPENDS=`echo "$DEPENDS" | sed 's,\([[^ ]]*\)/\([[^ ]]*\)\.c,\\ $(objects)/\2.lo: \1/\2.c \$(objects)/.created\\ \$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) '"$DEPENDENCY_TRACKING_OPTIONS"' -c \$< -o \$@,g'` OBJECTS_CXX=`echo $SOURCES_CXX` DEPENDS_CXX=`echo $SOURCES_CXX` OBJECTS_CXX=`echo "$OBJECTS_CXX" | sed 's,[[^ ]]*/\([[^ ]]*\)\.cpp,$(objects)/\1.lo,g'` DEPENDS_CXX=`echo "$DEPENDS_CXX" | sed 's,\([[^ ]]*\)/\([[^ ]]*\)\.cpp,\\ $(objects)/\2.lo: \1/\2.cpp \$(objects)/.created\\ \$(LIBTOOL) --mode=compile \$(CXX) \$(CFLAGS) \$(EXTRA_CFLAGS) '"$DEPENDENCY_TRACKING_OPTIONS"' -c \$< -o \$@,g'` OBJECTS="$OBJECTS $OBJECTS_CXX" DEPENDS="$DEPENDS $DEPENDS_CXX" DEPENDS=`echo "$DEPENDS" | sed 's,\\$,\\\\$,g'` VERSION_OBJECTS=`echo $VERSION_SOURCES` VERSION_DEPENDS=`echo $VERSION_SOURCES` VERSION_OBJECTS=`echo "$VERSION_OBJECTS" | sed 's,[[^ ]]*/\([[^ ]]*\)\.rc,$(objects)/\1.lo,g'` VERSION_DEPENDS=`echo "$VERSION_DEPENDS" | sed 's,\([[^ ]]*\)/\([[^ ]]*\)\.rc,\\ $(objects)/\2.lo: \1/\2.rc \$(objects)/.created\\ \$(LIBTOOL) --tag=RC --mode=compile \$(RC) \$< -o \$@,g'` VERSION_DEPENDS=`echo "$VERSION_DEPENDS" | sed 's,\\$,\\\\$,g'` PLAYWAVE_SOURCES="$srcdir/playwave.c" PLAYWAVE_OBJECTS=`echo $PLAYWAVE_SOURCES` PLAYWAVE_DEPENDS=`echo $PLAYWAVE_SOURCES` PLAYWAVE_OBJECTS=`echo "$PLAYWAVE_OBJECTS" | sed 's,[[^ ]]*/\([[^ ]]*\)\.c,$(objects)/\1.lo,g'` PLAYWAVE_DEPENDS=`echo "$PLAYWAVE_DEPENDS" | sed 's,\([[^ ]]*\)/\([[^ ]]*\)\.c,\\ $(objects)/\2.lo: \1/\2.c \$(objects)/.created\\ \$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) '"$DEPENDENCY_TRACKING_OPTIONS"' -c \$< -o \$@,g'` PLAYWAVE_DEPENDS=`echo "$PLAYWAVE_DEPENDS" | sed 's,\\$,\\\\$,g'` PLAYMUS_SOURCES="$srcdir/playmus.c" PLAYMUS_OBJECTS=`echo $PLAYMUS_SOURCES` PLAYMUS_DEPENDS=`echo $PLAYMUS_SOURCES` PLAYMUS_OBJECTS=`echo "$PLAYMUS_OBJECTS" | sed 's,[[^ ]]*/\([[^ ]]*\)\.c,$(objects)/\1.lo,g'` PLAYMUS_DEPENDS=`echo "$PLAYMUS_DEPENDS" | sed 's,\([[^ ]]*\)/\([[^ ]]*\)\.c,\\ $(objects)/\2.lo: \1/\2.c \$(objects)/.created\\ \$(LIBTOOL) --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) '"$DEPENDENCY_TRACKING_OPTIONS"' -c \$< -o \$@,g'` PLAYMUS_DEPENDS=`echo "$PLAYMUS_DEPENDS" | sed 's,\\$,\\\\$,g'` dnl Calculate the location of the prefix, relative to the cmake folder pkg_cmakedir='$libdir/cmake/SDL2_mixer' AX_COMPUTE_RELATIVE_PATHS([pkg_cmakedir:prefix:cmake_prefix_relpath]) AC_SUBST([cmake_prefix_relpath]) AC_SUBST([cmake_prefix_relpath]) dnl Expand the sources and objects needed to build the library AC_SUBST(ac_aux_dir) AC_SUBST(OBJECTS) AC_SUBST(VERSION_OBJECTS) AC_SUBST(PLAYWAVE_OBJECTS) AC_SUBST(PLAYMUS_OBJECTS) AC_SUBST(BUILD_CFLAGS) AC_SUBST(EXTRA_CFLAGS) AC_SUBST(BUILD_LDFLAGS) AC_SUBST(EXTRA_LDFLAGS) AC_SUBST(MIXER_LIBS) AC_SUBST(EXE) AC_SUBST(SDL_VERSION) AC_SUBST(SDL_CFLAGS) AC_SUBST(SDL_LIBS) AC_SUBST(PC_REQUIRES) AC_SUBST(PC_LIBS) AC_SUBST(SDL2MIXER_CMD) AC_SUBST(SDL2MIXER_FLAC_DRFLAC) AC_SUBST(SDL2MIXER_FLAC_LIBFLAC) AC_SUBST(SDL2MIXER_GME) AC_SUBST(SDL2MIXER_MOD_MODPLUG) AC_SUBST(SDL2MIXER_MOD_XMP) AC_SUBST(SDL2MIXER_MOD_XMP_LITE) AC_SUBST(SDL2MIXER_MP3_MINIMP3) AC_SUBST(SDL2MIXER_MP3_MPG123) AC_SUBST(SDL2MIXER_MIDI_FLUIDSYNTH) AC_SUBST(SDL2MIXER_MIDI_NATIVE) AC_SUBST(SDL2MIXER_MIDI_TIMIDITY) AC_SUBST(SDL2MIXER_OPUS) AC_SUBST(SDL2MIXER_VORBIS_STB) AC_SUBST(SDL2MIXER_VORBIS_TREMOR) AC_SUBST(SDL2MIXER_VORBIS_VORBISFILE) AC_SUBST(SDL2MIXER_WAVE) AC_SUBST(SDL2MIXER_WAVPACK) AC_SUBST(SDL2MIXER_GME) AC_CONFIG_FILES([ Makefile SDL2_mixer.spec SDL2_mixer.pc sdl2_mixer-config.cmake sdl2_mixer-config-version.cmake ]) AC_CONFIG_COMMANDS([default], [cat >>Makefile <<__EOF__ # Build rules for objects -include \$(OBJECTS:.lo=.d) $DEPENDS $VERSION_DEPENDS -include \$(PLAYWAVE_OBJECTS:.lo=.d) $PLAYWAVE_DEPENDS -include \$(PLAYMUS_OBJECTS:.lo=.d) $PLAYMUS_DEPENDS __EOF__ ], [ DEPENDS="$DEPENDS" VERSION_DEPENDS="$VERSION_DEPENDS" PLAYWAVE_DEPENDS="$PLAYWAVE_DEPENDS" PLAYMUS_DEPENDS="$PLAYMUS_DEPENDS" ]) AC_OUTPUT SDL2_mixer-2.8.0/Android.mk0000644000076500000240000001110014551252471014341 0ustar valvestaff# Save the local path SDL_MIXER_LOCAL_PATH := $(call my-dir) # Enable this if you want to support loading WAV music SUPPORT_WAV ?= true # Enable this if you want to support loading FLAC music via dr_flac SUPPORT_FLAC_DRFLAC ?= true # Enable this if you want to support loading FLAC music with libFLAC SUPPORT_FLAC_LIBFLAC ?= false FLAC_LIBRARY_PATH := external/flac # Enable this if you want to support loading OGG Vorbis music via stb_vorbis SUPPORT_OGG_STB ?= true # Enable this if you want to support loading OGG Vorbis music via Tremor SUPPORT_OGG ?= false OGG_LIBRARY_PATH := external/ogg 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 MPG123 SUPPORT_MP3_MPG123 ?= false MPG123_LIBRARY_PATH := external/mpg123 # Enable this if you want to support loading WavPack music via libwavpack SUPPORT_WAVPACK ?= true WAVPACK_LIBRARY_PATH := external/wavpack # Enable this if you want to support loading MOD music via XMP-lite SUPPORT_MOD_XMP ?= false XMP_LIBRARY_PATH := external/libxmp # Enable this if you want to support TiMidity SUPPORT_MID_TIMIDITY ?= false TIMIDITY_LIBRARY_PATH := src/codecs/timidity # Build the library ifeq ($(SUPPORT_FLAC_LIBFLAC),true) include $(SDL_MIXER_LOCAL_PATH)/$(FLAC_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_OGG),true) include $(SDL_MIXER_LOCAL_PATH)/$(OGG_LIBRARY_PATH)/Android.mk include $(SDL_MIXER_LOCAL_PATH)/$(VORBIS_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_MP3_MPG123),true) include $(SDL_MIXER_LOCAL_PATH)/$(MPG123_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_WAVPACK),true) include $(SDL_MIXER_LOCAL_PATH)/$(WAVPACK_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_MOD_XMP),true) include $(SDL_MIXER_LOCAL_PATH)/$(XMP_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_MID_TIMIDITY),true) include $(SDL_MIXER_LOCAL_PATH)/$(TIMIDITY_LIBRARY_PATH)/Android.mk endif # Restore local path LOCAL_PATH := $(SDL_MIXER_LOCAL_PATH) include $(CLEAR_VARS) LOCAL_MODULE := SDL2_mixer LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ $(LOCAL_PATH)/src/ \ $(LOCAL_PATH)/src/codecs \ LOCAL_SRC_FILES := \ $(subst $(LOCAL_PATH)/,, \ $(wildcard $(LOCAL_PATH)/src/*.c) \ $(wildcard $(LOCAL_PATH)/src/codecs/*.c) \ ) LOCAL_CFLAGS := LOCAL_LDLIBS := LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := SDL2 ifeq ($(SUPPORT_WAV),true) LOCAL_CFLAGS += -DMUSIC_WAV endif ifeq ($(SUPPORT_FLAC_DRFLAC),true) LOCAL_CFLAGS += -DMUSIC_FLAC_DRFLAC endif ifeq ($(SUPPORT_FLAC_LIBFLAC),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(FLAC_LIBRARY_PATH)/include LOCAL_CFLAGS += -DMUSIC_FLAC_LIBFLAC LOCAL_STATIC_LIBRARIES += libFLAC endif ifeq ($(SUPPORT_OGG_STB),true) LOCAL_CFLAGS += -DMUSIC_OGG -DOGG_USE_STB endif ifeq ($(SUPPORT_OGG),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(OGG_LIBRARY_PATH)/include LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(VORBIS_LIBRARY_PATH) LOCAL_CFLAGS += -DMUSIC_OGG -DOGG_USE_TREMOR -DOGG_HEADER="" LOCAL_STATIC_LIBRARIES += ogg vorbisidec endif ifeq ($(SUPPORT_MP3_MINIMP3),true) LOCAL_CFLAGS += -DMUSIC_MP3_MINIMP3 endif # This needs to be a shared library to comply with the LGPL license ifeq ($(SUPPORT_MP3_MPG123),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(MPG123_LIBRARY_PATH) LOCAL_CFLAGS += -DMUSIC_MP3_MPG123 LOCAL_SHARED_LIBRARIES += mpg123 endif ifeq ($(SUPPORT_WAVPACK),true) LOCAL_CFLAGS += -DMUSIC_WAVPACK -DMUSIC_WAVPACK_DSD -DWAVPACK_HEADER=\"../external/wavpack/include/wavpack.h\" LOCAL_STATIC_LIBRARIES += wavpack endif ifeq ($(SUPPORT_MOD_XMP),true) LOCAL_CFLAGS += -DMUSIC_MOD_XMP -DLIBXMP_HEADER=\"../external/libxmp/include/xmp.h\" LOCAL_STATIC_LIBRARIES += xmp endif ifeq ($(SUPPORT_MID_TIMIDITY),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(TIMIDITY_LIBRARY_PATH) LOCAL_CFLAGS += -DMUSIC_MID_TIMIDITY LOCAL_STATIC_LIBRARIES += timidity endif LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/include include $(BUILD_SHARED_LIBRARY) ########################### # # SDL2_mixer static library # ########################### LOCAL_MODULE := SDL2_mixer_static LOCAL_MODULE_FILENAME := libSDL2_mixer LOCAL_LDLIBS := LOCAL_EXPORT_LDLIBS := include $(BUILD_STATIC_LIBRARY) SDL2_mixer-2.8.0/CMakeLists.txt0000644000076500000240000012241214553225265015204 0ustar valvestaffcmake_minimum_required(VERSION 3.16) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # See docs/release_checklist.md set(MAJOR_VERSION 2) set(MINOR_VERSION 8) set(MICRO_VERSION 0) set(SDL_REQUIRED_VERSION 2.0.9) include(PrivateSdlFunctions) sdl_calculate_derived_version_variables() if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the SDL_mixer source code and call cmake from there") endif() project(SDL2_mixer LANGUAGES C VERSION "${FULL_VERSION}" ) message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL2MIXER_ROOTPROJECT ON) else() set(SDL2MIXER_ROOTPROJECT OFF) endif() set(SDL2MIXER_SAMPLES_DEFAULT ${SDL2MIXER_ROOTPROJECT}) if(ANDROID) set(SDL2MIXER_SAMPLES_DEFAULT OFF) endif() # option() honors normal variables. cmake_policy(SET CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) if(POLICY CMP0112) # Target file component generator expressions do not add target dependencies. cmake_policy(SET CMP0112 NEW) endif() # Set defaults preventing destination file conflicts set(SDL2MIXER_DEBUG_POSTFIX "d" CACHE STRING "Name suffix for debug builds") mark_as_advanced(SDL2MIXER_DEBUG_POSTFIX) if(POLICY CMP0099) # Make `INTERFACE_LINK_DIRECTORIES` a transitive usage requirement. # This is needed for static dependencies which have transitive dependencies # outside of compiler default search paths. cmake_policy(SET CMP0099 NEW) endif() # Assume MSVC projects don't have a package manager and need vendored dependencies (by default). # Most other platforms have some kind of package manager. # FIXME: consider a package manager such as conan/vcpkg instead of vendoring if(ANDROID OR MSVC) set(vendored_default ON) else() set(vendored_default OFF) endif() set(sdl2mixer_install_enableable ON) if ((TARGET SDL2 OR TARGET SDL2-static) AND SDL2_DISABLE_INSTALL) # Cannot install SDL2_mixer when SDL2 is built in same built, and is not installed. set(sdl2mixer_install_enableable OFF) endif() if(NOT DEFINED CMAKE_FIND_PACKAGE_PREFER_CONFIG) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) endif() include(CheckIncludeFile) include(CheckSymbolExists) include(CMakeDependentOption) include(CMakePackageConfigHelpers) include(GNUInstallDirs) include(PkgConfigHelper) option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON) option(BUILD_SHARED_LIBS "Build the library as a shared library" ON) # Save BUILD_SHARED_LIBS variable as soon as possible set(SDL2MIXER_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) cmake_dependent_option(SDL2MIXER_INSTALL "Enable SDL2mixer install target" ${SDL2MIXER_ROOTPROJECT} "${sdl2mixer_install_enableable}" OFF) option(SDL2MIXER_DEPS_SHARED "Default value for loading dependencies dynamically" ON) option(SDL2MIXER_VENDORED "Use vendored third-party libraries" ${vendored_default}) option(SDL2MIXER_SAMPLES "Build the SDL2_mixer sample program(s)" ${SDL2MIXER_SAMPLES_DEFAULT}) cmake_dependent_option(SDL2MIXER_SAMPLES_INSTALL "Install the SDL2_mixer sample program(s)" OFF "SDL2MIXER_SAMPLES;SDL2MIXER_INSTALL" OFF) if(UNIX AND NOT APPLE) set(sdl2mixer_cmd_default ON) else() set(sdl2mixer_cmd_default OFF) endif() option(SDL2MIXER_CMD "Support an external music player" ${sdl2mixer_cmd_default}) option(SDL2MIXER_FLAC "Enable FLAC music" ON) cmake_dependent_option(SDL2MIXER_FLAC_LIBFLAC "Enable FLAC music using libFLAC" OFF SDL2MIXER_FLAC OFF) cmake_dependent_option(SDL2MIXER_FLAC_LIBFLAC_SHARED "Dynamically load LIBFLAC" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_FLAC_LIBFLAC OFF) cmake_dependent_option(SDL2MIXER_FLAC_DRFLAC "Enable FLAC music using drflac" ON SDL2MIXER_FLAC OFF) option(SDL2MIXER_GME "Support loading GME music via game-music-emu" OFF) option(SDL2MIXER_GME_SHARED "Dynamically load libgme" "${SDL2MIXER_DEPS_SHARED}") option(SDL2MIXER_MOD "Support loading MOD music" ON) cmake_dependent_option(SDL2MIXER_MOD_MODPLUG "Support loading MOD music via modplug" OFF SDL2MIXER_MOD OFF) cmake_dependent_option(SDL2MIXER_MOD_MODPLUG_SHARED "Dynamically load modplug" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MOD_MODPLUG OFF) cmake_dependent_option(SDL2MIXER_MOD_XMP "Support loading MOD music via libxmp" ON SDL2MIXER_MOD OFF) cmake_dependent_option(SDL2MIXER_MOD_XMP_LITE "Use libxmp-lite instead of libxmp" OFF "SDL2MIXER_MOD_XMP;NOT SDL2MIXER_VENDORED" OFF) cmake_dependent_option(SDL2MIXER_MOD_XMP_SHARED "Dynamically load libxmp(-lite)" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MOD_XMP OFF) if(SDL2MIXER_MOD AND NOT (SDL2MIXER_MOD_MODPLUG OR SDL2MIXER_MOD_XMP)) message(FATAL_ERROR "MOD support was enabled (SDL2MIXER_MOD) but neither modplug (SDL2MIXER_MOD_MODPLUG) or xmp (SDL2MIXER_MOD_XMP) was enabled.") endif() option(SDL2MIXER_MP3 "Enable MP3 music" ON) cmake_dependent_option(SDL2MIXER_MP3_MINIMP3 "Support loading MP3 music via minimp3" ON SDL2MIXER_MP3 OFF) cmake_dependent_option(SDL2MIXER_MP3_MPG123 "Support loading MP3 music via MPG123" OFF SDL2MIXER_MP3 OFF) cmake_dependent_option(SDL2MIXER_MP3_MPG123_SHARED "Dynamically load mpg123" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MP3_MPG123 OFF) if(SDL2MIXER_MP3 AND NOT (SDL2MIXER_MP3_MINIMP3 OR SDL2MIXER_MP3_MPG123)) message(FATAL_ERROR "MP3 support was enabled (SDL2MIXER_MP3) but neither minimp3 (SDL2MIXER_MP3_MINIMP3) or mpg123 (SDL2MIXER_MP3_MPG123) were enabled.") endif() option(SDL2MIXER_MIDI "Enable MIDI music" ON) cmake_dependent_option(SDL2MIXER_MIDI_FLUIDSYNTH "Support FluidSynth MIDI output" ON "SDL2MIXER_MIDI;NOT SDL2MIXER_VENDORED" OFF) cmake_dependent_option(SDL2MIXER_MIDI_FLUIDSYNTH_SHARED "Dynamically load libfluidsynth" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_MIDI_FLUIDSYNTH OFF) if(WIN32 OR APPLE OR HAIKU) cmake_dependent_option(SDL2MIXER_MIDI_NATIVE "Support native MIDI output" ON SDL2MIXER_MIDI OFF) else() set(SDL2MIXER_MIDI_NATIVE OFF) endif() cmake_dependent_option(SDL2MIXER_MIDI_TIMIDITY "Support timidity MIDI output" ON SDL2MIXER_MIDI OFF) if(SDL2MIXER_MIDI AND NOT (SDL2MIXER_MIDI_TIMIDITY OR SDL2MIXER_MIDI_NATIVE OR SDL2MIXER_MIDI_FLUIDSYNTH)) message(FATAL_ERROR "MIDI support was enabled (SDL2MIXER_MIDI) but neither FluidSynth (SDL2MIXER_MIDI_FLUIDSYNTH), native (SDL2MIXER_MIDI_NATIVE) or timidity (SDL2MIXER_MIDI_TIMIDITY) was enabled") endif() option(SDL2MIXER_OPUS "Enable Opus music" ON) cmake_dependent_option(SDL2MIXER_OPUS_SHARED "Dynamically load libopus" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_OPUS OFF) set(sdl2mixer_vorbis_strings STB TREMOR VORBISFILE) set(SDL2MIXER_VORBIS "STB" CACHE STRING "Enable OGG Vorbis music") set_property(CACHE SDL2MIXER_VORBIS PROPERTY STRINGS "${sdl2mixer_vorbis_strings}") if(SDL2MIXER_VORBIS) if(NOT SDL2MIXER_VORBIS IN_LIST sdl2mixer_vorbis_strings) message(FATAL_ERROR "SDL2MIXER_VORBIS contains an invalid value (=${SDL2MIXER_VORBIS}). It must be one of ${sdl2mixer_vorbis_strings}.") endif() endif() set(SDL2MIXER_VORBIS_STB OFF) set(SDL2MIXER_VORBIS_TREMOR OFF) set(SDL2MIXER_VORBIS_VORBISFILE OFF) if(SDL2MIXER_VORBIS STREQUAL "STB") set(SDL2MIXER_VORBIS_STB ON) endif() if(SDL2MIXER_VORBIS STREQUAL "TREMOR") set(SDL2MIXER_VORBIS_TREMOR ON) endif() if(SDL2MIXER_VORBIS STREQUAL "VORBISFILE") set(SDL2MIXER_VORBIS_VORBISFILE ON) endif() cmake_dependent_option(SDL2MIXER_VORBIS_TREMOR_SHARED "Dynamically load tremor library" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_VORBIS_TREMOR OFF) cmake_dependent_option(SDL2MIXER_VORBIS_VORBISFILE_SHARED "Dynamically load vorbisfile library" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_VORBIS_VORBISFILE OFF) option(SDL2MIXER_WAVE "Enable streaming WAVE music" ON) option(SDL2MIXER_WAVPACK "Enable WavPack music" ON) cmake_dependent_option(SDL2MIXER_WAVPACK_DSD "Enable WavPack DSD music support" OFF SDL2MIXER_WAVPACK OFF) cmake_dependent_option(SDL2MIXER_WAVPACK_SHARED "Dynamically load WavPack library" "${SDL2MIXER_DEPS_SHARED}" SDL2MIXER_WAVPACK OFF) if(SDL2MIXER_VORBIS_TREMOR OR SDL2MIXER_VORBIS_VORBISFILE OR SDL2MIXER_FLAC_LIBFLAC OR SDL2MIXER_OPUS) set(SDL2MIXER_OGG TRUE) set(SDL2MIXER_OGG_install FALSE) if(SDL2MIXER_VORBIS_VORBISFILE_SHARED OR SDL2MIXER_FLAC_SHARED OR SDL2MIXER_OPUS_SHARED) set(SDL2MIXER_OGG_SHARED TRUE) set(SDL2MIXER_OGG_install TRUE) else() set(SDL2MIXER_OGG_SHARED FALSE) if(NOT SDL2MIXER_BUILD_SHARED_LIBS) set(SDL2MIXER_OGG_install TRUE) endif() endif() else() set(SDL2MIXER_OGG FALSE) endif() if(SDL2MIXER_BUILD_SHARED_LIBS) set(sdl2_mixer_export_name SDL2_mixer) set(sdl2_mixer_install_name_infix shared) set(sdl2_target_name SDL2::SDL2) else() set(sdl2_mixer_export_name SDL2_mixer-static) set(sdl2_mixer_install_name_infix static) set(sdl2_target_name SDL2::SDL2-static) endif() sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION}) set(BUILD_SHARED_LIBS ${SDL2MIXER_BUILD_SHARED_LIBS}) add_library(SDL2_mixer src/codecs/load_aiff.c src/codecs/load_voc.c src/codecs/mp3utils.c src/codecs/music_cmd.c src/codecs/music_drflac.c src/codecs/music_flac.c src/codecs/music_fluidsynth.c src/codecs/music_gme.c src/codecs/music_minimp3.c src/codecs/music_modplug.c src/codecs/music_mpg123.c src/codecs/music_nativemidi.c src/codecs/music_ogg.c src/codecs/music_ogg_stb.c src/codecs/music_opus.c src/codecs/music_timidity.c src/codecs/music_wav.c src/codecs/music_wavpack.c src/codecs/music_xmp.c src/effect_position.c src/effect_stereoreverse.c src/effects_internal.c src/mixer.c src/music.c src/utils.c ) add_library(SDL2_mixer::${sdl2_mixer_export_name} ALIAS SDL2_mixer) target_include_directories(SDL2_mixer PUBLIC "$" "$" PRIVATE src src/codecs ) target_compile_definitions(SDL2_mixer PRIVATE BUILD_SDL SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION} SDL_BUILD_MINOR_VERSION=${MINOR_VERSION} SDL_BUILD_MICRO_VERSION=${MICRO_VERSION} ) target_link_libraries(SDL2_mixer PRIVATE $) if(WIN32 AND BUILD_SHARED_LIBS) target_sources(SDL2_mixer PRIVATE version.rc ) endif() set_target_properties(SDL2_mixer PROPERTIES DEFINE_SYMBOL DLL_EXPORT EXPORT_NAME ${sdl2_mixer_export_name} C_VISIBILITY_PRESET "hidden" ) if(NOT ANDROID) set_target_properties(SDL2_mixer PROPERTIES DEBUG_POSTFIX "${SDL2MIXER_DEBUG_POSTFIX}" ) if(APPLE) # the SOVERSION property corresponds to the compatibility version and VERSION corresponds to the current version # https://cmake.org/cmake/help/latest/prop_tgt/SOVERSION.html#mach-o-versions set_target_properties(SDL2_mixer PROPERTIES SOVERSION "${DYLIB_COMPATIBILITY_VERSION}" VERSION "${DYLIB_CURRENT_VERSION}" ) else() set_target_properties(SDL2_mixer PROPERTIES SOVERSION "${LT_MAJOR}" VERSION "${LT_VERSION}" ) endif() endif() if(SDL2MIXER_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) add_custom_command(TARGET SDL2_mixer POST_BUILD COMMAND "${CMAKE_COMMAND}" -E create_symlink "$" "libSDL2_mixer$<$:${SDL2MIXER_DEBUG_POSTFIX}>$" # BYPRODUCTS "libSDL2_mixer$<$:${SDL2MIXER_DEBUG_POSTFIX}>$" # Needs CMake 3.20 WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" ) endif() if(SDL2MIXER_BUILD_SHARED_LIBS) if(WIN32 OR OS2) set_target_properties(SDL2_mixer PROPERTIES PREFIX "" DLL_NAME_WITH_SOVERSION FALSE ) endif() if(OS2) # OS/2 doesn't support a DLL name longer than 8 characters. set_target_properties(SDL2_mixer PROPERTIES OUTPUT_NAME "SDL2mix" ) elseif(UNIX AND NOT ANDROID) set_target_properties(SDL2_mixer PROPERTIES OUTPUT_NAME "SDL2_mixer-${LT_RELEASE}" ) endif() else() if(MSVC OR (WATCOM AND (WIN32 OR OS2))) set_target_properties(SDL2_mixer PROPERTIES OUTPUT_NAME "SDL2_mixer-static" ) endif() endif() if(SDL2MIXER_BUILD_SHARED_LIBS) # Use `Compatible Interface Properties` to ensure a shared SDL2_mixer is linked to a shared SDL2 library set_property(TARGET SDL2_mixer PROPERTY INTERFACE_SDL2_SHARED ${SDL2MIXER_BUILD_SHARED_LIBS}) set_property(TARGET SDL2_mixer APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL SDL2_SHARED) endif() if(SDL2MIXER_BUILD_SHARED_LIBS) sdl_target_link_options_no_undefined(SDL2_mixer) endif() if(SDL2MIXER_BUILD_SHARED_LIBS) # Make sure static library dependencies are built with -fPIC when building a shared SDL2_mixer set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() set(INSTALL_EXTRA_TARGETS) set(PC_LIBS) set(PC_REQUIRES) if(SDL2MIXER_CMD) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_CMD) set(fork_found OFF) if(NOT fork_found) check_symbol_exists(fork unistd.h HAVE_FORK) if(HAVE_FORK) set(fork_found ON) target_compile_definitions(SDL2_mixer PRIVATE HAVE_FORK) endif() endif() if(NOT fork_found) check_symbol_exists(vfork unistd.h HAVE_VFORK) if(HAVE_VFORK) set(fork_found ON) target_compile_definitions(SDL2_mixer PRIVATE HAVE_VFORK) endif() endif() if(NOT fork_found) message(FATAL_ERROR "Neither fork() nor vfork() or available on this platform. Reconfigure with -DSDL2MIXER_CMD=OFF.") endif() endif() if(SDL2MIXER_OGG) # libogg is a requirement of libflac, libtremor and libvorbisfile, so only need this library when vendoring if(SDL2MIXER_VENDORED) message(STATUS "Using vendored libogg") set(BUILD_SHARED_LIBS ${SDL2MIXER_OGG_SHARED}) set(INSTALL_CMAKE_PACKAGE_MODULE FALSE) set(BUILD_TESTING OFF) sdl_check_project_in_subfolder(external/ogg ogg SDL2MIXER_VENDORED) add_subdirectory(external/ogg EXCLUDE_FROM_ALL) if(SDL2MIXER_OGG_install) list(APPEND INSTALL_EXTRA_TARGETS ogg) endif() endif() endif() if(SDL2MIXER_OPUS) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_OPUS) if(SDL2MIXER_VENDORED) # vendored libogg alread handled if(NOT TARGET ogg) message(FATAL_ERROR "ogg target not present") endif() message(STATUS "Using vendored opus") set(BUILD_SHARED_LIBS ${SDL2MIXER_OPUS_SHARED}) set(BUILD_PROGRAMS OFF) sdl_check_project_in_subfolder(external/opus opus SDL2MIXER_VENDORED) add_subdirectory(external/opus EXCLUDE_FROM_ALL) set(OP_DISABLE_DOCS TRUE) set(OP_DISABLE_EXAMPLES TRUE) set(OP_DISABLE_HTTP TRUE) message(STATUS "Using vendored opusfile") set(BUILD_SHARED_LIBS ${SDL2MIXER_OPUS_SHARED}) sdl_check_project_in_subfolder(external/opusfile opusfile SDL2MIXER_VENDORED) add_subdirectory(external/opusfile EXCLUDE_FROM_ALL) if(MSVC) set_property(TARGET opusfile PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/opusfile.h" "#include \"${CMAKE_CURRENT_SOURCE_DIR}/external/opusfile/include/opusfile.h\"\n") execute_process( COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/opus" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/opusfile.h" "${CMAKE_CURRENT_BINARY_DIR}/opus/opusfile.h" ) target_include_directories(SDL2_mixer PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") if(NOT TARGET OpusFile::opusfile) add_library(OpusFile::opusfile ALIAS opusfile) endif() if(SDL2MIXER_OPUS_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS opus opusfile) endif() if(NOT SDL2MIXER_OPUS_SHARED) list(APPEND PC_LIBS -l$ -l$ -l$) endif() else() message(STATUS "Using system opusfile") find_package(OpusFile REQUIRED) if(NOT SDL2MIXER_OPUS_SHARED) list(APPEND PC_REQUIRES opusfile) endif() endif() if(SDL2MIXER_OPUS_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_opusfile OpusFile::opusfile) message(STATUS "Dynamic opus (opusfile): ${dynamic_opusfile}") target_compile_definitions(SDL2_mixer PRIVATE "OPUS_DYNAMIC=\"${dynamic_opusfile}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer OpusFile::opusfile) endif() else() target_link_libraries(SDL2_mixer PRIVATE OpusFile::opusfile) endif() endif() if(SDL2MIXER_VORBIS_STB) message(STATUS "Enabled ogg music: using stb_vorbis") target_compile_definitions(SDL2_mixer PRIVATE MUSIC_OGG) target_compile_definitions(SDL2_mixer PRIVATE OGG_USE_STB) endif() if(SDL2MIXER_VORBIS_TREMOR) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_OGG OGG_USE_TREMOR) if(SDL2MIXER_VENDORED) # vendored libogg alread handled if(NOT TARGET ogg) message(FATAL_ERROR "ogg target not present") endif() message(STATUS "Using vendored tremor") set(BUILD_SHARED_LIBS ${SDL2MIXER_VORBIS_TREMOR_SHARED}) sdl_check_project_in_subfolder(external/tremor tremor SDL2MIXER_VENDORED) add_subdirectory(external/tremor EXCLUDE_FROM_ALL) if(NOT TARGET tremor::tremor) add_library(tremor::tremor ALIAS vorbisidec) endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ivorbisfile.h" "#include \"${CMAKE_CURRENT_SOURCE_DIR}/external/tremor/ivorbisfile.h\"\n") execute_process( COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/tremor" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/ivorbisfile.h" "${CMAKE_CURRENT_BINARY_DIR}/tremor/ivorbisfile.h" ) target_include_directories(SDL2_mixer PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") if(SDL2MIXER_VORBIS_TREMOR_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS vorbisidec) endif() if(NOT SDL2MIXER_VORBIS_TREMOR_SHARED) list(APPEND PC_LIBS -l$ -l$) endif() else() message(STATUS "Using system tremor") find_package(tremor REQUIRED) if(NOT SDL2MIXER_VORBIS_TREMOR_SHARED) list(APPEND PC_REQUIRES tremor) endif() endif() if(SDL2MIXER_VORBIS_TREMOR_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_tremor tremor::tremor) message(STATUS "Dynamic vorbis (tremor): ${dynamic_tremor}") target_compile_definitions(SDL2_mixer PRIVATE "OGG_DYNAMIC=\"${dynamic_tremor}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer tremor::tremor) endif() else() target_link_libraries(SDL2_mixer PRIVATE tremor::tremor) endif() endif() if(SDL2MIXER_VORBIS_VORBISFILE) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_OGG) if(SDL2MIXER_VENDORED) # vendored libogg alread handled if(NOT TARGET ogg) message(FATAL_ERROR "ogg target not present") endif() message(STATUS "Using vendored vorbis + vorbisfile") set(BUILD_SHARED_LIBS ${SDL2MIXER_VORBIS_VORBISFILE_SHARED}) sdl_check_project_in_subfolder(external/vorbis vorbisfile SDL2MIXER_VENDORED) add_subdirectory(external/vorbis EXCLUDE_FROM_ALL) if(NOT TARGET Vorbis::vorbisfile) add_library(Vorbis::vorbisfile ALIAS vorbisfile) endif() if(SDL2MIXER_VORBIS_VORBISFILE_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS vorbis vorbisfile) endif() if(NOT SDL2MIXER_VORBIS_VORBISFILE_SHARED) list(APPEND PC_LIBS -l$) endif() else() message(STATUS "Using system vorbisfile") find_package(Vorbis REQUIRED) if(NOT SDL2MIXER_VORBIS_VORBISFILE_SHARED) list(APPEND PC_REQUIRES vorbisfile) endif() endif() if(SDL2MIXER_VORBIS_VORBISFILE_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_vorbisfile Vorbis::vorbisfile) message(STATUS "Dynamic vorbisfile: ${dynamic_vorbisfile}") target_compile_definitions(SDL2_mixer PRIVATE "OGG_DYNAMIC=\"${dynamic_vorbisfile}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer Vorbis::vorbisfile) endif() else() target_link_libraries(SDL2_mixer PRIVATE Vorbis::vorbisfile) endif() endif() if(SDL2MIXER_FLAC_LIBFLAC) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_FLAC_LIBFLAC) if(SDL2MIXER_VENDORED) # vendored libogg already handled if(NOT TARGET ogg) message(FATAL_ERROR "ogg target not present") endif() set(BUILD_SHARED_LIBS "${SDL2MIXER_FLAC_LIBFLAC_SHARED}") set(INSTALL_CMAKE_CONFIG_MODULE OFF) set(WITH_OGG OFF) set(BUILD_CXXLIBS OFF) set(BUILD_EXAMPLES OFF) set(BUILD_PROGRAMS OFF) set(BUILD_TESTING OFF) set(INSTALL_MANPAGES OFF) message(STATUS "Using vendored libflac") sdl_check_project_in_subfolder(external/flac libflac SDL2MIXER_VENDORED) add_subdirectory(external/flac EXCLUDE_FROM_ALL) if(SDL2MIXER_FLAC_LIBFLAC_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS FLAC) endif() if(NOT SDL2MIXER_FLAC_LIBFLAC_SHARED) list(APPEND PC_LIBS -l$ -l$) endif() else() message(STATUS "Using system libflac") find_package(FLAC REQUIRED) if(NOT SDL2MIXER_FLAC_LIBFLAC_SHARED) list(APPEND PC_REQUIRES flac) endif() endif() if(SDL2MIXER_FLAC_LIBFLAC_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_flac FLAC::FLAC) message(STATUS "Dynamic libflac: ${dynamic_flac}") target_compile_definitions(SDL2_mixer PRIVATE "FLAC_DYNAMIC=\"${dynamic_flac}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer FLAC) endif() else() target_link_libraries(SDL2_mixer PRIVATE FLAC::FLAC) endif() endif() if(SDL2MIXER_FLAC_DRFLAC) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_FLAC_DRFLAC) endif() if(SDL2MIXER_GME) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_GME) if(SDL2MIXER_VENDORED) set(BUILD_SHARED_LIBS "${SDL2MIXER_GME_SHARED}") set(ENABLE_UBSAN OFF) set(BUILD_FRAMEWORK OFF) set(GME_ZLIB OFF) set(GME_UNRAR OFF) message(STATUS "Using vendored libgme") sdl_check_project_in_subfolder(external/libgme libgme SDL2MIXER_VENDORED) add_subdirectory(external/libgme EXCLUDE_FROM_ALL) add_library(gme::gme ALIAS gme) if(SDL2MIXER_GME_SHARED) list(APPEND INSTALL_EXTRA_TARGETS gme) endif() if(NOT SDL2MIXER_GME_SHARED) list(APPEND PC_LIBS -l$) endif() else() message(STATUS "Using system libgme") find_package(gme REQUIRED) if(NOT SDL2MIXER_GME_SHARED) list(APPEND PC_REQUIRES libgme) endif() endif() if(SDL2MIXER_GME_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_gme gme::gme) message(STATUS "Dynamic libgme: ${dynamic_gme}") target_compile_definitions(SDL2_mixer PRIVATE "GME_DYNAMIC=\"${dynamic_gme}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer gme::gme) endif() else() target_link_libraries(SDL2_mixer PRIVATE gme::gme) endif() endif() if(SDL2MIXER_MOD_MODPLUG) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MOD_MODPLUG) if(SDL2MIXER_VENDORED) message(STATUS "Using vendored libmodplug") message(FATAL_ERROR "libmodplug is not vendored.") else() message(STATUS "Using system libmodplug") find_package(modplug REQUIRED) if(NOT SDL2MIXER_MOD_MODPLUG_SHARED) list(APPEND PC_REQUIRES libmodplug) endif() endif() if(SDL2MIXER_MOD_MODPLUG_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_modplug modplug::modplug) message(STATUS "Dynamic modplug: ${dynamic_modplug}") target_compile_definitions(SDL2_mixer PRIVATE "MODPLUG_DYNAMIC=\"${dynamic_modplug}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer modplug::modplug) endif() else() target_link_libraries(SDL2_mixer PRIVATE modplug::modplug) endif() endif() if(SDL2MIXER_MOD_XMP) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MOD_XMP) if(SDL2MIXER_VENDORED) message(STATUS "Using vendored libxmp") sdl_check_project_in_subfolder(external/libxmp libxmp SDL2MIXER_VENDORED) set(LIBXMP_DISABLE_DEPACKERS ON) set(LIBXMP_DISABLE_PROWIZARD ON) if(SDL2MIXER_MOD_XMP_SHARED) set(BUILD_STATIC OFF) set(BUILD_SHARED ON) set(tgt_xmp xmp_shared) else() set(BUILD_STATIC ON) set(BUILD_SHARED OFF) set(tgt_xmp xmp_static) endif() set(xmp_name libxmp) add_subdirectory(external/libxmp EXCLUDE_FROM_ALL) if(SDL2MIXER_MOD_XMP_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS ${tgt_xmp}) endif() if(NOT SDL2MIXER_MOD_XMP_SHARED) list(APPEND PC_LIBS -l$) endif() else() if(SDL2MIXER_MOD_XMP_LITE) message(STATUS "Using system libxmp-lite") find_package(libxmp-lite REQUIRED) set(tgt_xmp libxmp-lite::libxmp-lite) set(xmp_name libxmp-lite) if(NOT SDL2MIXER_MOD_XMP_SHARED) list(APPEND PC_REQUIRES libxmp-lite) endif() else() message(STATUS "Using system libxmp") find_package(libxmp REQUIRED) if(TARGET libxmp::xmp_shared AND SDL2MIXER_MOD_XMP_SHARED) set(tgt_xmp libxmp::xmp_shared) elseif(TARGET libxmp::xmp_static) set(tgt_xmp libxmp::xmp_static) else() set(tgt_xmp libxmp::libxmp) endif() set(xmp_name libxmp) if(NOT SDL2MIXER_MOD_XMP_SHARED) list(APPEND PC_REQUIRES libxmp) endif() endif() endif() if(SDL2MIXER_MOD_XMP_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_xmp ${tgt_xmp}) message(STATUS "Dynamic ${xmp_name}: ${dynamic_xmp}") target_compile_definitions(SDL2_mixer PRIVATE "XMP_DYNAMIC=\"${dynamic_xmp}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer ${tgt_xmp}) endif() else() target_link_libraries(SDL2_mixer PRIVATE ${tgt_xmp}) endif() endif() if(SDL2MIXER_MP3_MINIMP3) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MP3_MINIMP3) endif() if(SDL2MIXER_MP3_MPG123) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MP3_MPG123) if(SDL2MIXER_VENDORED) message(STATUS "Using vendored mpg123") sdl_check_project_in_subfolder(external/mpg123/ports/cmake mpg123 SDL2MIXER_VENDORED) set(BUILD_LIBOUT123 FALSE) set(BUILD_PROGRAMS OFF) set(BUILD_SHARED_LIBS "${SDL2MIXER_MP3_MPG123_SHARED}") add_subdirectory(external/mpg123/ports/cmake EXCLUDE_FROM_ALL) if(NOT TARGET MPG123::libmpg123) add_library(MPG123::libmpg123 ALIAS libmpg123) endif() if(SDL2MIXER_MP3_MPG123_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS libmpg123) endif() if(NOT SDL2MIXER_MP3_MPG123_SHARED) list(APPEND PC_LIBS -l$) endif() else() message(STATUS "Using system mpg123") find_package(mpg123 REQUIRED) if(NOT SDL2MIXER_MP3_MPG123_SHARED) list(APPEND PC_REQUIRES libmpg123) endif() endif() if(SDL2MIXER_MP3_MPG123_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_mpg123 MPG123::libmpg123) message(STATUS "Dynamic mpg123}: ${dynamic_mpg123}") target_compile_definitions(SDL2_mixer PRIVATE "MPG123_DYNAMIC=\"${dynamic_mpg123}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer MPG123::libmpg123) endif() else() target_link_libraries(SDL2_mixer PRIVATE MPG123::libmpg123) endif() endif() if(SDL2MIXER_MIDI_FLUIDSYNTH) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MID_FLUIDSYNTH) if(SDL2MIXER_VENDORED) message(STATUS "Using vendored FluidSynth") message(FATAL_ERROR "FluidSynth is not vendored.") else() message(STATUS "Using system FluidSynth") find_package(FluidSynth REQUIRED) if(NOT SDL2MIXER_MIDI_FLUIDSYNTH_SHARED) list(APPEND PC_REQUIRES fluidsynth) endif() endif() if(SDL2MIXER_MIDI_FLUIDSYNTH_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_fluidsynth FluidSynth::libfluidsynth) message(STATUS "Dynamic fluidsynth: ${dynamic_fluidsynth}") target_compile_definitions(SDL2_mixer PRIVATE "FLUIDSYNTH_DYNAMIC=\"${dynamic_fluidsynth}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer FluidSynth::libfluidsynth) endif() else() target_link_libraries(SDL2_mixer PRIVATE FluidSynth::libfluidsynth) endif() endif() if(SDL2MIXER_MIDI_NATIVE) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MID_NATIVE) target_sources(SDL2_mixer PRIVATE src/codecs/native_midi/native_midi_common.c src/codecs/native_midi/native_midi_common.h ) if(WIN32) target_sources(SDL2_mixer PRIVATE src/codecs/native_midi/native_midi_win32.c) target_link_libraries(SDL2_mixer PRIVATE winmm) elseif(APPLE) target_sources(SDL2_mixer PRIVATE src/codecs/native_midi/native_midi_macosx.c) target_link_libraries(SDL2_mixer PRIVATE -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices) elseif(HAIKU) enable_language(CXX) target_sources(SDL2_mixer PRIVATE src/codecs/native_midi/native_midi_haiku.cpp) target_link_libraries(SDL2_mixer PRIVATE midi) endif() endif() if(SDL2MIXER_MIDI_TIMIDITY) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_MID_TIMIDITY) target_sources(SDL2_mixer PRIVATE src/codecs/timidity/common.c src/codecs/timidity/instrum.c src/codecs/timidity/mix.c src/codecs/timidity/output.c src/codecs/timidity/playmidi.c src/codecs/timidity/readmidi.c src/codecs/timidity/resample.c src/codecs/timidity/tables.c src/codecs/timidity/timidity.c ) endif() if(SDL2MIXER_WAVE) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_WAV) endif() if(SDL2MIXER_WAVPACK) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_WAVPACK) if(SDL2MIXER_VENDORED) message(STATUS "Using vendored WavPack") sdl_check_project_in_subfolder(external/wavpack WavPack SDL2MIXER_VENDORED) set(WAVPACK_BUILD_PROGRAMS FALSE) set(WAVPACK_BUILD_COOLEDIT_PLUGIN OFF) set(WAVPACK_BUILD_WINAMP_PLUGIN OFF) set(WAVPACK_BUILD_DOCS OFF) set(BUILD_SHARED_LIBS "${SDL2MIXER_WAVPACK_SHARED}") add_subdirectory(external/wavpack EXCLUDE_FROM_ALL) if(SDL2MIXER_WAVPACK_SHARED OR NOT SDL2MIXER_BUILD_SHARED_LIBS) list(APPEND INSTALL_EXTRA_TARGETS wavpack) endif() if(NOT SDL2MIXER_WAVPACK_SHARED) list(APPEND PC_LIBS -l$) endif() target_compile_definitions(SDL2_mixer PRIVATE HAVE_WAVPACK_H) else() message(STATUS "Using system WavPack") find_package(wavpack REQUIRED) if(NOT SDL2MIXER_WAVPACK_SHARED) list(APPEND PC_REQUIRES wavpack) endif() endif() if(SDL2MIXER_WAVPACK_DSD) target_compile_definitions(SDL2_mixer PRIVATE MUSIC_WAVPACK_DSD) endif() if(SDL2MIXER_WAVPACK_SHARED) target_include_directories(SDL2_mixer PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_wavpack WavPack::WavPack) message(STATUS "Dynamic WavPack: ${dynamic_wavpack}") target_compile_definitions(SDL2_mixer PRIVATE "WAVPACK_DYNAMIC=\"${dynamic_wavpack}\"") if(SDL2MIXER_VENDORED) add_dependencies(SDL2_mixer WavPack::WavPack) endif() else() target_link_libraries(SDL2_mixer PRIVATE WavPack::WavPack) endif() endif() if(SDL2MIXER_INSTALL) install( TARGETS SDL2_mixer EXPORT SDL2MixerTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2" COMPONENT devel ) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/SDL_mixer.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2" COMPONENT devel ) if(INSTALL_EXTRA_TARGETS) install(TARGETS ${INSTALL_EXTRA_TARGETS} EXPORT SDL2MixerTargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library ) endif() ##### export files ##### if(WIN32 AND NOT MINGW) set(SDLMIXER_INSTALL_CMAKEDIR_DEFAULT "cmake") else() set(SDLMIXER_INSTALL_CMAKEDIR_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake/SDL2_mixer") endif() set(SDLMIXER_INSTALL_CMAKEDIR "${SDLMIXER_INSTALL_CMAKEDIR_DEFAULT}" CACHE STRING "Location where to install SDL2_mixerConfig.cmake") configure_package_config_file(SDL2_mixerConfig.cmake.in SDL2_mixerConfig.cmake INSTALL_DESTINATION "${SDLMIXER_INSTALL_CMAKEDIR}" ) write_basic_package_version_file("${PROJECT_BINARY_DIR}/SDL2_mixerConfigVersion.cmake" VERSION ${FULL_VERSION} COMPATIBILITY AnyNewerVersion ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixerConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixerConfigVersion.cmake" DESTINATION "${SDLMIXER_INSTALL_CMAKEDIR}" COMPONENT devel ) if(NOT SDL2MIXER_BUILD_SHARED_LIBS) install( FILES cmake/PkgConfigHelper.cmake cmake/FindFLAC.cmake cmake/FindFluidSynth.cmake cmake/Findgme.cmake cmake/Findlibxmp.cmake cmake/Findlibxmp-lite.cmake cmake/Findmodplug.cmake cmake/Findmpg123.cmake cmake/FindOpusFile.cmake cmake/Findtremor.cmake cmake/FindVorbis.cmake cmake/Findwavpack.cmake DESTINATION "${SDLMIXER_INSTALL_CMAKEDIR}" COMPONENT devel ) endif() install(EXPORT SDL2MixerTargets FILE SDL2_mixer-${sdl2_mixer_install_name_infix}-targets.cmake NAMESPACE SDL2_mixer:: DESTINATION "${SDLMIXER_INSTALL_CMAKEDIR}" COMPONENT devel ) set(VERSION ${FULL_VERSION}) set(SDL_VERSION ${SDL_REQUIRED_VERSION}) set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") set(bindir "\${exec_prefix}/bin") set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") string(JOIN " " PC_REQUIRES ${PC_REQUIRES}) string(JOIN " " PC_LIBS ${PC_LIBS}) configure_file(SDL2_mixer.pc.in ${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer.pc.intermediate @ONLY) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer-$.pc" INPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer.pc.intermediate") set(PC_DESTDIR) if(CMAKE_SYSTEM_NAME MATCHES FreeBSD) # FreeBSD uses ${PREFIX}/libdata/pkgconfig set(PC_DESTDIR "libdata/pkgconfig") else() set(PC_DESTDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endif() # Always install SDL2_mixer.pc file: libraries might be different between config modes install(CODE " # FIXME: use file(COPY_FILE) if minimum CMake version >= 3.21 execute_process(COMMAND \"\${CMAKE_COMMAND}\" -E copy_if_different \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer-$.pc\" \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer.pc\") file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${PC_DESTDIR}\" TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_mixer.pc\")" COMPONENT devel) if(SDL2MIXER_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) install( FILES "${PROJECT_BINARY_DIR}/libSDL2_mixer$<$:${SDL2MIXER_DEBUG_POSTFIX}>$" DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel ) endif() install(FILES "LICENSE.txt" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}" COMPONENT library ) endif() if(SDL2MIXER_SAMPLES) check_include_file("signal.h" HAVE_SIGNAL_H) check_symbol_exists("setbuf" "stdio.h" HAVE_SETBUF) add_executable(playmus playmus.c) add_executable(playwave playwave.c) find_package(SDL2main) foreach(prog playmus playwave) # FIXME: mingw should be handled by SDL2::SDL2(-static) target if(MINGW) target_link_libraries(${prog} PRIVATE mingw32) target_link_options(${prog} PRIVATE -mwindows) endif() target_link_libraries(${prog} PRIVATE SDL2_mixer::${sdl2_mixer_export_name}) if(TARGET SDL2::SDL2main) target_link_libraries(${prog} PRIVATE SDL2::SDL2main) endif() target_link_libraries(${prog} PRIVATE ${sdl2_target_name}) if(HAVE_SIGNAL_H) target_compile_definitions(${prog} PRIVATE HAVE_SIGNAL_H) endif() if(HAVE_SETBUF) target_compile_definitions(${prog} PRIVATE HAVE_SETBUF) endif() if(SDL2MIXER_SAMPLES_INSTALL) install(TARGETS ${prog} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() endforeach() endif() add_library(SDL2::mixer INTERFACE IMPORTED GLOBAL) set_target_properties(SDL2::mixer PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2_mixer" ) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") set_target_properties(SDL2::mixer PROPERTIES DEPRECATION "Use SDL2_mixer::SDL2_mixer or SDL2_mixer::SDL2_mixer-static instead" ) endif() SDL2_mixer-2.8.0/sdl2_mixer-config-version.cmake.in0000644000076500000240000000060714277744147021062 0ustar valvestaff# sdl2_mixer cmake project-config-version input for ./configure scripts set(PACKAGE_VERSION "@MAJOR_VERSION@.@MINOR_VERSION@.@MICRO_VERSION@") if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) set(PACKAGE_VERSION_EXACT TRUE) endif() endif() SDL2_mixer-2.8.0/cmake/0000755000076500000240000000000014553251273013520 5ustar valvestaffSDL2_mixer-2.8.0/cmake/Findwavpack.cmake0000644000076500000240000000215114551252471016755 0ustar valvestaffinclude(FindPackageHandleStandardArgs) if(WIN32) set(wavpack_find_names wavpack libwavpack wavpackdll) else() set(wavpack_find_names wavpack) endif() find_library(wavpack_LIBRARY NAMES ${wavpack_find_names} ) find_path(wavpack_INCLUDE_PATH NAMES wavpack/wavpack.h ) set(wavpack_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of wavpack") set(wavpack_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of wavpack") set(wavpack_LINK_OPTIONS "" CACHE STRING "Extra link flags of wavpack") find_package_handle_standard_args(wavpack REQUIRED_VARS wavpack_LIBRARY wavpack_INCLUDE_PATH ) if (wavpack_FOUND) if (NOT TARGET WavPack::WavPack) add_library(WavPack::WavPack UNKNOWN IMPORTED) set_target_properties(WavPack::WavPack PROPERTIES IMPORTED_LOCATION "${wavpack_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${wavpack_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${wavpack_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${wavpack_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${wavpack_LINK_OPTIONS}" ) endif() endif() SDL2_mixer-2.8.0/cmake/Findlibxmp-lite.cmake0000644000076500000240000000317314551252471017554 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_XMPLITE QUIET libxmp-lite) find_library(libxmp_lite_LIBRARY NAMES xmp-lite libxmp-lite HINTS ${PC_XMPLITE_LIBDIR} ) find_path(libxmp_lite_INCLUDE_PATH NAMES xmp.h PATH_SUFFIXES libxmp-lite HINTS ${PC_XMPLITE_INCLUDEDIR} ) if(PC_XMPLITE_FOUND) get_flags_from_pkg_config("${libxmp_lite_LIBRARY}" "PC_XMPLITE" "_libxmp_lite") endif() set(libxmp_lite_COMPILE_OPTIONS "${_libxmp_lite_compile_options}" CACHE STRING "Extra compile options of libxmp_lite") set(libxmp_lite_LINK_LIBRARIES "${_libxmp_lite_link_libraries}" CACHE STRING "Extra link libraries of libxmp_lite") set(libxmp_lite_LINK_OPTIONS "${_libxmp_lite_link_options}" CACHE STRING "Extra link flags of libxmp_lite") set(libxmp_lite_LINK_DIRECTORIES "${_libxmp_lite_link_directories}" CACHE PATH "Extra link directories of libxmp_lite") find_package_handle_standard_args(libxmp-lite REQUIRED_VARS libxmp_lite_LIBRARY libxmp_lite_INCLUDE_PATH ) if(libxmp-lite_FOUND) if(NOT TARGET libxmp-lite::libxmp-lite) add_library(libxmp-lite::libxmp-lite UNKNOWN IMPORTED) set_target_properties(libxmp-lite::libxmp-lite PROPERTIES IMPORTED_LOCATION "${libxmp_lite_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${libxmp_lite_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${libxmp_lite_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${libxmp_lite_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${libxmp_lite_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${libxmp_lite_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/FindSDL2main.cmake0000644000076500000240000000120014277744147016677 0ustar valvestaff# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_MAIN_LIBRARY NAMES SDL2main HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_package_handle_standard_args(SDL2main REQUIRED_VARS SDL2_MAIN_LIBRARY ) if(SDL2main_FOUND) if(NOT TARGET SDL2::SDL2main) add_library(SDL2::SDL2main UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2main PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}" ) endif() endif() SDL2_mixer-2.8.0/cmake/test/0000755000076500000240000000000014553251273014477 5ustar valvestaffSDL2_mixer-2.8.0/cmake/test/CMakeLists.txt0000644000076500000240000000353414277744147017256 0ustar valvestaff# This cmake build script is meant for verifying the various CMake configuration script. cmake_minimum_required(VERSION 3.12) project(sdl_test LANGUAGES C) cmake_policy(SET CMP0074 NEW) # Override CMAKE_FIND_ROOT_PATH_MODE to allow search for SDL2_mixer outside of sysroot set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) include(FeatureSummary) option(TEST_SHARED "Test linking to shared SDL2_mixer library" ON) add_feature_info("TEST_SHARED" TEST_SHARED "Test linking with shared library") option(TEST_STATIC "Test linking to static SDL2_mixer libary" ON) add_feature_info("TEST_STATIC" TEST_STATIC "Test linking with static library") if(TEST_SHARED) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2 2.0) endif() find_package(SDL2_mixer REQUIRED CONFIG) add_executable(main_shared main.c) target_link_libraries(main_shared PRIVATE SDL2::SDL2 SDL2_mixer::SDL2_mixer) endif() if(TEST_STATIC) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2-static) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2-static) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2-static 2.0) endif() # some static vendored libraries use c++ (enable CXX after `find_package` might show a warning) enable_language(CXX) find_package(SDL2_mixer REQUIRED CONFIG) add_executable(main_static main.c) target_link_libraries(main_static PRIVATE SDL2::SDL2-static SDL2_mixer::SDL2_mixer-static) endif() feature_summary(WHAT ALL) SDL2_mixer-2.8.0/cmake/test/main.c0000644000076500000240000000071014277744147015577 0ustar valvestaff#define SDL_MAIN_HANDLED #include "SDL.h" #include "SDL_mixer.h" #include int main(int argc, char *argv[]) { SDL_SetMainReady(); if (SDL_Init(0) < 0) { fprintf(stderr, "SDL_Init: could not initialize SDL: %s\n", SDL_GetError()); return 1; } if (Mix_Init(0) == 0) { fprintf(stderr, "Mix_Init: no sound/music loaders supported (%s)\n", Mix_GetError()); } Mix_Quit(); SDL_Quit(); return 0; } SDL2_mixer-2.8.0/cmake/Findgme.cmake0000644000076500000240000000270214551252471016073 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_GME QUIET libgme) find_library(gme_LIBRARY NAMES gme HINTS ${PC_GME_LIBDIR} ) find_path(gme_INCLUDE_PATH NAMES gme/gme.h HINTS ${PC_GME_INCLUDEDIR} ) if(PC_GME_FOUND) get_flags_from_pkg_config("${gme_LIBRARY}" "PC_GME" "_gme") endif() set(gme_COMPILE_OPTIONS "${_gme_compile_options}" CACHE STRING "Extra compile options of gme") set(gme_LINK_LIBRARIES "${_gme_link_libraries}" CACHE STRING "Extra link libraries of gme") set(gme_LINK_OPTIONS "${_gme_link_options}" CACHE STRING "Extra link flags of gme") set(gme_LINK_DIRECTORIES "${_gme_link_directories}" CACHE PATH "Extra link directories of gme") find_package_handle_standard_args(gme REQUIRED_VARS gme_LIBRARY gme_INCLUDE_PATH ) if(gme_FOUND) set(gme_dirs ${gme_INCLUDE_PATH}) if(EXISTS "${gme_INCLUDE_PATH}/gme") list(APPEND gme_dirs "${gme_INCLUDE_PATH}/gme") endif() if(NOT TARGET gme::gme) add_library(gme::gme UNKNOWN IMPORTED) set_target_properties(gme::gme PROPERTIES IMPORTED_LOCATION "${gme_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${gme_dirs}" INTERFACE_COMPILE_OPTIONS "${gme_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${gme_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${gme_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${gme_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/FindVorbis.cmake0000644000076500000240000000322214551252471016565 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_VORBIS QUIET vorbisfile) find_library(Vorbis_vorbisfile_LIBRARY NAMES vorbisfile HINTS ${PC_VORBIS_LIBDIR} ) find_path(Vorbis_vorbisfile_INCLUDE_PATH NAMES vorbis/vorbisfile.h HINTS ${PC_VORBIS_INCLUDEDIR} ) if(PC_VORBIS_FOUND) get_flags_from_pkg_config("${Vorbis_vorbisfile_LIBRARY}" "PC_VORBIS" "_vorbisfile") endif() set(Vorbis_vorbisfile_COMPILE_OPTIONS "${_vorbisfile_compile_options}" CACHE STRING "Extra compile options of vorbisfile") set(Vorbis_vorbisfile_LINK_LIBRARIES "${_vorbisfile_link_libraries}" CACHE STRING "Extra link libraries of vorbisfile") set(Vorbis_vorbisfile_LINK_OPTIONS "${_vorbisfile_link_options}" CACHE STRING "Extra link flags of vorbisfile") set(Vorbis_vorbisfile_LINK_DIRECTORIES "${_vorbisfile_link_directories}" CACHE PATH "Extra link directories of vorbisfile") find_package_handle_standard_args(Vorbis REQUIRED_VARS Vorbis_vorbisfile_LIBRARY Vorbis_vorbisfile_INCLUDE_PATH ) if (Vorbis_FOUND) if (NOT TARGET Vorbis::vorbisfile) add_library(Vorbis::vorbisfile UNKNOWN IMPORTED) set_target_properties(Vorbis::vorbisfile PROPERTIES IMPORTED_LOCATION "${Vorbis_vorbisfile_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${Vorbis_vorbisfile_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${Vorbis_vorbisfile_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${Vorbis_vorbisfile_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${Vorbis_vorbisfile_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${Vorbis_vorbisfile_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/FindFluidSynth.cmake0000644000076500000240000000313714551252471017417 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_FLUIDSYNTH QUIET fluidsynth) find_library(FluidSynth_LIBRARY NAMES fluidsynth libfluidsynth HINTS ${PC_FLUIDSYNTH_LIBDIR} ) find_path(FluidSynth_INCLUDE_PATH NAMES fluidsynth.h HINTS ${PC_FLUIDSYNTH_INCLUDEDIR} ) if(PC_FLUIDSYNTH_FOUND) get_flags_from_pkg_config("${FluidSynth_LIBRARY}" "PC_FLUIDSYNTH" "_fluidsynth") endif() set(FluidSynth_COMPILE_OPTIONS "${_fluidsynth_compile_options}" CACHE STRING "Extra compile options of FluidSynth") set(FluidSynth_LINK_LIBRARIES "${_fluidsynth_link_libraries}" CACHE STRING "Extra link libraries of FluidSynth") set(FluidSynth_LINK_OPTIONS "${_fluidsynth_link_options}" CACHE STRING "Extra link flags of FluidSynth") set(FluidSynth_LINK_DIRECTORIES "${_fluidsynth_link_directories}" CACHE PATH "Extra link directories of FluidSynth") find_package_handle_standard_args(FluidSynth REQUIRED_VARS FluidSynth_LIBRARY FluidSynth_INCLUDE_PATH ) if(FluidSynth_FOUND) if(NOT TARGET FluidSynth::libfluidsynth) add_library(FluidSynth::libfluidsynth UNKNOWN IMPORTED) set_target_properties(FluidSynth::libfluidsynth PROPERTIES IMPORTED_LOCATION "${FluidSynth_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${FluidSynth_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${FluidSynth_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${FluidSynth_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${FluidSynth_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${FluidSynth_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/FindPrivateSDL2.cmake0000644000076500000240000000311014277744147017367 0ustar valvestaff# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_LIBRARY NAMES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_path(SDL2_INCLUDE_DIR NAMES SDL_haptic.h PATH_SUFFIXES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_inc_suffixes} ) set(SDL2_VERSION) if(SDL2_INCLUDE_DIR) file(READ "${SDL2_INCLUDE_DIR}/SDL_version.h" _sdl_version_h) string(REGEX MATCH "#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)" _sdl2_major_re "${_sdl_version_h}") set(_sdl2_major "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)" _sdl2_minor_re "${_sdl_version_h}") set(_sdl2_minor "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)" _sdl2_patch_re "${_sdl_version_h}") set(_sdl2_patch "${CMAKE_MATCH_1}") if(_sdl2_major_re AND _sdl2_minor_re AND _sdl2_patch_re) set(SDL2_VERSION "${_sdl2_major}.${_sdl2_minor}.${_sdl2_patch}") endif() endif() find_package_handle_standard_args(PrivateSDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION ) if(PrivateSDL2_FOUND) if(NOT TARGET PrivateSDL2::PrivateSDL2) add_library(PrivateSDL2::PrivateSDL2 UNKNOWN IMPORTED) set_target_properties(PrivateSDL2::PrivateSDL2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_LIBRARY}" ) endif() endif() SDL2_mixer-2.8.0/cmake/FindFLAC.cmake0000644000076500000240000000253414551252471016033 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_FLAC QUIET flac) find_library(FLAC_LIBRARY NAMES FLAC HINTS ${PC_FLAC_LIBDIR} ) find_path(FLAC_INCLUDE_PATH NAMES FLAC/all.h HINTS ${PC_FLAC_INCLUDEDIR} ) if(PC_FLAC_FOUND) get_flags_from_pkg_config("${FLAC_LIBRARY}" "PC_FLAC" "_flac") endif() set(FLAC_COMPILE_OPTIONS "${_flac_compile_options}" CACHE STRING "Extra compile options of FLAC") set(FLAC_LINK_LIBRARIES "${_flac_link_libraries}" CACHE STRING "Extra link libraries of FLAC") set(FLAC_LINK_OPTIONS "${_flac_link_options}" CACHE STRING "Extra link flags of FLAC") set(FLAC_LINK_DIRECTORIES "${_flac_link_directories}" CACHE PATH "Extra link directories of FLAC") find_package_handle_standard_args(FLAC REQUIRED_VARS FLAC_LIBRARY FLAC_INCLUDE_PATH ) if(FLAC_FOUND) if(NOT TARGET FLAC::FLAC) add_library(FLAC::FLAC UNKNOWN IMPORTED) set_target_properties(FLAC::FLAC PROPERTIES IMPORTED_LOCATION "${FLAC_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${FLAC_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${FLAC_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${FLAC_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${FLAC_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${FLAC_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/Findmpg123.cmake0000644000076500000240000000266414551252471016343 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_MPG123 QUIET libmpg123) find_library(mpg123_LIBRARY NAMES mpg123 HINTS ${PC_MPG123_LIBDIR} ) find_path(mpg123_INCLUDE_PATH NAMES mpg123.h HINTS ${PC_MPG123_INCLUDEDIR} ) if(PC_MPG123_FOUND) get_flags_from_pkg_config("${mpg123_LIBRARY}" "PC_MPG123" "_mpg123") endif() set(mpg123_COMPILE_OPTIONS "${_mpg123_compile_options}" CACHE STRING "Extra compile options of mpg123") set(mpg123_LINK_LIBRARIES "${_mpg123_link_libraries}" CACHE STRING "Extra link libraries of mpg123") set(mpg123_LINK_OPTIONS "${_mpg123_link_options}" CACHE STRING "Extra link flags of mpg123") set(mpg123_LINK_DIRECTORIES "${_mpg123_link_directories}" CACHE PATH "Extra link directories of mpg123") find_package_handle_standard_args(mpg123 REQUIRED_VARS mpg123_LIBRARY mpg123_INCLUDE_PATH ) if(mpg123_FOUND) if(NOT TARGET MPG123::libmpg123) add_library(MPG123::libmpg123 UNKNOWN IMPORTED) set_target_properties(MPG123::libmpg123 PROPERTIES IMPORTED_LOCATION "${mpg123_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${mpg123_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${mpg123_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${mpg123_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${mpg123_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${mpg123_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/CommonFindSDL2.cmake0000644000076500000240000000121614277744147017212 0ustar valvestaff# Common variables for FindSDL2*.cmake modules set(_inc_suffixes include) set(_lib_suffixes) if(MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "lib/x86") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "lib/x64") endif() endif() if(MINGW) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "i686-w64-mingw32/lib") list(APPEND _inc_suffixes "i686-w64-mingw32/include") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "x86_46-w64-mingw32/lib") list(APPEND _inc_suffixes "x86_46-w64-mingw32/include") endif() endif()SDL2_mixer-2.8.0/cmake/Findlibxmp.cmake0000644000076500000240000000261514551252471016621 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_XMP QUIET libxmp) find_library(libxmp_LIBRARY NAMES xmp HINTS ${PC_XMP_LIBDIR} ) find_path(libxmp_INCLUDE_PATH NAMES xmp.h HINTS ${PC_XMP_INCLUDEDIR} ) if(PC_XMP_FOUND) get_flags_from_pkg_config("${libxmp_LIBRARY}" "PC_XMP" "_libxmp") endif() set(libxmp_COMPILE_OPTIONS "${_libxmp_compile_options}" CACHE STRING "Extra compile options of libxmp") set(libxmp_LINK_LIBRARIES "${_libxmp_link_libraries}" CACHE STRING "Extra link libraries of libxmp") set(libxmp_LINK_OPTIONS "${_libxmp_link_options}" CACHE STRING "Extra link flags of libxmp") set(libxmp_LINK_DIRECTORIES "${_libxmp_link_directories}" CACHE PATH "Extra link flags of libxmp") find_package_handle_standard_args(libxmp REQUIRED_VARS libxmp_LIBRARY libxmp_INCLUDE_PATH ) if(libxmp_FOUND) if(NOT TARGET libxmp::libxmp) add_library(libxmp::libxmp UNKNOWN IMPORTED) set_target_properties(libxmp::libxmp PROPERTIES IMPORTED_LOCATION "${libxmp_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${libxmp_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${libxmp_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${libxmp_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${libxmp_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${libxmp_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/Findmodplug.cmake0000644000076500000240000000272214551252471016774 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_MODPLUG QUIET libmodplug) find_library(modplug_LIBRARY NAMES modplug ) find_path(modplug_INCLUDE_PATH NAMES libmodplug/modplug.h ) if(PC_MODPLUG_FOUND) get_flags_from_pkg_config("${modplug_LIBRARY}" "PC_MODPLUG" "_modplug") endif() set(modplug_COMPILE_OPTIONS "${_modplug_compile_options}" CACHE STRING "Extra compile options of modplug") set(modplug_LINK_LIBRARIES "${_modplug_link_libraries}" CACHE STRING "Extra link libraries of modplug") set(modplug_LINK_OPTIONS "${_modplug_link_options}" CACHE STRING "Extra link flags of modplug") set(modplug_LINK_DIRECTORIES "${_modplug_link_directories}" CACHE PATH "Extra link directories of modplug") find_package_handle_standard_args(modplug REQUIRED_VARS modplug_LIBRARY modplug_INCLUDE_PATH ) if (modplug_FOUND) if (NOT TARGET modplug::modplug) add_library(modplug::modplug UNKNOWN IMPORTED) set_target_properties(modplug::modplug PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${modplug_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${modplug_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${modplug_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${modplug_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${modplug_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${modplug_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/PrivateSdlFunctions.cmake0000644000076500000240000003042714551252471020475 0ustar valvestaff# This file is shared amongst SDL_image/SDL_mixer/SDL_ttf macro(sdl_calculate_derived_version_variables) if(NOT DEFINED MAJOR_VERSION OR NOT DEFINED MINOR_VERSION OR NOT DEFINED MICRO_VERSION) message(FATAL_ERROR "MAJOR_VERSION, MINOR_VERSION and MICRO_VERSION need to be defined") endif() set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}") # Calculate a libtool-like version number math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}") math(EXPR IS_DEVELOPMENT "${MINOR_VERSION} % 2") if(IS_DEVELOPMENT) # Development branch, 2.5.1 -> libSDL2_XXXXX-2.0.so.0.501.0 set(INTERFACE_AGE 0) else() # Stable branch, 2.6.1 -> libSDL2_XXXXX-2.0.so.0.600.1 set(INTERFACE_AGE ${MICRO_VERSION}) endif() # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. set(LT_MAJOR "0") math(EXPR LT_AGE "${BINARY_AGE} - ${INTERFACE_AGE}") math(EXPR LT_CURRENT "${LT_MAJOR} + ${LT_AGE}") set(LT_REVISION "${INTERFACE_AGE}") # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_XXXXX-2.0.so.0. # TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_XXXXX, which will simplify # it to libSDL2_XXXXX.so.0 set(LT_RELEASE "2.0") set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}") # The following should match the versions in the Xcode project file. # Each version is 1 higher than you might expect, for compatibility # with libtool: macOS ABI versioning is 1-based, unlike other platforms # which are normally 0-based. math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${LT_MAJOR} + ${LT_AGE} + 1") math(EXPR DYLIB_CURRENT_VERSION_MINOR "${LT_REVISION}") set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.0") set(DYLIB_COMPATIBILITY_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.0.0") endmacro() macro(sdl_find_sdl2 TARGET VERSION) if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2ConfigVersion.cmake (or sdl2-config-version.cmake) find_package(SDL2 ${VERSION} QUIET) endif() if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2Config.cmake (or sdl2-config.cmake) find_package(SDL2 QUIET) if(SDL2_FOUND) message(WARNING "Could not verify SDL2 version. Assuming SDL2 has version of at least ${VERSION}.") endif() endif() # Use Private FindSDL2.cmake module to find SDL2 for installations where no SDL2Config.cmake is available, # or for those installations where no target is generated. if(NOT TARGET ${TARGET}) message(STATUS "Using private SDL2 find module") find_package(PrivateSDL2 ${VERSION} REQUIRED) add_library(${TARGET} INTERFACE IMPORTED) set_target_properties(${TARGET} PROPERTIES INTERFACE_LINK_LIBRARIES "PrivateSDL2::PrivateSDL2" ) endif() endmacro() function(read_absolute_symlink DEST PATH) file(READ_SYMLINK "${PATH}" p) if(NOT IS_ABSOLUTE "${p}") get_filename_component(pdir "${PATH}" DIRECTORY) set(p "${pdir}/${p}") endif() get_filename_component(p "${p}" ABSOLUTE) set("${DEST}" "${p}" PARENT_SCOPE) endfunction() function(win32_implib_identify_dll DEST IMPLIB) cmake_parse_arguments(ARGS "NOTFATAL" "" "" ${ARGN}) if(CMAKE_DLLTOOL) execute_process( COMMAND "${CMAKE_DLLTOOL}" --identify "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "${CMAKE_DLLTOOL} failed.") else() set("${DEST}" "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(STRIP "${stdout}" result) set(${DEST} "${result}" PARENT_SCOPE) elseif(MSVC) get_filename_component(CMAKE_C_COMPILER_DIRECTORY "${CMAKE_C_COMPILER}" DIRECTORY CACHE) find_program(CMAKE_DUMPBIN NAMES dumpbin PATHS "${CMAKE_C_COMPILER_DIRECTORY}") if(CMAKE_DUMPBIN) execute_process( COMMAND "${CMAKE_DUMPBIN}" "-headers" "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin failed.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(REGEX MATCH "DLL name[ ]+:[ ]+([^\n]+)\n" match "${stdout}") if(NOT match) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin did not find any associated dll for ${IMPLIB}.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() set(result "${CMAKE_MATCH_1}") set(${DEST} "${result}" PARENT_SCOPE) else() message(FATAL_ERROR "Cannot find dumpbin, please set CMAKE_DUMPBIN cmake variable") endif() else() if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "Don't know how to identify dll from import library. Set CMAKE_DLLTOOL (for mingw) or CMAKE_DUMPBIN (for MSVC)") else() set(${DEST} "${DEST}-NOTFOUND") endif() endif() endfunction() function(get_actual_target) set(dst "${ARGV0}") set(target "${${dst}}") get_target_property(alias "${target}" ALIASED_TARGET) while(alias) set(target "${alias}") get_target_property(alias "${target}" ALIASED_TARGET) endwhile() set("${dst}" "${target}" PARENT_SCOPE) endfunction() function(target_get_dynamic_library DEST TARGET) set(result) get_actual_target(TARGET) if(WIN32) # Use the target dll of the import library set(props_to_check IMPORTED_IMPLIB) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE}) endif() list(APPEND props_to_check IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND props_to_check IMPORTED_IMPLIB_${config_type}) list(APPEND props_to_check IMPORTED_LOCATION_${config_type}) endforeach() foreach(prop_to_check ${props_to_check}) if(NOT result) get_target_property(propvalue "${TARGET}" ${prop_to_check}) if(propvalue AND EXISTS "${propvalue}") win32_implib_identify_dll(result "${propvalue}" NOTFATAL) endif() endif() endforeach() else() # 1. find the target library a file might be symbolic linking to # 2. find all other files in the same folder that symolic link to it # 3. sort all these files, and select the 1st item on Linux, and last on Macos set(location_properties IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND location_properties IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND location_properties IMPORTED_LOCATION_${config_type}) endforeach() if(APPLE) set(valid_shared_library_regex "\\.[0-9]+\\.dylib$") else() set(valid_shared_library_regex "\\.so\\.([0-9.]+)?[0-9]") endif() foreach(location_property ${location_properties}) if(NOT result) get_target_property(library_path "${TARGET}" ${location_property}) if(EXISTS "${library_path}") get_filename_component(library_path "${library_path}" ABSOLUTE) while (IS_SYMLINK "${library_path}") read_absolute_symlink(library_path "${library_path}") endwhile() get_filename_component(libdir "${library_path}" DIRECTORY) file(GLOB subfiles "${libdir}/*") set(similar_files "${library_path}") foreach(subfile ${subfiles}) if(IS_SYMLINK "${subfile}") read_absolute_symlink(subfile_target "${subfile}") while (IS_SYMLINK "${subfile_target}") read_absolute_symlink(subfile_target "${subfile_target}") endwhile() get_filename_component(subfile_target "${subfile_target}" ABSOLUTE) if(subfile_target STREQUAL library_path AND subfile MATCHES "${valid_shared_library_regex}") list(APPEND similar_files "${subfile}") endif() endif() endforeach() list(SORT similar_files) set(index 0) if(APPLE) list(LENGTH similar_files len) math(EXPR index "${len}-1") endif() list(GET similar_files ${index} item) get_filename_component(result "${item}" NAME) endif() endif() endforeach() endif() if(result) string(TOLOWER "${result}" result_lower) if(WIN32 OR OS2) if(NOT result_lower MATCHES ".*dll") message(FATAL_ERROR "\"${result}\" is not a .dll library") endif() elseif(APPLE) if(NOT result_lower MATCHES ".*dylib.*") message(FATAL_ERROR "\"${result}\" is not a .dylib shared library") endif() else() if(NOT result_lower MATCHES ".*so.*") message(FATAL_ERROR "\"${result}\" is not a .so shared library") endif() endif() else() get_target_property(target_type ${TARGET} TYPE) if(target_type MATCHES "SHARED_LIBRARY|MODULE_LIBRARY") # OK elseif(target_type MATCHES "STATIC_LIBRARY|OBJECT_LIBRARY|INTERFACE_LIBRARY|EXECUTABLE") message(SEND_ERROR "${TARGET} is not a shared library, but has type=${target_type}") else() message(WARNING "Unable to extract dynamic library from target=${TARGET}, type=${target_type}.") endif() # TARGET_SONAME_FILE is not allowed for DLL target platforms. if(WIN32) set(result "$") else() set(result "$") endif() endif() set(${DEST} ${result} PARENT_SCOPE) endfunction() macro(sdl_check_project_in_subfolder relative_subfolder name vendored_option) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${relative_subfolder}/CMakeLists.txt") message(FATAL_ERROR "No cmake project for ${name} found in ${relative_subfolder}.\n" "Run the download script in the external folder, or re-configure with -D${vendored_option}=OFF to use system packages.") endif() endmacro() macro(sdl_check_linker_flag flag var) # FIXME: Use CheckLinkerFlag module once cmake minimum version >= 3.18 include(CMakePushCheckState) include(CheckCSourceCompiles) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") check_c_source_compiles("int main() { return 0; }" ${var}) cmake_pop_check_state() endmacro() function(sdl_target_link_options_no_undefined TARGET) if(NOT MSVC) if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") target_link_options(${TARGET} PRIVATE "-Wl,-undefined,error") else() sdl_check_linker_flag("-Wl,--no-undefined" HAVE_WL_NO_UNDEFINED) if(HAVE_WL_NO_UNDEFINED AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") AND WIN32)) target_link_options(${TARGET} PRIVATE "-Wl,--no-undefined") endif() endif() endif() endfunction() SDL2_mixer-2.8.0/cmake/PkgConfigHelper.cmake0000644000076500000240000000234514551252471017534 0ustar valvestaff# Helper for Find modules function(get_flags_from_pkg_config _library _pc_prefix _out_prefix) if("${_library}" MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$") set(_cflags ${_pc_prefix}_STATIC_CFLAGS_OTHER) set(_link_libraries ${_pc_prefix}_STATIC_LIBRARIES) set(_link_options ${_pc_prefix}_STATIC_LDFLAGS_OTHER) set(_library_dirs ${_pc_prefix}_STATIC_LIBRARY_DIRS) else() set(_cflags ${_pc_prefix}_CFLAGS_OTHER) set(_link_libraries ${_pc_prefix}_LIBRARIES) set(_link_options ${_pc_prefix}_LDFLAGS_OTHER) set(_library_dirs ${_pc_prefix}_LIBRARY_DIRS) endif() # The *_LIBRARIES lists always start with the library itself list(POP_FRONT "${_link_libraries}") # Work around CMake's flag deduplication when pc files use `-framework A` instead of `-Wl,-framework,A` string(REPLACE "-framework;" "-Wl,-framework," "_filtered_link_options" "${${_link_options}}") set(${_out_prefix}_compile_options "${${_cflags}}" PARENT_SCOPE) set(${_out_prefix}_link_libraries "${${_link_libraries}}" PARENT_SCOPE) set(${_out_prefix}_link_options "${_filtered_link_options}" PARENT_SCOPE) set(${_out_prefix}_link_directories "${${_library_dirs}}" PARENT_SCOPE) endfunction() SDL2_mixer-2.8.0/cmake/FindOpusFile.cmake0000644000076500000240000000326714551252471017060 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_OPUSFILE QUIET opusfile) find_library(OpusFile_LIBRARY NAMES opusfile HINTS ${PC_OPUSFILE_LIBDIR} ) find_path(OpusFile_INCLUDE_PATH NAMES opusfile.h PATH_SUFFIXES opus HINTS ${PC_OPUSFILE_INCLUDEDIR} ) if(PC_OPUSFILE_FOUND) get_flags_from_pkg_config("${OpusFile_LIBRARY}" "PC_OPUSFILE" "_opusfile") endif() set(OpusFile_COMPILE_OPTIONS "${_opusfile_compile_options}" CACHE STRING "Extra compile options of opusfile") set(OpusFile_LINK_LIBRARIES "${_opusfile_link_libraries}" CACHE STRING "Extra link libraries of opusfile") set(OpusFile_LINK_OPTIONS "${_opusfile_link_options}" CACHE STRING "Extra link flags of opusfile") set(OpusFile_LINK_DIRECTORIES "${_opusfile_link_directories}" CACHE PATH "Extra link directories of opusfile") find_package_handle_standard_args(OpusFile REQUIRED_VARS OpusFile_LIBRARY OpusFile_INCLUDE_PATH ) if (OpusFile_FOUND) set(OpusFile_dirs ${OpusFile_INCLUDE_PATH}) if(EXISTS "${OpusFile_INCLUDE_PATH}/opus") list(APPEND OpusFile_dirs "${OpusFile_INCLUDE_PATH}/opus") endif() if (NOT TARGET OpusFile::opusfile) add_library(OpusFile::opusfile UNKNOWN IMPORTED) set_target_properties(OpusFile::opusfile PROPERTIES IMPORTED_LOCATION "${OpusFile_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${OpusFile_dirs}" INTERFACE_COMPILE_OPTIONS "${OpusFile_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${OpusFile_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${OpusFile_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${OpusFile_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/cmake/Findtremor.cmake0000644000076500000240000000271414551252471016636 0ustar valvestaffinclude(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) pkg_check_modules(PC_TREMOR QUIET vorbisidec) find_library(tremor_LIBRARY NAMES vorbisidec libvorbisidec HINTS ${PC_TREMOR_LIBDIR} ) find_path(tremor_INCLUDE_PATH NAMES tremor/ivorbisfile.h HINTS ${PC_TREMOR_INCLUDEDIR} ) if(PC_TREMOR_FOUND) get_flags_from_pkg_config("${tremor_LIBRARY}" "PC_TREMOR" "_tremor") endif() set(tremor_COMPILE_OPTIONS "${_tremor_compile_options}" CACHE STRING "Extra compile options of vorbis") set(tremor_LINK_LIBRARIES "${_tremor_link_libraries}" CACHE STRING "Extra link libraries of vorbis") set(tremor_LINK_OPTIONS "${_tremor_link_options}" CACHE STRING "Extra link flags of vorbis") set(tremor_LINK_DIRECTORIES "${_tremor_link_directories}" CACHE PATH "Extra link directories of vorbis") find_package_handle_standard_args(tremor REQUIRED_VARS tremor_LIBRARY tremor_INCLUDE_PATH ) if (tremor_FOUND) if (NOT TARGET tremor::tremor) add_library(tremor::tremor UNKNOWN IMPORTED) set_target_properties(tremor::tremor PROPERTIES IMPORTED_LOCATION "${tremor_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${tremor_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${tremor_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${tremor_LINK_LIBRARIES}" INTERFACE_LINK_OPTIONS "${tremor_LINK_OPTIONS}" INTERFACE_LINK_DIRECTORIES "${tremor_LINK_DIRECTORIES}" ) endif() endif() SDL2_mixer-2.8.0/playwave.c0000644000076500000240000003270514551252471014442 0ustar valvestaff/* PLAYWAVE: A test application for the SDL mixer library. Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_stdinc.h" #include #include #include #ifdef unix #include #endif #include "SDL.h" #include "SDL_mixer.h" #ifdef HAVE_SIGNAL_H #include #endif /* * rcg06132001 various mixer tests. Define the ones you want. */ /*#define TEST_MIX_DECODERS*/ /*#define TEST_MIX_VERSIONS*/ /*#define TEST_MIX_CHANNELFINISHED*/ /*#define TEST_MIX_PANNING*/ /*#define TEST_MIX_DISTANCE*/ /*#define TEST_MIX_POSITION*/ #if (defined TEST_MIX_POSITION) #if (defined TEST_MIX_PANNING) #error TEST_MIX_POSITION interferes with TEST_MIX_PANNING. #endif #if (defined TEST_MIX_DISTANCE) #error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE. #endif #endif /* rcg06192001 for debugging purposes. */ static void output_test_warnings(void) { #if (defined TEST_MIX_CHANNELFINISHED) SDL_Log("Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n"); #endif #if (defined TEST_MIX_PANNING) SDL_Log("Warning: TEST_MIX_PANNING is enabled in this binary...\n"); #endif #if (defined TEST_MIX_VERSIONS) SDL_Log("Warning: TEST_MIX_VERSIONS is enabled in this binary...\n"); #endif #if (defined TEST_MIX_DISTANCE) SDL_Log("Warning: TEST_MIX_DISTANCE is enabled in this binary...\n"); #endif #if (defined TEST_MIX_POSITION) SDL_Log("Warning: TEST_MIX_POSITION is enabled in this binary...\n"); #endif } static int audio_open = 0; static Mix_Chunk *wave = NULL; /* rcg06042009 Report available decoders. */ #if (defined TEST_MIX_DECODERS) static void report_decoders(void) { int i, total; SDL_Log("Supported decoders...\n"); total = Mix_GetNumChunkDecoders(); for (i = 0; i < total; i++) { SDL_Log(" - chunk decoder: %s\n", Mix_GetChunkDecoder(i)); } total = Mix_GetNumMusicDecoders(); for (i = 0; i < total; i++) { SDL_Log(" - music decoder: %s\n", Mix_GetMusicDecoder(i)); } } #endif /* rcg06192001 Check new Mixer version API. */ #if (defined TEST_MIX_VERSIONS) static void output_versions(const char *libname, const SDL_version *compiled, const SDL_version *linked) { SDL_Log("This program was compiled against %s %d.%d.%d,\n" " and is dynamically linked to %d.%d.%d.\n", libname, compiled->major, compiled->minor, compiled->patch, linked->major, linked->minor, linked->patch); } static void test_versions(void) { SDL_version compiled; SDL_version linked; SDL_VERSION(&compiled); SDL_GetVersion(&linked); output_versions("SDL", &compiled, &linked); SDL_MIXER_VERSION(&compiled); SDL_memcpy(&linked, Mix_Linked_Version(), sizeof(SDL_version)); output_versions("SDL_mixer", &compiled, &linked); } #endif #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */ static volatile int channel_is_done = 0; static void SDLCALL channel_complete_callback (int chan) { Mix_Chunk *done_chunk = Mix_GetChunk(chan); SDL_Log("We were just alerted that Mixer channel #%d is done.\n", chan); SDL_Log("Channel's chunk pointer is (%p).\n", (void*)done_chunk); SDL_Log(" Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT"); channel_is_done = 1; } #endif /* rcg06192001 abstract this out for testing purposes. */ static int still_playing(void) { #ifdef TEST_MIX_CHANNELFINISHED return !channel_is_done; #else return Mix_Playing(0); #endif } #if (defined TEST_MIX_PANNING) static void do_panning_update(void) { static Uint8 leftvol = 128; static Uint8 rightvol = 128; static Sint8 leftincr = -1; static Sint8 rightincr = 1; static int panningok = 1; static Uint32 next_panning_update = 0; if (panningok && (SDL_GetTicks() >= next_panning_update)) { panningok = Mix_SetPanning(0, leftvol, rightvol); if (!panningok) { SDL_Log("Mix_SetPanning(0, %d, %d) failed!\n", (int) leftvol, (int) rightvol); SDL_Log("Reason: [%s].\n", Mix_GetError()); } if ((leftvol == 255) || (leftvol == 0)) { if (leftvol == 255) { SDL_Log("All the way in the left speaker.\n"); } leftincr *= -1; } if ((rightvol == 255) || (rightvol == 0)) { if (rightvol == 255) { SDL_Log("All the way in the right speaker.\n"); } rightincr *= -1; } leftvol += leftincr; rightvol += rightincr; next_panning_update = SDL_GetTicks() + 10; } } #endif #if (defined TEST_MIX_DISTANCE) static void do_distance_update(void) { static Uint8 distance = 1; static Sint8 distincr = 1; static int distanceok = 1; static Uint32 next_distance_update = 0; if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) { distanceok = Mix_SetDistance(0, distance); if (!distanceok) { SDL_Log("Mix_SetDistance(0, %d) failed!\n", (int) distance); SDL_Log("Reason: [%s].\n", Mix_GetError()); } if (distance == 0) { SDL_Log("Distance at nearest point.\n"); distincr *= -1; } else if (distance == 255) { SDL_Log("Distance at furthest point.\n"); distincr *= -1; } distance += distincr; next_distance_update = SDL_GetTicks() + 15; } } #endif #if (defined TEST_MIX_POSITION) static void do_position_update(void) { static Sint16 distance = 1; static Sint8 distincr = 1; static Sint16 angle = 0; static Sint8 angleincr = 1; static int positionok = 1; static Uint32 next_position_update = 0; if (positionok && (SDL_GetTicks() >= next_position_update)) { positionok = Mix_SetPosition(0, angle, (Uint8)distance); if (!positionok) { SDL_Log("Mix_SetPosition(0, %d, %d) failed!\n", (int) angle, (int) distance); SDL_Log("Reason: [%s].\n", Mix_GetError()); } if (angle == 0) { SDL_Log("Due north; now rotating clockwise...\n"); angleincr = 1; } else if (angle == 360) { SDL_Log("Due north; now rotating counter-clockwise...\n"); angleincr = -1; } distance += distincr; if (distance < 0) { distance = 0; distincr = 3; SDL_Log("Distance is very, very near. Stepping away by threes...\n"); } else if (distance > 255) { distance = 255; distincr = -3; SDL_Log("Distance is very, very far. Stepping towards by threes...\n"); } angle += angleincr; next_position_update = SDL_GetTicks() + 30; } } #endif static void CleanUp(int exitcode) { if (wave) { Mix_FreeChunk(wave); wave = NULL; } if (audio_open) { Mix_CloseAudio(); audio_open = 0; } SDL_Quit(); exit(exitcode); } static void Usage(char *argv0) { SDL_Log("Usage: %s [-8] [-f32] [-r rate] [-c channels] [-f] [-F] [-l] [-m] \n", argv0); } /* * rcg06182001 This is sick, but cool. * * Actually, it's meant to be an example of how to manipulate a voice * without having to use the mixer effects API. This is more processing * up front, but no extra during the mixing process. Also, in a case like * this, when you need to touch the whole sample at once, it's the only * option you've got. And, with the effects API, you are altering a copy of * the original sample for each playback, and thus, your changes aren't * permanent; here, you've got a reversed sample, and that's that until * you either reverse it again, or reload it. */ static void flip_sample(Mix_Chunk *wave) { Uint16 format; int channels, i, incr; Uint8 *start = wave->abuf; Uint8 *end = wave->abuf + wave->alen; Mix_QuerySpec(NULL, &format, &channels); incr = (format & 0xFF) * channels; end -= incr; switch (incr) { case 8: for (i = wave->alen / 2; i >= 0; i -= 1) { Uint8 tmp = *start; *start = *end; *end = tmp; start++; end--; } break; case 16: for (i = wave->alen / 2; i >= 0; i -= 2) { Uint16 tmp = *start; *((Uint16 *) start) = *((Uint16 *) end); *((Uint16 *) end) = tmp; start += 2; end -= 2; } break; case 32: for (i = wave->alen / 2; i >= 0; i -= 4) { Uint32 tmp = *start; *((Uint32 *) start) = *((Uint32 *) end); *((Uint32 *) end) = tmp; start += 4; end -= 4; } break; default: SDL_Log("Unhandled format in sample flipping.\n"); return; } } int main(int argc, char *argv[]) { int audio_rate; Uint16 audio_format; int audio_channels; int loops = 0; int i; int reverse_stereo = 0; int reverse_sample = 0; (void) argc; #ifdef HAVE_SETBUF setbuf(stdout, NULL); /* rcg06132001 for debugging purposes. */ setbuf(stderr, NULL); /* rcg06192001 for debugging purposes, too. */ #endif output_test_warnings(); /* Initialize variables */ audio_rate = MIX_DEFAULT_FREQUENCY; audio_format = MIX_DEFAULT_FORMAT; audio_channels = MIX_DEFAULT_CHANNELS; /* Check command line usage */ for (i = 1; argv[i] && (*argv[i] == '-'); ++i) { if ((strcmp(argv[i], "-r") == 0) && argv[i+1]) { ++i; audio_rate = atoi(argv[i]); } else if (strcmp(argv[i], "-m") == 0) { audio_channels = 1; } else if ((strcmp(argv[i], "-c") == 0) && argv[i+1]) { ++i; audio_channels = atoi(argv[i]); } else if (strcmp(argv[i], "-l") == 0) { loops = -1; } else if (strcmp(argv[i], "-8") == 0) { audio_format = AUDIO_U8; } else if (strcmp(argv[i], "-f32") == 0) { audio_format = AUDIO_F32; } else if (strcmp(argv[i], "-f") == 0) { /* rcg06122001 flip stereo */ reverse_stereo = 1; } else if (strcmp(argv[i], "-F") == 0) { /* rcg06172001 flip sample */ reverse_sample = 1; } else { Usage(argv[0]); return 1; } } if (!argv[i]) { Usage(argv[0]); return 1; } /* Initialize the SDL library */ if (SDL_Init(SDL_INIT_AUDIO) < 0) { SDL_Log("Couldn't initialize SDL: %s\n",SDL_GetError()); return 255; } #ifdef HAVE_SIGNAL_H signal(SIGINT, CleanUp); signal(SIGTERM, CleanUp); #endif /* Open the audio device */ if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, 4096) < 0) { SDL_Log("Couldn't open audio: %s\n", SDL_GetError()); CleanUp(2); } else { Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); SDL_Log("Opened audio at %d Hz %d bit%s %s", audio_rate, (audio_format&0xFF), (SDL_AUDIO_ISFLOAT(audio_format) ? " (float)" : ""), (audio_channels > 2) ? "surround" : (audio_channels > 1) ? "stereo" : "mono"); if (loops) { SDL_Log(" (looping)\n"); } else { putchar('\n'); } } audio_open = 1; #if (defined TEST_MIX_VERSIONS) test_versions(); #endif #if (defined TEST_MIX_DECODERS) report_decoders(); #endif /* Load the requested wave file */ wave = Mix_LoadWAV(argv[i]); if (wave == NULL) { SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError()); CleanUp(2); } if (reverse_sample) { flip_sample(wave); } #ifdef TEST_MIX_CHANNELFINISHED /* rcg06072001 */ Mix_ChannelFinished(channel_complete_callback); #endif if ((!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) && (reverse_stereo)) { SDL_Log("Failed to set up reverse stereo effect!\n"); SDL_Log("Reason: [%s].\n", Mix_GetError()); } /* Play and then exit */ Mix_PlayChannel(0, wave, loops); while (still_playing()) { #if (defined TEST_MIX_PANNING) /* rcg06132001 */ do_panning_update(); #endif #if (defined TEST_MIX_DISTANCE) /* rcg06192001 */ do_distance_update(); #endif #if (defined TEST_MIX_POSITION) /* rcg06202001 */ do_position_update(); #endif SDL_Delay(1); } /* while still_playing() loop... */ CleanUp(0); /* Not reached, but fixes compiler warnings */ return 0; } /* end of playwave.c ... */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/acinclude/0000755000076500000240000000000014553251273014367 5ustar valvestaffSDL2_mixer-2.8.0/acinclude/pkg.m40000644000076500000240000002400714277744147015427 0ustar valvestaff# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR SDL2_mixer-2.8.0/acinclude/ax_normalize_path.m40000644000076500000240000001134214277744147020350 0ustar valvestaff# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_normalize_path.html # =========================================================================== # # SYNOPSIS # # AX_NORMALIZE_PATH(VARNAME, [REFERENCE_STRING]) # # DESCRIPTION # # Perform some cleanups on the value of $VARNAME (interpreted as a path): # # - empty paths are changed to '.' # - trailing slashes are removed # - repeated slashes are squeezed except a leading doubled slash '//' # (which might indicate a networked disk on some OS). # # REFERENCE_STRING is used to turn '/' into '\' and vice-versa: if # REFERENCE_STRING contains some backslashes, all slashes and backslashes # are turned into backslashes, otherwise they are all turned into slashes. # # This makes processing of DOS filenames quite easier, because you can # turn a filename to the Unix notation, make your processing, and turn it # back to original notation. # # filename='A:\FOO\\BAR\' # old_filename="$filename" # # Switch to the unix notation # AX_NORMALIZE_PATH([filename], ["/"]) # # now we have $filename = 'A:/FOO/BAR' and we can process it as if # # it was a Unix path. For instance let's say that you want # # to append '/subpath': # filename="$filename/subpath" # # finally switch back to the original notation # AX_NORMALIZE_PATH([filename], ["$old_filename"]) # # now $filename equals to 'A:\FOO\BAR\subpath' # # One good reason to make all path processing with the unix convention is # that backslashes have a special meaning in many cases. For instance # # expr 'A:\FOO' : 'A:\Foo' # # will return 0 because the second argument is a regex in which # backslashes have to be backslashed. In other words, to have the two # strings to match you should write this instead: # # expr 'A:\Foo' : 'A:\\Foo' # # Such behavior makes DOS filenames extremely unpleasant to work with. So # temporary turn your paths to the Unix notation, and revert them to the # original notation after the processing. See the macro # AX_COMPUTE_RELATIVE_PATHS for a concrete example of this. # # REFERENCE_STRING defaults to $VARIABLE, this means that slashes will be # converted to backslashes if $VARIABLE already contains some backslashes # (see $thirddir below). # # firstdir='/usr/local//share' # seconddir='C:\Program Files\\' # thirddir='C:\home/usr/' # AX_NORMALIZE_PATH([firstdir]) # AX_NORMALIZE_PATH([seconddir]) # AX_NORMALIZE_PATH([thirddir]) # # $firstdir = '/usr/local/share' # # $seconddir = 'C:\Program Files' # # $thirddir = 'C:\home\usr' # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 8 AU_ALIAS([ADL_NORMALIZE_PATH], [AX_NORMALIZE_PATH]) AC_DEFUN([AX_NORMALIZE_PATH], [case ":[$]$1:" in # change empty paths to '.' ::) $1='.' ;; # strip trailing slashes :*[[\\/]]:) $1=`echo "[$]$1" | sed 's,[[\\/]]*[$],,'` ;; :*:) ;; esac # squeeze repeated slashes case ifelse($2,,"[$]$1",$2) in # if the path contains any backslashes, turn slashes into backslashes *\\*) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1\\\\,g'` ;; # if the path contains slashes, also turn backslashes into slashes *) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1/,g'` ;; esac]) SDL2_mixer-2.8.0/acinclude/ax_recursive_eval.m40000644000076500000240000000455014277744147020355 0ustar valvestaff# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_recursive_eval.html # =========================================================================== # # SYNOPSIS # # AX_RECURSIVE_EVAL(VALUE, RESULT) # # DESCRIPTION # # Interpolate the VALUE in loop until it doesn't change, and set the # result to $RESULT. WARNING: It's easy to get an infinite loop with some # unsane input. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 1 AC_DEFUN([AX_RECURSIVE_EVAL], [_lcl_receval="$1" $2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" _lcl_receval_old='' while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do _lcl_receval_old="[$]_lcl_receval" eval _lcl_receval="\"[$]_lcl_receval\"" done echo "[$]_lcl_receval")`]) SDL2_mixer-2.8.0/acinclude/ax_compute_relative_paths.m40000644000076500000240000001605414277744147022107 0ustar valvestaff# ============================================================================== # https://www.gnu.org/software/autoconf-archive/ax_compute_relative_paths.html # ============================================================================== # # SYNOPSIS # # AX_COMPUTE_RELATIVE_PATHS(PATH_LIST) # # DESCRIPTION # # PATH_LIST is a space-separated list of colon-separated triplets of the # form 'FROM:TO:RESULT'. This function iterates over these triplets and # set $RESULT to the relative path from $FROM to $TO. Note that $FROM and # $TO needs to be absolute filenames for this macro to success. # # For instance, # # first=/usr/local/bin # second=/usr/local/share # AX_COMPUTE_RELATIVE_PATHS([first:second:fs second:first:sf]) # # $fs is set to ../share # # $sf is set to ../bin # # $FROM and $TO are both eval'ed recursively and normalized, this means # that you can call this macro with autoconf's dirnames like `prefix' or # `datadir'. For example: # # AX_COMPUTE_RELATIVE_PATHS([bindir:datadir:bin_to_data]) # # AX_COMPUTE_RELATIVE_PATHS should also works with DOS filenames. # # You may want to use this macro in order to make your package # relocatable. Instead of hardcoding $datadir into your programs just # encode $bin_to_data and try to determine $bindir at run-time. # # This macro requires AX_NORMALIZE_PATH and AX_RECURSIVE_EVAL. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 12 AU_ALIAS([ADL_COMPUTE_RELATIVE_PATHS], [AX_COMPUTE_RELATIVE_PATHS]) AC_DEFUN([AX_COMPUTE_RELATIVE_PATHS], [for _lcl_i in $1; do _lcl_from=\[$]`echo "[$]_lcl_i" | sed 's,:.*$,,'` _lcl_to=\[$]`echo "[$]_lcl_i" | sed 's,^[[^:]]*:,,' | sed 's,:[[^:]]*$,,'` _lcl_result_var=`echo "[$]_lcl_i" | sed 's,^.*:,,'` AX_RECURSIVE_EVAL([[$]_lcl_from], [_lcl_from]) AX_RECURSIVE_EVAL([[$]_lcl_to], [_lcl_to]) _lcl_notation="$_lcl_from$_lcl_to" AX_NORMALIZE_PATH([_lcl_from],['/']) AX_NORMALIZE_PATH([_lcl_to],['/']) AX_COMPUTE_RELATIVE_PATH([_lcl_from], [_lcl_to], [_lcl_result_tmp]) AX_NORMALIZE_PATH([_lcl_result_tmp],["[$]_lcl_notation"]) eval $_lcl_result_var='[$]_lcl_result_tmp' done]) ## Note: ## ***** ## The following helper macros are too fragile to be used out ## of AX_COMPUTE_RELATIVE_PATHS (mainly because they assume that ## paths are normalized), that's why I'm keeping them in the same file. ## Still, some of them maybe worth to reuse. dnl AX_COMPUTE_RELATIVE_PATH(FROM, TO, RESULT) dnl =========================================== dnl Compute the relative path to go from $FROM to $TO and set the value dnl of $RESULT to that value. This function work on raw filenames dnl (for instead it will considerate /usr//local and /usr/local as dnl two distinct paths), you should really use AX_COMPUTE_RELATIVE_PATHS dnl instead to have the paths sanitized automatically. dnl dnl For instance: dnl first_dir=/somewhere/on/my/disk/bin dnl second_dir=/somewhere/on/another/disk/share dnl AX_COMPUTE_RELATIVE_PATH(first_dir, second_dir, first_to_second) dnl will set $first_to_second to '../../../another/disk/share'. AC_DEFUN([AX_COMPUTE_RELATIVE_PATH], [AX_COMPUTE_COMMON_PATH([$1], [$2], [_lcl_common_prefix]) AX_COMPUTE_BACK_PATH([$1], [_lcl_common_prefix], [_lcl_first_rel]) AX_COMPUTE_SUFFIX_PATH([$2], [_lcl_common_prefix], [_lcl_second_suffix]) $3="[$]_lcl_first_rel[$]_lcl_second_suffix"]) dnl AX_COMPUTE_COMMON_PATH(LEFT, RIGHT, RESULT) dnl ============================================ dnl Compute the common path to $LEFT and $RIGHT and set the result to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on/another/disk/share dnl AX_COMPUTE_COMMON_PATH(first_path, second_path, common_path) dnl will set $common_path to '/somewhere/on'. AC_DEFUN([AX_COMPUTE_COMMON_PATH], [$3='' _lcl_second_prefix_match='' while test "[$]_lcl_second_prefix_match" != 0; do _lcl_first_prefix=`expr "x[$]$1" : "x\([$]$3/*[[^/]]*\)"` _lcl_second_prefix_match=`expr "x[$]$2" : "x[$]_lcl_first_prefix"` if test "[$]_lcl_second_prefix_match" != 0; then if test "[$]_lcl_first_prefix" != "[$]$3"; then $3="[$]_lcl_first_prefix" else _lcl_second_prefix_match=0 fi fi done]) dnl AX_COMPUTE_SUFFIX_PATH(PATH, SUBPATH, RESULT) dnl ============================================== dnl Subtract $SUBPATH from $PATH, and set the resulting suffix dnl (or the empty string if $SUBPATH is not a subpath of $PATH) dnl to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_SUFFIX_PATH(first_path, second_path, common_path) dnl will set $common_path to '/my/disk/bin'. AC_DEFUN([AX_COMPUTE_SUFFIX_PATH], [$3=`expr "x[$]$1" : "x[$]$2/*\(.*\)"`]) dnl AX_COMPUTE_BACK_PATH(PATH, SUBPATH, RESULT) dnl ============================================ dnl Compute the relative path to go from $PATH to $SUBPATH, knowing that dnl $SUBPATH is a subpath of $PATH (any other words, only repeated '../' dnl should be needed to move from $PATH to $SUBPATH) and set the value dnl of $RESULT to that value. If $SUBPATH is not a subpath of PATH, dnl set $RESULT to the empty string. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_BACK_PATH(first_path, second_path, back_path) dnl will set $back_path to '../../../'. AC_DEFUN([AX_COMPUTE_BACK_PATH], [AX_COMPUTE_SUFFIX_PATH([$1], [$2], [_lcl_first_suffix]) $3='' _lcl_tmp='xxx' while test "[$]_lcl_tmp" != ''; do _lcl_tmp=`expr "x[$]_lcl_first_suffix" : "x[[^/]]*/*\(.*\)"` if test "[$]_lcl_first_suffix" != ''; then _lcl_first_suffix="[$]_lcl_tmp" $3="../[$]$3" fi done]) SDL2_mixer-2.8.0/acinclude/sdl2.m40000644000076500000240000001471314277744147015515 0ustar valvestaff# Configure paths for SDL # Sam Lantinga 9/21/99 # stolen from Manish Singh # stolen back from Frank Belew # stolen from Manish Singh # Shamelessly stolen from Owen Taylor # serial 2 dnl AM_PATH_SDL2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS dnl AC_DEFUN([AM_PATH_SDL2], [dnl dnl Get the cflags and libraries from the sdl2-config script dnl AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], sdl_prefix="$withval", sdl_prefix="") AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], sdl_exec_prefix="$withval", sdl_exec_prefix="") AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], , enable_sdltest=yes) min_sdl_version=ifelse([$1], ,2.0.0,$1) if test "x$sdl_prefix$sdl_exec_prefix" = x ; then PKG_CHECK_MODULES([SDL], [sdl2 >= $min_sdl_version], [sdl_pc=yes], [sdl_pc=no]) else sdl_pc=no if test x$sdl_exec_prefix != x ; then sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_exec_prefix/bin/sdl2-config fi fi if test x$sdl_prefix != x ; then sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_prefix/bin/sdl2-config fi fi fi if test "x$sdl_pc" = xyes ; then no_sdl="" SDL2_CONFIG="pkg-config sdl2" else as_save_PATH="$PATH" if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then PATH="$prefix/bin:$prefix/usr/bin:$PATH" fi AC_PATH_PROG(SDL2_CONFIG, sdl2-config, no, [$PATH]) PATH="$as_save_PATH" AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) no_sdl="" if test "$SDL2_CONFIG" = "no" ; then no_sdl=yes else SDL_CFLAGS=`$SDL2_CONFIG $sdl_config_args --cflags` SDL_LIBS=`$SDL2_CONFIG $sdl_config_args --libs` sdl_major_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` sdl_minor_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` sdl_micro_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_sdltest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_CXXFLAGS="$CXXFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" dnl dnl Now check if the installed SDL is sufficiently new. (Also sanity dnl checks the results of sdl2-config to some extent dnl rm -f conf.sdltest AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include "SDL.h" int main (int argc, char *argv[]) { int major, minor, micro; FILE *fp = fopen("conf.sdltest", "w"); if (fp) fclose(fp); if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_sdl_version"); exit(1); } if (($sdl_major_version > major) || (($sdl_major_version == major) && ($sdl_minor_version > minor)) || (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) { return 0; } else { printf("\n*** 'sdl2-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); printf("*** of SDL required is %d.%d.%d. If sdl2-config is correct, then it is\n", major, minor, micro); printf("*** best to upgrade to the required version.\n"); printf("*** If sdl2-config was wrong, set the environment variable SDL2_CONFIG\n"); printf("*** to point to the correct copy of sdl2-config, and remove the file\n"); printf("*** config.cache before re-running configure\n"); return 1; } } ]])], [], [no_sdl=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_sdl" = x ; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi fi if test "x$no_sdl" = x ; then ifelse([$2], , :, [$2]) else if test "$SDL2_CONFIG" = "no" ; then echo "*** The sdl2-config script installed by SDL could not be found" echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" echo "*** your path, or set the SDL2_CONFIG environment variable to the" echo "*** full path to sdl2-config." else if test -f conf.sdltest ; then : else echo "*** Could not run SDL test program, checking why..." CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "SDL.h" int main(int argc, char *argv[]) { return 0; } #undef main #define main K_and_R_C_main ]], [[ return 0; ]])], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding SDL or finding the wrong" echo "*** version of SDL. If it is not finding SDL, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means SDL was incorrectly installed" echo "*** or that you have moved SDL since it was installed. In the latter case, you" echo "*** may want to edit the sdl2-config script: $SDL2_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi SDL_CFLAGS="" SDL_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(SDL_CFLAGS) AC_SUBST(SDL_LIBS) rm -f conf.sdltest ]) SDL2_mixer-2.8.0/include/0000755000076500000240000000000014553251273014063 5ustar valvestaffSDL2_mixer-2.8.0/include/SDL_mixer.h0000644000076500000240000033117414553225265016075 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /** * \file SDL_mixer.h * * Header file for SDL_mixer library * * A simple library to play and mix sounds and musics */ #ifndef SDL_MIXER_H_ #define SDL_MIXER_H_ #include "SDL_stdinc.h" #include "SDL_rwops.h" #include "SDL_audio.h" #include "SDL_endian.h" #include "SDL_version.h" #include "begin_code.h" /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus extern "C" { #endif /** * Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL */ #define SDL_MIXER_MAJOR_VERSION 2 #define SDL_MIXER_MINOR_VERSION 8 #define SDL_MIXER_PATCHLEVEL 0 /** * This macro can be used to fill a version structure with the compile-time * version of the SDL_mixer library. */ #define SDL_MIXER_VERSION(X) \ { \ (X)->major = SDL_MIXER_MAJOR_VERSION; \ (X)->minor = SDL_MIXER_MINOR_VERSION; \ (X)->patch = SDL_MIXER_PATCHLEVEL; \ } /* Backwards compatibility */ #define MIX_MAJOR_VERSION SDL_MIXER_MAJOR_VERSION #define MIX_MINOR_VERSION SDL_MIXER_MINOR_VERSION #define MIX_PATCHLEVEL SDL_MIXER_PATCHLEVEL #define MIX_VERSION(X) SDL_MIXER_VERSION(X) #if SDL_MIXER_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 /** * This is the version number macro for the current SDL_mixer version. * * In versions higher than 2.9.0, the minor version overflows into * the thousands digit: for example, 2.23.0 is encoded as 4300. * This macro will not be available in SDL 3.x or SDL_mixer 3.x. * * Deprecated, use SDL_MIXER_VERSION_ATLEAST or SDL_MIXER_VERSION instead. */ #define SDL_MIXER_COMPILEDVERSION \ SDL_VERSIONNUM(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL) #endif /* SDL_MIXER_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 */ /** * This macro will evaluate to true if compiled with SDL_mixer at least X.Y.Z. */ #define SDL_MIXER_VERSION_ATLEAST(X, Y, Z) \ ((SDL_MIXER_MAJOR_VERSION >= X) && \ (SDL_MIXER_MAJOR_VERSION > X || SDL_MIXER_MINOR_VERSION >= Y) && \ (SDL_MIXER_MAJOR_VERSION > X || SDL_MIXER_MINOR_VERSION > Y || SDL_MIXER_PATCHLEVEL >= Z)) /** * Query the version of SDL_mixer that the program is linked against. * * This function gets the version of the dynamically linked SDL_mixer library. * This is separate from the SDL_MIXER_VERSION() macro, which tells you what * version of the SDL_mixer headers you compiled against. * * This returns static internal data; do not free or modify it! * * \returns a pointer to the version information. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC const SDL_version * SDLCALL Mix_Linked_Version(void); /** * Initialization flags */ typedef enum { MIX_INIT_FLAC = 0x00000001, MIX_INIT_MOD = 0x00000002, MIX_INIT_MP3 = 0x00000008, MIX_INIT_OGG = 0x00000010, MIX_INIT_MID = 0x00000020, MIX_INIT_OPUS = 0x00000040, MIX_INIT_WAVPACK= 0x00000080 } MIX_InitFlags; /** * Initialize SDL_mixer. * * This function loads dynamic libraries that SDL_mixer needs, and prepares * them for use. * * Note that, unlike other SDL libraries, this call is optional! If you load a * music file, SDL_mixer will handle initialization on the fly. This function * will let you know, up front, whether a specific format will be available * for use. * * Flags should be one or more flags from MIX_InitFlags OR'd together. It * returns the flags successfully initialized, or 0 on failure. * * Currently, these flags are: * * - `MIX_INIT_FLAC` * - `MIX_INIT_MOD` * - `MIX_INIT_MP3` * - `MIX_INIT_OGG` * - `MIX_INIT_MID` * - `MIX_INIT_OPUS` * - `MIX_INIT_WAVPACK` * * More flags may be added in a future SDL_mixer release. * * This function may need to load external shared libraries to support various * codecs, which means this function can fail to initialize that support on an * otherwise-reasonable system if the library isn't available; this is not * just a question of exceptional circumstances like running out of memory at * startup! * * Note that you may call this function more than once to initialize with * additional flags. The return value will reflect both new flags that * successfully initialized, and also include flags that had previously been * initialized as well. * * As this will return previously-initialized flags, it's legal to call this * with zero (no flags set). This is a safe no-op that can be used to query * the current initialization state without changing it at all. * * Since this returns previously-initialized flags as well as new ones, and * you can call this with zero, you should not check for a zero return value * to determine an error condition. Instead, you should check to make sure all * the flags you require are set in the return value. If you have a game with * data in a specific format, this might be a fatal error. If you're a generic * media player, perhaps you are fine with only having WAV and MP3 support and * can live without Opus playback, even if you request support for everything. * * Unlike other SDL satellite libraries, calls to Mix_Init do not stack; a * single call to Mix_Quit() will deinitialize everything and does not have to * be paired with a matching Mix_Init call. For that reason, it's considered * best practices to have a single Mix_Init and Mix_Quit call in your program. * While this isn't required, be aware of the risks of deviating from that * behavior. * * After initializing SDL_mixer, the next step is to open an audio device to * prepare to play sound (with Mix_OpenAudio() or Mix_OpenAudioDevice()), and * load audio data to play with that device. * * \param flags initialization flags, OR'd together. * \returns all currently initialized flags. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_Quit */ extern DECLSPEC int SDLCALL Mix_Init(int flags); /** * Deinitialize SDL_mixer. * * This should be the last function you call in SDL_mixer, after freeing all * other resources and closing all audio devices. This will unload any shared * libraries it is using for various codecs. * * After this call, a call to Mix_Init(0) will return 0 (no codecs loaded). * * You can safely call Mix_Init() to reload various codec support after this * call. * * Unlike other SDL satellite libraries, calls to Mix_Init do not stack; a * single call to Mix_Quit() will deinitialize everything and does not have to * be paired with a matching Mix_Init call. For that reason, it's considered * best practices to have a single Mix_Init and Mix_Quit call in your program. * While this isn't required, be aware of the risks of deviating from that * behavior. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_Init */ extern DECLSPEC void SDLCALL Mix_Quit(void); /** * The default mixer has 8 simultaneous mixing channels */ #ifndef MIX_CHANNELS #define MIX_CHANNELS 8 #endif /* Good default values for a PC soundcard */ #define MIX_DEFAULT_FREQUENCY 44100 #define MIX_DEFAULT_FORMAT AUDIO_S16SYS #define MIX_DEFAULT_CHANNELS 2 #define MIX_MAX_VOLUME SDL_MIX_MAXVOLUME /* Volume of a chunk */ /** * The internal format for an audio chunk */ typedef struct Mix_Chunk { int allocated; Uint8 *abuf; Uint32 alen; Uint8 volume; /* Per-sample volume, 0-128 */ } Mix_Chunk; /** * The different fading types supported */ typedef enum { MIX_NO_FADING, MIX_FADING_OUT, MIX_FADING_IN } Mix_Fading; /** * These are types of music files (not libraries used to load them) */ typedef enum { MUS_NONE, MUS_CMD, MUS_WAV, MUS_MOD, MUS_MID, MUS_OGG, MUS_MP3, MUS_MP3_MAD_UNUSED, MUS_FLAC, MUS_MODPLUG_UNUSED, MUS_OPUS, MUS_WAVPACK, MUS_GME } Mix_MusicType; /** * The internal format for a music chunk interpreted via codecs */ typedef struct _Mix_Music Mix_Music; /** * Open the default audio device for playback. * * An audio device is what generates sound, so the app must open one to make * noise. * * This function will check if SDL's audio system is initialized, and if not, * it will initialize it by calling `SDL_Init(SDL_INIT_AUDIO)` on your behalf. * You are free to (and encouraged to!) initialize it yourself before calling * this function, as this gives your program more control over the process. * * This function might cover all of an application's needs, but for those that * need more flexibility, the more powerful version of this function is * Mix_OpenAudioDevice(). This function is equivalent to calling: * * ```c * Mix_OpenAudioDevice(frequency, format, nchannels, chunksize, NULL, * SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | * SDL_AUDIO_ALLOW_CHANNELS_CHANGE); * ``` * * If you aren't particularly concerned with the specifics of the audio * device, and your data isn't in a specific format, the values you use here * can just be reasonable defaults. SDL_mixer will convert audio data you feed * it to the correct format on demand. * * That being said, if you have control of your audio data and you know its * format ahead of time, you may save CPU time by opening the audio device in * that exact format so SDL_mixer does not have to spend time converting * anything behind the scenes, and can just pass the data straight through to * the hardware. On some platforms, where the hardware only supports specific * settings, you might have to be careful to make everything match, but your * own data is often easier to control, so aim to open the device for what you * need. * * The other reason to care about specific formats: if you plan to touch the * mix buffer directly (with Mix_SetPostMix, a registered effect, or * Mix_HookMusic), you might have code that expects it to be in a specific * format, and you should specify that here. * * The audio device frequency is specified in Hz; in modern times, 48000 is * often a reasonable default. * * The audio device format is one of SDL's AUDIO_* constants. AUDIO_S16SYS * (16-bit audio) is probably a safe default. More modern systems may prefer * AUDIO_F32SYS (32-bit floating point audio). * * The audio device channels are generally 1 for mono output, or 2 for stereo, * but the brave can try surround sound configs with 4 (quad), 6 (5.1), 7 * (6.1) or 8 (7.1). * * The audio device's chunk size is the number of sample frames (one sample * per frame for mono output, two samples per frame in a stereo setup, etc) * that are fed to the device at once. The lower the number, the lower the * latency, but you risk dropouts if it gets too low. 2048 is often a * reasonable default, but your app might want to experiment with 1024 or * 4096. * * You may only have one audio device open at a time; if you want to change a * setting, you must close the device and reopen it, which is not something * you can do seamlessly during playback. * * This function does not allow you to select a specific audio device on the * system, it always chooses the best default it can on your behalf (which, in * many cases, is exactly what you want anyhow). If you must choose a specific * device, you can do so with Mix_OpenAudioDevice() instead. * * If this function reports success, you are ready to start making noise! Load * some audio data and start playing! * * The app can use Mix_QuerySpec() to determine the final device settings. * * When done with an audio device, probably at the end of the program, the app * should dispose of the device with Mix_CloseAudio(). * * \param frequency the frequency to playback audio at (in Hz). * \param format audio format, one of SDL's AUDIO_* values. * \param channels number of channels (1 is mono, 2 is stereo, etc). * \param chunksize audio buffer size in sample FRAMES (total samples divided * by channel count). * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_OpenAudioDevice * \sa Mix_CloseAudio */ extern DECLSPEC int SDLCALL Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize); /** * Open a specific audio device for playback. * * (A slightly simpler version of this function is available in * Mix_OpenAudio(), which still might meet most applications' needs.) * * An audio device is what generates sound, so the app must open one to make * noise. * * This function will check if SDL's audio system is initialized, and if not, * it will initialize it by calling `SDL_Init(SDL_INIT_AUDIO)` on your behalf. * You are free to (and encouraged to!) initialize it yourself before calling * this function, as this gives your program more control over the process. * * If you aren't particularly concerned with the specifics of the audio * device, and your data isn't in a specific format, the values you use here * can just be reasonable defaults. SDL_mixer will convert audio data you feed * it to the correct format on demand. * * That being said, if you have control of your audio data and you know its * format ahead of time, you can save CPU time by opening the audio device in * that exact format so SDL_mixer does not have to spend time converting * anything behind the scenes, and can just pass the data straight through to * the hardware. On some platforms, where the hardware only supports specific * settings, you might have to be careful to make everything match, but your * own data is often easier to control, so aim to open the device for what you * need. * * The other reason to care about specific formats: if you plan to touch the * mix buffer directly (with Mix_SetPostMix, a registered effect, or * Mix_HookMusic), you might have code that expects it to be in a specific * format, and you should specify that here. * * The audio device frequency is specified in Hz; in modern times, 48000 is * often a reasonable default. * * The audio device format is one of SDL's AUDIO_* constants. AUDIO_S16SYS * (16-bit audio) is probably a safe default. More modern systems may prefer * AUDIO_F32SYS (32-bit floating point audio). * * The audio device channels are generally 1 for mono output, or 2 for stereo, * but the brave can try surround sound configs with 4 (quad), 6 (5.1), 7 * (6.1) or 8 (7.1). * * The audio device's chunk size is the number of sample frames (one sample * per frame for mono output, two samples per frame in a stereo setup, etc) * that are fed to the device at once. The lower the number, the lower the * latency, but you risk dropouts if it gets too low. 2048 is often a * reasonable default, but your app might want to experiment with 1024 or * 4096. * * You may only have one audio device open at a time; if you want to change a * setting, you must close the device and reopen it, which is not something * you can do seamlessly during playback. * * This function allows you to select specific audio hardware on the system * with the `device` parameter. If you specify NULL, SDL_mixer will choose the * best default it can on your behalf (which, in many cases, is exactly what * you want anyhow). SDL_mixer does not offer a mechanism to determine device * names to open, but you can use SDL_GetNumAudioDevices() to get a count of * available devices and then SDL_GetAudioDeviceName() in a loop to obtain a * list. If you do this, be sure to call `SDL_Init(SDL_INIT_AUDIO)` first to * initialize SDL's audio system! * * The `allowed_changes` parameter specifies what settings are flexible. These * are the `SDL_AUDIO_ALLOW_*` flags from SDL. These tell SDL_mixer that the * app doesn't mind if a specific setting changes. For example, the app might * need stereo data in Sint16 format, but if the sample rate or chunk size * changes, the app can handle that. In that case, the app would specify * `SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE`. In this * case, if the system's hardware requires something other than the requested * format, SDL_mixer can select what the hardware demands instead of the app. * If the `SDL_AUDIO_ALLOW_` flag is not specified, SDL_mixer must convert * data behind the scenes between what the app demands and what the hardware * requires. If your app needs precisely what is requested, specify zero for * `allowed_changes`. * * If changes were allowed, the app can use Mix_QuerySpec() to determine the * final device settings. * * If this function reports success, you are ready to start making noise! Load * some audio data and start playing! * * When done with an audio device, probably at the end of the program, the app * should dispose of the device with Mix_CloseDevice(). * * \param frequency the frequency to playback audio at (in Hz). * \param format audio format, one of SDL's AUDIO_* values. * \param channels number of channels (1 is mono, 2 is stereo, etc). * \param chunksize audio buffer size in sample FRAMES (total samples divided * by channel count). * \param device the device name to open, or NULL to choose a reasonable * default. * \param allowed_changes Allow change flags (see SDL_AUDIO_ALLOW_* flags) * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_mixer 2.0.2. * * \sa Mix_OpenAudio * \sa Mix_CloseDevice * \sa Mix_QuerySpec */ extern DECLSPEC int SDLCALL Mix_OpenAudioDevice(int frequency, Uint16 format, int channels, int chunksize, const char* device, int allowed_changes); /** * Suspend or resume the whole audio output. * * \param pause_on 1 to pause audio output, or 0 to resume. * * \since This function is available since SDL_mixer 2.8.0. */ extern DECLSPEC void SDLCALL Mix_PauseAudio(int pause_on); /** * Find out what the actual audio device parameters are. * * If Mix_OpenAudioDevice() was called with `allowed_changes` set to anything * but zero, or Mix_OpenAudio() was used, some audio device settings may be * different from the application's request. This function will report what * the device is actually running at. * * Note this is only important if the app intends to touch the audio buffers * being sent to the hardware directly. If an app just wants to play audio * files and let SDL_mixer handle the low-level details, this function can * probably be ignored. * * If the audio device is not opened, this function will return 0. * * \param frequency On return, will be filled with the audio device's * frequency in Hz. * \param format On return, will be filled with the audio device's format. * \param channels On return, will be filled with the audio device's channel * count. * \returns 1 if the audio device has been opened, 0 otherwise. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_OpenAudio * \sa Mix_OpenAudioDevice */ extern DECLSPEC int SDLCALL Mix_QuerySpec(int *frequency, Uint16 *format, int *channels); /** * Dynamically change the number of channels managed by the mixer. * * SDL_mixer deals with "channels," which is not the same thing as the * mono/stereo channels; they might be better described as "tracks," as each * one corresponds to a separate source of audio data. Three different WAV * files playing at the same time would be three separate SDL_mixer channels, * for example. * * An app needs as many channels as it has audio data it wants to play * simultaneously, mixing them into a single stream to send to the audio * device. * * SDL_mixer allocates `MIX_CHANNELS` (currently 8) channels when you open an * audio device, which may be more than an app needs, but if the app needs * more or wants less, this function can change it. * * If decreasing the number of channels, any upper channels currently playing * are stopped. This will deregister all effects on those channels and call * any callback specified by Mix_ChannelFinished() for each removed channel. * * If `numchans` is less than zero, this will return the current number of * channels without changing anything. * * \param numchans the new number of channels, or < 0 to query current channel * count. * \returns the new number of allocated channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_AllocateChannels(int numchans); /** * Load a supported audio format into a chunk. * * SDL_mixer has two separate data structures for audio data. One it calls a * "chunk," which is meant to be a file completely decoded into memory up * front, and the other it calls "music" which is a file intended to be * decoded on demand. Originally, simple formats like uncompressed WAV files * were meant to be chunks and compressed things, like MP3s, were meant to be * music, and you would stream one thing for a game's music and make repeating * sound effects with the chunks. * * In modern times, this isn't split by format anymore, and most are * interchangeable, so the question is what the app thinks is worth * predecoding or not. Chunks might take more memory, but once they are loaded * won't need to decode again, whereas music always needs to be decoded on the * fly. Also, crucially, there are as many channels for chunks as the app can * allocate, but SDL_mixer only offers a single "music" channel. * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_mixer reads everything it needs * from the RWops during this call in any case. * * There is a separate function (a macro, before SDL_mixer 2.6.0) to read * files from disk without having to deal with SDL_RWops: * `Mix_LoadWAV("filename.wav")` will call this function and manage those * details for you. * * When done with a chunk, the app should dispose of it with a call to * Mix_FreeChunk(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new chunk, or NULL on error. * * \since This function is available since SDL_mixer 2.6.0 (and as a macro * since 2.0.0). * * \sa Mix_LoadWAV * \sa Mix_FreeChunk */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_LoadWAV_RW(SDL_RWops *src, int freesrc); /** * Load a supported audio format into a chunk. * * SDL_mixer has two separate data structures for audio data. One it calls a * "chunk," which is meant to be a file completely decoded into memory up * front, and the other it calls "music" which is a file intended to be * decoded on demand. Originally, simple formats like uncompressed WAV files * were meant to be chunks and compressed things, like MP3s, were meant to be * music, and you would stream one thing for a game's music and make repeating * sound effects with the chunks. * * In modern times, this isn't split by format anymore, and most are * interchangeable, so the question is what the app thinks is worth * predecoding or not. Chunks might take more memory, but once they are loaded * won't need to decode again, whereas music always needs to be decoded on the * fly. Also, crucially, there are as many channels for chunks as the app can * allocate, but SDL_mixer only offers a single "music" channel. * * If you would rather use the abstract SDL_RWops interface to load data from * somewhere other than the filesystem, you can use Mix_LoadWAV_RW() instead. * * When done with a chunk, the app should dispose of it with a call to * Mix_FreeChunk(). * * Note that before SDL_mixer 2.6.0, this function was a macro that called * Mix_LoadWAV_RW(), creating a RWops and setting `freesrc` to 1. This macro * has since been promoted to a proper API function. Older binaries linked * against a newer SDL_mixer will still call Mix_LoadWAV_RW directly, as they * are using the macro, which was available since the dawn of time. * * \param file the filesystem path to load data from. * \returns a new chunk, or NULL on error. * * \since This function is available since SDL_mixer 2.6.0 (and as a macro * since 2.0.0). * * \sa Mix_LoadWAV_RW * \sa Mix_FreeChunk */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_LoadWAV(const char *file); /** * Load a supported audio format into a music object. * * SDL_mixer has two separate data structures for audio data. One it calls a * "chunk," which is meant to be a file completely decoded into memory up * front, and the other it calls "music" which is a file intended to be * decoded on demand. Originally, simple formats like uncompressed WAV files * were meant to be chunks and compressed things, like MP3s, were meant to be * music, and you would stream one thing for a game's music and make repeating * sound effects with the chunks. * * In modern times, this isn't split by format anymore, and most are * interchangeable, so the question is what the app thinks is worth * predecoding or not. Chunks might take more memory, but once they are loaded * won't need to decode again, whereas music always needs to be decoded on the * fly. Also, crucially, there are as many channels for chunks as the app can * allocate, but SDL_mixer only offers a single "music" channel. * * When done with this music, the app should dispose of it with a call to * Mix_FreeMusic(). * * \param file a file path from where to load music data. * \returns a new music object, or NULL on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_FreeMusic */ extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUS(const char *file); /** * Load a supported audio format into a music object. * * SDL_mixer has two separate data structures for audio data. One it calls a * "chunk," which is meant to be a file completely decoded into memory up * front, and the other it calls "music" which is a file intended to be * decoded on demand. Originally, simple formats like uncompressed WAV files * were meant to be chunks and compressed things, like MP3s, were meant to be * music, and you would stream one thing for a game's music and make repeating * sound effects with the chunks. * * In modern times, this isn't split by format anymore, and most are * interchangeable, so the question is what the app thinks is worth * predecoding or not. Chunks might take more memory, but once they are loaded * won't need to decode again, whereas music always needs to be decoded on the * fly. Also, crucially, there are as many channels for chunks as the app can * allocate, but SDL_mixer only offers a single "music" channel. * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_mixer reads everything it needs * from the RWops during this call in any case. * * As a convenience, there is a function to read files from disk without * having to deal with SDL_RWops: `Mix_LoadMUS("filename.mp3")` will manage * those details for you. * * This function attempts to guess the file format from incoming data. If the * caller knows the format, or wants to force it, it should use * Mix_LoadMUSType_RW() instead. * * When done with this music, the app should dispose of it with a call to * Mix_FreeMusic(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new music object, or NULL on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_FreeMusic */ extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUS_RW(SDL_RWops *src, int freesrc); /** * Load an audio format into a music object, assuming a specific format. * * SDL_mixer has two separate data structures for audio data. One it calls a * "chunk," which is meant to be a file completely decoded into memory up * front, and the other it calls "music" which is a file intended to be * decoded on demand. Originally, simple formats like uncompressed WAV files * were meant to be chunks and compressed things, like MP3s, were meant to be * music, and you would stream one thing for a game's music and make repeating * sound effects with the chunks. * * In modern times, this isn't split by format anymore, and most are * interchangeable, so the question is what the app thinks is worth * predecoding or not. Chunks might take more memory, but once they are loaded * won't need to decode again, whereas music always needs to be decoded on the * fly. Also, crucially, there are as many channels for chunks as the app can * allocate, but SDL_mixer only offers a single "music" channel. * * This function loads music data, and lets the application specify the type * of music being loaded, which might be useful if SDL_mixer cannot figure it * out from the data stream itself. * * Currently, the following types are supported: * * - `MUS_NONE` (SDL_mixer should guess, based on the data) * - `MUS_WAV` (Microsoft WAV files) * - `MUS_MOD` (Various tracker formats) * - `MUS_MID` (MIDI files) * - `MUS_OGG` (Ogg Vorbis files) * - `MUS_MP3` (MP3 files) * - `MUS_FLAC` (FLAC files) * - `MUS_OPUS` (Opus files) * - `MUS_WAVPACK` (WavPack files) * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_mixer reads everything it needs * from the RWops during this call in any case. * * As a convenience, there is a function to read files from disk without * having to deal with SDL_RWops: `Mix_LoadMUS("filename.mp3")` will manage * those details for you (but not let you specify the music type explicitly).. * * When done with this music, the app should dispose of it with a call to * Mix_FreeMusic(). * * \param src an SDL_RWops that data will be read from. * \param type the type of audio data provided by `src`. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new music object, or NULL on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_FreeMusic */ extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUSType_RW(SDL_RWops *src, Mix_MusicType type, int freesrc); /** * Load a WAV file from memory as quickly as possible. * * Unlike Mix_LoadWAV_RW, this function has several requirements, and unless * you control all your audio data and know what you're doing, you should * consider this function unsafe and not use it. * * - The provided audio data MUST be in Microsoft WAV format. * - The provided audio data shouldn't use any strange WAV extensions. * - The audio data MUST be in the exact same format as the audio device. This * function will not attempt to convert it, or even verify it's in the right * format. * - The audio data must be valid; this function does not know the size of the * memory buffer, so if the WAV data is corrupted, it can read past the end * of the buffer, causing a crash. * - The audio data must live at least as long as the returned Mix_Chunk, * because SDL_mixer will use that data directly and not make a copy of it. * * This function will do NO error checking! Be extremely careful here! * * (Seriously, use Mix_LoadWAV_RW instead.) * * If this function is successful, the provided memory buffer must remain * available until Mix_FreeChunk() is called on the returned chunk. * * \param mem memory buffer containing of a WAV file. * \returns a new chunk, or NULL on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_LoadWAV_RW * \sa Mix_FreeChunk */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_QuickLoad_WAV(Uint8 *mem); /** * Load a raw audio data from memory as quickly as possible. * * The audio data MUST be in the exact same format as the audio device. This * function will not attempt to convert it, or even verify it's in the right * format. * * If this function is successful, the provided memory buffer must remain * available until Mix_FreeChunk() is called on the returned chunk. * * \param mem memory buffer containing raw PCM data. * \param len length of buffer pointed to by `mem`, in bytes. * \returns a new chunk, or NULL on error. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_FreeChunk */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len); /** * Free an audio chunk. * * An app should call this function when it is done with a Mix_Chunk and wants * to dispose of its resources. * * SDL_mixer will stop any channels this chunk is currently playing on. This * will deregister all effects on those channels and call any callback * specified by Mix_ChannelFinished() for each removed channel. * * \param chunk the chunk to free. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_LoadWAV * \sa Mix_LoadWAV_RW * \sa Mix_QuickLoad_WAV * \sa Mix_QuickLoad_RAW */ extern DECLSPEC void SDLCALL Mix_FreeChunk(Mix_Chunk *chunk); /** * Free a music object. * * If this music is currently playing, it will be stopped. * * If this music is in the process of fading out (via Mix_FadeOutMusic()), * this function will *block* until the fade completes. If you need to avoid * this, be sure to call Mix_HaltMusic() before freeing the music. * * \param music the music object to free. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_LoadMUS * \sa Mix_LoadMUS_RW * \sa Mix_LoadMUSType_RW */ extern DECLSPEC void SDLCALL Mix_FreeMusic(Mix_Music *music); /** * Get a list of chunk decoders that this build of SDL_mixer provides. * * This list can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * Appearing in this list doesn't promise your specific audio file will * decode...but it's handy to know if you have, say, a functioning Ogg Vorbis * install. * * These return values are static, read-only data; do not modify or free it. * The pointers remain valid until you call Mix_CloseAudio(). * * \returns number of chunk decoders available. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GetChunkDecoder * \sa Mix_HasChunkDecoder */ extern DECLSPEC int SDLCALL Mix_GetNumChunkDecoders(void); /** * Get a chunk decoder's name. * * The requested decoder's index must be between zero and * Mix_GetNumChunkDecoders()-1. It's safe to call this with an invalid index; * this function will return NULL in that case. * * This list can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * \param index index of the chunk decoder. * \returns the chunk decoder's name. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GetNumChunkDecoders */ extern DECLSPEC const char * SDLCALL Mix_GetChunkDecoder(int index); /** * Check if a chunk decoder is available by name. * * This result can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * Decoder names are arbitrary but also obvious, so you have to know what * you're looking for ahead of time, but usually it's the file extension in * capital letters (some example names are "AIFF", "VOC", "WAV"). * * \param name the decoder name to query. * \returns SDL_TRUE if a decoder by that name is available, SDL_FALSE * otherwise. * * \since This function is available since SDL_mixer 2.0.2. * * \sa Mix_GetNumChunkDecoders * \sa Mix_GetChunkDecoder */ extern DECLSPEC SDL_bool SDLCALL Mix_HasChunkDecoder(const char *name); /** * Get a list of music decoders that this build of SDL_mixer provides. * * This list can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * Appearing in this list doesn't promise your specific audio file will * decode...but it's handy to know if you have, say, a functioning Ogg Vorbis * install. * * These return values are static, read-only data; do not modify or free it. * The pointers remain valid until you call Mix_CloseAudio(). * * \returns number of music decoders available. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GetMusicDecoder * \sa Mix_HasMusicDecoder */ extern DECLSPEC int SDLCALL Mix_GetNumMusicDecoders(void); /** * Get a music decoder's name. * * The requested decoder's index must be between zero and * Mix_GetNumMusicDecoders()-1. It's safe to call this with an invalid index; * this function will return NULL in that case. * * This list can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * \param index index of the music decoder. * \returns the music decoder's name. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GetNumMusicDecoders */ extern DECLSPEC const char * SDLCALL Mix_GetMusicDecoder(int index); /** * Check if a music decoder is available by name. * * This result can change between builds AND runs of the program, if external * libraries that add functionality become available. You must successfully * call Mix_OpenAudio() or Mix_OpenAudioDevice() before calling this function, * as decoders are activated at device open time. * * Decoder names are arbitrary but also obvious, so you have to know what * you're looking for ahead of time, but usually it's the file extension in * capital letters (some example names are "MOD", "MP3", "FLAC"). * * \param name the decoder name to query. * \returns SDL_TRUE if a decoder by that name is available, SDL_FALSE * otherwise. * * \since This function is available since SDL_mixer 2.6.0 * * \sa Mix_GetNumMusicDecoders * \sa Mix_GetMusicDecoder */ extern DECLSPEC SDL_bool SDLCALL Mix_HasMusicDecoder(const char *name); /** * Find out the format of a mixer music. * * If `music` is NULL, this will query the currently playing music (and return * MUS_NONE if nothing is currently playing). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the Mix_MusicType for the music object. * * \since This function is available since SDL_mixer 2.0.0 */ extern DECLSPEC Mix_MusicType SDLCALL Mix_GetMusicType(const Mix_Music *music); /** * Get the title for a music object, or its filename. * * This returns format-specific metadata. Not all file formats supply this! * * If `music` is NULL, this will query the currently-playing music. * * If music's title tag is missing or empty, the filename will be returned. If * you'd rather have the actual metadata or nothing, use * Mix_GetMusicTitleTag() instead. * * Please note that if the music was loaded from an SDL_RWops instead of a * filename, the filename returned will be an empty string (""). * * This function never returns NULL! If no data is available, it will return * an empty string (""). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the music's title if available, or the filename if not, or "". * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_GetMusicTitleTag * \sa Mix_GetMusicArtistTag * \sa Mix_GetMusicAlbumTag * \sa Mix_GetMusicCopyrightTag */ extern DECLSPEC const char *SDLCALL Mix_GetMusicTitle(const Mix_Music *music); /** * Get the title for a music object. * * This returns format-specific metadata. Not all file formats supply this! * * If `music` is NULL, this will query the currently-playing music. * * Unlike this function, Mix_GetMusicTitle() produce a string with the music's * filename if a title isn't available, which might be preferable for some * applications. * * This function never returns NULL! If no data is available, it will return * an empty string (""). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the music's title if available, or "". * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_GetMusicTitle * \sa Mix_GetMusicArtistTag * \sa Mix_GetMusicAlbumTag * \sa Mix_GetMusicCopyrightTag */ extern DECLSPEC const char *SDLCALL Mix_GetMusicTitleTag(const Mix_Music *music); /** * Get the artist name for a music object. * * This returns format-specific metadata. Not all file formats supply this! * * If `music` is NULL, this will query the currently-playing music. * * This function never returns NULL! If no data is available, it will return * an empty string (""). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the music's artist name if available, or "". * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_GetMusicTitleTag * \sa Mix_GetMusicAlbumTag * \sa Mix_GetMusicCopyrightTag */ extern DECLSPEC const char *SDLCALL Mix_GetMusicArtistTag(const Mix_Music *music); /** * Get the album name for a music object. * * This returns format-specific metadata. Not all file formats supply this! * * If `music` is NULL, this will query the currently-playing music. * * This function never returns NULL! If no data is available, it will return * an empty string (""). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the music's album name if available, or "". * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_GetMusicTitleTag * \sa Mix_GetMusicArtistTag * \sa Mix_GetMusicCopyrightTag */ extern DECLSPEC const char *SDLCALL Mix_GetMusicAlbumTag(const Mix_Music *music); /** * Get the copyright text for a music object. * * This returns format-specific metadata. Not all file formats supply this! * * If `music` is NULL, this will query the currently-playing music. * * This function never returns NULL! If no data is available, it will return * an empty string (""). * * \param music the music object to query, or NULL for the currently-playing * music. * \returns the music's copyright text if available, or "". * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_GetMusicTitleTag * \sa Mix_GetMusicArtistTag * \sa Mix_GetMusicAlbumTag */ extern DECLSPEC const char *SDLCALL Mix_GetMusicCopyrightTag(const Mix_Music *music); /** * Set a function that is called after all mixing is performed. * * This can be used to provide real-time visual display of the audio stream or * add a custom mixer filter for the stream data. * * The callback will fire every time SDL_mixer is ready to supply more data to * the audio device, after it has finished all its mixing work. This runs * inside an SDL audio callback, so it's important that the callback return * quickly, or there could be problems in the audio playback. * * The data provided to the callback is in the format that the audio device * was opened in, and it represents the exact waveform SDL_mixer has mixed * from all playing chunks and music for playback. You are allowed to modify * the data, but it cannot be resized (so you can't add a reverb effect that * goes past the end of the buffer without saving some state between runs to * add it into the next callback, or resample the buffer to a smaller size to * speed it up, etc). * * The `arg` pointer supplied here is passed to the callback as-is, for * whatever the callback might want to do with it (keep track of some ongoing * state, settings, etc). * * Passing a NULL callback disables the post-mix callback until such a time as * a new one callback is set. * * There is only one callback available. If you need to mix multiple inputs, * be prepared to handle them from a single function. * * \param mix_func the callback function to become the new post-mix callback. * \param arg a pointer that is passed, untouched, to the callback. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_HookMusic */ extern DECLSPEC void SDLCALL Mix_SetPostMix(void (SDLCALL *mix_func)(void *udata, Uint8 *stream, int len), void *arg); /** * Add your own music player or additional mixer function. * * This works something like Mix_SetPostMix(), but it has some crucial * differences. Note that an app can use this _and_ Mix_SetPostMix() at the * same time. This allows an app to replace the built-in music playback, * either with it's own music decoder or with some sort of * procedurally-generated audio output. * * The supplied callback will fire every time SDL_mixer is preparing to supply * more data to the audio device. This runs inside an SDL audio callback, so * it's important that the callback return quickly, or there could be problems * in the audio playback. * * Running this callback is the first thing SDL_mixer will do when starting to * mix more audio. The buffer will contain silence upon entry, so the callback * does not need to mix into existing data or initialize the buffer. * * Note that while a callback is set through this function, SDL_mixer will not * mix any playing music; this callback is used instead. To disable this * callback (and thus reenable built-in music playback) call this function * with a NULL callback. * * The data written to by the callback is in the format that the audio device * was opened in, and upon return from the callback, SDL_mixer will mix any * playing chunks (but not music!) into the buffer. The callback cannot resize * the buffer (so you must be prepared to provide exactly the amount of data * demanded or leave it as silence). * * The `arg` pointer supplied here is passed to the callback as-is, for * whatever the callback might want to do with it (keep track of some ongoing * state, settings, etc). * * As there is only one music "channel" mixed, there is only one callback * available. If you need to mix multiple inputs, be prepared to handle them * from a single function. * * \param mix_func the callback function to become the new post-mix callback. * \param arg a pointer that is passed, untouched, to the callback. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_SetPostMix */ extern DECLSPEC void SDLCALL Mix_HookMusic(void (SDLCALL *mix_func)(void *udata, Uint8 *stream, int len), void *arg); /** * Set a callback that runs when a music object has stopped playing. * * This callback will fire when the currently-playing music has completed, or * when it has been explicitly stopped from a call to Mix_HaltMusic. As such, * this callback might fire from an arbitrary background thread at almost any * time; try to limit what you do here. * * It is legal to start a new music object playing in this callback (or * restart the one that just stopped). If the music finished normally, this * can be used to loop the music without a gap in the audio playback. * * Do not call SDL_LockAudio() from this callback; you will either be inside * the audio callback, or SDL_mixer will explicitly lock the audio before * calling your callback. * * A NULL pointer will disable the callback. * * \param music_finished the callback function to become the new notification * mechanism. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_HookMusicFinished(void (SDLCALL *music_finished)(void)); /** * Get a pointer to the user data for the current music hook. * * This returns the `arg` pointer last passed to Mix_HookMusic(), or NULL if * that function has never been called. * * \returns pointer to the user data previously passed to Mix_HookMusic. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void * SDLCALL Mix_GetMusicHookData(void); /** * Set a callback that runs when a channel has finished playing. * * The callback may be called from the mixer's audio callback or it could be * called as a result of Mix_HaltChannel(), etc. * * The callback has a single parameter, `channel`, which says what mixer * channel has just stopped. * * Do not call SDL_LockAudio() from this callback; you will either be inside * the audio callback, or SDL_mixer will explicitly lock the audio before * calling your callback. * * A NULL pointer will disable the callback. * * \param channel_finished the callback function to become the new * notification mechanism. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_ChannelFinished(void (SDLCALL *channel_finished)(int channel)); #define MIX_CHANNEL_POST (-2) /** * This is the format of a special effect callback: * * myeffect(int chan, void *stream, int len, void *udata); * * (chan) is the channel number that your effect is affecting. (stream) is * the buffer of data to work upon. (len) is the size of (stream), and * (udata) is a user-defined bit of data, which you pass as the last arg of * Mix_RegisterEffect(), and is passed back unmolested to your callback. * Your effect changes the contents of (stream) based on whatever parameters * are significant, or just leaves it be, if you prefer. You can do whatever * you like to the buffer, though, and it will continue in its changed state * down the mixing pipeline, through any other effect functions, then finally * to be mixed with the rest of the channels and music for the final output * stream. * * DO NOT EVER call SDL_LockAudio() from your callback function! */ typedef void (SDLCALL *Mix_EffectFunc_t)(int chan, void *stream, int len, void *udata); /** * This is a callback that signifies that a channel has finished all its * loops and has completed playback. This gets called if the buffer * plays out normally, or if you call Mix_HaltChannel(), implicitly stop * a channel via Mix_AllocateChannels(), or unregister a callback while * it's still playing. * * DO NOT EVER call SDL_LockAudio() from your callback function! */ typedef void (SDLCALL *Mix_EffectDone_t)(int chan, void *udata); /** * Register a special effect function. * * At mixing time, the channel data is copied into a buffer and passed through * each registered effect function. After it passes through all the functions, * it is mixed into the final output stream. The copy to buffer is performed * once, then each effect function performs on the output of the previous * effect. Understand that this extra copy to a buffer is not performed if * there are no effects registered for a given chunk, which saves CPU cycles, * and any given effect will be extra cycles, too, so it is crucial that your * code run fast. Also note that the data that your function is given is in * the format of the sound device, and not the format you gave to * Mix_OpenAudio(), although they may in reality be the same. This is an * unfortunate but necessary speed concern. Use Mix_QuerySpec() to determine * if you can handle the data before you register your effect, and take * appropriate actions. * * You may also specify a callback (Mix_EffectDone_t) that is called when the * channel finishes playing. This gives you a more fine-grained control than * Mix_ChannelFinished(), in case you need to free effect-specific resources, * etc. If you don't need this, you can specify NULL. * * You may set the callbacks before or after calling Mix_PlayChannel(). * * Things like Mix_SetPanning() are just internal special effect functions, so * if you are using that, you've already incurred the overhead of a copy to a * separate buffer, and that these effects will be in the queue with any * functions you've registered. The list of registered effects for a channel * is reset when a chunk finishes playing, so you need to explicitly set them * with each call to Mix_PlayChannel*(). * * You may also register a special effect function that is to be run after * final mixing occurs. The rules for these callbacks are identical to those * in Mix_RegisterEffect, but they are run after all the channels and the * music have been mixed into a single stream, whereas channel-specific * effects run on a given channel before any other mixing occurs. These global * effect callbacks are call "posteffects". Posteffects only have their * Mix_EffectDone_t function called when they are unregistered (since the main * output stream is never "done" in the same sense as a channel). You must * unregister them manually when you've had enough. Your callback will be told * that the channel being mixed is `MIX_CHANNEL_POST` if the processing is * considered a posteffect. * * After all these effects have finished processing, the callback registered * through Mix_SetPostMix() runs, and then the stream goes to the audio * device. * * DO NOT EVER call SDL_LockAudio() from your callback function! You are * already running in the audio thread and the lock is already held! * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param chan the channel to register an effect to, or MIX_CHANNEL_POST. * \param f effect the callback to run when more of this channel is to be * mixed. * \param d effect done callback * \param arg argument * \returns zero if error (no such channel), nonzero if added. Error messages * can be retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_RegisterEffect(int chan, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg); /** * Explicitly unregister a special effect function. * * You may not need to call this at all, unless you need to stop an effect * from processing in the middle of a chunk's playback. * * Posteffects are never implicitly unregistered as they are for channels (as * the output stream does not have an end), but they may be explicitly * unregistered through this function by specifying MIX_CHANNEL_POST for a * channel. * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel the channel to unregister an effect on, or MIX_CHANNEL_POST. * \param f effect the callback stop calling in future mixing iterations. * \returns zero if error (no such channel or effect), nonzero if removed. * Error messages can be retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f); /** * Explicitly unregister all special effect functions. * * You may not need to call this at all, unless you need to stop all effects * from processing in the middle of a chunk's playback. * * Note that this will also shut off some internal effect processing, since * Mix_SetPanning() and others may use this API under the hood. This is called * internally when a channel completes playback. Posteffects are never * implicitly unregistered as they are for channels, but they may be * explicitly unregistered through this function by specifying * MIX_CHANNEL_POST for a channel. * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel the channel to unregister all effects on, or * MIX_CHANNEL_POST. * \returns zero if error (no such channel), nonzero if all effects removed. * Error messages can be retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_UnregisterAllEffects(int channel); #define MIX_EFFECTSMAXSPEED "MIX_EFFECTSMAXSPEED" /* * These are the internally-defined mixing effects. They use the same API that * effects defined in the application use, but are provided here as a * convenience. Some effects can reduce their quality or use more memory in * the name of speed; to enable this, make sure the environment variable * MIX_EFFECTSMAXSPEED (see above) is defined before you call * Mix_OpenAudio(). */ /** * Set the panning of a channel. * * The left and right channels are specified as integers between 0 and 255, * quietest to loudest, respectively. * * Technically, this is just individual volume control for a sample with two * (stereo) channels, so it can be used for more than just panning. If you * want real panning, call it like this: * * ```c * Mix_SetPanning(channel, left, 255 - left); * ``` * * Setting `channel` to MIX_CHANNEL_POST registers this as a posteffect, and * the panning will be done to the final mixed stream before passing it on to * the audio device. * * This uses the Mix_RegisterEffect() API internally, and returns without * registering the effect function if the audio device is not configured for * stereo output. Setting both `left` and `right` to 255 causes this effect to * be unregistered, since that is the data's normal state. * * Note that an audio device in mono mode is a no-op, but this call will * return successful in that case. Error messages can be retrieved from * Mix_GetError(). * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel The mixer channel to pan or MIX_CHANNEL_POST. * \param left Volume of stereo left channel, 0 is silence, 255 is full * volume. * \param right Volume of stereo right channel, 0 is silence, 255 is full * volume. * \returns zero if error (no such channel or Mix_RegisterEffect() fails), * nonzero if panning effect enabled. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_SetPosition * \sa Mix_SetDistance */ extern DECLSPEC int SDLCALL Mix_SetPanning(int channel, Uint8 left, Uint8 right); /** * Set the position of a channel. * * `angle` is an integer from 0 to 360, that specifies the location of the * sound in relation to the listener. `angle` will be reduced as necessary * (540 becomes 180 degrees, -100 becomes 260). Angle 0 is due north, and * rotates clockwise as the value increases. For efficiency, the precision of * this effect may be limited (angles 1 through 7 might all produce the same * effect, 8 through 15 are equal, etc). `distance` is an integer between 0 * and 255 that specifies the space between the sound and the listener. The * larger the number, the further away the sound is. Using 255 does not * guarantee that the channel will be removed from the mixing process or be * completely silent. For efficiency, the precision of this effect may be * limited (distance 0 through 5 might all produce the same effect, 6 through * 10 are equal, etc). Setting `angle` and `distance` to 0 unregisters this * effect, since the data would be unchanged. * * If you need more precise positional audio, consider using OpenAL for * spatialized effects instead of SDL_mixer. This is only meant to be a basic * effect for simple "3D" games. * * If the audio device is configured for mono output, then you won't get any * effectiveness from the angle; however, distance attenuation on the channel * will still occur. While this effect will function with stereo voices, it * makes more sense to use voices with only one channel of sound, so when they * are mixed through this effect, the positioning will sound correct. You can * convert them to mono through SDL before giving them to the mixer in the * first place if you like. * * Setting the channel to MIX_CHANNEL_POST registers this as a posteffect, and * the positioning will be done to the final mixed stream before passing it on * to the audio device. * * This is a convenience wrapper over Mix_SetDistance() and Mix_SetPanning(). * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel The mixer channel to position, or MIX_CHANNEL_POST. * \param angle angle, in degrees. North is 0, and goes clockwise. * \param distance distance; 0 is the listener, 255 is maxiumum distance away. * \returns zero if error (no such channel or Mix_RegisterEffect() fails), * nonzero if position effect is enabled. Error messages can be * retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetPosition(int channel, Sint16 angle, Uint8 distance); /** * Set the "distance" of a channel. * * `distance` is an integer from 0 to 255 that specifies the location of the * sound in relation to the listener. Distance 0 is overlapping the listener, * and 255 is as far away as possible. A distance of 255 does not guarantee * silence; in such a case, you might want to try changing the chunk's volume, * or just cull the sample from the mixing process with Mix_HaltChannel(). For * efficiency, the precision of this effect may be limited (distances 1 * through 7 might all produce the same effect, 8 through 15 are equal, etc). * (distance) is an integer between 0 and 255 that specifies the space between * the sound and the listener. The larger the number, the further away the * sound is. Setting the distance to 0 unregisters this effect, since the data * would be unchanged. If you need more precise positional audio, consider * using OpenAL for spatialized effects instead of SDL_mixer. This is only * meant to be a basic effect for simple "3D" games. * * Setting the channel to MIX_CHANNEL_POST registers this as a posteffect, and * the distance attenuation will be done to the final mixed stream before * passing it on to the audio device. * * This uses the Mix_RegisterEffect() API internally. * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel The mixer channel to attenuate, or MIX_CHANNEL_POST. * \param distance distance; 0 is the listener, 255 is maxiumum distance away. * \returns zero if error (no such channel or Mix_RegisterEffect() fails), * nonzero if position effect is enabled. Error messages can be * retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetDistance(int channel, Uint8 distance); /** * Cause a channel to reverse its stereo. * * This is handy if the user has his speakers hooked up backwards, or you * would like to have a trippy sound effect. * * Calling this function with `flip` set to non-zero reverses the chunks's * usual channels. If `flip` is zero, the effect is unregistered. * * This uses the Mix_RegisterEffect() API internally, and thus is probably * more CPU intensive than having the user just plug in his speakers * correctly. Mix_SetReverseStereo() returns without registering the effect * function if the audio device is not configured for stereo output. * * If you specify MIX_CHANNEL_POST for `channel`, then this effect is used on * the final mixed stream before sending it on to the audio device (a * posteffect). * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param channel The mixer channel to reverse, or MIX_CHANNEL_POST. * \param flip non-zero to reverse stereo, zero to disable this effect. * \returns zero if error (no such channel or Mix_RegisterEffect() fails), * nonzero if reversing effect is enabled. Note that an audio device * in mono mode is a no-op, but this call will return successful in * that case. Error messages can be retrieved from Mix_GetError(). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetReverseStereo(int channel, int flip); /* end of effects API. */ /** * Reserve the first channels for the application. * * While SDL_mixer will use up to the number of channels allocated by * Mix_AllocateChannels(), this sets channels aside that will not be available * when calling Mix_PlayChannel with a channel of -1 (play on the first unused * channel). In this case, SDL_mixer will treat reserved channels as "used" * whether anything is playing on them at the moment or not. * * This is useful if you've budgeted some channels for dedicated audio and the * rest are just used as they are available. * * Calling this function will set channels 0 to `n - 1` to be reserved. This * will not change channel allocations. The number of reserved channels will * be clamped to the current number allocated. * * By default, no channels are reserved. * * \param num number of channels to reserve, starting at index zero. * \returns the number of reserved channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_ReserveChannels(int num); /* Channel grouping functions */ /** * Assign a tag to a channel. * * A tag is an arbitary number that can be assigned to several mixer channels, * to form groups of channels. * * If 'tag' is -1, the tag is removed (actually -1 is the tag used to * represent the group of all the channels). * * This function replaces the requested channel's current tag; you may only * have one tag per channel. * * You may not specify MAX_CHANNEL_POST for a channel. * * \param which the channel to set the tag on. * \param tag an arbitrary value to assign a channel. * \returns non-zero on success, zero on error (no such channel). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_GroupChannel(int which, int tag); /** * Assign several consecutive channels to the same tag. * * A tag is an arbitary number that can be assigned to several mixer channels, * to form groups of channels. * * If 'tag' is -1, the tag is removed (actually -1 is the tag used to * represent the group of all the channels). * * This function replaces the requested channels' current tags; you may only * have one tag per channel. * * You may not specify MAX_CHANNEL_POST for a channel. * * Note that this returns success and failure in the _opposite_ way from * Mix_GroupChannel(). We regret the API design mistake. * * \param from the first channel to set the tag on. * \param to the last channel to set the tag on, inclusive. * \param tag an arbitrary value to assign a channel. * \returns 0 if successful, negative on error * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_GroupChannels(int from, int to, int tag); /** * Finds the first available channel in a group of channels. * * A tag is an arbitary number that can be assigned to several mixer channels, * to form groups of channels. * * This function searches all channels with a specified tag, and returns the * channel number of the first one it finds that is currently unused. * * If no channels with the specified tag are unused, this function returns -1. * * \param tag an arbitrary value, assigned to channels, to search for. * \returns first available channel, or -1 if none are available. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_GroupAvailable(int tag); /** * Returns the number of channels in a group. * * If tag is -1, this will return the total number of channels allocated, * regardless of what their tag might be. * * \param tag an arbitrary value, assigned to channels, to search for. * \returns the number of channels assigned the specified tag. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_GroupCount(int tag); /** * Find the "oldest" sample playing in a group of channels. * * Specifically, this function returns the channel number that is assigned the * specified tag, is currently playing, and has the lowest start time, based * on the value of SDL_GetTicks() when the channel started playing. * * If no channel with this tag is currently playing, this function returns -1. * * \param tag an arbitrary value, assigned to channels, to search through. * \returns the "oldest" sample playing in a group of channels * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GroupNewer */ extern DECLSPEC int SDLCALL Mix_GroupOldest(int tag); /** * Find the "most recent" sample playing in a group of channels. * * Specifically, this function returns the channel number that is assigned the * specified tag, is currently playing, and has the highest start time, based * on the value of SDL_GetTicks() when the channel started playing. * * If no channel with this tag is currently playing, this function returns -1. * * \param tag an arbitrary value, assigned to channels, to search through. * \returns the "most recent" sample playing in a group of channels * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GroupOldest */ extern DECLSPEC int SDLCALL Mix_GroupNewer(int tag); /** * Play an audio chunk on a specific channel. * * If the specified channel is -1, play on the first free channel (and return * -1 without playing anything new if no free channel was available). * * If a specific channel was requested, and there is a chunk already playing * there, that chunk will be halted and the new chunk will take its place. * * If `loops` is greater than zero, loop the sound that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * Note that before SDL_mixer 2.6.0, this function was a macro that called * Mix_PlayChannelTimed() with a fourth parameter ("ticks") of -1. This * function still does the same thing, but promotes it to a proper API * function. Older binaries linked against a newer SDL_mixer will still call * Mix_PlayChannelTimed directly, as they are using the macro, which was * available since the dawn of time. * * \param channel the channel on which to play the new chunk. * \param chunk the new chunk to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \returns which channel was used to play the sound, or -1 if sound could not * be played. * * \since This function is available since SDL_mixer 2.6.0 (and as a macro * since 2.0.0). */ extern DECLSPEC int SDLCALL Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops); /** * Play an audio chunk on a specific channel for a maximum time. * * If the specified channel is -1, play on the first free channel (and return * -1 without playing anything new if no free channel was available). * * If a specific channel was requested, and there is a chunk already playing * there, that chunk will be halted and the new chunk will take its place. * * If `loops` is greater than zero, loop the sound that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * `ticks` specifies the maximum number of milliseconds to play this chunk * before halting it. If you want the chunk to play until all data has been * mixed, specify -1. * * Note that this function does not block for the number of ticks requested; * it just schedules the chunk to play and notes the maximum for the mixer to * manage later, and returns immediately. * * \param channel the channel on which to play the new chunk. * \param chunk the new chunk to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \param ticks the maximum number of milliseconds of this chunk to mix for * playback. * \returns which channel was used to play the sound, or -1 if sound could not * be played. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_PlayChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ticks); /** * Play a new music object. * * This will schedule the music object to begin mixing for playback. * * There is only ever one music object playing at a time; if this is called * when another music object is playing, the currently-playing music is halted * and the new music will replace it. * * Please note that if the currently-playing music is in the process of fading * out (via Mix_FadeOutMusic()), this function will *block* until the fade * completes. If you need to avoid this, be sure to call Mix_HaltMusic() * before starting new music. * * \param music the new music object to schedule for mixing. * \param loops the number of loops to play the music for (0 means "play once * and stop"). * \returns zero on success, -1 on error. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_PlayMusic(Mix_Music *music, int loops); /** * Play a new music object, fading in the audio. * * This will start the new music playing, much like Mix_PlayMusic() will, but * will start the music playing at silence and fade in to its normal volume * over the specified number of milliseconds. * * If there is already music playing, that music will be halted and the new * music object will take its place. * * If `loops` is greater than zero, loop the music that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * Fading music will change it's volume progressively, as if Mix_VolumeMusic() * was called on it (which is to say: you probably shouldn't call * Mix_VolumeMusic() on fading music). * * \param music the new music object to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \param ms the number of milliseconds to spend fading in. * \returns zero on success, -1 on error. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeInMusic(Mix_Music *music, int loops, int ms); /** * Play a new music object, fading in the audio, from a starting position. * * This will start the new music playing, much like Mix_PlayMusic() will, but * will start the music playing at silence and fade in to its normal volume * over the specified number of milliseconds. * * If there is already music playing, that music will be halted and the new * music object will take its place. * * If `loops` is greater than zero, loop the music that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * Fading music will change it's volume progressively, as if Mix_VolumeMusic() * was called on it (which is to say: you probably shouldn't call * Mix_VolumeMusic() on fading music). * * This function allows the caller to start the music playback past the * beginning of its audio data. You may specify a start position, in seconds, * and the playback and fade-in will start there instead of with the first * samples of the music. * * An app can specify a `position` of 0.0 to start at the beginning of the * music (or just call Mix_FadeInMusic() instead). * * To convert from milliseconds, divide by 1000.0. * * \param music the new music object to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \param ms the number of milliseconds to spend fading in. * \param position the start position within the music, in seconds, where * playback should start. * \returns zero on success, -1 on error. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position); /** * Play an audio chunk on a specific channel, fading in the audio. * * This will start the new sound playing, much like Mix_PlayChannel() will, * but will start the sound playing at silence and fade in to its normal * volume over the specified number of milliseconds. * * If the specified channel is -1, play on the first free channel (and return * -1 without playing anything new if no free channel was available). * * If a specific channel was requested, and there is a chunk already playing * there, that chunk will be halted and the new chunk will take its place. * * If `loops` is greater than zero, loop the sound that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * A fading channel will change it's volume progressively, as if Mix_Volume() * was called on it (which is to say: you probably shouldn't call Mix_Volume() * on a fading channel). * * Note that before SDL_mixer 2.6.0, this function was a macro that called * Mix_FadeInChannelTimed() with a fourth parameter ("ticks") of -1. This * function still does the same thing, but promotes it to a proper API * function. Older binaries linked against a newer SDL_mixer will still call * Mix_FadeInChannelTimed directly, as they are using the macro, which was * available since the dawn of time. * * \param channel the channel on which to play the new chunk, or -1 to find * any available. * \param chunk the new chunk to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \param ms the number of milliseconds to spend fading in. * \returns which channel was used to play the sound, or -1 if sound could not * be played. * * \since This function is available since SDL_mixer 2.6.0 (and as a macro * since 2.0.0). */ extern DECLSPEC int SDLCALL Mix_FadeInChannel(int channel, Mix_Chunk *chunk, int loops, int ms); /** * Play an audio chunk on a specific channel, fading in the audio, for a * maximum time. * * This will start the new sound playing, much like Mix_PlayChannel() will, * but will start the sound playing at silence and fade in to its normal * volume over the specified number of milliseconds. * * If the specified channel is -1, play on the first free channel (and return * -1 without playing anything new if no free channel was available). * * If a specific channel was requested, and there is a chunk already playing * there, that chunk will be halted and the new chunk will take its place. * * If `loops` is greater than zero, loop the sound that many times. If `loops` * is -1, loop "infinitely" (~65000 times). * * `ticks` specifies the maximum number of milliseconds to play this chunk * before halting it. If you want the chunk to play until all data has been * mixed, specify -1. * * Note that this function does not block for the number of ticks requested; * it just schedules the chunk to play and notes the maximum for the mixer to * manage later, and returns immediately. * * A fading channel will change it's volume progressively, as if Mix_Volume() * was called on it (which is to say: you probably shouldn't call Mix_Volume() * on a fading channel). * * \param channel the channel on which to play the new chunk, or -1 to find * any available. * \param chunk the new chunk to play. * \param loops the number of times the chunk should loop, -1 to loop (not * actually) infinitely. * \param ms the number of milliseconds to spend fading in. * \param ticks the maximum number of milliseconds of this chunk to mix for * playback. * \returns which channel was used to play the sound, or -1 if sound could not * be played. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeInChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ms, int ticks); /** * Set the volume for a specific channel. * * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are * clamped to MIX_MAX_VOLUME. * * Specifying a negative volume will not change the current volume; as such, * this can be used to query the current volume without making changes, as * this function returns the previous (in this case, still-current) value. * * If the specified channel is -1, this function sets the volume for all * channels, and returns _the average_ of all channels' volumes prior to this * call. * * The default volume for a channel is MIX_MAX_VOLUME (no attenuation). * * \param channel the channel on set/query the volume on, or -1 for all * channels. * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. * \returns the previous volume. If the specified volume is -1, this returns * the current volume. If `channel` is -1, this returns the average * of all channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_Volume(int channel, int volume); /** * Set the volume for a specific chunk. * * In addition to channels having a volume setting, individual chunks also * maintain a separate volume. Both values are considered when mixing, so both * affect the final attenuation of the sound. This lets an app adjust the * volume for all instances of a sound in addition to specific instances of * that sound. * * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are * clamped to MIX_MAX_VOLUME. * * Specifying a negative volume will not change the current volume; as such, * this can be used to query the current volume without making changes, as * this function returns the previous (in this case, still-current) value. * * The default volume for a chunk is MIX_MAX_VOLUME (no attenuation). * * \param chunk the chunk whose volume to adjust. * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. * \returns the previous volume. If the specified volume is -1, this returns * the current volume. If `chunk` is NULL, this returns -1. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_VolumeChunk(Mix_Chunk *chunk, int volume); /** * Set the volume for the music channel. * * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are * clamped to MIX_MAX_VOLUME. * * Specifying a negative volume will not change the current volume; as such, * this can be used to query the current volume without making changes, as * this function returns the previous (in this case, still-current) value. * * The default volume for music is MIX_MAX_VOLUME (no attenuation). * * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. * \returns the previous volume. If the specified volume is -1, this returns * the current volume. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_VolumeMusic(int volume); /** * Query the current volume value for a music object. * * \param music the music object to query. * \returns the music's current volume, between 0 and MIX_MAX_VOLUME (128). * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC int SDLCALL Mix_GetMusicVolume(Mix_Music *music); /** * Set the master volume for all channels. * * SDL_mixer keeps a per-channel volume, a per-chunk volume, and a master * volume, and considers all three when mixing audio. This function sets the * master volume, which is applied to all playing channels when mixing. * * The volume must be between 0 (silence) and MIX_MAX_VOLUME (full volume). * Note that MIX_MAX_VOLUME is 128. Values greater than MIX_MAX_VOLUME are * clamped to MIX_MAX_VOLUME. * * Specifying a negative volume will not change the current volume; as such, * this can be used to query the current volume without making changes, as * this function returns the previous (in this case, still-current) value. * * Note that the master volume does not affect any playing music; it is only * applied when mixing chunks. Use Mix_MusicVolume() for that.\ * * \param volume the new volume, between 0 and MIX_MAX_VOLUME, or -1 to query. * \returns the previous volume. If the specified volume is -1, this returns * the current volume. * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC int SDLCALL Mix_MasterVolume(int volume); /** * Halt playing of a particular channel. * * This will stop further playback on that channel until a new chunk is * started there. * * Specifying a channel of -1 will halt _all_ channels, except for any playing * music. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * before this function returns. * * You may not specify MAX_CHANNEL_POST for a channel. * * \param channel channel to halt, or -1 to halt all channels. * \returns 0 on success, or -1 on error. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_HaltChannel(int channel); /** * Halt playing of a group of channels by arbitrary tag. * * This will stop further playback on all channels with a specific tag, until * a new chunk is started there. * * A tag is an arbitrary number that can be assigned to several mixer * channels, to form groups of channels. * * The default tag for a channel is -1. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * before this function returns. * * \param tag an arbitrary value, assigned to channels, to search for. * \returns zero, whether any channels were halted or not. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_HaltGroup(int tag); /** * Halt playing of the music stream. * * This will stop further playback of music until a new music object is * started there. * * Any halted music will call any callback specified by * Mix_HookMusicFinished() before this function returns. * * \returns zero, regardless of whether any music was halted. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_HaltMusic(void); /** * Change the expiration delay for a particular channel. * * The channel will halt after the 'ticks' milliseconds have elapsed, or * remove the expiration if 'ticks' is -1. * * This overrides the value passed to the fourth parameter of * Mix_PlayChannelTimed(). * * Specifying a channel of -1 will set an expiration for _all_ channels. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * once the halt occurs. * * Note that this function does not block for the number of ticks requested; * it just schedules the chunk to expire and notes the time for the mixer to * manage later, and returns immediately. * * \param channel the channel to change the expiration time on. * \param ticks number of milliseconds from now to let channel play before * halting, -1 to not halt. * \returns the number of channels that changed expirations. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_ExpireChannel(int channel, int ticks); /** * Halt a channel after fading it out for a specified time. * * This will begin a channel fading from its current volume to silence over * `ms` milliseconds. After that time, the channel is halted. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * once the halt occurs. * * A fading channel will change it's volume progressively, as if Mix_Volume() * was called on it (which is to say: you probably shouldn't call Mix_Volume() * on a fading channel). * * Note that this function does not block for the number of milliseconds * requested; it just schedules the chunk to fade and notes the time for the * mixer to manage later, and returns immediately. * * \param which the channel to fade out. * \param ms number of milliseconds to fade before halting the channel. * \returns the number of channels scheduled to fade. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeOutChannel(int which, int ms); /** * Halt a playing group of channels by arbitrary tag, after fading them out * for a specified time. * * This will begin fading a group of channels with a specific tag from their * current volumes to silence over `ms` milliseconds. After that time, those * channels are halted. * * A tag is an arbitrary number that can be assigned to several mixer * channels, to form groups of channels. * * The default tag for a channel is -1. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * once the halt occurs. * * A fading channel will change it's volume progressively, as if Mix_Volume() * was called on it (which is to say: you probably shouldn't call Mix_Volume() * on a fading channel). * * Note that this function does not block for the number of milliseconds * requested; it just schedules the group to fade and notes the time for the * mixer to manage later, and returns immediately. * * \param tag an arbitrary value, assigned to channels, to search for. * \param ms number of milliseconds to fade before halting the group. * \returns the number of channels that were scheduled for fading. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeOutGroup(int tag, int ms); /** * Halt the music stream after fading it out for a specified time. * * This will begin the music fading from its current volume to silence over * `ms` milliseconds. After that time, the music is halted. * * Any halted music will call any callback specified by * Mix_HookMusicFinished() once the halt occurs. * * Fading music will change it's volume progressively, as if Mix_VolumeMusic() * was called on it (which is to say: you probably shouldn't call * Mix_VolumeMusic() on a fading channel). * * Note that this function does not block for the number of milliseconds * requested; it just schedules the music to fade and notes the time for the * mixer to manage later, and returns immediately. * * \param ms number of milliseconds to fade before halting the channel. * \returns non-zero if music was scheduled to fade, zero otherwise. If no * music is currently playing, this returns zero. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_FadeOutMusic(int ms); /** * Query the fading status of the music stream. * * This reports one of three values: * * - `MIX_NO_FADING` * - `MIX_FADING_OUT` * - `MIX_FADING_IN` * * If music is not currently playing, this returns `MIX_NO_FADING`. * * \returns the current fading status of the music stream. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC Mix_Fading SDLCALL Mix_FadingMusic(void); /** * Query the fading status of a channel. * * This reports one of three values: * * - `MIX_NO_FADING` * - `MIX_FADING_OUT` * - `MIX_FADING_IN` * * If nothing is currently playing on the channel, or an invalid channel is * specified, this returns `MIX_NO_FADING`. * * You may not specify MAX_CHANNEL_POST for a channel. * * You may not specify -1 for all channels; only individual channels may be * queried. * * \param which the channel to query. * \returns the current fading status of the channel. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC Mix_Fading SDLCALL Mix_FadingChannel(int which); /** * Pause a particular channel. * * Pausing a channel will prevent further playback of the assigned chunk but * will maintain the chunk's current mixing position. When resumed, this * channel will continue to mix the chunk where it left off. * * A paused channel can be resumed by calling Mix_Resume(). * * A paused channel with an expiration will not expire while paused (the * expiration countdown will be adjusted once resumed). * * It is legal to halt a paused channel. Playing a new chunk on a paused * channel will replace the current chunk and unpause the channel. * * Specifying a channel of -1 will pause _all_ channels. Any music is * unaffected. * * You may not specify MAX_CHANNEL_POST for a channel. * * \param channel the channel to pause, or -1 to pause all channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_Pause(int channel); /** * Resume a particular channel. * * It is legal to resume an unpaused or invalid channel; it causes no effect * and reports no error. * * If the paused channel has an expiration, its expiration countdown resumes * now, as well. * * Specifying a channel of -1 will resume _all_ paused channels. Any music is * unaffected. * * \param channel the channel to resume, or -1 to resume all paused channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_Resume(int channel); /** * Query whether a particular channel is paused. * * If an invalid channel is specified, this function returns zero. * * \param channel the channel to query, or -1 to query all channels. * \return 1 if channel paused, 0 otherwise. If `channel` is -1, returns the * number of paused channels. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_Paused(int channel); /** * Pause the music stream. * * Pausing the music stream will prevent further playback of the assigned * music object, but will maintain the object's current mixing position. When * resumed, this channel will continue to mix the music where it left off. * * Paused music can be resumed by calling Mix_ResumeMusic(). * * It is legal to halt paused music. Playing a new music object when music is * paused will replace the current music and unpause the music stream. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_PauseMusic(void); /** * Resume the music stream. * * It is legal to resume an unpaused music stream; it causes no effect and * reports no error. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_ResumeMusic(void); /** * Rewind the music stream. * * This causes the currently-playing music to start mixing from the beginning * of the music, as if it were just started. * * It's a legal no-op to rewind the music stream when not playing. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC void SDLCALL Mix_RewindMusic(void); /** * Query whether the music stream is paused. * * \return 1 if music is paused, 0 otherwise. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_PauseMusic * \sa Mix_ResumeMusic */ extern DECLSPEC int SDLCALL Mix_PausedMusic(void); /** * Jump to a given order in mod music. * * This only applies to MOD music formats. * * \param order order * \returns 0 if successful, or -1 if failed or isn't implemented. * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC int SDLCALL Mix_ModMusicJumpToOrder(int order); /** * Start a track in music object. * * This only applies to GME music formats. * * \param music the music object. * \param track the track number to play. 0 is the first track. * \returns 0 if successful, or -1 if failed or isn't implemented. * * \since This function is available since SDL_mixer 2.8.0. */ extern DECLSPEC int SDLCALL Mix_StartTrack(Mix_Music *music, int track); /** * Get number of tracks in music object. * * This only applies to GME music formats. * * \param music the music object. * \returns number of tracks if successful, or -1 if failed or isn't * implemented. * * \since This function is available since SDL_mixer 2.8.0. */ extern DECLSPEC int SDLCALL Mix_GetNumTracks(Mix_Music *music); /** * Set the current position in the music stream, in seconds. * * To convert from milliseconds, divide by 1000.0. * * This function is only implemented for MOD music formats (set pattern order * number) and for WAV, OGG, FLAC, MP3, and MODPLUG music at the moment. * * \param position the new position, in seconds (as a double). * \returns 0 if successful, or -1 if it failed or not implemented. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetMusicPosition(double position); /** * Get the time current position of music stream, in seconds. * * To convert to milliseconds, multiply by 1000.0. * * \param music the music object to query. * \returns -1.0 if this feature is not supported for some codec. * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC double SDLCALL Mix_GetMusicPosition(Mix_Music *music); /** * Get a music object's duration, in seconds. * * To convert to milliseconds, multiply by 1000.0. * * If NULL is passed, returns duration of current playing music. * * \param music the music object to query. * \returns music duration in seconds, or -1.0 on error. * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC double SDLCALL Mix_MusicDuration(Mix_Music *music); /** * Get the loop start time position of music stream, in seconds. * * To convert to milliseconds, multiply by 1000.0. * * If NULL is passed, returns duration of current playing music. * * \param music the music object to query. * \returns -1.0 if this feature is not used for this music or not supported * for some codec * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC double SDLCALL Mix_GetMusicLoopStartTime(Mix_Music *music); /** * Get the loop end time position of music stream, in seconds. * * To convert to milliseconds, multiply by 1000.0. * * If NULL is passed, returns duration of current playing music. * * \param music the music object to query. * \returns -1.0 if this feature is not used for this music or not supported * for some codec * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC double SDLCALL Mix_GetMusicLoopEndTime(Mix_Music *music); /** * Get the loop time length of music stream, in seconds. * * To convert to milliseconds, multiply by 1000.0. * * If NULL is passed, returns duration of current playing music. * * \param music the music object to query. * \returns -1.0 if this feature is not used for this music or not supported * for some codec * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC double SDLCALL Mix_GetMusicLoopLengthTime(Mix_Music *music); /** * Check the playing status of a specific channel. * * If the channel is currently playing, this function returns 1. Otherwise it * returns 0. * * If the specified channel is -1, all channels are checked, and this function * returns the number of channels currently playing. * * You may not specify MAX_CHANNEL_POST for a channel. * * Paused channels are treated as playing, even though they are not currently * making forward progress in mixing. * * \param channel channel * \returns non-zero if channel is playing, zero otherwise. If `channel` is * -1, return the total number of channel playings. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_Playing(int channel); /** * Check the playing status of the music stream. * * If music is currently playing, this function returns 1. Otherwise it * returns 0. * * Paused music is treated as playing, even though it is not currently making * forward progress in mixing. * * \returns non-zero if music is playing, zero otherwise. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_PlayingMusic(void); /** * Run an external command as the music stream. * * This halts any currently-playing music, and next time the music stream is * played, SDL_mixer will spawn a process using the command line specified in * `command`. This command is not subject to shell expansion, and beyond some * basic splitting up of arguments, is passed to execvp() on most platforms, * not system(). * * The command is responsible for generating sound; it is NOT mixed by * SDL_mixer! SDL_mixer will kill the child process if asked to halt the * music, but otherwise does not have any control over what the process does. * * You are strongly encouraged not to use this function without an extremely * good reason. * * \param command command * \returns 0 if successful, -1 on error * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetMusicCMD(const char *command); /** * This function does nothing, do not use. * * This was probably meant to expose a feature, but no codecs support it, so * it only remains for binary compatibility. * * Calling this function is a legal no-op that returns -1. * * \param value this parameter is ignored. * \returns -1. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value); /** * This function does nothing, do not use. * * This was probably meant to expose a feature, but no codecs support it, so * it only remains for binary compatibility. * * Calling this function is a legal no-op that returns -1. * * \returns -1. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void); /** * Set SoundFonts paths to use by supported MIDI backends. * * You may specify multiple paths in a single string by separating them with * semicolons; they will be searched in the order listed. * * This function replaces any previously-specified paths. * * Passing a NULL path will remove any previously-specified paths. * * Note that unlike most SDL and SDL_mixer functions, this function returns * zero if there's an error, not on success. We apologize for the API design * inconsistency here. * * \param paths Paths on the filesystem where SoundFonts are available, * separated by semicolons. * \returns 1 if successful, 0 on error (out of memory). * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC int SDLCALL Mix_SetSoundFonts(const char *paths); /** * Get SoundFonts paths to use by supported MIDI backends. * * There are several factors that determine what will be reported by this * function: * * - If the boolean _SDL hint_ `"SDL_FORCE_SOUNDFONTS"` is set, AND the * `"SDL_SOUNDFONTS"` _environment variable_ is also set, this function will * return that environment variable regardless of whether * Mix_SetSoundFounts() was ever called. * - Otherwise, if Mix_SetSoundFonts() was successfully called with a non-NULL * path, this function will return the string passed to that function. * - Otherwise, if the `"SDL_SOUNDFONTS"` variable is set, this function will * return that environment variable. * - Otherwise, this function will search some common locations on the * filesystem, and if it finds a SoundFont there, it will return that. * - Failing everything else, this function returns NULL. * * This returns a pointer to internal (possibly read-only) memory, and it * should not be modified or free'd by the caller. * * \returns semicolon-separated list of sound font paths. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC const char* SDLCALL Mix_GetSoundFonts(void); /** * Iterate SoundFonts paths to use by supported MIDI backends. * * This function will take the string reported by Mix_GetSoundFonts(), split * it up into separate paths, as delimited by semicolons in the string, and * call a callback function for each separate path. * * If there are no paths available, this returns 0 without calling the * callback at all. * * If the callback returns non-zero, this function stops iterating and returns * non-zero. If the callback returns 0, this function will continue iterating, * calling the callback again for further paths. If the callback never returns * 1, this function returns 0, so this can be used to decide if an available * soundfont is acceptable for use. * * \param function the callback function to call once per path. * \param data a pointer to pass to the callback for its own personal use. * \returns non-zero if callback ever returned non-zero, 0 on error or the * callback never returned non-zero. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_GetSoundFonts */ extern DECLSPEC int SDLCALL Mix_EachSoundFont(int (SDLCALL *function)(const char*, void*), void *data); /** * Set full path of the Timidity config file. * * For example, "/etc/timidity.cfg" * * This is obviously only useful if SDL_mixer is using Timidity internally to * play MIDI files. * * \param path path to a Timidity config file. * \returns 1 if successful, 0 on error * * \since This function is available since SDL_mixer 2.6.0. */ extern DECLSPEC int SDLCALL Mix_SetTimidityCfg(const char *path); /** * Get full path of a previously-specified Timidity config file. * * For example, "/etc/timidity.cfg" * * If a path has never been specified, this returns NULL. * * This returns a pointer to internal memory, and it should not be modified or * free'd by the caller. * * \returns the previously-specified path, or NULL if not set. * * \since This function is available since SDL_mixer 2.6.0. * * \sa Mix_SetTimidityCfg */ extern DECLSPEC const char* SDLCALL Mix_GetTimidityCfg(void); /** * Get the Mix_Chunk currently associated with a mixer channel. * * You may not specify MAX_CHANNEL_POST or -1 for a channel. * * \param channel the channel to query. * \returns the associated chunk, if any, or NULL if it's an invalid channel. * * \since This function is available since SDL_mixer 2.0.0. */ extern DECLSPEC Mix_Chunk * SDLCALL Mix_GetChunk(int channel); /** * Close the mixer, halting all playing audio. * * Any halted channels will have any currently-registered effects * deregistered, and will call any callback specified by Mix_ChannelFinished() * before this function returns. * * Any halted music will call any callback specified by * Mix_HookMusicFinished() before this function returns. * * Do not start any new audio playing during callbacks in this function. * * This will close the audio device. Attempting to play new audio after this * function returns will fail, until another successful call to * Mix_OpenAudio() or Mix_OpenAudioDevice(). * * Note that (unlike Mix_OpenAudio optionally calling SDL_Init(SDL_INIT_AUDIO) * on the app's behalf), this will _not_ deinitialize the SDL audio subsystem * in any case. At some point after calling this function and Mix_Quit(), some * part of the application should be responsible for calling SDL_Quit() to * deinitialize all of SDL, including its audio subsystem. * * This function should be the last thing you call in SDL_mixer before * Mix_Quit(). However, the following notes apply if you don't follow this * advice: * * Note that this will not free any loaded chunks or music; you should dispose * of those resources separately. It is probably poor form to dispose of them * _after_ this function, but it is safe to call Mix_FreeChunk() and * Mix_FreeMusic() after closing the device. * * Note that any chunks or music you don't free may or may not work if you * call Mix_OpenAudio again, as the audio device may be in a new format and * the existing chunks will not be converted to match. * * \since This function is available since SDL_mixer 2.0.0. * * \sa Mix_Quit */ extern DECLSPEC void SDLCALL Mix_CloseAudio(void); /* We'll use SDL for reporting errors */ /** * Report SDL_mixer errors * * \sa Mix_GetError */ #define Mix_SetError SDL_SetError /** * Get last SDL_mixer error * * \sa Mix_SetError */ #define Mix_GetError SDL_GetError /** * Clear last SDL_mixer error * * \sa Mix_SetError */ #define Mix_ClearError SDL_ClearError /** * Set OutOfMemory error */ #define Mix_OutOfMemory SDL_OutOfMemory /* Ends C function definitions when using C++ */ #ifdef __cplusplus } #endif #include "close_code.h" #endif /* SDL_MIXER_H_ */ SDL2_mixer-2.8.0/SDL2_mixerConfig.cmake.in0000644000076500000240000001031014551252471017077 0ustar valvestaff# sdl2_mixer cmake project-config input for CMakeLists.txt script include(FeatureSummary) set_package_properties(SDL2_mixer PROPERTIES URL "https://www.libsdl.org/projects/SDL_mixer/" DESCRIPTION "SDL_mixer is a sample multi-channel audio mixer library" ) set(SDL2_mixer_FOUND ON) set(SDL2MIXER_VENDORED @SDL2MIXER_VENDORED@) set(SDL2MIXER_CMD @SDL2MIXER_CMD@) set(SDL2MIXER_FLAC_LIBFLAC @SDL2MIXER_FLAC_LIBFLAC@) set(SDL2MIXER_FLAC_DRFLAC @SDL2MIXER_FLAC_DRFLAC@) set(SDL2MIXER_GME @SDL2MIXER_GME@) set(SDL2MIXER_MOD @SDL2MIXER_MOD@) set(SDL2MIXER_MOD_MODPLUG @SDL2MIXER_MOD_MODPLUG@) set(SDL2MIXER_MOD_XMP @SDL2MIXER_MOD_XMP@) set(SDL2MIXER_MOD_XMP_LITE @SDL2MIXER_MOD_XMP_LITE@) set(SDL2MIXER_MP3 @SDL2MIXER_MP3@) set(SDL2MIXER_MP3_MINIMP3 @SDL2MIXER_MP3_MINIMP3@) set(SDL2MIXER_MP3_MPG123 @SDL2MIXER_MP3_MPG123@) set(SDL2MIXER_MIDI @SDL2MIXER_MIDI@) set(SDL2MIXER_MIDI_FLUIDSYNTH @SDL2MIXER_MIDI_FLUIDSYNTH@) set(SDL2MIXER_MIDI_NATIVE @SDL2MIXER_MIDI_NATIVE@) set(SDL2MIXER_MIDI_TIMIDITY @SDL2MIXER_MIDI_TIMIDITY@) set(SDL2MIXER_OPUS @SDL2MIXER_OPUS@) set(SDL2MIXER_VORBIS @SDL2MIXER_VORBIS@) set(SDL2MIXER_VORBIS_STB @SDL2MIXER_VORBIS_STB@) set(SDL2MIXER_VORBIS_TREMOR @SDL2MIXER_VORBIS_TREMOR@) set(SDL2MIXER_VORBIS_VORBISFILE @SDL2MIXER_VORBIS_VORBISFILE@) set(SDL2MIXER_WAVE @SDL2MIXER_WAVE@) set(SDL2MIXER_WAVPACK @SDL2MIXER_WAVPACK@) set(SDL2MIXER_SDL2_REQUIRED_VERSION @SDL_REQUIRED_VERSION@) if(NOT SDL2MIXER_VENDORED) set(_sdl_cmake_module_path "${CMAKE_MODULE_PATH}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") endif() if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_mixer-shared-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/SDL2_mixer-shared-targets.cmake") endif() if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_mixer-static-targets.cmake") include(CMakeFindDependencyMacro) include(PkgConfigHelper) if(NOT DEFINED CMAKE_FIND_PACKAGE_PREFER_CONFIG) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) endif() if(SDL2MIXER_FLAC_LIBFLAC AND NOT SDL2MIXER_VENDORED AND NOT TARGET FLAC::FLAC) find_dependency(FLAC) endif() if(SDL2MIXER_GME AND NOT SDL2MIXER_VENDORED AND NOT TARGET gme::gme) find_dependency(gme) endif() if(SDL2MIXER_MOD_MODPLUG AND NOT SDL2MIXER_VENDORED AND NOT TARGET modplug::modplug) find_dependency(modplug) endif() if(SDL2MIXER_MOD_XMP AND NOT SDL2MIXER_VENDORED AND NOT TARGET libxmp::libxmp) find_dependency(libxmp) endif() if(SDL2MIXER_MOD_XMP_LITE AND NOT SDL2MIXER_VENDORED AND NOT TARGET libxmp-lite::libxmp-lite) find_dependency(libxmp-lite) endif() if(SDL2MIXER_MP3_MPG123 AND NOT SDL2MIXER_VENDORED AND NOT TARGET MPG123::mpg123) find_dependency(mpg123) endif() if(SDL2MIXER_MIDI_FLUIDSYNTH AND NOT SDL2MIXER_VENDORED AND NOT TARGET FluidSynth::libfluidsynth) find_dependency(FluidSynth) endif() if(SDL2MIXER_VORBIS_TREMOR AND NOT SDL2MIXER_VENDORED AND NOT TARGET tremor::tremor) find_dependency(tremor) endif() if(SDL2MIXER_VORBIS_VORBISFILE AND NOT SDL2MIXER_VENDORED AND NOT TARGET Vorbis::vorbisfile) find_dependency(Vorbis) endif() if(SDL2MIXER_OPUS AND NOT SDL2MIXER_VENDORED AND NOT TARGET OpusFile::opusfile) find_dependency(OpusFile) endif() if(SDL2MIXER_WAVPACK AND NOT SDL2MIXER_VENDORED AND NOT TARGET WavPack::WavPack) find_dependency(wavpack) endif() if((NOT SDL2MIXER_VENDORED AND SDL2MIXER_MOD_MODPLUG) OR (HAIKU AND SDL2MIXER_MIDI_NATIVE)) include(CheckLanguage) check_language(CXX) if(NOT CMAKE_CXX_COMPILER) message(WARNING "CXX language not enabled. Linking to SDL2_mixer::SDL2_mixer-static might fail.") endif() endif() include("${CMAKE_CURRENT_LIST_DIR}/SDL2_mixer-static-targets.cmake") endif() if(NOT SDL2MIXER_VENDORED) set(CMAKE_MODULE_PATH "${_sdl_cmake_module_path}") unset(_sdl_cmake_module_path) endif() SDL2_mixer-2.8.0/sdl2_mixer-config.cmake.in0000644000076500000240000001274514551252471017372 0ustar valvestaff# sdl2_mixer cmake project-config input for ./configure scripts include(FeatureSummary) set_package_properties(SDL2_mixer PROPERTIES URL "https://www.libsdl.org/projects/SDL_mixer/" DESCRIPTION "SDL_mixer is a sample multi-channel audio mixer library" ) set(SDL2_mixer_FOUND TRUE) set(SDL2MIXER_VENDORED 0) set(SDL2MIXER_CMD @SDL2MIXER_CMD@) set(SDL2MIXER_FLAC_LIBFLAC @SDL2MIXER_FLAC_LIBFLAC@) set(SDL2MIXER_FLAC_DRFLAC @SDL2MIXER_FLAC_DRFLAC@) if(SDL2MIXER_FLAC_LIBFLAC OR SDL2MIXER_FLAC_DRFLAC) set(SDL2MIXER_FLAC 1) else() set(SDL2MIXER_FLAC 0) endif() set(SDL2MIXER_GME @SDL2MIXER_GME@) set(SDL2MIXER_MOD_MODPLUG @SDL2MIXER_MOD_MODPLUG@) set(SDL2MIXER_MOD_XMP @SDL2MIXER_MOD_XMP@) set(SDL2MIXER_MOD_XMP_LITE @SDL2MIXER_MOD_XMP_LITE@) if(SDL2MIXER_MOD_MODPLUG OR SDL2MIXER_MOD_XMP OR SDL2MIXER_MOD_XMP_LITE) set(SDL2MIXER_MOD 1) else() set(SDL2MIXER_MOD 0) endif() set(SDL2MIXER_MP3_MINIMP3 @SDL2MIXER_MP3_MINIMP3@) set(SDL2MIXER_MP3_MPG123 @SDL2MIXER_MP3_MPG123@) if(SDL2MIXER_MP3_MINIMP3 OR SDL2MIXER_MP3_MPG123) set(SDL2MIXER_MP3 1) else() set(SDL2MIXER_MP3 0) endif() set(SDL2MIXER_MIDI_FLUIDSYNTH @SDL2MIXER_MIDI_FLUIDSYNTH@) set(SDL2MIXER_MIDI_NATIVE @SDL2MIXER_MIDI_NATIVE@) set(SDL2MIXER_MIDI_TIMIDITY @SDL2MIXER_MIDI_TIMIDITY@) if(SDL2MIXER_MIDI_FLUIDSYNTH OR SDL2MIXER_MIDI_NATIVE OR SDL2MIXER_MIDI_TIMIDITY) set(SDL2MIXER_MIDI 1) else() set(SDL2MIXER_MIDI 0) endif() set(SDL2MIXER_OPUS @SDL2MIXER_OPUS@) set(SDL2MIXER_VORBIS) set(SDL2MIXER_VORBIS_STB @SDL2MIXER_VORBIS_STB@) set(SDL2MIXER_VORBIS_VORBISFILE @SDL2MIXER_VORBIS_VORBISFILE@) set(SDL2MIXER_VORBIS_TREMOR @SDL2MIXER_VORBIS_TREMOR@) if(SDL2MIXER_VORBIS_STB) set(SDL2MIXER_VORBIS STB) endif() if(SDL2MIXER_VORBIS_VORBISFILE) set(SDL2MIXER_VORBIS VORBISFILE) endif() if(SDL2MIXER_VORBIS_TREMOR) set(SDL2MIXER_VORBIS TREMOR) endif() set(SDL2MIXER_WAVE @SDL2MIXER_WAVE@) set(SDL2MIXER_WAVPACK @SDL2MIXER_WAVPACK@) set(SDL2MIXER_SDL2_REQUIRED_VERSION @SDL_VERSION@) get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_DIR} REALPATH) get_filename_component(prefix "${CMAKE_CURRENT_LIST_DIR}/@cmake_prefix_relpath@" ABSOLUTE) set(exec_prefix "@exec_prefix@") set(bindir "@bindir@") set(includedir "@includedir@") set(libdir "@libdir@") set(_sdl2mixer_extra_static_libraries "@MIXER_LIBS@ @PC_LIBS@") string(STRIP "${_sdl2mixer_extra_static_libraries}" _sdl2mixer_extra_static_libraries) set(_sdl2mixer_bindir "${bindir}") set(_sdl2mixer_libdir "${libdir}") set(_sdl2mixer_incdir "${includedir}/SDL2") # Convert _sdl2mixer_extra_static_libraries to list and keep only libraries string(REGEX MATCHALL "(-[lm]([-a-zA-Z0-9._]+))|(-Wl,[^ ]*framework[^ ]*)" _sdl2mixer_extra_static_libraries "${_sdl2mixer_extra_static_libraries}") string(REGEX REPLACE "^-l" "" _sdl2mixer_extra_static_libraries "${_sdl2mixer_extra_static_libraries}") string(REGEX REPLACE ";-l" ";" _sdl2mixer_extra_static_libraries "${_sdl2mixer_extra_static_libraries}") unset(prefix) unset(exec_prefix) unset(bindir) unset(includedir) unset(libdir) include(CMakeFindDependencyMacro) if(NOT TARGET SDL2_mixer::SDL2_mixer) if(WIN32) set(_sdl2mixer_dll "${_sdl2mixer_bindir}/SDL2_mixer.dll") set(_sdl2mixer_imp "${_sdl2mixer_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_mixer.dll${CMAKE_STATIC_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2mixer_dll}" AND EXISTS "${_sdl2mixer_imp}") add_library(SDL2_mixer::SDL2_mixer SHARED IMPORTED) set_target_properties(SDL2_mixer::SDL2_mixer PROPERTIES IMPORTED_LOCATION "${_sdl2mixer_dll}" IMPORTED_IMPLIB "${_sdl2mixer_imp}" ) endif() unset(_sdl2mixer_dll) unset(_sdl2mixer_imp) else() set(_sdl2mixer_shl "${_sdl2mixer_libdir}/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2_mixer${CMAKE_SHARED_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2mixer_shl}") add_library(SDL2_mixer::SDL2_mixer SHARED IMPORTED) set_target_properties(SDL2_mixer::SDL2_mixer PROPERTIES IMPORTED_LOCATION "${_sdl2mixer_shl}" ) endif() endif() if(TARGET SDL2_mixer::SDL2_mixer) set_target_properties(SDL2_mixer::SDL2_mixer PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2mixer_incdir}" COMPATIBLE_INTERFACE_BOOL "SDL2_SHARED" INTERFACE_SDL2_SHARED "ON" ) endif() endif() if(NOT TARGET SDL2_mixer::SDL2_mixer-static) set(_sdl2mixer_stl "${_sdl2mixer_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_mixer${CMAKE_STATIC_LIBRARY_SUFFIX}") if(EXISTS "${_sdl2mixer_stl}") add_library(SDL2_mixer::SDL2_mixer-static STATIC IMPORTED) set_target_properties(SDL2_mixer::SDL2_mixer-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2mixer_incdir}" IMPORTED_LOCATION "${_sdl2mixer_stl}" INTERFACE_LINK_LIBRARIES "${_sdl2mixer_extra_static_libraries}" ) endif() unset(_sdl2mixer_stl) endif() unset(_sdl2mixer_extra_static_libraries) unset(_sdl2mixer_bindir) unset(_sdl2mixer_libdir) unset(_sdl2mixer_incdir) SDL2_mixer-2.8.0/playmus.c0000644000076500000240000002165714551252471014310 0ustar valvestaff/* PLAYMUS: A test application for the SDL mixer library. Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* Quiet windows compiler warnings */ #define _CRT_SECURE_NO_WARNINGS #include "SDL_stdinc.h" #include #include #include #ifdef unix #include #endif #include "SDL.h" #include "SDL_mixer.h" #ifdef HAVE_SIGNAL_H #include #endif static int audio_open = 0; static Mix_Music *music = NULL; static int next_track = 0; void CleanUp(int exitcode) { if(Mix_PlayingMusic()) { Mix_FadeOutMusic(1500); SDL_Delay(1500); } if (music) { Mix_FreeMusic(music); music = NULL; } if (audio_open) { Mix_CloseAudio(); audio_open = 0; } SDL_Quit(); exit(exitcode); } void Usage(char *argv0) { SDL_Log("Usage: %s [-i] [-l] [-8] [-f32] [-r rate] [-c channels] [-b buffers] [-v N] [-rwops] \n", argv0); } /*#define SEEK_TEST */ void Menu(void) { char buf[10]; printf("Available commands: (p)ause (r)esume (h)alt volume(v#) > "); fflush(stdin); if (scanf("%s",buf) == 1) { switch(buf[0]) { #if defined(SEEK_TEST) case '0': Mix_SetMusicPosition(0); break; case '1': Mix_SetMusicPosition(10);break; case '2': Mix_SetMusicPosition(20);break; case '3': Mix_SetMusicPosition(30);break; case '4': Mix_SetMusicPosition(40);break; #endif /* SEEK_TEST */ case 'p': case 'P': Mix_PauseMusic(); break; case 'r': case 'R': Mix_ResumeMusic(); break; case 'h': case 'H': Mix_HaltMusic(); break; case 'v': case 'V': Mix_VolumeMusic(atoi(buf+1)); break; } } printf("Music playing: %s Paused: %s\n", Mix_PlayingMusic() ? "yes" : "no", Mix_PausedMusic() ? "yes" : "no"); } #ifdef HAVE_SIGNAL_H void IntHandler(int sig) { switch (sig) { case SIGINT: next_track++; break; } } #endif int main(int argc, char *argv[]) { int audio_rate; Uint16 audio_format; int audio_channels; int audio_buffers; int audio_volume = MIX_MAX_VOLUME; int looping = 0; int interactive = 0; int rwops = 0; int i; const char *typ; const char *tag_title = NULL; const char *tag_artist = NULL; const char *tag_album = NULL; const char *tag_copyright = NULL; double loop_start, loop_end, loop_length, current_position; (void) argc; /* Initialize variables */ audio_rate = MIX_DEFAULT_FREQUENCY; audio_format = MIX_DEFAULT_FORMAT; audio_channels = MIX_DEFAULT_CHANNELS; audio_buffers = 4096; /* Check command line usage */ for (i = 1; argv[i] && (*argv[i] == '-'); ++i) { if ((strcmp(argv[i], "-r") == 0) && argv[i+1]) { ++i; audio_rate = atoi(argv[i]); } else if (strcmp(argv[i], "-m") == 0) { audio_channels = 1; } else if ((strcmp(argv[i], "-c") == 0) && argv[i+1]) { ++i; audio_channels = atoi(argv[i]); } else if ((strcmp(argv[i], "-b") == 0) && argv[i+1]) { ++i; audio_buffers = atoi(argv[i]); } else if ((strcmp(argv[i], "-v") == 0) && argv[i+1]) { ++i; audio_volume = atoi(argv[i]); } else if (strcmp(argv[i], "-l") == 0) { looping = -1; } else if (strcmp(argv[i], "-i") == 0) { interactive = 1; } else if (strcmp(argv[i], "-8") == 0) { audio_format = AUDIO_U8; } else if (strcmp(argv[i], "-f32") == 0) { audio_format = AUDIO_F32; } else if (strcmp(argv[i], "-rwops") == 0) { rwops = 1; } else { Usage(argv[0]); return 1; } } if (!argv[i]) { Usage(argv[0]); return 1; } /* Initialize the SDL library */ if (SDL_Init(SDL_INIT_AUDIO) < 0) { SDL_Log("Couldn't initialize SDL: %s\n",SDL_GetError()); return 255; } #ifdef HAVE_SIGNAL_H signal(SIGINT, IntHandler); signal(SIGTERM, CleanUp); #endif /* Open the audio device */ if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) { SDL_Log("Couldn't open audio: %s\n", SDL_GetError()); return 2; } else { Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); SDL_Log("Opened audio at %d Hz %d bit%s %s %d bytes audio buffer\n", audio_rate, (audio_format&0xFF), (SDL_AUDIO_ISFLOAT(audio_format) ? " (float)" : ""), (audio_channels > 2) ? "surround" : (audio_channels > 1) ? "stereo" : "mono", audio_buffers); } audio_open = 1; /* Set the music volume */ Mix_VolumeMusic(audio_volume); /* Set the external music player, if any */ Mix_SetMusicCMD(SDL_getenv("MUSIC_CMD")); while (argv[i]) { next_track = 0; /* Load the requested music file */ if (rwops) { music = Mix_LoadMUS_RW(SDL_RWFromFile(argv[i], "rb"), SDL_TRUE); } else { music = Mix_LoadMUS(argv[i]); } if (music == NULL) { SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError()); CleanUp(2); } switch (Mix_GetMusicType(music)) { case MUS_CMD: typ = "CMD"; break; case MUS_WAV: typ = "WAV"; break; case MUS_MOD: case MUS_MODPLUG_UNUSED: typ = "MOD"; break; case MUS_FLAC: typ = "FLAC"; break; case MUS_MID: typ = "MIDI"; break; case MUS_OGG: typ = "OGG Vorbis"; break; case MUS_MP3: case MUS_MP3_MAD_UNUSED: typ = "MP3"; break; case MUS_OPUS: typ = "OPUS"; break; case MUS_WAVPACK: typ = "WavPack"; break; case MUS_NONE: default: typ = "NONE"; break; } SDL_Log("Detected music type: %s", typ); tag_title = Mix_GetMusicTitleTag(music); if (tag_title && SDL_strlen(tag_title) > 0) { SDL_Log("Title: %s", tag_title); } tag_artist = Mix_GetMusicArtistTag(music); if (tag_artist && SDL_strlen(tag_artist) > 0) { SDL_Log("Artist: %s", tag_artist); } tag_album = Mix_GetMusicAlbumTag(music); if (tag_album && SDL_strlen(tag_album) > 0) { SDL_Log("Album: %s", tag_album); } tag_copyright = Mix_GetMusicCopyrightTag(music); if (tag_copyright && SDL_strlen(tag_copyright) > 0) { SDL_Log("Copyright: %s", tag_copyright); } loop_start = Mix_GetMusicLoopStartTime(music); loop_end = Mix_GetMusicLoopEndTime(music); loop_length = Mix_GetMusicLoopLengthTime(music); /* Play and then exit */ SDL_Log("Playing %s, duration %f\n", argv[i], Mix_MusicDuration(music)); if (loop_start > 0.0 && loop_end > 0.0 && loop_length > 0.0) { SDL_Log("Loop points: start %g s, end %g s, length %g s\n", loop_start, loop_end, loop_length); } Mix_FadeInMusic(music,looping,2000); while (!next_track && (Mix_PlayingMusic() || Mix_PausedMusic())) { if (interactive) { Menu(); } else { current_position = Mix_GetMusicPosition(music); if (current_position >= 0.0) { printf("Position: %g seconds \r", current_position); fflush(stdout); } SDL_Delay(100); } } Mix_FreeMusic(music); music = NULL; /* If the user presses Ctrl-C more than once, exit. */ SDL_Delay(500); if (next_track > 1) { break; } i++; } CleanUp(0); /* Not reached, but fixes compiler warnings */ return 0; } /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/.gitmodules0000644000076500000240000000227414551252471014621 0ustar valvestaff[submodule "external/flac"] path = external/flac url = https://github.com/libsdl-org/flac.git branch = 1.3.4-SDL [submodule "external/ogg"] path = external/ogg url = https://github.com/libsdl-org/ogg.git branch = v1.3.5-SDL [submodule "external/vorbis"] path = external/vorbis url = https://github.com/libsdl-org/vorbis.git branch = v1.3.7-SDL [submodule "external/opus"] path = external/opus url = https://github.com/libsdl-org/opus.git branch = v1.4-SDL [submodule "external/opusfile"] path = external/opusfile url = https://github.com/libsdl-org/opusfile.git branch = v0.12-SDL [submodule "external/tremor"] path = external/tremor url = https://github.com/libsdl-org/tremor.git branch = v1.2.1-SDL [submodule "external/mpg123"] path = external/mpg123 url = https://github.com/libsdl-org/mpg123.git branch = v1.31.3-SDL [submodule "libxmp"] path = external/libxmp url = https://github.com/libsdl-org/libxmp.git branch = 4.6.x-SDL [submodule "external/wavpack"] path = external/wavpack url = https://github.com/libsdl-org/wavpack.git branch = 5.6.0-sdl [submodule "external/libgme"] path = external/libgme url = https://github.com/libsdl-org/game-music-emu.git branch = v0.6.3-SDL SDL2_mixer-2.8.0/mingw/0000755000076500000240000000000014553251273013561 5ustar valvestaffSDL2_mixer-2.8.0/mingw/pkg-support/0000755000076500000240000000000014277744147016066 5ustar valvestaffSDL2_mixer-2.8.0/mingw/pkg-support/cmake/0000755000076500000240000000000014277744147017146 5ustar valvestaffSDL2_mixer-2.8.0/mingw/pkg-support/cmake/sdl2_mixer-config-version.cmake0000644000076500000240000000147014277744147025150 0ustar valvestaff# SDL2_mixer CMake version configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_mixer-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_mixer_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_mixer/sdl2_mixer-config-version.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_mixer_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_mixer/sdl2_mixer-config-version.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() if(NOT EXISTS "${sdl2_mixer_config_path}") message(WARNING "${sdl2_mixer_config_path} does not exist: MinGW development package is corrupted") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() include("${sdl2_mixer_config_path}") SDL2_mixer-2.8.0/mingw/pkg-support/cmake/sdl2_mixer-config.cmake0000644000076500000240000000157614277744147023474 0ustar valvestaff# SDL2_mixer CMake configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_mixer-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_mixer_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_mixer/sdl2_mixer-config.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_mixer_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_mixer/sdl2_mixer-config.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(SDL2_mixer_FOUND FALSE) return() endif() if(NOT EXISTS "${sdl2_mixer_config_path}") message(WARNING "${sdl2_mixer_config_path} does not exist: MinGW development package is corrupted") set(SDL2_mixer_FOUND FALSE) return() endif() include("${sdl2_mixer_config_path}") # The SDL_mixer MinGW development package ships with vendored libraries set(SDL2MIXER_VENDORED 1) SDL2_mixer-2.8.0/Makefile.os20000644000076500000240000001300414553225265014602 0ustar valvestaff# Open Watcom makefile to build SDL2mix.dll for OS/2 # wmake -f Makefile.os2 # # Remember to edit DEPS_INC and DEPS_LIB below to meet # your own environment!. LIBNAME = SDL2mix MAJOR_VERSION = 2 MINOR_VERSION = 8 MICRO_VERSION = 0 VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION) TITLENAME = $(LIBNAME) $(VERSION) # change DEPS_INC in order to point to the dependency headers. DEPS_INC=-IC:\SDL2DEV\h\SDL2 -IC:\SDL2DEV\h # change DEPS_LIB in order to point to the dependency libraries. DEPS_LIB=C:\SDL2DEV\lib # wav music support USE_WAV=yes # ogg/vorbis music support USE_OGG=yes # use integer-only Tremor (libvorbisidec) instead of libvorbis? USE_TREMOR=no # use stb_vorbis instead of libvorbis? USE_STBVORBIS=no # flac music support (using libflac) USE_FLAC=yes # flac music support (using dr_flac) USE_DRFLAC=no # opus music support USE_OPUS=yes # mp3 music support (using mpg123) USE_MPG123=yes # mp3 music support (using minimp3) USE_MINIMP3=no # wavpack music support USE_WAVPACK=yes # wavpack DSD music support USE_WAVPACK_DSD= no # midi music support (using timidity) USE_TIMIDITY=yes # midi music support (using fluidsynth) USE_FLUIDSYNTH=no # tracker music support (using libxmp) USE_XMP=yes # tracker music support (using libmodplug) USE_MODPLUG=no # game-music-emu support (using libgme) USE_GME=no LIBFILE = $(LIBNAME).lib DLLFILE = $(LIBNAME).dll LNKFILE = $(LIBNAME).lnk TIMILIB = timidity.lib SRCS = utils.c effect_position.c effects_internal.c effect_stereoreverse.c mixer.c music.c # codec sources: SRCS+= load_aiff.c load_voc.c music_wav.c & music_ogg.c music_ogg_stb.c music_opus.c & music_flac.c music_drflac.c music_wavpack.c & mp3utils.c music_mpg123.c music_minimp3.c & music_xmp.c music_modplug.c music_gme.c & music_fluidsynth.c music_timidity.c # timidity sources: TIMISRCS = common.c instrum.c mix.c output.c playmidi.c readmidi.c resample.c tables.c timidity.c OBJS = $(SRCS:.c=.obj) TIMIOBJS = $(TIMISRCS:.c=.obj) LIBS = SDL2.lib CFLAGS_BASE = -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oeatxh -ei -j # warnings: CFLAGS_BASE+= -wx # newer OpenWatcom versions enable W303 by default CFLAGS_BASE+= -wcd=303 # include paths: CFLAGS_BASE+= -Iinclude -Isrc -I"src/codecs" -I"src/codecs/timidity" $(DEPS_INC) CFLAGS_BASE+= -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h" CFLAGS = $(CFLAGS_BASE) # to build a dll: CFLAGS+= -bd # for DECLSPEC: CFLAGS+= -DBUILD_SDL !ifeq USE_TIMIDITY yes CFLAGS+= -DMUSIC_MID_TIMIDITY LIBS+= $(TIMILIB) !endif !ifeq USE_FLUIDSYNTH yes CFLAGS+= -DMUSIC_MID_FLUIDSYNTH LIBS+= fluidsyn.lib !endif !ifeq USE_WAV yes CFLAGS+= -DMUSIC_WAV !endif !ifeq USE_TREMOR yes VORBIS_LIBS=vorbisidec.lib !else VORBIS_LIBS=vorbisfile.lib vorbis.lib !endif !ifeq USE_OGG yes CFLAGS+= -DMUSIC_OGG !ifeq USE_STBVORBIS yes CFLAGS+= -DOGG_USE_STB !else !ifeq USE_TREMOR yes CFLAGS+= -DOGG_USE_TREMOR !endif LIBS+= $(VORBIS_LIBS) NEED_LIBOGG=yes !endif !endif !ifeq USE_FLAC yes CFLAGS+= -DMUSIC_FLAC_LIBFLAC LIBS+= FLAC.lib !endif !ifeq USE_DRFLAC yes CFLAGS+= -DMUSIC_FLAC_DRFLAC !endif !ifeq USE_WAVPACK yes CFLAGS+= -DMUSIC_WAVPACK !ifeq USE_WAVPACK_DSD yes CFLAGS+= -DMUSIC_WAVPACK_DSD !endif LIBS+= wavpack.lib !endif !ifeq USE_OPUS yes CFLAGS+= -DMUSIC_OPUS LIBS+= opusfile.lib opus.lib NEED_LIBOGG=yes !endif !ifeq USE_MPG123 yes CFLAGS+= -DMUSIC_MP3_MPG123 LIBS+= mpg123.lib !endif !ifeq USE_MINIMP3 yes CFLAGS+= -DMUSIC_MP3_MINIMP3 !endif !ifeq USE_XMP yes CFLAGS+= -DMUSIC_MOD_XMP LIBS+= libxmp.lib !endif !ifeq USE_MODPLUG yes CFLAGS+= -DMUSIC_MOD_MODPLUG LIBS+= modplug.lib !endif !ifeq USE_GME yes CFLAGS+= -DMUSIC_GME LIBS+= gme.lib !endif !ifeq NEED_LIBOGG yes LIBS+= ogg.lib !endif # For the static assertions in mixer.c CFLAGS+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION) CFLAGS+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION) CFLAGS+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION) all: $(DLLFILE) playwave.exe playmus.exe $(LIBFILE): $(DLLFILE) @echo * Create library: $@... wlib -b -n -q -c -pa -s -t -zld -ii -io $@ $(DLLFILE) $(DLLFILE): $(OBJS) $(TIMILIB) $(LNKFILE) @echo * Link: $@ wlink @$(LNKFILE) $(LNKFILE): @%create $@ @%append $@ SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE @%append $@ NAME $(LIBNAME) @for %i in ($(OBJS)) do @%append $@ FILE %i @%append $@ OPTION QUIET @%append $@ OPTION DESCRIPTION '@$#libsdl org:$(VERSION)$#@Simple DirectMedia Layer Mixer Library' @%append $@ LIBPATH $(DEPS_LIB) @for %i in ($(LIBS)) do @%append $@ LIB %i @%append $@ OPTION MAP=$* @%append $@ OPTION ELIMINATE @%append $@ OPTION MANYAUTODATA @%append $@ OPTION OSNAME='OS/2 and eComStation' @%append $@ OPTION SHOWDEAD .c: ./src;./src/codecs; .c.obj: wcc386 $(CFLAGS) -fo=$^@ $< music_minimp3.obj: music_minimp3.c wcc386 $(CFLAGS) -za99 -fo=$^@ $< playmus.obj: playmus.c wcc386 $(CFLAGS_BASE) -fo=$^@ $< playwave.obj: playwave.c wcc386 $(CFLAGS_BASE) -fo=$^@ $< playmus.exe: $(LIBFILE) playmus.obj wlink SYS os2v2 OP q LIBPATH $(DEPS_LIB) LIBR {$(LIBFILE) SDL2.lib} F {playmus.obj} N playmus.exe playwave.exe: $(LIBFILE) playwave.obj wlink SYS os2v2 OP q LIBPATH $(DEPS_LIB) LIBR {$(LIBFILE) SDL2.lib} F {playwave.obj} N playwave.exe .c: ./src/codecs/timidity; timidity.lib: $(TIMIOBJS) wlib -b -n -q -c -pa -s -t -zld -ii -io $@ $(TIMIOBJS) clean: .SYMBOLIC @echo * Clean: $(TITLENAME) @if exist *.obj rm *.obj @if exist *.err rm *.err @if exist $(TIMILIB) rm $(TIMILIB) @if exist $(LNKFILE) rm $(LNKFILE) distclean: .SYMBOLIC clean @if exist $(DLLFILE) rm $(DLLFILE) @if exist $(LIBFILE) rm $(LIBFILE) @if exist *.map rm *.map @if exist *.exe rm *.exe SDL2_mixer-2.8.0/CHANGES.txt0000644000076500000240000002776214551252471014266 0ustar valvestaff2.8.0: * Added support for loading wavpack sound files (https://www.wavpack.com/) * Added support for loading classic console sound files using Game_Music_Emu (https://github.com/libgme/game-music-emu) * Use minimp3 instead of dr_mp3 as the default backend for MP3 music * Use libxmp instead of modplug as the default backend for MOD music To use libmodplug instead, configure using --enable-music-mod-modplug --disable-music-mod-xmp * Added support for FLAC audio in Ogg containers * Added Mix_PauseAudio() to pause and resume all audio playback * Added Mix_GetNumTracks() and Mix_StartTrack() for managing tracks in GME files 2.6.2: * Updated autotools to use ax_compute_relative_paths, fixing homebrew on macOS 2.6.1: * Fixed issue with incorrect version reported by pkg-config 2.6.0: * Added support for building with CMake * Added support for playing Ogg files using stb_vorbis, which is now the default Vorbis backend. To use libvorbis instead, configure using --disable-music-ogg-stb --enable-music-ogg-vorbis * Added support for playing FLAC files using dr_flac, which is now the default FLAC music backend. To use libflac instead, configure using --disable-music-flac-drflac --enable-music-flac-libflac * Added support for playing MP3 files using dr_mp3, which is now the default MP3 music backend. To use libmpg123 instead, configure using --disable-music-mp3-drmp3 --enable-music-mp3-mpg123 * Added libxmp support for mod music playback. Modplug is still the default backend for MOD music. To use libxmp instead, configure using --disable-music-mod-modplug --enable-music-mod-xmp * Removed support for libmad as a MP3 music backend. * Removed support for libmikmod as a MOD music backend. * Added Mix_MasterVolume() for additional volume control over all channels * Update Mix_Init() return value to match documentation, including MIXER_INIT_* flags for already-initialized modules * Added Mix_HasMusicDecoder() * Memory leak fixes and F32 format support to fluidsynth player * Fixed distorted MIDI playback with FluidSynth if sample rate is out of library's limits * Added Mix_ModMusicJumpToOrder() for mod music formats * Enabled module internal loops in modplug player * Respect original mp3 file offset * Support setting soundfont via SDL_SOUNDFONTS in OSX native midi * Fixed mp3 file detection * Fixes to ogg playback on big-endian devices * Added functions to get metadata information: Mix_GetMusicTitle() Mix_GetMusicTitleTag() Mix_GetMusicArtistTag() Mix_GetMusicAlbumTag() Mix_GetMusicCopyrightTag() * Add functions to get loop point information: Mix_GetMusicLoopStartTime() Mix_GetMusicLoopEndTime() Mix_GetMusicLoopLengthTime() * Added Mix_GetMusicVolume() and Mix_GetMusicPosition() * Added Mix_MusicDuration() to return music duration in seconds * Fixed music_mpg123 seek bug when sample rate of the file and the stream don't match * Timidity improvements, added Mix_SetTimidityCfg() * Improved mp3 tag detection/skipping * Extended support for WAV files * Allow rmid files be opened by native midi * Fixed possible crash in win32 native midi * Prevent clipping due to volume settings in modplug music * Added looping support for Opus files * Added looping support for FLAC files * Improved OGG looping support * Fixed loading Opus audio as audio chunks 2.0.4: Ozkan Sezer - Wed, 10 Oct 2018 14:56:10 * Removed smpeg support for mp3 music, now that it's replaced by libmpg123 Ozkan Sezer - Sun, 07 Oct 2018 08:50:02 * Fixed mp3 mad decoder to skip tags, which otherwise would lead to crashes Ozkan Sezer - Fri, 15 Jun 2018 05:32:56 * Added support for Opus music playback using opusfile library 2.0.3: Sam Lantinga - Thu, Mar 1, 2018 9:06:58 AM * Fixed regression where Mix_Init() would return 0 for available music formats 2.0.2: Sam Lantinga - Fri Oct 20 22:04:50 PDT 2017 * Implemented 24-bit and surround sound support for FLAC audio files Sam Lantinga - Thu Oct 12 21:32:44 PDT 2017 * Updated external libraries flac-1.3.2, libmodplug-0.8.9.0, libogg-1.3.2 and libvorbis-1.3.5 Ryan Gordon - Thu Oct 12 21:29:59 PDT 2017 * Updated for SDL 2.0.6 and newer Franc[e]sco - Thu Jul 20 22:03:19 2017 +0200 * Added support for MP3 playback using mpg123 David Ludwig - Sun Apr 10 22:35:38 2016 * Added support for UWP / Windows 10 apps Juha Kuikka - Fri Jan 29 12:44:01 PST 2016 * Added Mix_OpenAudioDevice() so you can specify the audio device to open 2.0.1: Sam Lantinga - Tue Jul 7 11:40:33 PDT 2015 * Added support for 'smpl' format loop points in music WAV files Sam Lantinga - Sat Aug 23 10:57:26 2014 * Fixed floating point exception in Mix_Volume() David Ludwig - Mon Apr 14 22:15:36 2014 * Added support for building for Windows RT and Windows Phone Isaac Burns - Sun Sep 15 21:50:27 PDT 2013 * Added support for loading MP3 files as sound chunks 2.0.0: Sam Lantinga - Sun Jun 9 14:45:30 PDT 2013 * Made libmodplug the default MOD player as it is now in the public domain Sam Lantinga - Sat Jun 1 19:11:08 PDT 2013 * Updated for SDL 2.0 release * SDL_LoadMUS_RW() now takes an argument telling whether or not the data source should be freed when done. 1.2.13: Paul P Komkoff Jr - Sun Jul 22 16:12:28 PDT 2012 * Fixed malloc/free mismatch in the MikMod driver 1.2.12: Sam Lantinga - Sat Jan 14 22:00:29 2012 -0500 * Fixed seek offset with SMPEG (was relative, should be absolute) Sam Lantinga - Fri Jan 13 03:04:27 EST 2012 * Fixed memory crash loading Ogg Vorbis files on Windows Sam Lantinga - Thu Jan 05 22:51:54 2012 -0500 * Added an Xcode project for iOS Nikos Chantziaras - 2012-01-02 17:37:36 PST * Added Mix_LoadMUSType_RW() so you can tell SDL_mixer what type the music is Sam Lantinga - Sun Jan 01 16:45:58 2012 -0500 * Fixed looping native MIDI on Mac OS X and Windows Sam Lantinga - Sun Jan 01 01:00:51 2012 -0500 * Added /usr/local/share/timidity to the timidity data path Sam Lantinga - Sat Dec 31 21:26:46 2011 -0500 * Fixed timidity loading of some MIDI files Sam Lantinga - Sat Dec 31 19:11:59 EST 2011 * Fixed dropping audio in the FLAC audio decoding Sam Lantinga - Sat Dec 31 18:32:05 EST 2011 * Fixed memory leak in SDL_LoadMUS() Sam Lantinga - Sat Dec 31 10:22:05 EST 2011 * Removed GPL native MIDI code for new licensing Sam Lantinga - Sat Dec 31 10:22:05 EST 2011 * SDL_mixer is now under the zlib license Manuel Montezelo - 2011-12-28 11:42:44 PST * Fixed drums playing on MIDI channel 16 with timidity Ryan C. Gordon - Wed Jun 15 03:41:31 2011 -0400 * The music-finished hook can start a track immediately James Le Cuirot - Mon Mar 21 16:54:11 PDT 2011 * Added support for FluidSynth Egor Suvorov - Tue Jan 18 11:06:47 PST 2011 * Added support for native MIDI on Haiku Sam Lantinga - Tue Jan 11 01:29:19 2011 -0800 * Added Android.mk to build on the Android platform Jon Atkins - Sat Nov 14 13:00:18 PST 2009 * Added support for libmodplug (disabled by default) 1.2.11: Sam Lantinga - Sat Nov 14 12:38:01 PST 2009 * Fixed initialization error and crashes if MikMod library isn't available Sam Lantinga - Sat Nov 14 11:22:14 PST 2009 * Fixed bug loading multiple music files 1.2.10: Sam Lantinga - Sun Nov 8 08:34:48 PST 2009 * Added Mix_Init()/Mix_Quit() to prevent constantly loading and unloading DLLs Mike Frysinger - 2009-11-05 09:11:43 PST * Check for fork/vfork on any platform, don't just assume it on UNIX Jon Atkins - Thu Nov 5 00:02:50 2009 UTC * Fixed export of Mix_GetNumChunkDecoders() and Mix_GetNumMusicDecoders() C.W. Betts - 2009-11-02 00:16:21 PST * Use newer MIDI API on Mac OS X 10.5+ 1.2.9: Ryan Gordon - Sun Oct 18 11:42:31 PDT 2009 * Updated native MIDI support on Mac OS X for 10.6 Ryan Gordon - Sun Oct 11 05:29:55 2009 UTC * Reset channel volumes after a fade out interrupts a fade in. Ryan Gordon - Sun Oct 11 02:59:12 2009 UTC * Fixed crash race condition with position audio functions Ryan Gordon - Sat Oct 10 17:05:45 2009 UTC * Fixed stereo panning in 8-bit mode Sam Lantinga - Sat Oct 10 11:07:15 2009 UTC * Added /usr/share/timidity to the default timidity.cfg locations Sam Lantinga - Sat Oct 3 13:33:36 PDT 2009 * MOD support uses libmikmod and is dynamically loaded by default * A patched version of libmikmod is included in libmikmod-3.1.12.zip * The libmikmod patches fix security issues CVE-2007-6720 and CVE-2009-0179. Sam Lantinga - Sat Oct 3 02:49:41 PDT 2009 * Added TIMIDITY_CFG environment variable to fully locate timidity.cfg Sam Lantinga - Fri Oct 2 07:15:35 PDT 2009 * Implemented seamless looping for music playback Forrest Voight - 2009-06-13 20:31:38 PDT * ID3 files are now recognized as MP3 format Steven Noonan - 2008-05-13 13:31:36 PDT * Fixed native MIDI crash on 64-bit Windows Ryan Gordon - Fri Jun 5 16:07:08 2009 UTC * Added decoder enumeration API: Mix_GetNumChunkDecoders(), Mix_GetChunkDecoder(), Mix_GetNumMusicDecoders(), Mix_GetMusicDecoder() Austen Dicken - Tue Feb 26 23:28:27 PST 2008 * Added support for FLAC audio both as chunks and streaming Tilman Sauerbeck - Tue Feb 26 03:44:47 PST 2008 * Added support for streaming WAV files with Mix_LoadMUS_RW() Ryan Gordon - Mon Feb 4 17:10:08 UTC 2008 * Fixed crash caused by not resetting position_channels 1.2.8: Sam Lantinga - Wed Jul 18 09:45:54 PDT 2007 * Improved detection of Ogg Vorbis and Tremor libraries Ryan Gordon - Sun Jul 15 12:03:54 EDT 2007 * Fixed memory leaks in Effects API. David Rose - Sat Jul 14 22:16:09 PDT 2007 * Added support for MP3 playback with libmad (for GPL projects only!) Sam Lantinga - Sat Jul 14 21:39:30 PDT 2007 * Fixed the final loop of audio samples of a certain size Sam Lantinga - Sat Jul 14 21:05:09 PDT 2007 * Fixed opening Ogg Vorbis files using different C runtimes on Windows Philippe Simons - Sat Jul 14 20:33:17 PDT 2007 * Added support for Ogg Vorbis playback with Tremor (an integer decoder) Sam Lantinga - Sat Jul 14 07:02:09 PDT 2007 * Fixed memory corruption in timidity resampling code Ryan Gordon - Tue Jul 3 10:44:29 2007 UTC * Fixed building SDL_mixer with SDL 1.3 pre-release Ryan Gordon - Tue Feb 13 08:11:54 2007 UTC * Fixed compiling both timidity and native midi in the same build Hans de Goede - Sun Aug 20 23:25:46 2006 UTC * Added volume control to playmus Jonathan Atkins - Thu Aug 10 15:06:40 2006 UTC * Fixed linking with system libmikmod David Ergo - Fri Jun 23 09:07:19 2006 UTC * Corrected no-op conditions in SetDistance(), SetPanning() and SetPosition() * Fixed copy/paste errors in channel amplitudes 1.2.7: Sam Lantinga - Fri May 12 00:04:32 PDT 2006 * Added support for dynamically loading SMPEG library Sam Lantinga - Thu May 11 22:22:43 PDT 2006 * Added support for dynamically loading Ogg Vorbis library Sam Lantinga - Sun Apr 30 09:01:44 PDT 2006 * Removed automake dependency, to allow Universal binaries on Mac OS X * Added gcc-fat.sh for generating Universal binaries on Mac OS X Sam Lantinga - Sun Apr 30 01:48:40 PDT 2006 * Updated libtool support to version 1.5.22 Patrice Mandin - Sat Jul 16 16:43:24 UTC 2005 * Use SDL_RWops also for native midi mac and win32 Patrice Mandin - Sat Jul 9 14:40:09 UTC 2005 * Use SDL_RWops also for native midi gpl (todo: mac and win32) Ryan C. Gordon - Sat Jul 9 01:54:03 EDT 2005 * Tweaked Mix_Chunk's definition to make predeclaration easier. Patrice Mandin - Mon Jul 4 19:45:40 UTC 2005 * Search timidity.cfg also in /etc * Fix memory leaks in timidity player * Use also SDL_RWops to read midifiles for timidity Ryan C. Gordon - Mon Jun 13 18:18:12 EDT 2005 * Patch from Eric Wing to fix native midi compiling on MacOS/x86. Sam Lantinga - Wed Dec 22 17:14:32 PST 2004 * Disabled support for the system version of libmikmod by default Sam Lantinga - Tue Dec 21 09:51:29 PST 2004 * Fixed building mikmod support on UNIX * Always build SDL_RWops music support * Added SDL_RWops support for reading MP3 files 1.2.6: Jonathan Atkins - Wed, 15 Sep 2004 23:26:42 -0500 * Added support for using the system version of libmikmod SDL2_mixer-2.8.0/SDL2_mixer.spec0000644000076500000240000000304314553251272015223 0ustar valvestaff%define name SDL2_mixer %define version 2.8.0 %define release 1 Summary: Simple DirectMedia Layer - Sample Mixer Library Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{version}.tar.gz License: LGPL Group: System Environment/Libraries BuildRoot: /var/tmp/%{name}-buildroot Prefix: %{_prefix} %description Due to popular demand, here is a simple multi-channel audio mixer. It supports 4 channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular ModPlug, Timidity MIDI, Ogg Vorbis, Tremor, and libmpg123 MP3 libraries. %package devel Summary: Libraries, includes and more to develop SDL applications. Group: Development/Libraries Requires: %{name} %description devel Due to popular demand, here is a simple multi-channel audio mixer. It supports 4 channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular ModPlug, Timidity MIDI, Ogg Vorbis, Tremor, and libmpg123 MP3 libraries. %prep %setup %build CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} make %install rm -rf $RPM_BUILD_ROOT make install prefix=$RPM_BUILD_ROOT/%{prefix} %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc README.txt CHANGES.txt LICENSE.txt %{prefix}/lib/lib*.so.* %files devel %defattr(-,root,root) %{prefix}/lib/lib*.a %{prefix}/lib/lib*.la %{prefix}/lib/lib*.so %{prefix}/include/*/ %{prefix}/lib/pkgconfig/*.pc %changelog * Wed Jan 19 2000 Sam Lantinga - converted to get package information from configure * Sun Jan 16 2000 Hakan Tandogan - initial spec file SDL2_mixer-2.8.0/README.txt0000644000076500000240000000471414551252471014143 0ustar valvestaff SDL_mixer 2.0 The latest version of this library is available from GitHub: https://github.com/libsdl-org/SDL_mixer/releases Due to popular demand, here is a simple multi-channel audio mixer. It supports 8 channels of 16 bit stereo audio, plus a single channel of music. It can load FLAC, MP3, Ogg, VOC, and WAV format audio. It can also load MIDI, MOD, and Opus audio, depending on build options (see the note below for details.) See the header file SDL_mixer.h and the examples playwave.c and playmus.c for documentation on this mixer library. This documentation is also available online at https://wiki.libsdl.org/SDL_mixer The process of mixing MIDI files to wave output is very CPU intensive, so if playing regular WAVE files sound great, but playing MIDI files sound choppy, try using 8-bit audio, mono audio, or lower frequencies. If you have built with FluidSynth support, you'll need to set the SDL_SOUNDFONTS environment variable to a Sound Font 2 (.sf2) file containing the musical instruments you want to use for MIDI playback. (On some Linux distributions you can install the fluid-soundfont-gm package) To play MIDI files using Timidity, you'll need to get a complete set of GUS patches from: http://www.libsdl.org/projects/mixer/timidity/timidity.tar.gz and unpack them in /usr/local/lib under UNIX, and C:\ under Win32. This library is under the zlib license, see the file "LICENSE.txt" for details. Note: Support for software MIDI, MOD, and Opus are not included by default because of the size of the decode libraries, but you can get them by running external/download.sh - When building with CMake, you can enable the appropriate SDL2MIXER_* options defined in CMakeLists.txt. SDL2MIXER_VENDORED allows switching between system and vendored libraries. - When building with configure/make, you can build and install them normally and the configure script will detect and use them. - When building with Visual Studio, you will need to build the libraries and then add the appropriate LOAD_* preprocessor define to the Visual Studio project. - When building with Xcode, you can edit the config at the top of the project to enable them, and you will need to include the appropriate framework in your application. - For Android, you can edit the config at the top of Android.mk to enable them. The default MP3 support is provided using minimp3. SDL_mixer also supports using libmpg123: you can enable it by passing --enable-music-mp3-mpg123 to configure. SDL2_mixer-2.8.0/version.rc0000644000076500000240000000172014553225265014455 0ustar valvestaff #include "winresrc.h" LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 2,8,0,0 PRODUCTVERSION 2,8,0,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL_mixer\0" VALUE "FileVersion", "2, 8, 0, 0\0" VALUE "InternalName", "SDL_mixer\0" VALUE "LegalCopyright", "Copyright (C) 2024 Sam Lantinga\0" VALUE "OriginalFilename", "SDL_mixer.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" VALUE "ProductVersion", "2, 8, 0, 0\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END SDL2_mixer-2.8.0/LICENSE.txt0000644000076500000240000000155614551252471014271 0ustar valvestaffCopyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. SDL2_mixer-2.8.0/SDL2_mixer.pc.in0000644000076500000240000000050314277744147015311 0ustar valvestaffprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: SDL2_mixer Description: mixer library for Simple DirectMedia Layer Version: @VERSION@ Requires: sdl2 >= @SDL_VERSION@ Libs: -L${libdir} -lSDL2_mixer Cflags: -I${includedir}/SDL2 Requires.private: @PC_REQUIRES@ Libs.private: @PC_LIBS@ SDL2_mixer-2.8.0/Makefile.in0000644000076500000240000001232414551252471014506 0ustar valvestaff# Makefile to build and install the SDL_mixer library. top_builddir = . srcdir = @srcdir@ objects = build prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = $(DESTDIR)@bindir@ libdir = $(DESTDIR)@libdir@ includedir = $(DESTDIR)@includedir@ datarootdir = $(DESTDIR)@datarootdir@ datadir = @datadir@ mandir = @mandir@ auxdir = @ac_aux_dir@ distpath = $(srcdir)/.. distdir = @PACKAGE_NAME@-@PACKAGE_VERSION@ distfile = $(distdir).tar.gz @SET_MAKE@ EXE = @EXE@ SHELL = @SHELL@ CC = @CC@ CXX = @CXX@ CFLAGS = @BUILD_CFLAGS@ EXTRA_CFLAGS = @EXTRA_CFLAGS@ LDFLAGS = @BUILD_LDFLAGS@ EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ MIXER_LIBS = @MIXER_LIBS@ LIBTOOL = @LIBTOOL@ INSTALL = @INSTALL@ AR = @AR@ RANLIB = @RANLIB@ RC = @RC@ SDL_CFLAGS = @SDL_CFLAGS@ SDL_LIBS = @SDL_LIBS@ TARGET = libSDL2_mixer.la OBJECTS = @OBJECTS@ VERSION_OBJECTS = @VERSION_OBJECTS@ PLAYWAVE_OBJECTS = @PLAYWAVE_OBJECTS@ PLAYMUS_OBJECTS = @PLAYMUS_OBJECTS@ SRC_DIST = \ .gitmodules \ Android.mk \ CHANGES.txt \ CMakeLists.txt \ LICENSE.txt \ Makefile.in \ Makefile.os2 \ README.txt \ SDL2_mixer.pc.in \ SDL2_mixer.spec.in \ SDL2_mixerConfig.cmake.in \ VisualC \ VisualC-WinRT \ Xcode \ acinclude \ autogen.sh \ build-scripts \ cmake \ configure \ configure.ac \ external/Get-GitModules.ps1 \ external/download.sh \ include/SDL_mixer.h \ mingw \ playmus.c \ playwave.c \ sdl2_mixer-config-version.cmake.in \ sdl2_mixer-config.cmake.in \ src \ version.rc GEN_DIST = SDL2_mixer.spec LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_RELEASE = @LT_RELEASE@ LT_REVISION = @LT_REVISION@ LT_EXTRA = @LT_EXTRA@ LT_LDFLAGS = -no-undefined -rpath $(libdir) -release $(LT_RELEASE) -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LT_EXTRA) all: $(srcdir)/configure Makefile $(objects)/$(TARGET) $(objects)/playwave$(EXE) $(objects)/playmus$(EXE) $(srcdir)/configure: $(srcdir)/configure.ac @echo "Warning, configure is out of date, please re-run autogen.sh" Makefile: $(srcdir)/Makefile.in $(SHELL) config.status $@ $(objects)/.created: $(SHELL) $(auxdir)/mkinstalldirs $(objects) touch $@ .PHONY: all install install-hdrs install-lib install-bin uninstall uninstall-hdrs uninstall-lib uninstall-bin clean distclean dist $(objects)/$(TARGET): $(OBJECTS) $(VERSION_OBJECTS) $(LIBTOOL) --mode=link $(CC) -o $@ $(OBJECTS) $(VERSION_OBJECTS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LT_LDFLAGS) $(SDL_LIBS) $(MIXER_LIBS) $(objects)/playwave$(EXE): $(objects)/playwave.lo $(objects)/$(TARGET) $(LIBTOOL) --mode=link $(CC) -o $@ $(objects)/playwave.lo $(SDL_CFLAGS) $(objects)/$(TARGET) $(SDL_LIBS) $(LDFLAGS) $(objects)/playmus$(EXE): $(objects)/playmus.lo $(objects)/$(TARGET) $(LIBTOOL) --mode=link $(CC) -o $@ $(objects)/playmus.lo $(SDL_CFLAGS) $(objects)/$(TARGET) $(SDL_LIBS) $(LDFLAGS) install: all install-hdrs install-lib #install-bin install-hdrs: $(SHELL) $(auxdir)/mkinstalldirs $(includedir)/SDL2 for src in $(srcdir)/include/SDL_mixer.h; do \ file=`echo $$src | sed -e 's|^.*/||'`; \ $(INSTALL) -m 644 $$src $(includedir)/SDL2/$$file; \ done $(SHELL) $(auxdir)/mkinstalldirs $(libdir)/pkgconfig $(INSTALL) -m 644 SDL2_mixer.pc $(libdir)/pkgconfig/ $(SHELL) $(auxdir)/mkinstalldirs $(libdir)/cmake/SDL2_mixer/ $(INSTALL) -m 644 sdl2_mixer-config.cmake $(libdir)/cmake/SDL2_mixer $(INSTALL) -m 644 sdl2_mixer-config-version.cmake $(libdir)/cmake/SDL2_mixer install-lib: $(objects)/$(TARGET) $(SHELL) $(auxdir)/mkinstalldirs $(libdir) $(LIBTOOL) --mode=install $(INSTALL) $(objects)/$(TARGET) $(libdir)/$(TARGET) install-bin: $(SHELL) $(auxdir)/mkinstalldirs $(bindir) $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(objects)/playwave$(EXE) $(bindir)/playwave$(EXE) $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(objects)/playmus$(EXE) $(bindir)/playmus$(EXE) uninstall: uninstall-hdrs uninstall-lib uninstall-bin uninstall-hdrs: for src in $(srcdir)/include/SDL_mixer.h; do \ file=`echo $$src | sed -e 's|^.*/||'`; \ rm -f $(includedir)/SDL2/$$file; \ done -rmdir $(includedir)/SDL2 rm -f $(libdir)/pkgconfig/SDL2_mixer.pc -rmdir $(libdir)/pkgconfig rm -f $(libdir)/cmake/SDL2_mixer/sdl2_mixer-config.cmake rm -f $(libdir)/cmake/SDL2_mixer/sdl2_mixer-config-version.cmake -rmdir $(libdir)/cmake/SDL2_mixer -rmdir $(libdir)/cmake uninstall-lib: $(LIBTOOL) --mode=uninstall rm -f $(libdir)/$(TARGET) uninstall-bin: rm -f $(bindir)/playwave$(EXE) rm -f $(bindir)/playmus$(EXE) clean: rm -rf $(objects) distclean: clean rm -f Makefile rm -f config.status config.cache config.log libtool rm -f SDL2_mixer.pc rm -rf $(srcdir)/autom4te* find $(srcdir) \( \ -name '*~' -o \ -name '*.bak' -o \ -name '*.old' -o \ -name '*.rej' -o \ -name '*.orig' -o \ -name '.#*' \) \ -exec rm -f {} \; dist $(distfile): $(SHELL) $(auxdir)/mkinstalldirs $(distdir) (cd $(srcdir); tar cf - $(SRC_DIST)) | (cd $(distdir); tar xf -) tar cf - $(GEN_DIST) | (cd $(distdir); tar xf -) -rm -rf `find $(distdir)/external -name '.git*'` -rm -rf `find $(distdir)/external -name '.ci*'` -rm -rf `find $(distdir)/external -name '*.yml'` -rm -rf `find $(distdir)/external -name ci` -rm -f `find $(distdir) -name '.#*'` tar cf - $(distdir) | gzip --best >$(distfile) rm -rf $(distdir) rpm: $(distfile) rpmbuild -ta $? SDL2_mixer-2.8.0/build-scripts/0000755000076500000240000000000014553251273015224 5ustar valvestaffSDL2_mixer-2.8.0/build-scripts/mkinstalldirs0000755000076500000240000000667214277744147020057 0ustar valvestaff#! /bin/sh # mkinstalldirs --- make directory hierarchy scriptversion=2020-07-26.22; # UTC # Original author: Noah Friedman # Created: 1993-05-16 # Public domain. # # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' IFS=" "" $nl" errstatus=0 dirmode= usage="\ Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... Create each directory DIR (with mode MODE, if specified), including all leading file name components. Report bugs to ." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" exit $? ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --version) echo "$0 $scriptversion" exit $? ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and # mkdir -p a/c at the same time, both will detect that a is missing, # one will create a, then the other will try to create a and die with # a "File exists" error. This is a problem when calling mkinstalldirs # from a parallel make. We use --version in the probe to restrict # ourselves to GNU mkdir, which is thread-safe. case $dirmode in '') if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" else # On NextStep and OpenStep, the 'mkdir' command does not # recognize any option. It will interpret all options as # directories to create, and then abort because '.' already # exists. test -d ./-p && rmdir ./-p test -d ./--version && rmdir ./--version fi ;; *) if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "umask 22" umask 22 echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" else # Clean up after NextStep and OpenStep mkdir. for d in ./-m ./-p ./--version "./$dirmode"; do test -d $d && rmdir $d done fi ;; esac echo "umask 22" umask 22 for file do case $file in /*) pathcomp=/ ;; *) pathcomp= ;; esac oIFS=$IFS IFS=/ set fnord $file shift IFS=$oIFS for d do test "x$d" = x && continue pathcomp=$pathcomp$d case $pathcomp in -*) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp=$pathcomp/ done if test ! -z "$dirmode"; then echo "chmod $dirmode $file" chmod "$dirmode" "$file" || errstatus=$? fi done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: SDL2_mixer-2.8.0/build-scripts/android-prefab.sh0000755000076500000240000003024714551252471020445 0ustar valvestaff#!/bin/bash set -e if ! [ "x${ANDROID_NDK_HOME}" != "x" -a -d "${ANDROID_NDK_HOME}" ]; then echo "ANDROID_NDK_HOME environment variable is not set" exit 1 fi if ! [ "x${ANDROID_HOME}" != "x" -a -d "${ANDROID_HOME}" ]; then echo "ANDROID_HOME environment variable is not set" exit 1 fi ANDROID_PLATFORM="${ANDROID_PLATFORM:-16}" if [ "x${android_platform}" = "x" ]; then ANDROID_API="$(ls "${ANDROID_HOME}/platforms" | grep -E "^android-[0-9]+$" | sed 's/android-//' | sort -n -r | head -1)" if [ "x${ANDROID_API}" = "x" ]; then echo "No Android platform found in $ANDROID_HOME/platforms" exit 1 fi else if ! [ -d "${ANDROID_HOME}/platforms/android-${ANDROID_API}" ]; then echo "Android api version ${ANDROID_API} is not available (${ANDROID_HOME}/platforms/android-${ANDROID_API} does not exist)" >2 exit 1 fi fi android_platformdir="${ANDROID_HOME}/platforms/android-${ANDROID_API}" echo "Building with ANDROID_PLATFORM=${ANDROID_PLATFORM}" echo "android_platformdir=${android_platformdir}" scriptdir=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)") sdlmixer_root=$(cd -P -- "$(dirname -- "$0")/.." && printf '%s\n' "$(pwd -P)") build_root="${sdlmixer_root}/build-android-prefab" android_abis="armeabi-v7a arm64-v8a x86 x86_64" android_api=19 android_ndk=21 android_stl="c++_shared" sdlmixer_major=$(sed -ne 's/^#define SDL_MIXER_MAJOR_VERSION *//p' "${sdlmixer_root}/include/SDL_mixer.h") sdlmixer_minor=$(sed -ne 's/^#define SDL_MIXER_MINOR_VERSION *//p' "${sdlmixer_root}/include/SDL_mixer.h") sdlmixer_patch=$(sed -ne 's/^#define SDL_MIXER_PATCHLEVEL *//p' "${sdlmixer_root}/include/SDL_mixer.h") sdlmixer_version="${sdlmixer_major}.${sdlmixer_minor}.${sdlmixer_patch}" echo "Building Android prefab package for SDL_mixer version $sdlmixer_version" if test ! -d "${sdl_build_root}"; then echo "sdl_build_root is not defined or is not a directory." echo "Set this environment folder to the root of an android SDL${sdlmixer_major} prefab build" echo "This usually is SDL/build-android-prefab" exit 1 fi prefabhome="${build_root}/prefab-${sdlmixer_version}" rm -rf "$prefabhome" mkdir -p "${prefabhome}" build_cmake_projects() { for android_abi in $android_abis; do rm -rf "${build_root}/build_${android_abi}/prefix" for build_shared_libs in ON OFF; do echo "Configuring CMake project for $android_abi (shared=${build_shared_libs})" cmake -S "${sdlmixer_root}" -B "${build_root}/build_${android_abi}/shared_${build_shared_libs}" \ -DSDL2MIXER_DEPS_SHARED=ON \ -DSDL2MIXER_VENDORED=ON \ -DSDL2MIXER_FLAC=ON \ -DWITH_ASM=OFF \ -DSDL2MIXER_FLAC_LIBFLAC=ON \ -DSDL2MIXER_MOD=ON \ -DSDL2MIXER_MOD_MODPLUG=OFF \ -DSDL2MIXER_MOD_XMP=ON \ -DSDL2MIXER_MP3=ON \ -DSDL2MIXER_MP3_MPG123=ON \ -DSDL2MIXER_MIDI=ON \ -DSDL2MIXER_MIDI_TIMIDITY=ON \ -DSDL2MIXER_OPUS=ON \ -DSDL2MIXER_VORBIS=STB \ -DSDL2MIXER_WAVPACK=ON \ -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ -DSDL${sdlmixer_major}_DIR="${sdl_build_root}/build_${android_abi}/prefix/lib/cmake/SDL${sdlmixer_major}" \ -DANDROID_PLATFORM=${ANDROID_PLATFORM} \ -DANDROID_ABI=${android_abi} \ -DBUILD_SHARED_LIBS=${build_shared_libs} \ -DCMAKE_INSTALL_PREFIX="${build_root}/build_${android_abi}/prefix" \ -DCMAKE_INSTALL_INCLUDEDIR=include \ -DCMAKE_INSTALL_LIBDIR=lib \ -DCMAKE_BUILD_TYPE=Release \ -DSDL${sdlmixer_major}MIXER_SAMPLES=OFF \ -GNinja echo "Building CMake project for $android_abi (shared=${build_shared_libs})" cmake --build "${build_root}/build_${android_abi}/shared_${build_shared_libs}" echo "Installing CMake project for $android_abi (shared=${build_shared_libs})" cmake --install "${build_root}/build_${android_abi}/shared_${build_shared_libs}" done done } pom_filename="SDL${sdlmixer_major}_mixer-${sdlmixer_version}.pom" pom_filepath="${prefabhome}/${pom_filename}" create_pom_xml() { echo "Creating ${pom_filename}" cat >"${pom_filepath}" < 4.0.0 org.libsdl.android SDL${sdlmixer_major}_mixer ${sdlmixer_version} aar SDL${sdlmixer_major}_mixer The AAR for SDL${sdlmixer_major}_mixer https://libsdl.org/ zlib License https://github.com/libsdl-org/SDL_mixer/blob/main/LICENSE.txt repo Sam Lantinga slouken@libsdl.org SDL https://www.libsdl.org scm:git:https://github.com/libsdl-org/SDL_mixer scm:git:ssh://github.com:libsdl-org/SDL_mixer.git https://github.com/libsdl-org/SDL_mixer ossrh https://s01.oss.sonatype.org/content/repositories/snapshots ossrh https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ com.simpligility.maven.plugins android-maven-plugin 4.6.0 true false EOF } create_aar_androidmanifest() { echo "Creating AndroidManifest.xml" cat >"${aar_root}/AndroidManifest.xml" < EOF } echo "Creating AAR root directory" aar_root="${prefabhome}/SDL${sdlmixer_major}_mixer-${sdlmixer_version}" mkdir -p "${aar_root}" aar_metainfdir_path=${aar_root}/META-INF mkdir -p "${aar_metainfdir_path}" cp "${sdlmixer_root}/LICENSE.txt" "${aar_metainfdir_path}" prefabworkdir="${aar_root}/prefab" mkdir -p "${prefabworkdir}" cat >"${prefabworkdir}/prefab.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${aar_metainfdir_path}/LICENSE.libxmp.txt" create_shared_module external_libwavpack libwavpack "" tail -n15 "${sdlmixer_root}/external/wavpack/COPYING" >"${aar_metainfdir_path}/LICENSE.wavpack.txt" pushd "${aar_root}" aar_filename="SDL${sdlmixer_major}_mixer-${sdlmixer_version}.aar" zip -r "${aar_filename}" AndroidManifest.xml prefab META-INF zip -Tv "${aar_filename}" 2>/dev/null ; mv "${aar_filename}" "${prefabhome}" popd maven_filename="SDL${sdlmixer_major}_mixer-${sdlmixer_version}.zip" pushd "${prefabhome}" zip_filename="SDL${sdlmixer_major}_mixer-${sdlmixer_version}.zip" zip "${maven_filename}" "${aar_filename}" "${pom_filename}" 2>/dev/null; zip -Tv "${zip_filename}" 2>/dev/null; popd echo "Prefab zip is ready at ${prefabhome}/${aar_filename}" echo "Maven archive is ready at ${prefabhome}/${zip_filename}" SDL2_mixer-2.8.0/autogen.sh0000755000076500000240000000041714277744147014455 0ustar valvestaff#!/bin/sh set -e echo "Generating build information using autoconf" echo "This may take a while ..." cat acinclude/* >aclocal.m4 "${AUTOCONF:-autoconf}" rm aclocal.m4 rm -rf autom4te.cache # Run configure for this platform echo "Now you are ready to run ./configure" SDL2_mixer-2.8.0/SDL2_mixer.spec.in0000644000076500000240000000304714277744147015647 0ustar valvestaff%define name SDL2_mixer %define version @VERSION@ %define release 1 Summary: Simple DirectMedia Layer - Sample Mixer Library Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{version}.tar.gz License: LGPL Group: System Environment/Libraries BuildRoot: /var/tmp/%{name}-buildroot Prefix: %{_prefix} %description Due to popular demand, here is a simple multi-channel audio mixer. It supports 4 channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular ModPlug, Timidity MIDI, Ogg Vorbis, Tremor, and libmpg123 MP3 libraries. %package devel Summary: Libraries, includes and more to develop SDL applications. Group: Development/Libraries Requires: %{name} %description devel Due to popular demand, here is a simple multi-channel audio mixer. It supports 4 channels of 16 bit stereo audio, plus a single channel of music, mixed by the popular ModPlug, Timidity MIDI, Ogg Vorbis, Tremor, and libmpg123 MP3 libraries. %prep %setup %build CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} make %install rm -rf $RPM_BUILD_ROOT make install prefix=$RPM_BUILD_ROOT/%{prefix} %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc README.txt CHANGES.txt LICENSE.txt %{prefix}/lib/lib*.so.* %files devel %defattr(-,root,root) %{prefix}/lib/lib*.a %{prefix}/lib/lib*.la %{prefix}/lib/lib*.so %{prefix}/include/*/ %{prefix}/lib/pkgconfig/*.pc %changelog * Wed Jan 19 2000 Sam Lantinga - converted to get package information from configure * Sun Jan 16 2000 Hakan Tandogan - initial spec file SDL2_mixer-2.8.0/src/0000755000076500000240000000000014553225265013231 5ustar valvestaffSDL2_mixer-2.8.0/src/music.h0000644000076500000240000001220614551252471014520 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef MUSIC_H_ #define MUSIC_H_ #include "SDL_mixer.h" /* Supported music APIs, in order of preference */ typedef enum { MIX_MUSIC_CMD, MIX_MUSIC_WAVE, MIX_MUSIC_MODPLUG, MIX_MUSIC_FLUIDSYNTH, MIX_MUSIC_TIMIDITY, MIX_MUSIC_NATIVEMIDI, MIX_MUSIC_OGG, MIX_MUSIC_MINIMP3, MIX_MUSIC_MPG123, MIX_MUSIC_DRFLAC, MIX_MUSIC_FLAC, MIX_MUSIC_OPUS, MIX_MUSIC_LIBXMP, MIX_MUSIC_WAVPACK, MIX_MUSIC_GME, MIX_MUSIC_LAST } Mix_MusicAPI; /* Supported meta-tags */ typedef enum { MIX_META_TITLE, MIX_META_ARTIST, MIX_META_ALBUM, MIX_META_COPYRIGHT, MIX_META_LAST } Mix_MusicMetaTag; /* MIXER-X: Meta-tags utility structure */ typedef struct { char *tags[4]; } Mix_MusicMetaTags; extern void meta_tags_init(Mix_MusicMetaTags *tags); extern void meta_tags_clear(Mix_MusicMetaTags *tags); extern void meta_tags_set(Mix_MusicMetaTags *tags, Mix_MusicMetaTag type, const char *value); extern const char* meta_tags_get(Mix_MusicMetaTags *tags, Mix_MusicMetaTag type); /* Music API implementation */ typedef struct { const char *tag; Mix_MusicAPI api; Mix_MusicType type; SDL_bool loaded; SDL_bool opened; /* Load the library */ int (*Load)(void); /* Initialize for the audio output */ int (*Open)(const SDL_AudioSpec *spec); /* Create a music object from an SDL_RWops stream * If the function returns NULL, 'src' will be freed if needed by the caller. */ void *(*CreateFromRW)(SDL_RWops *src, int freesrc); /* Create a music object from a file, if SDL_RWops are not supported */ void *(*CreateFromFile)(const char *file); /* Set the volume */ void (*SetVolume)(void *music, int volume); /* Get the volume */ int (*GetVolume)(void *music); /* Start playing music from the beginning with an optional loop count */ int (*Play)(void *music, int play_count); /* Returns SDL_TRUE if music is still playing */ SDL_bool (*IsPlaying)(void *music); /* Get music data, returns the number of bytes left */ int (*GetAudio)(void *music, void *data, int bytes); /* Jump to a given order in mod music */ int (*Jump)(void *music, int order); /* Seek to a play position (in seconds) */ int (*Seek)(void *music, double position); /* Tell a play position (in seconds) */ double (*Tell)(void *music); /* Get Music duration (in seconds) */ double (*Duration)(void *music); /* Tell a loop start position (in seconds) */ double (*LoopStart)(void *music); /* Tell a loop end position (in seconds) */ double (*LoopEnd)(void *music); /* Tell a loop length position (in seconds) */ double (*LoopLength)(void *music); /* Get a meta-tag string if available */ const char* (*GetMetaTag)(void *music, Mix_MusicMetaTag tag_type); /* Get number of tracks. Returns -1 if not applicable */ int (*GetNumTracks)(void *music); /* Start a specific track */ int (*StartTrack)(void *music, int track); /* Pause playing music */ void (*Pause)(void *music); /* Resume playing music */ void (*Resume)(void *music); /* Stop playing music */ void (*Stop)(void *music); /* Delete a music object */ void (*Delete)(void *music); /* Close the library and clean up */ void (*Close)(void); /* Unload the library */ void (*Unload)(void); } Mix_MusicInterface; extern int get_num_music_interfaces(void); extern Mix_MusicInterface *get_music_interface(int index); extern Mix_MusicType detect_music_type(SDL_RWops *src); extern SDL_bool load_music_type(Mix_MusicType type); extern SDL_bool open_music_type(Mix_MusicType type); extern SDL_bool has_music(Mix_MusicType type); extern void open_music(const SDL_AudioSpec *spec); extern int music_pcm_getaudio(void *context, void *data, int bytes, int volume, int (*GetSome)(void *context, void *data, int bytes, SDL_bool *done)); extern void SDLCALL music_mixer(void *udata, Uint8 *stream, int len); extern void pause_async_music(int pause_on); extern void close_music(void); extern void unload_music(void); extern char *music_cmd; extern SDL_AudioSpec music_spec; #endif /* MUSIC_H_ */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/utils.h0000644000076500000240000000271614551252471014545 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef UTILS_H_ #define UTILS_H_ /* misc helper routines */ #include "SDL_stdinc.h" #include "SDL_version.h" #if SDL_VERSION_ATLEAST(2,0,12) #define HAVE_SDL_STRTOKR #else #define SDL_strtokr _MIX_strtokr extern char *SDL_strtokr(char *s1, const char *s2, char **saveptr); #endif /* Parse time string of the form HH:MM:SS.mmm and return equivalent sample * position */ extern Sint64 _Mix_ParseTime(char *time, long samplerate_hz); extern SDL_bool _Mix_IsLoopTag(const char *tag); #endif /* UTILS_H_ */ SDL2_mixer-2.8.0/src/mixer.c0000644000076500000240000014273214553225265014532 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL.h" #include "SDL_mixer.h" #include "mixer.h" #include "music.h" #include "load_aiff.h" #include "load_voc.h" #define MIX_INTERNAL_EFFECT__ #include "effects_internal.h" /* Magic numbers for various audio file formats */ #define RIFF 0x46464952 /* "RIFF" */ #define WAVE 0x45564157 /* "WAVE" */ #define FORM 0x4d524f46 /* "FORM" */ #define CREA 0x61657243 /* "Crea" */ #if defined(SDL_BUILD_MAJOR_VERSION) && defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION, SDL_MIXER_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION, SDL_MIXER_MINOR_VERSION == SDL_BUILD_MINOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION, SDL_MIXER_PATCHLEVEL == SDL_BUILD_MICRO_VERSION); #endif #if defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MAJOR_VERSION_min, SDL_MIXER_MAJOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MAJOR_VERSION_max, SDL_MIXER_MAJOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MINOR_VERSION_min, SDL_MIXER_MINOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_MIXER_MINOR_VERSION_max, SDL_MIXER_MINOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_MIXER_PATCHLEVEL_min, SDL_MIXER_PATCHLEVEL >= 0); /* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */ SDL_COMPILE_TIME_ASSERT(SDL_MIXER_PATCHLEVEL_max, SDL_MIXER_PATCHLEVEL <= 99); #endif static int audio_opened = 0; static SDL_AudioSpec mixer; static SDL_AudioDeviceID audio_device; typedef struct _Mix_effectinfo { Mix_EffectFunc_t callback; Mix_EffectDone_t done_callback; void *udata; struct _Mix_effectinfo *next; } effect_info; static struct _Mix_Channel { Mix_Chunk *chunk; int playing; int paused; Uint8 *samples; int volume; int looping; int tag; Uint32 expire; Uint32 start_time; Mix_Fading fading; int fade_volume; int fade_volume_reset; Uint32 fade_length; Uint32 ticks_fade; effect_info *effects; } *mix_channel = NULL; static effect_info *posteffects = NULL; static int num_channels; static int reserved_channels = 0; /* Support for hooking into the mixer callback system */ static void (SDLCALL *mix_postmix)(void *udata, Uint8 *stream, int len) = NULL; static void *mix_postmix_data = NULL; /* rcg07062001 callback to alert when channels are done playing. */ static void (SDLCALL *channel_done_callback)(int channel) = NULL; /* Support for user defined music functions */ static void (SDLCALL *mix_music)(void *udata, Uint8 *stream, int len) = music_mixer; static void *music_data = NULL; /* rcg06042009 report available decoders at runtime. */ static const char **chunk_decoders = NULL; static int num_decoders = 0; static SDL_atomic_t master_volume = { MIX_MAX_VOLUME }; int Mix_GetNumChunkDecoders(void) { return num_decoders; } const char *Mix_GetChunkDecoder(int index) { if ((index < 0) || (index >= num_decoders)) { return NULL; } return chunk_decoders[index]; } SDL_bool Mix_HasChunkDecoder(const char *name) { int index; for (index = 0; index < num_decoders; ++index) { if (SDL_strcasecmp(name, chunk_decoders[index]) == 0) { return SDL_TRUE; } } return SDL_FALSE; } void add_chunk_decoder(const char *decoder) { int i; void *ptr; /* Check to see if we already have this decoder */ for (i = 0; i < num_decoders; ++i) { if (SDL_strcmp(chunk_decoders[i], decoder) == 0) { return; } } ptr = SDL_realloc((void *)chunk_decoders, (size_t)(num_decoders + 1) * sizeof(const char *)); if (ptr == NULL) { return; /* oh well, go on without it. */ } chunk_decoders = (const char **) ptr; chunk_decoders[num_decoders++] = decoder; } /* rcg06192001 get linked library's version. */ const SDL_version *Mix_Linked_Version(void) { static SDL_version linked_version; SDL_MIXER_VERSION(&linked_version); return &linked_version; } /* * Returns a bitmap of already loaded modules (MIX_INIT_* flags). * * Note that functions other than Mix_Init() may cause a module to get loaded * (hence the looping over the interfaces instead of maintaining a set of flags * just in Mix_Init() and Mix_Quit()). */ static int get_loaded_mix_init_flags(void) { int i; int loaded_init_flags = 0; for (i = 0; i < get_num_music_interfaces(); ++i) { Mix_MusicInterface *interface; interface = get_music_interface(i); if (interface->loaded) { switch (interface->type) { case MUS_FLAC: loaded_init_flags |= MIX_INIT_FLAC; break; case MUS_WAVPACK: loaded_init_flags |= MIX_INIT_WAVPACK; break; case MUS_MOD: loaded_init_flags |= MIX_INIT_MOD; break; case MUS_MP3: loaded_init_flags |= MIX_INIT_MP3; break; case MUS_OGG: loaded_init_flags |= MIX_INIT_OGG; break; case MUS_MID: loaded_init_flags |= MIX_INIT_MID; break; case MUS_OPUS: loaded_init_flags |= MIX_INIT_OPUS; break; default: break; } } } return loaded_init_flags; } int Mix_Init(int flags) { int result = 0; int already_loaded = get_loaded_mix_init_flags(); if (flags & MIX_INIT_FLAC) { if (load_music_type(MUS_FLAC)) { open_music_type(MUS_FLAC); result |= MIX_INIT_FLAC; } else { Mix_SetError("FLAC support not available"); } } if (flags & MIX_INIT_WAVPACK) { if (load_music_type(MUS_WAVPACK)) { open_music_type(MUS_WAVPACK); result |= MIX_INIT_WAVPACK; } else { Mix_SetError("WavPack support not available"); } } if (flags & MIX_INIT_MOD) { if (load_music_type(MUS_MOD)) { open_music_type(MUS_MOD); result |= MIX_INIT_MOD; } else { Mix_SetError("MOD support not available"); } } if (flags & MIX_INIT_MP3) { if (load_music_type(MUS_MP3)) { open_music_type(MUS_MP3); result |= MIX_INIT_MP3; } else { Mix_SetError("MP3 support not available"); } } if (flags & MIX_INIT_OGG) { if (load_music_type(MUS_OGG)) { open_music_type(MUS_OGG); result |= MIX_INIT_OGG; } else { Mix_SetError("OGG support not available"); } } if (flags & MIX_INIT_OPUS) { if (load_music_type(MUS_OPUS)) { open_music_type(MUS_OPUS); result |= MIX_INIT_OPUS; } else { Mix_SetError("OPUS support not available"); } } if (flags & MIX_INIT_MID) { if (load_music_type(MUS_MID)) { open_music_type(MUS_MID); result |= MIX_INIT_MID; } else { Mix_SetError("MIDI support not available"); } } result |= already_loaded; return result; } void Mix_Quit() { unload_music(); } static int _Mix_remove_all_effects(int channel, effect_info **e); /* * rcg06122001 Cleanup effect callbacks. * MAKE SURE Mix_LockAudio() is called before this (or you're in the * audio callback). */ static void _Mix_channel_done_playing(int channel) { if (channel_done_callback) { channel_done_callback(channel); } /* * Call internal function directly, to avoid locking audio from * inside audio callback. */ _Mix_remove_all_effects(channel, &mix_channel[channel].effects); } static void *Mix_DoEffects(int chan, void *snd, int len) { int posteffect = (chan == MIX_CHANNEL_POST); effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects); void *buf = snd; if (e != NULL) { /* are there any registered effects? */ /* if this is the postmix, we can just overwrite the original. */ if (!posteffect) { buf = SDL_malloc((size_t)len); if (buf == NULL) { return snd; } SDL_memcpy(buf, snd, (size_t)len); } for (; e != NULL; e = e->next) { if (e->callback != NULL) { e->callback(chan, buf, len, e->udata); } } } /* be sure to SDL_free() the return value if != snd ... */ return buf; } /* Mixing function */ static void SDLCALL mix_channels(void *udata, Uint8 *stream, int len) { Uint8 *mix_input; int i, mixable, master_vol; Uint32 sdl_ticks; (void)udata; /* Need to initialize the stream in SDL 1.3+ */ SDL_memset(stream, mixer.silence, (size_t)len); /* Mix the music (must be done before the channels are added) */ mix_music(music_data, stream, len); master_vol = SDL_AtomicGet(&master_volume); /* Mix any playing channels... */ sdl_ticks = SDL_GetTicks(); for (i = 0; i < num_channels; ++i) { if (!mix_channel[i].paused) { if (mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks) { /* Expiration delay for that channel is reached */ mix_channel[i].playing = 0; mix_channel[i].looping = 0; mix_channel[i].fading = MIX_NO_FADING; mix_channel[i].expire = 0; _Mix_channel_done_playing(i); } else if (mix_channel[i].fading != MIX_NO_FADING) { Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade; if (ticks >= mix_channel[i].fade_length) { Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */ if (mix_channel[i].fading == MIX_FADING_OUT) { mix_channel[i].playing = 0; mix_channel[i].looping = 0; mix_channel[i].expire = 0; _Mix_channel_done_playing(i); } mix_channel[i].fading = MIX_NO_FADING; } else { if (mix_channel[i].fading == MIX_FADING_OUT) { Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks)) / mix_channel[i].fade_length); } else { Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length); } } } if (mix_channel[i].playing > 0) { int volume = (master_vol * (mix_channel[i].volume * mix_channel[i].chunk->volume)) / (MIX_MAX_VOLUME * MIX_MAX_VOLUME); int index = 0; int remaining = len; while (mix_channel[i].playing > 0 && index < len) { remaining = len - index; mixable = mix_channel[i].playing; if (mixable > remaining) { mixable = remaining; } mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable); SDL_MixAudioFormat(stream+index, mix_input, mixer.format, mixable, volume); if (mix_input != mix_channel[i].samples) SDL_free(mix_input); mix_channel[i].samples += mixable; mix_channel[i].playing -= mixable; index += mixable; /* rcg06072001 Alert app if channel is done playing. */ if (!mix_channel[i].playing && !mix_channel[i].looping) { mix_channel[i].fading = MIX_NO_FADING; mix_channel[i].expire = 0; _Mix_channel_done_playing(i); /* Update the volume after the application callback */ volume = (master_vol * (mix_channel[i].volume * mix_channel[i].chunk->volume)) / (MIX_MAX_VOLUME * MIX_MAX_VOLUME); } } /* If looping the sample and we are at its end, make sure we will still return a full buffer */ while (mix_channel[i].looping && index < len) { int alen = mix_channel[i].chunk->alen; remaining = len - index; if (remaining > alen) { remaining = alen; } mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining); SDL_MixAudioFormat(stream+index, mix_input, mixer.format, remaining, volume); if (mix_input != mix_channel[i].chunk->abuf) SDL_free(mix_input); if (mix_channel[i].looping > 0) { --mix_channel[i].looping; } mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining; mix_channel[i].playing = mix_channel[i].chunk->alen - remaining; index += remaining; } if (! mix_channel[i].playing && mix_channel[i].looping) { if (mix_channel[i].looping > 0) { --mix_channel[i].looping; } mix_channel[i].samples = mix_channel[i].chunk->abuf; mix_channel[i].playing = mix_channel[i].chunk->alen; } } } } /* rcg06122001 run posteffects... */ Mix_DoEffects(MIX_CHANNEL_POST, stream, len); if (mix_postmix) { mix_postmix(mix_postmix_data, stream, len); } } #if 0 static void PrintFormat(char *title, SDL_AudioSpec *fmt) { printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF), (fmt->format&0x8000) ? "signed" : "unsigned", (fmt->channels > 2) ? "surround" : (fmt->channels > 1) ? "stereo" : "mono", fmt->freq); } #endif /* Open the mixer with a certain desired audio format */ int Mix_OpenAudioDevice(int frequency, Uint16 format, int nchannels, int chunksize, const char* device, int allowed_changes) { int i; SDL_AudioSpec desired; /* This used to call SDL_OpenAudio(), which initializes the audio subsystem if necessary. Since SDL_OpenAudioDevice() doesn't, we have to handle this case here. */ if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { return -1; } } /* If the mixer is already opened, increment open count */ if (audio_opened) { if (format == mixer.format && nchannels == mixer.channels) { ++audio_opened; return 0; } while (audio_opened) { Mix_CloseAudio(); } } /* Set the desired format and frequency */ desired.freq = frequency; desired.format = format; desired.channels = nchannels; desired.samples = chunksize; desired.callback = mix_channels; desired.userdata = NULL; /* Accept nearly any audio format */ if ((audio_device = SDL_OpenAudioDevice(device, 0, &desired, &mixer, allowed_changes)) == 0) { return -1; } #if 0 PrintFormat("Audio device", &mixer); #endif num_channels = MIX_CHANNELS; mix_channel = (struct _Mix_Channel *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel)); /* Clear out the audio channels */ for (i = 0; i < num_channels; ++i) { mix_channel[i].chunk = NULL; mix_channel[i].playing = 0; mix_channel[i].looping = 0; mix_channel[i].volume = SDL_MIX_MAXVOLUME; mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME; mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME; mix_channel[i].fading = MIX_NO_FADING; mix_channel[i].tag = -1; mix_channel[i].expire = 0; mix_channel[i].effects = NULL; mix_channel[i].paused = 0; } Mix_VolumeMusic(SDL_MIX_MAXVOLUME); _Mix_InitEffects(); add_chunk_decoder("WAVE"); add_chunk_decoder("AIFF"); add_chunk_decoder("VOC"); /* Initialize the music players */ open_music(&mixer); audio_opened = 1; SDL_PauseAudioDevice(audio_device, 0); return 0; } /* Open the mixer with a certain desired audio format */ int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize) { return Mix_OpenAudioDevice(frequency, format, nchannels, chunksize, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE); } /* Pause or resume the audio streaming */ void Mix_PauseAudio(int pause_on) { SDL_PauseAudioDevice(audio_device, pause_on); Mix_LockAudio(); pause_async_music(pause_on); Mix_UnlockAudio(); } /* Dynamically change the number of channels managed by the mixer. If decreasing the number of channels, the upper channels are stopped. */ int Mix_AllocateChannels(int numchans) { if (numchans<0 || numchans==num_channels) return num_channels; if (numchans < num_channels) { /* Stop the affected channels */ int i; for (i = numchans; i < num_channels; i++) { Mix_UnregisterAllEffects(i); Mix_HaltChannel(i); } } Mix_LockAudio(); mix_channel = (struct _Mix_Channel *) SDL_realloc(mix_channel, numchans * sizeof(struct _Mix_Channel)); if (numchans > num_channels) { /* Initialize the new channels */ int i; for (i = num_channels; i < numchans; i++) { mix_channel[i].chunk = NULL; mix_channel[i].playing = 0; mix_channel[i].looping = 0; mix_channel[i].volume = MIX_MAX_VOLUME; mix_channel[i].fade_volume = MIX_MAX_VOLUME; mix_channel[i].fade_volume_reset = MIX_MAX_VOLUME; mix_channel[i].fading = MIX_NO_FADING; mix_channel[i].tag = -1; mix_channel[i].expire = 0; mix_channel[i].effects = NULL; mix_channel[i].paused = 0; } } num_channels = numchans; Mix_UnlockAudio(); return num_channels; } /* Return the actual mixer parameters */ int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels) { if (audio_opened) { if (frequency) { *frequency = mixer.freq; } if (format) { *format = mixer.format; } if (channels) { *channels = mixer.channels; } } return audio_opened; } typedef struct _MusicFragment { Uint8 *data; int size; struct _MusicFragment *next; } MusicFragment; static SDL_AudioSpec *Mix_LoadMusic_RW(SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) { int i; Mix_MusicType music_type; Mix_MusicInterface *interface = NULL; void *music = NULL; Sint64 start; SDL_bool playing; MusicFragment *first = NULL, *last = NULL, *fragment = NULL; int count = 0; int fragment_size; music_type = detect_music_type(src); if (!load_music_type(music_type) || !open_music_type(music_type)) { return NULL; } *spec = mixer; /* Use fragments sized on full audio frame boundaries - this'll do */ fragment_size = spec->size; start = SDL_RWtell(src); for (i = 0; i < get_num_music_interfaces(); ++i) { interface = get_music_interface(i); if (!interface->opened) { continue; } if (interface->type != music_type) { continue; } if (!interface->CreateFromRW || !interface->GetAudio) { continue; } /* These music interfaces are not safe to use while music is playing */ if (interface->api == MIX_MUSIC_CMD || interface->api == MIX_MUSIC_NATIVEMIDI) { continue; } music = interface->CreateFromRW(src, freesrc); if (music) { /* The interface owns the data source now */ freesrc = SDL_FALSE; break; } /* Reset the stream for the next decoder */ SDL_RWseek(src, start, RW_SEEK_SET); } if (!music) { if (freesrc) { SDL_RWclose(src); } Mix_SetError("Unrecognized audio format"); return NULL; } Mix_LockAudio(); if (interface->Play) { interface->Play(music, 1); } playing = SDL_TRUE; while (playing) { int left; fragment = (MusicFragment *)SDL_malloc(sizeof(*fragment)); if (!fragment) { /* Uh oh, out of memory, let's return what we have */ break; } fragment->data = (Uint8 *)SDL_malloc(fragment_size); if (!fragment->data) { /* Uh oh, out of memory, let's return what we have */ SDL_free(fragment); break; } fragment->next = NULL; left = interface->GetAudio(music, fragment->data, fragment_size); if (left > 0) { playing = SDL_FALSE; } else if (interface->IsPlaying) { playing = interface->IsPlaying(music); } fragment->size = (fragment_size - left); if (!first) { first = fragment; } if (last) { last->next = fragment; } last = fragment; ++count; } if (interface->Stop) { interface->Stop(music); } if (music) { interface->Delete(music); } Mix_UnlockAudio(); if (count > 0) { *audio_len = (count - 1) * fragment_size + last->size; *audio_buf = (Uint8 *)SDL_malloc(*audio_len); if (*audio_buf) { Uint8 *dst = *audio_buf; for (fragment = first; fragment; fragment = fragment->next) { SDL_memcpy(dst, fragment->data, fragment->size); dst += fragment->size; } } else { Mix_OutOfMemory(); spec = NULL; } } else { Mix_SetError("No audio data"); spec = NULL; } while (first) { fragment = first; first = first->next; SDL_free(fragment->data); SDL_free(fragment); } if (freesrc) { SDL_RWclose(src); } return spec; } /* Load a wave file */ Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc) { Uint8 magic[4]; Mix_Chunk *chunk; SDL_AudioSpec wavespec, *loaded; SDL_AudioCVT wavecvt; int samplesize; int wavfree; /* to decide how to free chunk->abuf. */ Uint8 *resized_buf; /* rcg06012001 Make sure src is valid */ if (!src) { Mix_SetError("Mix_LoadWAV_RW with NULL src"); return NULL; } /* Make sure audio has been opened */ if (!audio_opened) { Mix_SetError("Audio device hasn't been opened"); if (freesrc) { SDL_RWclose(src); } return NULL; } /* Allocate the chunk memory */ chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); if (chunk == NULL) { Mix_OutOfMemory(); if (freesrc) { SDL_RWclose(src); } return NULL; } /* Find out what kind of audio file this is */ if (SDL_RWread(src, magic, 1, 4) != 4) { SDL_free(chunk); if (freesrc) { SDL_RWclose(src); } Mix_SetError("Couldn't read first 4 bytes of audio data"); return NULL; } /* Seek backwards for compatibility with older loaders */ SDL_RWseek(src, -4, RW_SEEK_CUR); wavfree = 0; if (SDL_memcmp(magic, "WAVE", 4) == 0 || SDL_memcmp(magic, "RIFF", 4) == 0) { wavfree = 1; loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen); } else if (SDL_memcmp(magic, "FORM", 4) == 0) { loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen); } else if (SDL_memcmp(magic, "Crea", 4) == 0) { loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen); } else { loaded = Mix_LoadMusic_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen); } if (!loaded) { /* The individual loaders have closed src if needed */ SDL_free(chunk); return NULL; } #if 0 PrintFormat("Audio device", &mixer); PrintFormat("-- Wave file", &wavespec); #endif /* Build the audio converter and create conversion buffers */ if (wavespec.format != mixer.format || wavespec.channels != mixer.channels || wavespec.freq != mixer.freq) { if (SDL_BuildAudioCVT(&wavecvt, wavespec.format, wavespec.channels, wavespec.freq, mixer.format, mixer.channels, mixer.freq) < 0) { if (wavfree) { SDL_FreeWAV(chunk->abuf); } else { SDL_free(chunk->abuf); } SDL_free(chunk); return NULL; } samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels; wavecvt.len = chunk->alen & ~(samplesize - 1); wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult); if (wavecvt.buf == NULL) { Mix_OutOfMemory(); if (wavfree) { SDL_FreeWAV(chunk->abuf); } else { SDL_free(chunk->abuf); } SDL_free(chunk); return NULL; } SDL_memcpy(wavecvt.buf, chunk->abuf, wavecvt.len); if (wavfree) { SDL_FreeWAV(chunk->abuf); } else { SDL_free(chunk->abuf); } /* Run the audio converter */ if (SDL_ConvertAudio(&wavecvt) < 0) { SDL_free(wavecvt.buf); SDL_free(chunk); return NULL; } resized_buf = SDL_realloc(wavecvt.buf, wavecvt.len_cvt); if (resized_buf == NULL) { chunk->abuf = wavecvt.buf; } else { chunk->abuf = resized_buf; } chunk->alen = wavecvt.len_cvt; wavfree = 0; } chunk->allocated = (wavfree == 0) ? 1 : 2; /* see Mix_FreeChunk() */ chunk->volume = MIX_MAX_VOLUME; return chunk; } Mix_Chunk *Mix_LoadWAV(const char *file) { return Mix_LoadWAV_RW(SDL_RWFromFile(file, "rb"), 1); } /* Load a wave file of the mixer format from a memory buffer */ Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem) { Mix_Chunk *chunk; Uint8 magic[4]; /* Make sure audio has been opened */ if (! audio_opened) { Mix_SetError("Audio device hasn't been opened"); return NULL; } /* Allocate the chunk memory */ chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk)); if (chunk == NULL) { Mix_OutOfMemory(); return NULL; } /* Essentially just skip to the audio data (no error checking - fast) */ chunk->allocated = 0; mem += 12; /* WAV header */ do { SDL_memcpy(magic, mem, 4); mem += 4; chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0])); mem += 4; chunk->abuf = mem; mem += chunk->alen; } while (SDL_memcmp(magic, "data", 4) != 0); chunk->volume = MIX_MAX_VOLUME; return chunk; } /* Load raw audio data of the mixer format from a memory buffer */ Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len) { Mix_Chunk *chunk; /* Make sure audio has been opened */ if (! audio_opened) { Mix_SetError("Audio device hasn't been opened"); return NULL; } /* Allocate the chunk memory */ chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); if (chunk == NULL) { Mix_OutOfMemory(); return NULL; } /* Essentially just point at the audio data (no error checking - fast) */ chunk->allocated = 0; chunk->alen = len; chunk->abuf = mem; chunk->volume = MIX_MAX_VOLUME; return chunk; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ static void Mix_HaltChannel_locked(int which) { if (Mix_Playing(which)) { mix_channel[which].playing = 0; mix_channel[which].looping = 0; _Mix_channel_done_playing(which); } mix_channel[which].expire = 0; if (mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */ mix_channel[which].volume = mix_channel[which].fade_volume_reset; mix_channel[which].fading = MIX_NO_FADING; } /* Free an audio chunk previously loaded */ void Mix_FreeChunk(Mix_Chunk *chunk) { int i; /* Caution -- if the chunk is playing, the mixer will crash */ if (chunk) { /* Guarantee that this chunk isn't playing */ Mix_LockAudio(); if (mix_channel) { for (i = 0; i < num_channels; ++i) { if (chunk == mix_channel[i].chunk) { Mix_HaltChannel_locked(i); } } } Mix_UnlockAudio(); /* Actually free the chunk */ switch (chunk->allocated) { case 1: SDL_free(chunk->abuf); break; case 2: SDL_FreeWAV(chunk->abuf); break; } SDL_free(chunk); } } /* Set a function that is called after all mixing is performed. This can be used to provide real-time visual display of the audio stream or add a custom mixer filter for the stream data. */ void Mix_SetPostMix(void (SDLCALL *mix_func) (void *udata, Uint8 *stream, int len), void *arg) { Mix_LockAudio(); mix_postmix_data = arg; mix_postmix = mix_func; Mix_UnlockAudio(); } /* Add your own music player or mixer function. If 'mix_func' is NULL, the default music player is re-enabled. */ void Mix_HookMusic(void (SDLCALL *mix_func)(void *udata, Uint8 *stream, int len), void *arg) { Mix_LockAudio(); if (mix_func != NULL) { music_data = arg; mix_music = mix_func; } else { music_data = NULL; mix_music = music_mixer; } Mix_UnlockAudio(); } void *Mix_GetMusicHookData(void) { return music_data; } void Mix_ChannelFinished(void (SDLCALL *channel_finished)(int channel)) { Mix_LockAudio(); channel_done_callback = channel_finished; Mix_UnlockAudio(); } /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate them dynamically to the next sample if requested with a -1 value below. Returns the number of reserved channels. */ int Mix_ReserveChannels(int num) { if (num < 0) num = 0; if (num > num_channels) num = num_channels; reserved_channels = num; return num; } static int checkchunkintegral(Mix_Chunk *chunk) { int frame_width = 1; if ((mixer.format & 0xFF) == 16) frame_width = 2; frame_width *= mixer.channels; while (chunk->alen % frame_width) chunk->alen--; return chunk->alen; } /* Play an audio chunk on a specific channel. If the specified channel is -1, play on the first free channel. 'ticks' is the number of milliseconds at most to play the sample, or -1 if there is no limit. Returns which channel was used to play the sound. */ int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks) { int i; /* Don't play null pointers :-) */ if (chunk == NULL) { return Mix_SetError("Tried to play a NULL chunk"); } if (!checkchunkintegral(chunk)) { return Mix_SetError("Tried to play a chunk with a bad frame"); } /* Lock the mixer while modifying the playing channels */ Mix_LockAudio(); { /* If which is -1, play on the first free channel */ if (which == -1) { for (i = reserved_channels; i < num_channels; ++i) { if (!Mix_Playing(i)) break; } if (i == num_channels) { Mix_SetError("No free channels available"); which = -1; } else { which = i; } } else { if (Mix_Playing(which)) _Mix_channel_done_playing(which); } /* Queue up the audio data for this channel */ if (which >= 0 && which < num_channels) { Uint32 sdl_ticks = SDL_GetTicks(); mix_channel[which].samples = chunk->abuf; mix_channel[which].playing = (int)chunk->alen; mix_channel[which].looping = loops; mix_channel[which].chunk = chunk; mix_channel[which].paused = 0; mix_channel[which].fading = MIX_NO_FADING; mix_channel[which].start_time = sdl_ticks; mix_channel[which].expire = (ticks > 0) ? (sdl_ticks + (Uint32)ticks) : 0; } } Mix_UnlockAudio(); /* Return the channel on which the sound is being played */ return which; } int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops) { return Mix_PlayChannelTimed(channel, chunk, loops, -1); } /* Change the expiration delay for a channel */ int Mix_ExpireChannel(int which, int ticks) { int status = 0; if (which == -1) { int i; for (i = 0; i < num_channels; ++i) { status += Mix_ExpireChannel(i, ticks); } } else if (which < num_channels) { Mix_LockAudio(); mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + (Uint32)ticks) : 0; Mix_UnlockAudio(); ++status; } return status; } /* Fade in a sound on a channel, over ms milliseconds */ int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks) { int i; /* Don't play null pointers :-) */ if (chunk == NULL) { return -1; } if (!checkchunkintegral(chunk)) { return Mix_SetError("Tried to play a chunk with a bad frame"); } /* Lock the mixer while modifying the playing channels */ Mix_LockAudio(); { /* If which is -1, play on the first free channel */ if (which == -1) { for (i = reserved_channels; i < num_channels; ++i) { if (!Mix_Playing(i)) break; } if (i == num_channels) { which = -1; } else { which = i; } } else { if (Mix_Playing(which)) _Mix_channel_done_playing(which); } /* Queue up the audio data for this channel */ if (which >= 0 && which < num_channels) { Uint32 sdl_ticks = SDL_GetTicks(); mix_channel[which].samples = chunk->abuf; mix_channel[which].playing = (int)chunk->alen; mix_channel[which].looping = loops; mix_channel[which].chunk = chunk; mix_channel[which].paused = 0; if (mix_channel[which].fading == MIX_NO_FADING) { mix_channel[which].fade_volume_reset = mix_channel[which].volume; } mix_channel[which].fading = MIX_FADING_IN; mix_channel[which].fade_volume = mix_channel[which].volume; mix_channel[which].volume = 0; mix_channel[which].fade_length = (Uint32)ms; mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks; mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+(Uint32)ticks) : 0; } } Mix_UnlockAudio(); /* Return the channel on which the sound is being played */ return which; } int Mix_FadeInChannel(int channel, Mix_Chunk *chunk, int loops, int ms) { return Mix_FadeInChannelTimed(channel, chunk, loops, ms, -1); } /* Set volume of a particular channel */ int Mix_Volume(int which, int volume) { int i; int prev_volume = 0; if (which == -1) { for (i = 0; i < num_channels; ++i) { prev_volume += Mix_Volume(i, volume); } prev_volume /= num_channels; } else if (which < num_channels) { prev_volume = mix_channel[which].volume; if (volume >= 0) { if (volume > MIX_MAX_VOLUME) { volume = MIX_MAX_VOLUME; } mix_channel[which].volume = volume; } } return prev_volume; } /* Set volume of a particular chunk */ int Mix_VolumeChunk(Mix_Chunk *chunk, int volume) { int prev_volume; if (chunk == NULL) { return -1; } prev_volume = chunk->volume; if (volume >= 0) { if (volume > MIX_MAX_VOLUME) { volume = MIX_MAX_VOLUME; } chunk->volume = volume; } return prev_volume; } /* Halt playing of a particular channel */ int Mix_HaltChannel(int which) { int i; Mix_LockAudio(); if (which == -1) { for (i = 0; i < num_channels; ++i) { Mix_HaltChannel_locked(i); } } else if (which < num_channels) { Mix_HaltChannel_locked(which); } Mix_UnlockAudio(); return 0; } /* Halt playing of a particular group of channels */ int Mix_HaltGroup(int tag) { int i; for (i = 0; i < num_channels; ++i) { if (mix_channel[i].tag == tag) { Mix_HaltChannel(i); } } return 0; } /* Fade out a channel and then stop it automatically */ int Mix_FadeOutChannel(int which, int ms) { int status; status = 0; if (audio_opened) { if (which == -1) { int i; for (i = 0; i < num_channels; ++i) { status += Mix_FadeOutChannel(i, ms); } } else if (which < num_channels) { Mix_LockAudio(); if (Mix_Playing(which) && (mix_channel[which].volume > 0) && (mix_channel[which].fading != MIX_FADING_OUT)) { mix_channel[which].fade_volume = mix_channel[which].volume; mix_channel[which].fade_length = (Uint32)ms; mix_channel[which].ticks_fade = SDL_GetTicks(); /* only change fade_volume_reset if we're not fading. */ if (mix_channel[which].fading == MIX_NO_FADING) { mix_channel[which].fade_volume_reset = mix_channel[which].volume; } mix_channel[which].fading = MIX_FADING_OUT; ++status; } Mix_UnlockAudio(); } } return status; } /* Halt playing of a particular group of channels */ int Mix_FadeOutGroup(int tag, int ms) { int i; int status = 0; for (i = 0; i < num_channels; ++i) { if (mix_channel[i].tag == tag) { status += Mix_FadeOutChannel(i,ms); } } return status; } Mix_Fading Mix_FadingChannel(int which) { if (which < 0 || which >= num_channels) { return MIX_NO_FADING; } return mix_channel[which].fading; } /* Check the status of a specific channel. If the specified mix_channel is -1, check all mix channels. */ int Mix_Playing(int which) { int status; status = 0; if (which == -1) { int i; for (i = 0; i < num_channels; ++i) { if ((mix_channel[i].playing > 0) || mix_channel[i].looping) { ++status; } } } else if (which < num_channels) { if ((mix_channel[which].playing > 0) || mix_channel[which].looping) { ++status; } } return status; } /* rcg06072001 Get the chunk associated with a channel. */ Mix_Chunk *Mix_GetChunk(int channel) { Mix_Chunk *retval = NULL; if ((channel >= 0) && (channel < num_channels)) { retval = mix_channel[channel].chunk; } return retval; } /* Close the mixer, halting all playing audio */ void Mix_CloseAudio(void) { int i; if (audio_opened) { if (audio_opened == 1) { for (i = 0; i < num_channels; i++) { Mix_UnregisterAllEffects(i); } Mix_UnregisterAllEffects(MIX_CHANNEL_POST); close_music(); Mix_SetMusicCMD(NULL); Mix_HaltChannel(-1); _Mix_DeinitEffects(); SDL_CloseAudioDevice(audio_device); audio_device = 0; SDL_free(mix_channel); mix_channel = NULL; /* rcg06042009 report available decoders at runtime. */ SDL_free((void *)chunk_decoders); chunk_decoders = NULL; num_decoders = 0; } --audio_opened; } } /* Pause a particular channel (or all) */ void Mix_Pause(int which) { Uint32 sdl_ticks = SDL_GetTicks(); if (which == -1) { int i; for (i = 0; i < num_channels; ++i) { if (Mix_Playing(i)) { mix_channel[i].paused = sdl_ticks; } } } else if (which < num_channels) { if (Mix_Playing(which)) { mix_channel[which].paused = sdl_ticks; } } } /* Resume a paused channel */ void Mix_Resume(int which) { Uint32 sdl_ticks = SDL_GetTicks(); Mix_LockAudio(); if (which == -1) { int i; for (i = 0; i < num_channels; ++i) { if (Mix_Playing(i)) { if (mix_channel[i].expire > 0) { mix_channel[i].expire += sdl_ticks - mix_channel[i].paused; } mix_channel[i].paused = 0; } } } else if (which < num_channels) { if (Mix_Playing(which)) { if (mix_channel[which].expire > 0) { mix_channel[which].expire += sdl_ticks - mix_channel[which].paused; } mix_channel[which].paused = 0; } } Mix_UnlockAudio(); } int Mix_Paused(int which) { if (which < 0) { int status = 0; int i; for (i = 0; i < num_channels; ++i) { if (Mix_Playing(i) && mix_channel[i].paused) { ++status; } } return status; } if (which < num_channels) { return (Mix_Playing(which) && mix_channel[which].paused != 0); } return 0; } /* Change the group of a channel */ int Mix_GroupChannel(int which, int tag) { if (which < 0 || which > num_channels) { return 0; } Mix_LockAudio(); mix_channel[which].tag = tag; Mix_UnlockAudio(); return 1; } /* Assign several consecutive channels to a group */ int Mix_GroupChannels(int from, int to, int tag) { int status = 0; for (; from <= to; ++from) { status += Mix_GroupChannel(from, tag); } return status; } /* Finds the first available channel in a group of channels */ int Mix_GroupAvailable(int tag) { int i; for (i = 0; i < num_channels; i++) { if ((tag == -1 || tag == mix_channel[i].tag) && !Mix_Playing(i)) { return i; } } return -1; } int Mix_GroupCount(int tag) { int count = 0; int i; if (tag == -1) { return num_channels; /* minor optimization; no need to go through the loop. */ } for (i = 0; i < num_channels; i++) { if (mix_channel[i].tag == tag) { ++count; } } return count; } /* Finds the "oldest" sample playing in a group of channels */ int Mix_GroupOldest(int tag) { int chan = -1; Uint32 mintime = SDL_GetTicks(); int i; for (i = 0; i < num_channels; i++) { if ((mix_channel[i].tag == tag || tag == -1) && Mix_Playing(i) && mix_channel[i].start_time <= mintime) { mintime = mix_channel[i].start_time; chan = i; } } return chan; } /* Finds the "most recent" (i.e. last) sample playing in a group of channels */ int Mix_GroupNewer(int tag) { int chan = -1; Uint32 maxtime = 0; int i; for (i = 0; i < num_channels; i++) { if ((mix_channel[i].tag == tag || tag == -1) && Mix_Playing(i) && mix_channel[i].start_time >= maxtime) { maxtime = mix_channel[i].start_time; chan = i; } } return chan; } /* * rcg06122001 The special effects exportable API. * Please see effect_*.c for internally-implemented effects, such * as Mix_SetPanning(). */ /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg) { effect_info *new_e; if (!e) { Mix_SetError("Internal error"); return 0; } if (f == NULL) { Mix_SetError("NULL effect callback"); return 0; } new_e = SDL_malloc(sizeof(effect_info)); if (new_e == NULL) { Mix_OutOfMemory(); return 0; } new_e->callback = f; new_e->done_callback = d; new_e->udata = arg; new_e->next = NULL; /* add new effect to end of linked list... */ if (*e == NULL) { *e = new_e; } else { effect_info *cur = *e; while (1) { if (cur->next == NULL) { cur->next = new_e; break; } cur = cur->next; } } return 1; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f) { effect_info *cur; effect_info *prev = NULL; effect_info *next = NULL; if (!e) { Mix_SetError("Internal error"); return 0; } for (cur = *e; cur != NULL; cur = cur->next) { if (cur->callback == f) { next = cur->next; if (cur->done_callback != NULL) { cur->done_callback(channel, cur->udata); } SDL_free(cur); if (prev == NULL) { /* removing first item of list? */ *e = next; } else { prev->next = next; } return 1; } prev = cur; } Mix_SetError("No such effect registered"); return 0; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ static int _Mix_remove_all_effects(int channel, effect_info **e) { effect_info *cur; effect_info *next; if (!e) { Mix_SetError("Internal error"); return 0; } for (cur = *e; cur != NULL; cur = next) { next = cur->next; if (cur->done_callback != NULL) { cur->done_callback(channel, cur->udata); } SDL_free(cur); } *e = NULL; return 1; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg) { effect_info **e = NULL; if (channel == MIX_CHANNEL_POST) { e = &posteffects; } else { if ((channel < 0) || (channel >= num_channels)) { Mix_SetError("Invalid channel number"); return 0; } e = &mix_channel[channel].effects; } return _Mix_register_effect(e, f, d, arg); } int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg) { int retval; Mix_LockAudio(); retval = _Mix_RegisterEffect_locked(channel, f, d, arg); Mix_UnlockAudio(); return retval; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f) { effect_info **e = NULL; if (channel == MIX_CHANNEL_POST) { e = &posteffects; } else { if ((channel < 0) || (channel >= num_channels)) { Mix_SetError("Invalid channel number"); return 0; } e = &mix_channel[channel].effects; } return _Mix_remove_effect(channel, e, f); } int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f) { int retval; Mix_LockAudio(); retval = _Mix_UnregisterEffect_locked(channel, f); Mix_UnlockAudio(); return retval; } /* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */ int _Mix_UnregisterAllEffects_locked(int channel) { effect_info **e = NULL; if (channel == MIX_CHANNEL_POST) { e = &posteffects; } else { if ((channel < 0) || (channel >= num_channels)) { Mix_SetError("Invalid channel number"); return 0; } e = &mix_channel[channel].effects; } return _Mix_remove_all_effects(channel, e); } int Mix_UnregisterAllEffects(int channel) { int retval; Mix_LockAudio(); retval = _Mix_UnregisterAllEffects_locked(channel); Mix_UnlockAudio(); return retval; } void Mix_LockAudio(void) { SDL_LockAudioDevice(audio_device); } void Mix_UnlockAudio(void) { SDL_UnlockAudioDevice(audio_device); } int Mix_MasterVolume(int volume) { int prev_volume = SDL_AtomicGet(&master_volume); if (volume < 0) { return prev_volume; } if (volume > SDL_MIX_MAXVOLUME) { volume = SDL_MIX_MAXVOLUME; } SDL_AtomicSet(&master_volume, volume); return prev_volume; } /* end of mixer.c ... */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/0000755000076500000240000000000014553251273014467 5ustar valvestaffSDL2_mixer-2.8.0/src/codecs/music_timidity.c0000644000076500000240000001750514551252471017676 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MIDI files with timidity */ #ifdef MUSIC_MID_TIMIDITY #include "music_timidity.h" #include "timidity/timidity.h" typedef struct { int play_count; MidiSong *song; SDL_AudioStream *stream; void *buffer; Sint32 buffer_size; int volume; } TIMIDITY_Music; static int TIMIDITY_Seek(void *context, double position); static void TIMIDITY_Delete(void *context); /* Config file should contain any other directory that needs * to be added to the search path. The library adds the path * of the config file to its search path, too. */ #if defined(__WIN32__) # define TIMIDITY_CFG "C:\\TIMIDITY\\TIMIDITY.CFG" #elif defined(__OS2__) # define TIMIDITY_CFG "C:\\TIMIDITY\\TIMIDITY.CFG" # define TIMIDITY_CFG_ETC "/@unixroot/etc/timidity/timidity.cfg" #else /* unix: */ # define TIMIDITY_CFG_ETC "/etc/timidity.cfg" # define TIMIDITY_CFG_FREEPATS "/etc/timidity/freepats.cfg" #endif static int TIMIDITY_Open(const SDL_AudioSpec *spec) { const char *cfg; int rc = -1; (void) spec; cfg = SDL_getenv("TIMIDITY_CFG"); if(!cfg) cfg = Mix_GetTimidityCfg(); if (cfg) { return Timidity_Init(cfg); /* env or user override: no other tries */ } #if defined(TIMIDITY_CFG) if (rc < 0) rc = Timidity_Init(TIMIDITY_CFG); #endif #if defined(TIMIDITY_CFG_ETC) if (rc < 0) rc = Timidity_Init(TIMIDITY_CFG_ETC); #endif #if defined(TIMIDITY_CFG_FREEPATS) if (rc < 0) rc = Timidity_Init(TIMIDITY_CFG_FREEPATS); #endif if (rc < 0) rc = Timidity_Init(NULL); /* library's default cfg. */ return rc; } static void TIMIDITY_Close(void) { Timidity_Exit(); } void *TIMIDITY_CreateFromRW(SDL_RWops *src, int freesrc) { TIMIDITY_Music *music; SDL_AudioSpec spec; SDL_bool need_stream = SDL_FALSE; music = (TIMIDITY_Music *)SDL_calloc(1, sizeof(*music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; SDL_memcpy(&spec, &music_spec, sizeof(spec)); if (spec.channels > 2) { need_stream = SDL_TRUE; spec.channels = 2; } music->song = Timidity_LoadSong(src, &spec); if (!music->song) { TIMIDITY_Delete(music); return NULL; } if (need_stream) { music->stream = SDL_NewAudioStream(spec.format, spec.channels, spec.freq, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { TIMIDITY_Delete(music); return NULL; } music->buffer_size = spec.samples * (SDL_AUDIO_BITSIZE(spec.format) / 8) * spec.channels; music->buffer = SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { SDL_OutOfMemory(); TIMIDITY_Delete(music); return NULL; } } if (freesrc) { SDL_RWclose(src); } return music; } static void TIMIDITY_SetVolume(void *context, int volume) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; music->volume = volume; Timidity_SetVolume(music->song, volume); } static int TIMIDITY_GetVolume(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; return music->volume; } static int TIMIDITY_Play(void *context, int play_count) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; music->play_count = play_count; Timidity_Start(music->song); return TIMIDITY_Seek(music, 0.0); } static SDL_bool TIMIDITY_IsPlaying(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; return Timidity_IsActive(music->song); } static int TIMIDITY_GetSome(void *context, void *data, int bytes, SDL_bool *done) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; int filled, amount, expected; if (music->stream) { filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } if (music->stream) { expected = music->buffer_size; amount = Timidity_PlaySome(music->song, music->buffer, music->buffer_size); if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else { expected = bytes; amount = Timidity_PlaySome(music->song, data, bytes); } if (amount < expected) { if (music->play_count == 1) { /* We didn't consume anything and we're done */ music->play_count = 0; } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (TIMIDITY_Play(music, play_count) < 0) { return -1; } } } if (music->stream) { /* We'll pick it up from the stream next time around */ return 0; } else { /* We wrote output data */ return amount; } } static int TIMIDITY_GetAudio(void *context, void *data, int bytes) { return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, TIMIDITY_GetSome); } static int TIMIDITY_Seek(void *context, double position) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; Timidity_Seek(music->song, (Uint32)(position * 1000)); return 0; } static double TIMIDITY_Tell(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; return Timidity_GetSongTime(music->song) / 1000.0; } static double TIMIDITY_Duration(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; return Timidity_GetSongLength(music->song) / 1000.0; } static void TIMIDITY_Delete(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; if (music->song) { Timidity_FreeSong(music->song); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } SDL_free(music); } static void TIMIDITY_Stop(void *context) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; Timidity_Stop(music->song); } Mix_MusicInterface Mix_MusicInterface_TIMIDITY = { "TIMIDITY", MIX_MUSIC_TIMIDITY, MUS_MID, SDL_FALSE, SDL_FALSE, NULL, /* Load */ TIMIDITY_Open, TIMIDITY_CreateFromRW, NULL, /* CreateFromFile */ TIMIDITY_SetVolume, TIMIDITY_GetVolume, TIMIDITY_Play, TIMIDITY_IsPlaying, TIMIDITY_GetAudio, NULL, /* Jump */ TIMIDITY_Seek, TIMIDITY_Tell, TIMIDITY_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ NULL, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ TIMIDITY_Stop, TIMIDITY_Delete, TIMIDITY_Close, NULL /* Unload */ }; #endif /* MUSIC_MID_TIMIDITY */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_gme.h0000644000076500000240000000212214551252471016604 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing chiptune files with libGME */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_GME; SDL2_mixer-2.8.0/src/codecs/music_ogg.h0000644000076500000240000000215414551252471016615 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports Ogg Vorbis music streams */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_OGG; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_mpg123.c0000644000076500000240000004051214553225265017050 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MP3 files with mpg123 */ #ifdef MUSIC_MP3_MPG123 #include "SDL_loadso.h" #include "SDL_assert.h" #include "music_mpg123.h" #include "mp3utils.h" #include /* For SEEK_SET */ #ifdef MPG123_HEADER #include MPG123_HEADER #else #include #endif #ifdef _MSC_VER typedef ptrdiff_t MIX_SSIZE_T; #else typedef ssize_t MIX_SSIZE_T; #endif typedef struct { int loaded; void *handle; int (*mpg123_close)(mpg123_handle *mh); void (*mpg123_delete)(mpg123_handle *mh); void (*mpg123_exit)(void); int (*mpg123_format)( mpg123_handle *mh, long rate, int channels, int encodings ); int (*mpg123_format_none)(mpg123_handle *mh); int (*mpg123_getformat)( mpg123_handle *mh, long *rate, int *channels, int *encoding ); int (*mpg123_init)(void); mpg123_handle *(*mpg123_new)(const char* decoder, int *error); int (*mpg123_open_handle)(mpg123_handle *mh, void *iohandle); const char* (*mpg123_plain_strerror)(int errcode); void (*mpg123_rates)(const long **list, size_t *number); #if (MPG123_API_VERSION >= 45) /* api (but not abi) change as of mpg123-1.26.0 */ int (*mpg123_read)(mpg123_handle *mh, void *outmemory, size_t outmemsize, size_t *done ); #else int (*mpg123_read)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done ); #endif int (*mpg123_replace_reader_handle)( mpg123_handle *mh, MIX_SSIZE_T (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) ); off_t (*mpg123_seek)( mpg123_handle *mh, off_t sampleoff, int whence ); off_t (*mpg123_tell)( mpg123_handle *mh); off_t (*mpg123_length)(mpg123_handle *mh); const char* (*mpg123_strerror)(mpg123_handle *mh); } mpg123_loader; static mpg123_loader mpg123; #ifdef MPG123_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ mpg123.FUNC = (SIG) SDL_LoadFunction(mpg123.handle, #FUNC); \ if (mpg123.FUNC == NULL) { SDL_UnloadObject(mpg123.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ mpg123.FUNC = FUNC; \ if (mpg123.FUNC == NULL) { Mix_SetError("Missing mpg123.framework"); return -1; } #endif static int MPG123_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (mpg123.loaded == 0) { #ifdef MPG123_DYNAMIC mpg123.handle = SDL_LoadObject(MPG123_DYNAMIC); if (mpg123.handle == NULL) { return -1; } #endif FUNCTION_LOADER(mpg123_close, int (*)(mpg123_handle *mh)) FUNCTION_LOADER(mpg123_delete, void (*)(mpg123_handle *mh)) FUNCTION_LOADER(mpg123_exit, void (*)(void)) FUNCTION_LOADER(mpg123_format, int (*)( mpg123_handle *mh, long rate, int channels, int encodings )) FUNCTION_LOADER(mpg123_format_none, int (*)(mpg123_handle *mh)) FUNCTION_LOADER(mpg123_getformat, int (*)( mpg123_handle *mh, long *rate, int *channels, int *encoding )) FUNCTION_LOADER(mpg123_init, int (*)(void)) FUNCTION_LOADER(mpg123_new, mpg123_handle *(*)(const char* decoder, int *error)) FUNCTION_LOADER(mpg123_open_handle, int (*)(mpg123_handle *mh, void *iohandle)) FUNCTION_LOADER(mpg123_plain_strerror, const char* (*)(int errcode)) FUNCTION_LOADER(mpg123_rates, void (*)(const long **list, size_t *number)) #if (MPG123_API_VERSION >= 45) /* api (but not abi) change as of mpg123-1.26.0 */ FUNCTION_LOADER(mpg123_read, int (*)(mpg123_handle *mh, void *outmemory, size_t outmemsize, size_t *done )) #else FUNCTION_LOADER(mpg123_read, int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done )) #endif FUNCTION_LOADER(mpg123_replace_reader_handle, int (*)( mpg123_handle *mh, MIX_SSIZE_T (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*) )) FUNCTION_LOADER(mpg123_seek, off_t (*)( mpg123_handle *mh, off_t sampleoff, int whence )) FUNCTION_LOADER(mpg123_tell, off_t (*)( mpg123_handle *mh)) FUNCTION_LOADER(mpg123_length, off_t (*)(mpg123_handle *mh)) FUNCTION_LOADER(mpg123_strerror, const char* (*)(mpg123_handle *mh)) } ++mpg123.loaded; return 0; } static void MPG123_Unload(void) { if (mpg123.loaded == 0) { return; } if (mpg123.loaded == 1) { #ifdef MPG123_DYNAMIC SDL_UnloadObject(mpg123.handle); #endif } --mpg123.loaded; } typedef struct { struct mp3file_t mp3file; int play_count; int freesrc; int volume; mpg123_handle* handle; SDL_AudioStream *stream; unsigned char *buffer; size_t buffer_size; long sample_rate; off_t total_length; Mix_MusicMetaTags tags; } MPG123_Music; static int MPG123_Seek(void *context, double secs); static void MPG123_Delete(void *context); static int mpg123_format_to_sdl(int fmt) { switch (fmt) { case MPG123_ENC_SIGNED_8: return AUDIO_S8; case MPG123_ENC_UNSIGNED_8: return AUDIO_U8; case MPG123_ENC_SIGNED_16: return AUDIO_S16SYS; case MPG123_ENC_UNSIGNED_16: return AUDIO_U16SYS; case MPG123_ENC_SIGNED_32: return AUDIO_S32SYS; case MPG123_ENC_FLOAT_32: return AUDIO_F32SYS; default: return -1; } } /*#define DEBUG_MPG123*/ #ifdef DEBUG_MPG123 static const char *mpg123_format_str(int fmt) { switch (fmt) { #define f(x) case x: return #x; f(MPG123_ENC_UNSIGNED_8) f(MPG123_ENC_UNSIGNED_16) f(MPG123_ENC_SIGNED_8) f(MPG123_ENC_SIGNED_16) f(MPG123_ENC_SIGNED_32) f(MPG123_ENC_FLOAT_32) #undef f } return "unknown"; } #endif static char const* mpg_err(mpg123_handle* mpg, int result) { char const* err = "unknown error"; if (mpg && result == MPG123_ERR) { err = mpg123.mpg123_strerror(mpg); } else { err = mpg123.mpg123_plain_strerror(result); } return err; } /* we're gonna override mpg123's I/O with these wrappers for RWops */ static MIX_SSIZE_T rwops_read(void* p, void* dst, size_t n) { return (MIX_SSIZE_T)MP3_RWread((struct mp3file_t *)p, dst, 1, n); } static off_t rwops_seek(void* p, off_t offset, int whence) { return (off_t)MP3_RWseek((struct mp3file_t *)p, (Sint64)offset, whence); } static void rwops_cleanup(void* p) { (void)p; /* do nothing, we will free the file later */ } static int MPG123_Open(const SDL_AudioSpec *spec) { (void)spec; if (mpg123.mpg123_init() != MPG123_OK) { return Mix_SetError("mpg123_init() failed"); } return 0; } static void *MPG123_CreateFromRW(SDL_RWops *src, int freesrc) { MPG123_Music *music; int result, format, channels, encoding; long rate; const long *rates; size_t i, num_rates; music = (MPG123_Music*)SDL_calloc(1, sizeof(*music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; if (MP3_RWinit(&music->mp3file, src) < 0) { SDL_free(music); return NULL; } meta_tags_init(&music->tags); if (mp3_read_tags(&music->tags, &music->mp3file, SDL_TRUE) < 0) { SDL_free(music); Mix_SetError("music_mpg123: corrupt mp3 file (bad tags.)"); return NULL; } /* Just assume 16-bit 2 channel audio for now */ music->buffer_size = music_spec.samples * sizeof(Sint16) * 2; music->buffer = (unsigned char *)SDL_malloc(music->buffer_size); if (!music->buffer) { MPG123_Delete(music); SDL_OutOfMemory(); return NULL; } music->handle = mpg123.mpg123_new(0, &result); if (result != MPG123_OK) { MPG123_Delete(music); Mix_SetError("mpg123_new failed"); return NULL; } result = mpg123.mpg123_replace_reader_handle( music->handle, rwops_read, rwops_seek, rwops_cleanup ); if (result != MPG123_OK) { Mix_SetError("mpg123_replace_reader_handle: %s", mpg_err(music->handle, result)); MPG123_Delete(music); return NULL; } result = mpg123.mpg123_format_none(music->handle); if (result != MPG123_OK) { Mix_SetError("mpg123_format_none: %s", mpg_err(music->handle, result)); MPG123_Delete(music); return NULL; } mpg123.mpg123_rates(&rates, &num_rates); for (i = 0; i < num_rates; ++i) { const int channels = (MPG123_MONO|MPG123_STEREO); const int formats = (MPG123_ENC_SIGNED_8 | MPG123_ENC_UNSIGNED_8 | MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16 | MPG123_ENC_SIGNED_32 | MPG123_ENC_FLOAT_32); mpg123.mpg123_format(music->handle, rates[i], channels, formats); } result = mpg123.mpg123_open_handle(music->handle, &music->mp3file); if (result != MPG123_OK) { Mix_SetError("mpg123_open_handle: %s", mpg_err(music->handle, result)); MPG123_Delete(music); return NULL; } result = mpg123.mpg123_getformat(music->handle, &rate, &channels, &encoding); if (result != MPG123_OK) { Mix_SetError("mpg123_getformat: %s", mpg_err(music->handle, result)); MPG123_Delete(music); return NULL; } #ifdef DEBUG_MPG123 printf("MPG123 format: %s, channels: %d, rate: %ld\n", mpg123_format_str(encoding), channels, rate); #endif format = mpg123_format_to_sdl(encoding); SDL_assert(format != -1); music->sample_rate = rate; music->stream = SDL_NewAudioStream((SDL_AudioFormat)format, (Uint8)channels, (int)rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { MPG123_Delete(music); return NULL; } music->total_length = mpg123.mpg123_length(music->handle); music->freesrc = freesrc; return music; } static void MPG123_SetVolume(void *context, int volume) { MPG123_Music *music = (MPG123_Music *)context; music->volume = volume; } static int MPG123_GetVolume(void *context) { MPG123_Music *music = (MPG123_Music *)context; return music->volume; } static int MPG123_Play(void *context, int play_count) { MPG123_Music *music = (MPG123_Music *)context; music->play_count = play_count; return MPG123_Seek(music, 0.0); } static void MPG123_Stop(void *context) { MPG123_Music *music = (MPG123_Music *)context; SDL_AudioStreamClear(music->stream); } /* read some mp3 stream data and convert it for output */ static int MPG123_GetSome(void *context, void *data, int bytes, SDL_bool *done) { MPG123_Music *music = (MPG123_Music *)context; int filled, result; size_t amount = 0; long rate; int channels, encoding, format; if (music->stream) { filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } result = mpg123.mpg123_read(music->handle, music->buffer, music->buffer_size, &amount); switch (result) { case MPG123_OK: if (SDL_AudioStreamPut(music->stream, music->buffer, (int)amount) < 0) { return -1; } break; case MPG123_NEW_FORMAT: result = mpg123.mpg123_getformat(music->handle, &rate, &channels, &encoding); if (result != MPG123_OK) { return Mix_SetError("mpg123_getformat: %s", mpg_err(music->handle, result)); } #ifdef DEBUG_MPG123 printf("MPG123 format: %s, channels: %d, rate: %ld\n", mpg123_format_str(encoding), channels, rate); #endif format = mpg123_format_to_sdl(encoding); SDL_assert(format != -1); if (music->stream) { SDL_FreeAudioStream(music->stream); } music->stream = SDL_NewAudioStream((SDL_AudioFormat)format, (Uint8)channels, (int)rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -1; } music->sample_rate = rate; break; case MPG123_DONE: if (amount > 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, (int)amount) < 0) { return -1; } break; } if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (MPG123_Play(music, play_count) < 0) { return -1; } } break; default: return Mix_SetError("mpg123_read: %s", mpg_err(music->handle, result)); } return 0; } static int MPG123_GetAudio(void *context, void *data, int bytes) { MPG123_Music *music = (MPG123_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, MPG123_GetSome); } static int MPG123_Seek(void *context, double secs) { MPG123_Music *music = (MPG123_Music *)context; off_t offset = (off_t)(music->sample_rate * secs); if ((offset = mpg123.mpg123_seek(music->handle, offset, SEEK_SET)) < 0) { return Mix_SetError("mpg123_seek: %s", mpg_err(music->handle, (int)-offset)); } return 0; } static double MPG123_Tell(void *context) { MPG123_Music *music = (MPG123_Music *)context; off_t offset = 0; if (!music->sample_rate) { return 0.0; } if ((offset = mpg123.mpg123_tell(music->handle)) < 0) { return Mix_SetError("mpg123_tell: %s", mpg_err(music->handle, (int)-offset)); } return (double)offset / music->sample_rate; } /* Return music duration in seconds */ static double MPG123_Duration(void *context) { MPG123_Music *music = (MPG123_Music *)context; if (music->total_length < 0) { return -1.0; } return (double)music->total_length / music->sample_rate; } static const char* MPG123_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { MPG123_Music *music = (MPG123_Music *)context; return meta_tags_get(&music->tags, tag_type); } static void MPG123_Delete(void *context) { MPG123_Music *music = (MPG123_Music *)context; meta_tags_clear(&music->tags); if (music->handle) { mpg123.mpg123_close(music->handle); mpg123.mpg123_delete(music->handle); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->mp3file.src); } SDL_free(music); } static void MPG123_Close(void) { mpg123.mpg123_exit(); } Mix_MusicInterface Mix_MusicInterface_MPG123 = { "MPG123", MIX_MUSIC_MPG123, MUS_MP3, SDL_FALSE, SDL_FALSE, MPG123_Load, MPG123_Open, MPG123_CreateFromRW, NULL, /* CreateFromFile */ MPG123_SetVolume, MPG123_GetVolume, MPG123_Play, NULL, /* IsPlaying */ MPG123_GetAudio, NULL, /* Jump */ MPG123_Seek, MPG123_Tell, MPG123_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ MPG123_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ MPG123_Stop, MPG123_Delete, MPG123_Close, MPG123_Unload }; #endif /* MUSIC_MP3_MPG123 */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/mp3utils.h0000644000076500000240000000372714551252471016430 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file provides utility functions to work with MP3 files including reading of tags. */ #ifndef MIX_MP3UTILS_H #define MIX_MP3UTILS_H #include "music.h" #define ENABLE_ALL_MP3_TAGS #if defined(MUSIC_WAV) || defined(ENABLE_ALL_MP3_TAGS) #define ENABLE_ID3V2_TAG struct mp3file_t { SDL_RWops *src; Sint64 start, length, pos; }; #endif #ifdef ENABLE_ALL_MP3_TAGS extern int mp3_read_tags(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, SDL_bool keep_id3v2); #endif /* ENABLE_ALL_MP3_TAGS */ #ifdef ENABLE_ID3V2_TAG extern int read_id3v2_from_mem(Mix_MusicMetaTags *out_tags, Uint8 *data, size_t length); #endif #ifdef ENABLE_ALL_MP3_TAGS extern int MP3_RWinit(struct mp3file_t *fil, SDL_RWops *src); extern size_t MP3_RWread(struct mp3file_t *fil, void *ptr, size_t size, size_t maxnum); extern Sint64 MP3_RWseek(struct mp3file_t *fil, Sint64 offset, int whence); extern Sint64 MP3_RWtell(struct mp3file_t *fil); #endif /* ENABLE_ALL_MP3_TAGS */ #endif /* MIX_MP3UTILS_H */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/load_voc.c0000644000076500000240000003411314553225265016425 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This is the source needed to decode a Creative Labs VOC file into a waveform. It's pretty straightforward once you get going. The only externally-callable function is Mix_LoadVOC_RW(), which is meant to act as identically to SDL_LoadWAV_RW() as possible. This file by Ryan C. Gordon (icculus@icculus.org). Heavily borrowed from sox v12.17.1's voc.c. (http://www.freshmeat.net/projects/sox/) */ #include "SDL_mixer.h" #include "load_voc.h" /* Private data for VOC file */ typedef struct vocstuff { Uint32 rest; /* bytes remaining in current block */ Uint32 rate; /* rate code (byte) of this chunk */ int silent; /* sound or silence? */ Uint32 srate; /* rate code (byte) of silence */ Uint32 blockseek; /* start of current output block */ Uint32 samples; /* number of samples output */ Uint32 size; /* word length of data */ Uint8 channels; /* number of sound channels */ int has_extended; /* Has an extended block been read? */ } vs_t; /* Size field */ /* SJB: note that the 1st 3 are sometimes used as sizeof(type) */ #define ST_SIZE_BYTE 1 #define ST_SIZE_8BIT 1 #define ST_SIZE_WORD 2 #define ST_SIZE_16BIT 2 #define ST_SIZE_DWORD 4 #define ST_SIZE_32BIT 4 #define ST_SIZE_FLOAT 5 #define ST_SIZE_DOUBLE 6 #define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */ /* Style field */ #define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */ #define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */ #define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */ #define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */ #define ST_ENCODING_ADPCM 5 /* Compressed PCM */ #define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */ #define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */ #define VOC_TERM 0 #define VOC_DATA 1 #define VOC_CONT 2 #define VOC_SILENCE 3 #define VOC_MARKER 4 #define VOC_TEXT 5 #define VOC_LOOP 6 #define VOC_LOOPEND 7 #define VOC_EXTENDED 8 #define VOC_DATA_16 9 #define VOC_BAD_RATE ~((Uint32)0) static int voc_check_header(SDL_RWops *src) { /* VOC magic header */ Uint8 signature[20]; /* "Creative Voice File\032" */ Uint16 datablockofs; SDL_RWseek(src, 0, RW_SEEK_SET); if (SDL_RWread(src, signature, sizeof(signature), 1) != 1) { return 0; } if (SDL_memcmp(signature, "Creative Voice File\032", sizeof(signature)) != 0) { Mix_SetError("Unrecognized file type (not VOC)"); return 0; } /* get the offset where the first datablock is located */ if (SDL_RWread(src, &datablockofs, sizeof(Uint16), 1) != 1) { return 0; } datablockofs = SDL_SwapLE16(datablockofs); if (SDL_RWseek(src, datablockofs, RW_SEEK_SET) != datablockofs) { return 0; } return 1; /* success! */ } /* voc_check_header */ /* Read next block header, save info, leave position at start of data */ static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec) { Uint8 bits24[3]; Uint8 uc, block; Uint32 sblen; Uint16 new_rate_short; Uint32 new_rate_long; Uint8 trash[6]; Uint16 period; unsigned int i; v->silent = 0; while (v->rest == 0) { if (SDL_RWread(src, &block, sizeof(block), 1) != 1) { return 1; /* assume that's the end of the file. */ } if (block == VOC_TERM) { return 1; } if (SDL_RWread(src, bits24, sizeof(bits24), 1) != 1) { return 1; /* assume that's the end of the file. */ } /* Size is an 24-bit value. Ugh. */ sblen = (Uint32)((bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16)); switch(block) { case VOC_DATA: if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } /* When DATA block preceeded by an EXTENDED */ /* block, the DATA blocks rate value is invalid */ if (!v->has_extended) { if (uc == 0) { Mix_SetError("VOC Sample rate is zero?"); return 0; } if ((v->rate != VOC_BAD_RATE) && (uc != v->rate)) { Mix_SetError("VOC sample rate codes differ"); return 0; } v->rate = uc; spec->freq = (Uint16)(1000000.0/(256 - v->rate)); v->channels = 1; } if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } if (uc != 0) { Mix_SetError("VOC decoder only interprets 8-bit data"); return 0; } v->has_extended = 0; v->rest = sblen - 2; v->size = ST_SIZE_BYTE; return 1; case VOC_DATA_16: if (SDL_RWread(src, &new_rate_long, sizeof(new_rate_long), 1) != 1) { return 0; } new_rate_long = SDL_SwapLE32(new_rate_long); if (new_rate_long == 0) { Mix_SetError("VOC Sample rate is zero?"); return 0; } if ((v->rate != VOC_BAD_RATE) && (new_rate_long != v->rate)) { Mix_SetError("VOC sample rate codes differ"); return 0; } v->rate = new_rate_long; spec->freq = (int)new_rate_long; if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } switch (uc) { case 8: v->size = ST_SIZE_BYTE; break; case 16: v->size = ST_SIZE_WORD; break; default: Mix_SetError("VOC with unknown data size"); return 0; } if (SDL_RWread(src, &v->channels, sizeof(Uint8), 1) != 1) { return 0; } if (SDL_RWread(src, trash, sizeof(Uint8), 6) != 6) { return 0; } v->rest = sblen - 12; return 1; case VOC_CONT: v->rest = sblen; return 1; case VOC_SILENCE: if (SDL_RWread(src, &period, sizeof(period), 1) != 1) { return 0; } period = SDL_SwapLE16(period); if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } if (uc == 0) { Mix_SetError("VOC silence sample rate is zero"); return 0; } /* * Some silence-packed files have gratuitously * different sample rate codes in silence. * Adjust period. */ if ((v->rate != VOC_BAD_RATE) && (uc != v->rate)) period = (Uint16)((period * (256 - uc))/(256 - v->rate)); else v->rate = uc; v->rest = period; v->silent = 1; return 1; case VOC_LOOP: case VOC_LOOPEND: for (i = 0; i < sblen; i++) { /* skip repeat loops. */ if (SDL_RWread(src, trash, sizeof(Uint8), 1) != 1) { return 0; } } break; case VOC_EXTENDED: /* An Extended block is followed by a data block */ /* Set this byte so we know to use the rate */ /* value from the extended block and not the */ /* data block. */ v->has_extended = 1; if (SDL_RWread(src, &new_rate_short, sizeof(new_rate_short), 1) != 1) { return 0; } new_rate_short = SDL_SwapLE16(new_rate_short); if (new_rate_short == 0) { Mix_SetError("VOC sample rate is zero"); return 0; } if ((v->rate != VOC_BAD_RATE) && (new_rate_short != v->rate)) { Mix_SetError("VOC sample rate codes differ"); return 0; } v->rate = new_rate_short; if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } if (uc != 0) { Mix_SetError("VOC decoder only interprets 8-bit data"); return 0; } if (SDL_RWread(src, &uc, sizeof(uc), 1) != 1) { return 0; } if (uc) /* Stereo */ spec->channels = 2; /* VOC_EXTENDED may be read before spec->channels inited: */ else spec->channels = 1; /* Needed number of channels before finishing compute for rate */ spec->freq = (256000000L / (65536L - v->rate)) / spec->channels; /* An extended block must be followed by a data */ /* block to be valid so loop back to top so it */ /* can be grabed. */ continue; case VOC_MARKER: if (SDL_RWread(src, trash, sizeof(Uint8), 2) != 2) { return 0; } /* fallthrough */ default: /* text block or other krapola. */ for (i = 0; i < sblen; i++) { if (SDL_RWread(src, trash, sizeof(Uint8), 1) != 1) { return 0; } } if (block == VOC_TEXT) { continue; /* get next block */ } } } return 1; } static Uint32 voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec) { Uint32 done = 0; Uint8 silence = 0x80; if (v->rest == 0) { if (!voc_get_block(src, v, spec)) { return 0; } } if (v->rest == 0) { return 0; } if (v->silent) { if (v->size == ST_SIZE_WORD) { silence = 0x00; } /* Fill in silence */ SDL_memset(buf, silence, v->rest); done = v->rest; v->rest = 0; } else { done = (Uint32)SDL_RWread(src, buf, 1, v->rest); v->rest -= done; if (v->size == ST_SIZE_WORD) { #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) Uint16 *samples = (Uint16 *)buf; for (; v->rest > 0; v->rest -= 2) { *samples = SDL_SwapLE16(*samples); samples++; } #endif done >>= 1; } } return done; } /* voc_read */ /* don't call this directly; use Mix_LoadWAV_RW() for now. */ SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) { vs_t v; int was_error = 1; int samplesize; Uint8 *fillptr; void *ptr; if (!src || !audio_buf || !audio_len) { /* sanity checks. */ goto done; } if (!voc_check_header(src)) { goto done; } SDL_memset(&v, 0, sizeof(vs_t)); v.rate = VOC_BAD_RATE; v.rest = 0; v.has_extended = 0; *audio_buf = NULL; *audio_len = 0; SDL_memset(spec, '\0', sizeof(SDL_AudioSpec)); if (!voc_get_block(src, &v, spec)) { goto done; } if (v.rate == VOC_BAD_RATE) { Mix_SetError("VOC data had no sound!"); goto done; } if (v.size == 0) { Mix_SetError("VOC data had invalid word size!"); goto done; } spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8); if (spec->channels == 0) { spec->channels = v.channels; } *audio_len = v.rest; *audio_buf = (v.rest == 0) ? NULL : SDL_malloc(v.rest); if (*audio_buf == NULL) { goto done; } fillptr = *audio_buf; while (voc_read(src, &v, fillptr, spec)) { if (!voc_get_block(src, &v, spec)) { goto done; } *audio_len += v.rest; ptr = SDL_realloc(*audio_buf, *audio_len); if (ptr == NULL) { SDL_free(*audio_buf); *audio_buf = NULL; *audio_len = 0; goto done; } *audio_buf = ptr; fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest); } spec->samples = (Uint16)(*audio_len / v.size); was_error = 0; /* success, baby! */ /* Don't return a buffer that isn't a multiple of samplesize */ samplesize = ((spec->format & 0xFF)/8)*spec->channels; *audio_len &= (Uint32) ~(samplesize-1); done: if (freesrc && src) { SDL_RWclose(src); } if (was_error) { spec = NULL; } return spec; } /* Mix_LoadVOC_RW */ /* end of load_voc.c ... */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/load_aiff.c0000644000076500000240000001660314551252471016544 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This is the source needed to decode an AIFF file into a waveform. It's pretty straightforward once you get going. The only externally-callable function is Mix_LoadAIFF_RW(), which is meant to act as identically to SDL_LoadWAV_RW() as possible. This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) 8SVX file support added by Marc Le Douarain (mavati@club-internet.fr) in december 2002. */ #include "SDL_endian.h" #include "SDL_mixer.h" #include "load_aiff.h" /*********************************************/ /* Define values for AIFF (IFF audio) format */ /*********************************************/ #define FORM 0x4d524f46 /* "FORM" */ #define AIFF 0x46464941 /* "AIFF" */ #define SSND 0x444e5353 /* "SSND" */ #define COMM 0x4d4d4f43 /* "COMM" */ #define _8SVX 0x58565338 /* "8SVX" */ #define VHDR 0x52444856 /* "VHDR" */ #define BODY 0x59444F42 /* "BODY" */ /* This function was taken from libsndfile. I don't pretend to fully * understand it. */ static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) { /* Is the frequency outside of what we can represent with Uint32? */ if ((sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40) || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)) return 0; return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]); } /* This function is based on SDL_LoadWAV_RW(). */ SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) { int found_SSND; int found_COMM; int found_VHDR; int found_BODY; int was_error = 0; Sint64 start = 0; Uint32 chunk_type; Uint32 chunk_length; Sint64 next_chunk; /* AIFF magic header */ Uint32 FORMchunk; Uint32 AIFFmagic; /* SSND chunk */ Uint32 offset; Uint32 blocksize; /* COMM format chunk */ Uint16 channels = 0; Uint32 numsamples = 0; Uint16 samplesize = 0; Uint8 sane_freq[10]; Uint32 frequency = 0; /* Make sure we are passed a valid data source */ if (src == NULL) { was_error = 1; goto done; } FORMchunk = SDL_ReadLE32(src); chunk_length = SDL_ReadBE32(src); if (chunk_length == AIFF) { /* The FORMchunk has already been read */ AIFFmagic = chunk_length; chunk_length = FORMchunk; FORMchunk = FORM; } else { AIFFmagic = SDL_ReadLE32(src); } if ((FORMchunk != FORM) || ((AIFFmagic != AIFF) && (AIFFmagic != _8SVX))) { Mix_SetError("Unrecognized file type (not AIFF nor 8SVX)"); was_error = 1; goto done; } /* TODO: Better santity-checking. */ found_SSND = 0; found_COMM = 0; found_VHDR = 0; found_BODY = 0; do { chunk_type = SDL_ReadLE32(src); chunk_length = SDL_ReadBE32(src); next_chunk = SDL_RWtell(src) + chunk_length; /* Paranoia to avoid infinite loops */ if (chunk_length == 0) { break; } switch (chunk_type) { case SSND: found_SSND = 1; offset = SDL_ReadBE32(src); blocksize = SDL_ReadBE32(src); start = SDL_RWtell(src) + offset; (void)blocksize; /* unused. */ break; case COMM: found_COMM = 1; channels = SDL_ReadBE16(src); numsamples = SDL_ReadBE32(src); samplesize = SDL_ReadBE16(src); SDL_RWread(src, sane_freq, sizeof(sane_freq), 1); frequency = SANE_to_Uint32(sane_freq); if (frequency == 0) { Mix_SetError("Bad AIFF sample frequency"); was_error = 1; goto done; } break; case VHDR: found_VHDR = 1; SDL_ReadBE32(src); SDL_ReadBE32(src); SDL_ReadBE32(src); frequency = SDL_ReadBE16(src); channels = 1; samplesize = 8; break; case BODY: found_BODY = 1; numsamples = chunk_length; start = SDL_RWtell(src); break; default: break; } /* a 0 pad byte can be stored for any odd-length chunk */ if (chunk_length&1) next_chunk++; } while ((((AIFFmagic == AIFF) && (!found_SSND || !found_COMM)) || ((AIFFmagic == _8SVX) && (!found_VHDR || !found_BODY))) && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1); if ((AIFFmagic == AIFF) && !found_SSND) { Mix_SetError("Bad AIFF (no SSND chunk)"); was_error = 1; goto done; } if ((AIFFmagic == AIFF) && !found_COMM) { Mix_SetError("Bad AIFF (no COMM chunk)"); was_error = 1; goto done; } if ((AIFFmagic == _8SVX) && !found_VHDR) { Mix_SetError("Bad 8SVX (no VHDR chunk)"); was_error = 1; goto done; } if ((AIFFmagic == _8SVX) && !found_BODY) { Mix_SetError("Bad 8SVX (no BODY chunk)"); was_error = 1; goto done; } /* Decode the audio data format */ SDL_memset(spec, 0, sizeof(*spec)); spec->freq = frequency; switch (samplesize) { case 8: spec->format = AUDIO_S8; break; case 16: spec->format = AUDIO_S16MSB; break; default: Mix_SetError("Unsupported AIFF samplesize"); was_error = 1; goto done; } spec->channels = (Uint8) channels; spec->samples = 4096; /* Good default buffer size */ *audio_len = channels * numsamples * (samplesize / 8); *audio_buf = (Uint8 *)SDL_malloc(*audio_len); if (*audio_buf == NULL) { Mix_OutOfMemory(); return NULL; } SDL_RWseek(src, start, RW_SEEK_SET); if (SDL_RWread(src, *audio_buf, *audio_len, 1) != 1) { Mix_SetError("Unable to read audio data"); return NULL; } /* Don't return a buffer that isn't a multiple of samplesize */ *audio_len &= ~((samplesize / 8) - 1); done: if (freesrc && src) { SDL_RWclose(src); } if (was_error) { spec = NULL; } return spec; } /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_flac.h0000644000076500000240000000207214551252471016745 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_FLAC; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_drflac.c0000644000076500000240000003145614553225265017301 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_FLAC_DRFLAC #include "music_drflac.h" #include "mp3utils.h" #include "../utils.h" #include "SDL.h" #define DR_FLAC_IMPLEMENTATION #if defined(__GNUC__) && (__GNUC__ >= 4) && \ !(defined(_WIN32) || defined(__EMX__)) #define DRFLAC_API __attribute__((visibility("hidden"))) #elif defined(__APPLE__) #define DRFLAC_API __private_extern__ #else #define DRFLAC_API /* just in case.. */ #endif #define DR_FLAC_NO_STDIO #define DRFLAC_ASSERT(expression) #define DRFLAC_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz)) #define DRFLAC_MOVE_MEMORY(dst, src, sz) SDL_memmove((dst), (src), (sz)) #define DRFLAC_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz)) #define DRFLAC_MALLOC(sz) SDL_malloc((sz)) #define DRFLAC_REALLOC(p, sz) SDL_realloc((p), (sz)) #define DRFLAC_FREE(p) SDL_free((p)) #include "dr_libs/dr_flac.h" typedef struct { struct mp3file_t file; drflac *dec; int play_count; int freesrc; int volume; int status; int sample_rate; int channels; SDL_AudioStream *stream; drflac_int16 *buffer; int buffer_size; int loop; SDL_bool loop_flag; Sint64 loop_start; Sint64 loop_end; Sint64 loop_len; Mix_MusicMetaTags tags; } DRFLAC_Music; static size_t DRFLAC_ReadCB(void *context, void *buf, size_t size) { DRFLAC_Music *music = (DRFLAC_Music *)context; return MP3_RWread(&music->file, buf, 1, size); } static drflac_bool32 DRFLAC_SeekCB(void *context, int offset, drflac_seek_origin origin) { DRFLAC_Music *music = (DRFLAC_Music *)context; int whence = (origin == drflac_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR; if (MP3_RWseek(&music->file, offset, whence) < 0) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } static void DRFLAC_MetaCB(void *context, drflac_metadata *metadata) { DRFLAC_Music *music = (DRFLAC_Music *)context; if (metadata->type == DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO) { music->sample_rate = metadata->data.streaminfo.sampleRate; music->channels = metadata->data.streaminfo.channels; } else if (metadata->type == DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT) { drflac_uint32 i; char *param, *argument, *value; SDL_bool is_loop_length = SDL_FALSE; const char *pRunningData = (const char *)metadata->data.vorbis_comment.pComments; for (i = 0; i < metadata->data.vorbis_comment.commentCount; ++i) { drflac_uint32 commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; param = (char *)SDL_malloc(commentLength + 1); if (param) { SDL_memcpy(param, pRunningData, commentLength); param[commentLength] = '\0'; argument = param; value = SDL_strchr(param, '='); if (value == NULL) { value = param + SDL_strlen(param); } else { *(value++) = '\0'; } /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from * string if it is present at position 4. */ if (_Mix_IsLoopTag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) { SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); } if (SDL_strcasecmp(argument, "LOOPSTART") == 0) music->loop_start = _Mix_ParseTime(value, music->sample_rate); else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { music->loop_len = SDL_strtoll(value, NULL, 10); is_loop_length = SDL_TRUE; } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { music->loop_end = _Mix_ParseTime(value, music->sample_rate); is_loop_length = SDL_FALSE; } else if (SDL_strcasecmp(argument, "TITLE") == 0) { meta_tags_set(&music->tags, MIX_META_TITLE, value); } else if (SDL_strcasecmp(argument, "ARTIST") == 0) { meta_tags_set(&music->tags, MIX_META_ARTIST, value); } else if (SDL_strcasecmp(argument, "ALBUM") == 0) { meta_tags_set(&music->tags, MIX_META_ALBUM, value); } else if (SDL_strcasecmp(argument, "COPYRIGHT") == 0) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, value); } SDL_free(param); } pRunningData += commentLength; } if (is_loop_length) { music->loop_end = music->loop_start + music->loop_len; } else { music->loop_len = music->loop_end - music->loop_start; } /* Ignore invalid loop tag */ if (music->loop_start < 0 || music->loop_len < 0 || music->loop_end < 0) { music->loop_start = 0; music->loop_len = 0; music->loop_end = 0; } } } static int DRFLAC_Seek(void *context, double position); static void *DRFLAC_CreateFromRW(SDL_RWops *src, int freesrc) { DRFLAC_Music *music; music = (DRFLAC_Music *)SDL_calloc(1, sizeof(DRFLAC_Music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; if (MP3_RWinit(&music->file, src) < 0) { SDL_free(music); return NULL; } meta_tags_init(&music->tags); music->dec = drflac_open_with_metadata(DRFLAC_ReadCB, DRFLAC_SeekCB, DRFLAC_MetaCB, music, NULL); if (!music->dec) { SDL_free(music); Mix_SetError("music_drflac: corrupt flac file (bad stream)."); return NULL; } /* We should have channels and sample rate set up here */ music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)music->channels, music->sample_rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { SDL_OutOfMemory(); drflac_close(music->dec); SDL_free(music); return NULL; } music->buffer_size = music_spec.samples * sizeof(drflac_int16) * music->channels; music->buffer = (drflac_int16*)SDL_calloc(1, music->buffer_size); if (!music->buffer) { drflac_close(music->dec); SDL_OutOfMemory(); SDL_free(music); return NULL; } /* loop_start, loop_end and loop_len get set by metadata callback if tags * are present in metadata. */ if ((music->loop_end > 0) && (music->loop_end <= (Sint64)music->dec->totalPCMFrameCount) && (music->loop_start < music->loop_end)) { music->loop = 1; } music->freesrc = freesrc; return music; } static void DRFLAC_SetVolume(void *context, int volume) { DRFLAC_Music *music = (DRFLAC_Music *)context; music->volume = volume; } static int DRFLAC_GetVolume(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; return music->volume; } /* Starts the playback. */ static int DRFLAC_Play(void *context, int play_count) { DRFLAC_Music *music = (DRFLAC_Music *)context; music->play_count = play_count; return DRFLAC_Seek(music, 0.0); } static void DRFLAC_Stop(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; SDL_AudioStreamClear(music->stream); } static int DRFLAC_GetSome(void *context, void *data, int bytes, SDL_bool *done) { DRFLAC_Music *music = (DRFLAC_Music *)context; int filled; drflac_uint64 amount; if (music->stream) { filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } if (music->loop_flag) { if (!drflac_seek_to_pcm_frame(music->dec, music->loop_start)) { return Mix_SetError("drflac_seek_to_pcm_frame() failed"); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } music->play_count = play_count; music->loop_flag = SDL_FALSE; } } amount = drflac_read_pcm_frames_s16(music->dec, music_spec.samples, music->buffer); if (amount > 0) { if (music->loop && (music->play_count != 1) && ((Sint64)music->dec->currentPCMFrame >= music->loop_end)) { amount -= (music->dec->currentPCMFrame - music->loop_end); music->loop_flag = SDL_TRUE; } if (SDL_AudioStreamPut(music->stream, music->buffer, (int)amount * sizeof(drflac_int16) * music->channels) < 0) { return -1; } } else { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (DRFLAC_Play(music, play_count) < 0) { return -1; } } } return 0; } static int DRFLAC_GetAudio(void *context, void *data, int bytes) { DRFLAC_Music *music = (DRFLAC_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, DRFLAC_GetSome); } static int DRFLAC_Seek(void *context, double position) { DRFLAC_Music *music = (DRFLAC_Music *)context; drflac_uint64 destpos = (drflac_uint64)(position * music->sample_rate); drflac_seek_to_pcm_frame(music->dec, destpos); return 0; } static double DRFLAC_Tell(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; return (double)music->dec->currentPCMFrame / music->sample_rate; } static double DRFLAC_Duration(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; drflac_uint64 samples = music->dec->totalPCMFrameCount; return (double)samples / music->sample_rate; } static double DRFLAC_LoopStart(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; if (music->loop > 0) { return (double)music->loop_start / music->sample_rate; } return -1.0; } static double DRFLAC_LoopEnd(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; if (music->loop > 0) { return (double)music->loop_end / music->sample_rate; } return -1.0; } static double DRFLAC_LoopLength(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; if (music->loop > 0) { return (double)music->loop_len / music->sample_rate; } return -1.0; } static const char* DRFLAC_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { DRFLAC_Music *music = (DRFLAC_Music *)context; return meta_tags_get(&music->tags, tag_type); } static void DRFLAC_Delete(void *context) { DRFLAC_Music *music = (DRFLAC_Music *)context; drflac_close(music->dec); meta_tags_clear(&music->tags); if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->file.src); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_DRFLAC = { "DRFLAC", MIX_MUSIC_DRFLAC, MUS_FLAC, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ DRFLAC_CreateFromRW, NULL, /* CreateFromFile */ DRFLAC_SetVolume, DRFLAC_GetVolume, DRFLAC_Play, NULL, /* IsPlaying */ DRFLAC_GetAudio, NULL, /* Jump */ DRFLAC_Seek, DRFLAC_Tell, DRFLAC_Duration, DRFLAC_LoopStart, DRFLAC_LoopEnd, DRFLAC_LoopLength, DRFLAC_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ DRFLAC_Stop, DRFLAC_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_FLAC_DRFLAC */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_xmp.c0000644000076500000240000003202514553251241016634 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_MOD_XMP #include "SDL_loadso.h" #include "music_xmp.h" #ifdef LIBXMP_HEADER #include LIBXMP_HEADER #else #include #endif /* libxmp >= 4.5.0 constified several funcs */ /* and also added load using file callbacks */ #if (XMP_VERCODE < 0x040500) struct xmp_callbacks { unsigned long (*read_func)(void *, unsigned long, unsigned long, void *); int (*seek_func)(void *, long, int); long (*tell_func)(void *); int (*close_func)(void*); }; #define LIBXMP_CONST #else #define LIBXMP_CONST const #endif typedef struct { int loaded; void *handle; xmp_context (*xmp_create_context)(void); int (*xmp_load_module_from_memory)(xmp_context, LIBXMP_CONST void *, long); int (*xmp_load_module_from_callbacks)(xmp_context, void *, struct xmp_callbacks); int (*xmp_start_player)(xmp_context, int, int); void (*xmp_end_player)(xmp_context); void (*xmp_get_module_info)(xmp_context, struct xmp_module_info *); int (*xmp_play_buffer)(xmp_context, void *, int, int); int (*xmp_set_position)(xmp_context, int); int (*xmp_seek_time)(xmp_context, int); void (*xmp_get_frame_info)(xmp_context, struct xmp_frame_info *); void (*xmp_stop_module)(xmp_context); void (*xmp_release_module)(xmp_context); void (*xmp_free_context)(xmp_context); } xmp_loader; static xmp_loader libxmp; #ifdef XMP_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ libxmp.FUNC = (SIG) SDL_LoadFunction(libxmp.handle, #FUNC); \ if (libxmp.FUNC == NULL) { SDL_UnloadObject(libxmp.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ libxmp.FUNC = FUNC; \ if (libxmp.FUNC == NULL) { Mix_SetError("Missing xmp.framework"); return -1; } #endif static int XMP_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (libxmp.loaded == 0) { #ifdef XMP_DYNAMIC libxmp.handle = SDL_LoadObject(XMP_DYNAMIC); if (libxmp.handle == NULL) { return -1; } #endif FUNCTION_LOADER(xmp_create_context, xmp_context(*)(void)) FUNCTION_LOADER(xmp_load_module_from_memory, int(*)(xmp_context,LIBXMP_CONST void *,long)) FUNCTION_LOADER(xmp_start_player, int(*)(xmp_context,int,int)) FUNCTION_LOADER(xmp_end_player, void(*)(xmp_context)) FUNCTION_LOADER(xmp_get_module_info, void(*)(xmp_context,struct xmp_module_info*)) FUNCTION_LOADER(xmp_play_buffer, int(*)(xmp_context,void*,int,int)) FUNCTION_LOADER(xmp_set_position, int(*)(xmp_context,int)) FUNCTION_LOADER(xmp_seek_time, int(*)(xmp_context,int)) FUNCTION_LOADER(xmp_get_frame_info, void(*)(xmp_context,struct xmp_frame_info*)) FUNCTION_LOADER(xmp_stop_module, void(*)(xmp_context)) FUNCTION_LOADER(xmp_release_module, void(*)(xmp_context)) FUNCTION_LOADER(xmp_free_context, void(*)(xmp_context)) #if defined(XMP_DYNAMIC) libxmp.xmp_load_module_from_callbacks = (int (*)(xmp_context,void*,struct xmp_callbacks)) SDL_LoadFunction(libxmp.handle, "xmp_load_module_from_callbacks"); if (libxmp.xmp_load_module_from_callbacks == NULL) { SDL_ClearError(); /* xmp_load_module_from_callbacks is optional. */ } #elif (XMP_VERCODE >= 0x040500) libxmp.xmp_load_module_from_callbacks = xmp_load_module_from_callbacks; #else libxmp.xmp_load_module_from_callbacks = NULL; #endif } ++libxmp.loaded; return 0; } static void XMP_Unload(void) { if (libxmp.loaded == 0) { return; } if (libxmp.loaded == 1) { #ifdef XMP_DYNAMIC SDL_UnloadObject(libxmp.handle); #endif } --libxmp.loaded; } typedef struct { SDL_RWops *src; Sint64 src_offset; int volume; int play_count; struct xmp_module_info mi; struct xmp_frame_info fi; xmp_context ctx; SDL_AudioStream *stream; void *buffer; int buffer_size; Mix_MusicMetaTags tags; } XMP_Music; static int XMP_Seek(void *ctx, double pos); static void XMP_Delete(void *ctx); static void libxmp_set_error(int e) { const char *msg; switch (e) { case -XMP_ERROR_INTERNAL: msg = "Internal error in libxmp"; break; case -XMP_ERROR_FORMAT: msg = "Unrecognized file format"; break; case -XMP_ERROR_LOAD: msg = "Error loading file"; break; case -XMP_ERROR_DEPACK: msg = "Error depacking file"; break; case -XMP_ERROR_SYSTEM: msg = "System error in libxmp"; break; case -XMP_ERROR_INVALID: msg = "Invalid parameter"; break; case -XMP_ERROR_STATE: msg = "Invalid player state"; break; default: msg = "Unknown error"; break; } Mix_SetError("XMP: %s", msg); } static unsigned long xmp_fread(void *dst, unsigned long len, unsigned long nmemb, void *src) { XMP_Music *music = (XMP_Music *)src; return (unsigned long)SDL_RWread(music->src, dst, len, nmemb); } static int xmp_fseek(void *src, long offset, int whence) { XMP_Music *music = (XMP_Music *)src; Sint64 offset64 = (Sint64)offset; if (whence == RW_SEEK_SET) { offset64 += music->src_offset; } return (SDL_RWseek(music->src, offset64, whence) < 0) ? -1 : 0; } static long xmp_ftell(void *src) { XMP_Music *music = (XMP_Music *)src; return (long)(SDL_RWtell(music->src) - music->src_offset); } /* Load a libxmp stream from an SDL_RWops object */ void *XMP_CreateFromRW(SDL_RWops *src, int freesrc) { XMP_Music *music; struct xmp_callbacks file_callbacks = { xmp_fread, xmp_fseek, xmp_ftell, NULL }; int err = 0; music = (XMP_Music *)SDL_calloc(1, sizeof(*music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->ctx = libxmp.xmp_create_context(); if (!music->ctx) { SDL_OutOfMemory(); goto e0; } music->buffer_size = music_spec.samples * 2 * 2; music->buffer = SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { SDL_OutOfMemory(); goto e1; } if (libxmp.xmp_load_module_from_callbacks) { music->src = src; music->src_offset = SDL_RWtell(src); err = libxmp.xmp_load_module_from_callbacks(music->ctx, music, file_callbacks); } else { size_t size; void *mem = SDL_LoadFile_RW(src, &size, SDL_FALSE); if (!mem) { SDL_OutOfMemory(); goto e1; } err = libxmp.xmp_load_module_from_memory(music->ctx, mem, (long)size); SDL_free(mem); } if (err < 0) { libxmp_set_error(err); goto e1; } err = libxmp.xmp_start_player(music->ctx, music_spec.freq, 0); if (err < 0) { libxmp_set_error(err); goto e2; } music->volume = MIX_MAX_VOLUME; music->stream = SDL_NewAudioStream(AUDIO_S16SYS, 2, music_spec.freq, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { goto e3; } meta_tags_init(&music->tags); libxmp.xmp_get_module_info(music->ctx, &music->mi); if (music->mi.mod->name[0]) { meta_tags_set(&music->tags, MIX_META_TITLE, music->mi.mod->name); } if (music->mi.comment) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, music->mi.comment); } if (freesrc) { SDL_RWclose(src); } return music; e3: libxmp.xmp_end_player(music->ctx); e2: libxmp.xmp_release_module(music->ctx); e1: libxmp.xmp_free_context(music->ctx); e0: SDL_free(music->buffer); SDL_free(music); return NULL; } /* Set the volume for a libxmp stream */ static void XMP_SetVolume(void *context, int volume) { XMP_Music *music = (XMP_Music *)context; music->volume = volume; } /* Get the volume for a libxmp stream */ static int XMP_GetVolume(void *context) { XMP_Music *music = (XMP_Music *)context; return music->volume; } /* Start playback of a given libxmp stream */ static int XMP_Play(void *context, int play_count) { XMP_Music *music = (XMP_Music *)context; music->play_count = play_count; return XMP_Seek(music, 0); } /* Clean-up the output buffer */ static void XMP_Stop(void *context) { XMP_Music *music = (XMP_Music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with xmp_play() */ static int XMP_GetSome(void *context, void *data, int bytes, SDL_bool *done) { XMP_Music *music = (XMP_Music *)context; int filled, amount, ret; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } /* if the data write is partial, rest of the buffer will be zero-filled. * the loop param is the max number that the current sequence of song * will be looped, or 0 to disable loop checking: 0 for play_count < 0 * for an endless loop, or 1 for our own loop checks to do their job. */ ret = libxmp.xmp_play_buffer(music->ctx, music->buffer, music->buffer_size, (music->play_count > 0)); amount = music->buffer_size; if (ret == 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else { if (ret != -XMP_END) { return -1; } if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (XMP_Play(music, play_count) < 0) { return -1; } } } return 0; } static int XMP_GetAudio(void *context, void *data, int bytes) { XMP_Music *music = (XMP_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, XMP_GetSome); } /* Jump to a given order */ static int XMP_Jump(void *context, int order) { XMP_Music *music = (XMP_Music *)context; return libxmp.xmp_set_position(music->ctx, order); } /* Jump (seek) to a given position */ static int XMP_Seek(void *context, double pos) { XMP_Music *music = (XMP_Music *)context; libxmp.xmp_seek_time(music->ctx, (int)(pos * 1000)); libxmp.xmp_play_buffer(music->ctx, NULL, 0, 0); /* reset internal state. */ return 0; } static double XMP_Tell(void *context) { XMP_Music *music = (XMP_Music *)context; libxmp.xmp_get_frame_info(music->ctx, &music->fi); return music->fi.time / 1000.0; } static double XMP_Duration(void *context) { XMP_Music *music = (XMP_Music *)context; libxmp.xmp_get_frame_info(music->ctx, &music->fi); return music->fi.total_time / 1000.0; } static const char* XMP_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { XMP_Music *music = (XMP_Music *)context; return meta_tags_get(&music->tags, tag_type); } /* Close the given libxmp stream */ static void XMP_Delete(void *context) { XMP_Music *music = (XMP_Music *)context; meta_tags_clear(&music->tags); if (music->ctx) { libxmp.xmp_stop_module(music->ctx); libxmp.xmp_end_player(music->ctx); libxmp.xmp_release_module(music->ctx); libxmp.xmp_free_context(music->ctx); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_XMP = { "XMP", MIX_MUSIC_LIBXMP, MUS_MOD, SDL_FALSE, SDL_FALSE, XMP_Load, NULL, /* Open */ XMP_CreateFromRW, NULL, /* CreateFromFile */ XMP_SetVolume, XMP_GetVolume, XMP_Play, NULL, /* IsPlaying */ XMP_GetAudio, XMP_Jump, XMP_Seek, XMP_Tell, XMP_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ XMP_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ XMP_Stop, XMP_Delete, NULL, /* Close */ XMP_Unload }; #endif /* MUSIC_MOD_XMP */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_wavpack.h0000644000076500000240000000215714551252471017500 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports streaming WavPack files */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_WAVPACK; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_wav.h0000644000076500000240000000214714551252471016640 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports streaming WAV files */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_WAV; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/timidity/0000755000076500000240000000000014553251273016323 5ustar valvestaffSDL2_mixer-2.8.0/src/codecs/timidity/resample.h0000644000076500000240000000114014277744147020312 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. resample.h */ #ifndef TIMIDITY_RESAMPLE_H #define TIMIDITY_RESAMPLE_H #define resample_voice TIMI_NAMESPACE(resample_voice) #define pre_resample TIMI_NAMESPACE(pre_resample) extern sample_t *resample_voice(MidiSong *song, int v, Sint32 *countptr); extern void pre_resample(MidiSong *song, Sample *sp); #endif /* TIMIDITY_RESAMPLE_H */ SDL2_mixer-2.8.0/src/codecs/timidity/common.c0000644000076500000240000000474714277744147020005 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. common.c */ #include "SDL.h" #include "options.h" #include "common.h" #if defined(__WIN32__) || defined(__OS2__) #define CHAR_DIRSEP '\\' #define is_dirsep(c) ((c) == '/' || (c) == '\\') #define is_abspath(p) ((p)[0] == '/' || (p)[0] == '\\' || ((p)[0] && (p)[1] == ':')) #else /* unix: */ #define CHAR_DIRSEP '/' #define is_dirsep(c) ((c) == '/') #define is_abspath(p) ((p)[0] == '/') #endif /* The paths in this list will be tried whenever we're reading a file */ typedef struct _PathList { char *path; struct _PathList *next; } PathList; static PathList *pathlist = NULL; /* This is meant to find and open files for reading */ SDL_RWops *timi_openfile(const char *name) { SDL_RWops *rw; if (!name || !(*name)) { SNDDBG(("Attempted to open nameless file.\n")); return NULL; } /* First try the given name */ SNDDBG(("Trying to open %s\n", name)); if ((rw = SDL_RWFromFile(name, "rb")) != NULL) return rw; if (!is_abspath(name)) { char current_filename[1024]; PathList *plp = pathlist; char *p; size_t l; while (plp) { /* Try along the path then */ *current_filename = 0; p = current_filename; l = SDL_strlen(plp->path); if(l >= sizeof(current_filename) - 3) l = 0; if(l) { SDL_memcpy(current_filename, plp->path, l); p += l; if(!is_dirsep(p[-1])) { *p++ = CHAR_DIRSEP; l++; } } SDL_strlcpy(p, name, sizeof(current_filename) - l); SNDDBG(("Trying to open %s\n", current_filename)); if ((rw = SDL_RWFromFile(current_filename, "rb"))) return rw; plp = plp->next; } } /* Nothing could be opened. */ SNDDBG(("Could not open %s\n", name)); return NULL; } /* This adds a directory to the path list */ int timi_add_pathlist(const char *s, size_t l) { PathList *plp = SDL_malloc(sizeof(PathList)); if (plp == NULL) return -2; plp->path = SDL_malloc(l + 1); if (plp->path == NULL) { SDL_free (plp); return -2; } SDL_memcpy(plp->path, s, l); plp->path[l] = 0; plp->next = pathlist; pathlist = plp; return 0; } void timi_free_pathlist(void) { PathList *plp = pathlist; PathList *next; while (plp) { next = plp->next; SDL_free(plp->path); SDL_free(plp); plp = next; } pathlist = NULL; } SDL2_mixer-2.8.0/src/codecs/timidity/tables.c0000644000076500000240000002710714277744147017762 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. */ #include "SDL.h" #include "common.h" #include "tables.h" const Sint32 freq_table[128] = { 8176, 8662, 9177, 9723, 10301, 10913, 11562, 12250, 12978, 13750, 14568, 15434, 16352, 17324, 18354, 19445, 20602, 21827, 23125, 24500, 25957, 27500, 29135, 30868, 32703, 34648, 36708, 38891, 41203, 43654, 46249, 48999, 51913, 55000, 58270, 61735, 65406, 69296, 73416, 77782, 82407, 87307, 92499, 97999, 103826, 110000, 116541, 123471, 130813, 138591, 146832, 155563, 164814, 174614, 184997, 195998, 207652, 220000, 233082, 246942, 261626, 277183, 293665, 311127, 329628, 349228, 369994, 391995, 415305, 440000, 466164, 493883, 523251, 554365, 587330, 622254, 659255, 698456, 739989, 783991, 830609, 880000, 932328, 987767, 1046502, 1108731, 1174659, 1244508, 1318510, 1396913, 1479978, 1567982, 1661219, 1760000, 1864655, 1975533, 2093005, 2217461, 2349318, 2489016, 2637020, 2793826, 2959955, 3135963, 3322438, 3520000, 3729310, 3951066, 4186009, 4434922, 4698636, 4978032, 5274041, 5587652, 5919911, 6271927, 6644875, 7040000, 7458620, 7902133, 8372018, 8869844, 9397273, 9956063, 10548082, 11175303, 11839822, 12543854 }; /* v=2.^((x/127-1) * 6) */ const double vol_table[128] = { 0.015625, 0.016145143728351113, 0.016682602624583379, 0.017237953096759438, 0.017811790741104401, 0.01840473098076444, 0.019017409725829021, 0.019650484055324921, 0.020304632921913132, 0.020980557880044631, 0.021678983838355849, 0.02240065983711079, 0.023146359851523596, 0.023916883621822989, 0.024713057510949051, 0.025535735390801884, 0.026385799557992876, 0.027264161680080529, 0.028171763773305786, 0.029109579212875332, 0.030078613776876421, 0.031079906724942836, 0.032114531912828696, 0.033183598944085631, 0.034288254360078256, 0.035429682869614412, 0.036609108619508737, 0.037827796507442342, 0.039087053538526394, 0.040388230227024875, 0.041732722044739302, 0.043121970917609151, 0.044557466772132896, 0.046040749133268132, 0.047573408775524545, 0.049157089429020417, 0.050793489542332405, 0.05248436410402918, 0.054231526524842463, 0.056036850582493913, 0.057902272431264008, 0.059829792678457581, 0.061821478529993396, 0.063879466007418645, 0.066005962238725971, 0.068203247825430205, 0.070473679288442961, 0.072819691595368496, 0.075243800771931268, 0.077748606600335793, 0.080336795407452768, 0.083011142945821612, 0.085774517370559328, 0.088629882315368294, 0.091580300070941839, 0.094628934869176312, 0.097779056276712184, 0.10103404270144323, 0.1043973850157546, 0.1078726903003755, 0.11146368571286204, 0.11517422248485852, 0.11900828005242428, 0.12296997032385605, 0.12706354208958254, 0.13129338557886089, 0.13566403716816194, 0.14018018424629392, 0.14484667024148207, 0.14966849981579558, 0.15465084423249356, 0.15979904690204472, 0.16511862911277009, 0.17061529595225433, 0.17629494242587571, 0.18216365977901747, 0.18822774202974024, 0.19449369271892172, 0.20096823188510385, 0.20765830327152621, 0.21457108177307616, 0.22171398113114205, 0.2290946618846218, 0.23672103958561411, 0.2446012932886038, 0.25274387432224471, 0.26115751535314891, 0.26985123975140174, 0.27883437126784744, 0.28811654403352405, 0.29770771289197112, 0.30761816407549192, 0.31785852623682015, 0.32843978184802081, 0.33937327897885317, 0.3506707434672246, 0.36234429149478936, 0.37440644258117928, 0.38687013301080181, 0.39974872970660535, 0.41305604456569134, 0.42680634927214656, 0.44101439060298442, 0.45569540624360722, 0.47086514112975281, 0.48653986433345225, 0.50273638651110641, 0.51947207793239625, 0.53676488710936021, 0.55463336004561792, 0.57309666012638816, 0.59217458867062556, 0.61188760616732485, 0.63225685421876243, 0.65330417821421161, 0.67505215075844849, 0.69752409588017272, 0.72074411404630734, 0.74473710800900605, 0.76952880951308478, 0.79514580689252357, 0.82161557358563286, 0.84896649759946774, 0.87722791195508854, 0.90643012614631979, 0.93660445864574493, 0.96778327049280244, 1 }; const double bend_fine[256] = { 1, 1.0002256593050698, 1.0004513695322617, 1.0006771306930664, 1.0009029427989777, 1.0011288058614922, 1.0013547198921082, 1.0015806849023274, 1.0018067009036538, 1.002032767907594, 1.0022588859256572, 1.0024850549693551, 1.0027112750502025, 1.0029375461797159, 1.0031638683694153, 1.0033902416308227, 1.0036166659754628, 1.0038431414148634, 1.0040696679605541, 1.0042962456240678, 1.0045228744169397, 1.0047495543507072, 1.0049762854369111, 1.0052030676870944, 1.0054299011128027, 1.0056567857255843, 1.00588372153699, 1.006110708558573, 1.0063377468018897, 1.0065648362784985, 1.0067919769999607, 1.0070191689778405, 1.0072464122237039, 1.0074737067491204, 1.0077010525656616, 1.0079284496849015, 1.0081558981184175, 1.008383397877789, 1.008610948974598, 1.0088385514204294, 1.0090662052268706, 1.0092939104055114, 1.0095216669679448, 1.0097494749257656, 1.009977334290572, 1.0102052450739643, 1.0104332072875455, 1.0106612209429215, 1.0108892860517005, 1.0111174026254934, 1.0113455706759138, 1.0115737902145781, 1.0118020612531047, 1.0120303838031153, 1.0122587578762337, 1.012487183484087, 1.0127156606383041, 1.0129441893505169, 1.0131727696323602, 1.0134014014954713, 1.0136300849514894, 1.0138588200120575, 1.0140876066888203, 1.0143164449934257, 1.0145453349375237, 1.0147742765327674, 1.0150032697908125, 1.0152323147233171, 1.015461411341942, 1.0156905596583505, 1.0159197596842091, 1.0161490114311862, 1.0163783149109531, 1.0166076701351838, 1.0168370771155553, 1.0170665358637463, 1.0172960463914391, 1.0175256087103179, 1.0177552228320703, 1.0179848887683858, 1.0182146065309567, 1.0184443761314785, 1.0186741975816487, 1.0189040708931674, 1.0191339960777379, 1.0193639731470658, 1.0195940021128593, 1.0198240829868295, 1.0200542157806898, 1.0202844005061564, 1.0205146371749483, 1.0207449257987866, 1.0209752663893958, 1.0212056589585028, 1.0214361035178368, 1.0216666000791297, 1.0218971486541166, 1.0221277492545349, 1.0223584018921241, 1.0225891065786274, 1.0228198633257899, 1.0230506721453596, 1.023281533049087, 1.0235124460487257, 1.0237434111560313, 1.0239744283827625, 1.0242054977406807, 1.0244366192415495, 1.0246677928971357, 1.0248990187192082, 1.025130296719539, 1.0253616269099028, 1.0255930093020766, 1.0258244439078401, 1.0260559307389761, 1.0262874698072693, 1.0265190611245079, 1.0267507047024822, 1.0269824005529853, 1.027214148687813, 1.0274459491187637, 1.0276778018576387, 1.0279097069162415, 1.0281416643063788, 1.0283736740398595, 1.0286057361284953, 1.0288378505841009, 1.0290700174184932, 1.0293022366434921, 1.0295345082709197, 1.0297668323126017, 1.0299992087803651, 1.030231637686041, 1.0304641190414621, 1.0306966528584645, 1.0309292391488862, 1.0311618779245688, 1.0313945691973556, 1.0316273129790936, 1.0318601092816313, 1.0320929581168212, 1.0323258594965172, 1.0325588134325767, 1.0327918199368598, 1.0330248790212284, 1.0332579906975481, 1.0334911549776868, 1.033724371873515, 1.0339576413969056, 1.0341909635597348, 1.0344243383738811, 1.0346577658512259, 1.034891246003653, 1.0351247788430489, 1.0353583643813031, 1.0355920026303078, 1.0358256936019572, 1.0360594373081489, 1.0362932337607829, 1.0365270829717617, 1.0367609849529913, 1.0369949397163791, 1.0372289472738365, 1.0374630076372766, 1.0376971208186156, 1.0379312868297725, 1.0381655056826686, 1.0383997773892284, 1.0386341019613787, 1.0388684794110492, 1.0391029097501721, 1.0393373929906822, 1.0395719291445176, 1.0398065182236185, 1.0400411602399278, 1.0402758552053915, 1.0405106031319582, 1.0407454040315787, 1.0409802579162071, 1.0412151647977996, 1.0414501246883161, 1.0416851375997183, 1.0419202035439705, 1.0421553225330404, 1.042390494578898, 1.042625719693516, 1.0428609978888699, 1.043096329176938, 1.0433317135697009, 1.0435671510791424, 1.0438026417172486, 1.0440381854960086, 1.0442737824274138, 1.044509432523459, 1.044745135796141, 1.0449808922574599, 1.0452167019194181, 1.0454525647940205, 1.0456884808932754, 1.0459244502291931, 1.0461604728137874, 1.0463965486590741, 1.046632677777072, 1.0468688601798024, 1.0471050958792898, 1.047341384887561, 1.0475777272166455, 1.047814122878576, 1.048050571885387, 1.0482870742491166, 1.0485236299818055, 1.0487602390954964, 1.0489969016022356, 1.0492336175140715, 1.0494703868430555, 1.0497072096012419, 1.0499440858006872, 1.0501810154534512, 1.050417998571596, 1.0506550351671864, 1.0508921252522903, 1.0511292688389782, 1.0513664659393229, 1.0516037165654004, 1.0518410207292894, 1.0520783784430709, 1.0523157897188296, 1.0525532545686513, 1.0527907730046264, 1.0530283450388465, 1.0532659706834067, 1.0535036499504049, 1.0537413828519411, 1.0539791694001188, 1.0542170096070436, 1.0544549034848243, 1.0546928510455722, 1.0549308523014012, 1.0551689072644284, 1.0554070159467728, 1.0556451783605572, 1.0558833945179062, 1.0561216644309479, 1.0563599881118126, 1.0565983655726334, 1.0568367968255465, 1.0570752818826903, 1.0573138207562065, 1.057552413458239, 1.0577910600009348, 1.0580297603964437, 1.058268514656918, 1.0585073227945128, 1.0587461848213857, 1.058985100749698, 1.0592240705916123 }; const double bend_coarse[128] = { 1, 1.0594630943592953, 1.122462048309373, 1.189207115002721, 1.2599210498948732, 1.3348398541700344, 1.4142135623730951, 1.4983070768766815, 1.5874010519681994, 1.681792830507429, 1.7817974362806785, 1.8877486253633868, 2, 2.1189261887185906, 2.244924096618746, 2.3784142300054421, 2.5198420997897464, 2.6696797083400687, 2.8284271247461903, 2.996614153753363, 3.1748021039363992, 3.363585661014858, 3.5635948725613571, 3.7754972507267741, 4, 4.2378523774371812, 4.4898481932374912, 4.7568284600108841, 5.0396841995794928, 5.3393594166801366, 5.6568542494923806, 5.993228307506727, 6.3496042078727974, 6.727171322029716, 7.1271897451227151, 7.5509945014535473, 8, 8.4757047548743625, 8.9796963864749824, 9.5136569200217682, 10.079368399158986, 10.678718833360273, 11.313708498984761, 11.986456615013454, 12.699208415745595, 13.454342644059432, 14.25437949024543, 15.101989002907095, 16, 16.951409509748721, 17.959392772949972, 19.027313840043536, 20.158736798317967, 21.357437666720553, 22.627416997969522, 23.972913230026901, 25.398416831491197, 26.908685288118864, 28.508758980490853, 30.203978005814196, 32, 33.902819019497443, 35.918785545899944, 38.054627680087073, 40.317473596635935, 42.714875333441107, 45.254833995939045, 47.945826460053802, 50.796833662982394, 53.817370576237728, 57.017517960981706, 60.407956011628393, 64, 67.805638038994886, 71.837571091799887, 76.109255360174146, 80.63494719327187, 85.429750666882214, 90.509667991878089, 95.891652920107603, 101.59366732596479, 107.63474115247546, 114.03503592196341, 120.81591202325679, 128, 135.61127607798977, 143.67514218359977, 152.21851072034829, 161.26989438654374, 170.85950133376443, 181.01933598375618, 191.78330584021521, 203.18733465192958, 215.26948230495091, 228.07007184392683, 241.63182404651357, 256, 271.22255215597971, 287.35028436719938, 304.43702144069658, 322.53978877308765, 341.71900266752868, 362.03867196751236, 383.56661168043064, 406.37466930385892, 430.53896460990183, 456.14014368785394, 483.26364809302686, 512, 542.44510431195943, 574.70056873439876, 608.87404288139317, 645.0795775461753, 683.43800533505737, 724.07734393502471, 767.13322336086128, 812.74933860771785, 861.07792921980365, 912.28028737570787, 966.52729618605372, 1024, 1084.8902086239189, 1149.4011374687975, 1217.7480857627863, 1290.1591550923506, 1366.8760106701147, 1448.1546878700494, 1534.2664467217226 }; SDL2_mixer-2.8.0/src/codecs/timidity/Android.mk0000644000076500000240000000052614277744147020251 0ustar valvestaffLOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := timidity LOCAL_C_INCLUDES := LOCAL_CFLAGS := LOCAL_SRC_FILES += \ common.c \ instrum.c \ mix.c \ output.c \ playmidi.c \ readmidi.c \ resample.c \ tables.c \ timidity.c LOCAL_SHARED_LIBRARIES := SDL2 include $(BUILD_STATIC_LIBRARY) SDL2_mixer-2.8.0/src/codecs/timidity/mix.h0000644000076500000240000000130714277744147017304 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. mix.h */ #ifndef TIMIDITY_MIX_H #define TIMIDITY_MIX_H #define mix_voice TIMI_NAMESPACE(mix_voice) #define recompute_envelope TIMI_NAMESPACE(recompute_envelope) #define apply_envelope_to_amp TIMI_NAMESPACE(apply_envelope_to_amp) extern void mix_voice(MidiSong *song, Sint32 *buf, int v, Sint32 c); extern int recompute_envelope(MidiSong *song, int v); extern void apply_envelope_to_amp(MidiSong *song, int v); #endif /* TIMIDITY_MIX_H */ SDL2_mixer-2.8.0/src/codecs/timidity/readmidi.c0000644000076500000240000003747314277744147020275 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. */ #include "SDL.h" #include "options.h" #include "timidity.h" #include "common.h" #include "instrum.h" #include "playmidi.h" #include "readmidi.h" /* Computes how many (fractional) samples one MIDI delta-time unit contains */ static void compute_sample_increment(MidiSong *song, Sint32 tempo, Sint32 divisions) { double a; a = (double) (tempo) * (double) (song->rate) * (65536.0/1000000.0) / (double)(divisions); song->sample_correction = (Sint32)(a) & 0xFFFF; song->sample_increment = (Sint32)(a) >> 16; SNDDBG(("Samples per delta-t: %d (correction %d)", song->sample_increment, song->sample_correction)); } /* Read variable-length number (7 bits per byte, MSB first) */ static Sint32 getvl(SDL_RWops *rw) { Sint32 l=0; Uint8 c; for (;;) { if (!SDL_RWread(rw, &c, 1, 1)) return l; l += (c & 0x7f); if (!(c & 0x80)) return l; l<<=7; } } #if (defined DEBUG_CHATTER) /* Print a string from the file, followed by a newline. Any non-ASCII or unprintable characters will be converted to periods. */ static int dumpstring(SDL_RWops *rw, Sint32 len, Uint8 type) { static const char *label[] = { "Text event: ", "Text: ", "Copyright: ", "Track name: ", "Instrument: ", "Lyric: ", "Marker: ", "Cue point: " }; signed char *s = SDL_malloc(len+1); if (!s) { SDL_RWseek(rw, len, RW_SEEK_CUR);/* should I ? */ return -1; } if (len != (Sint32) SDL_RWread(rw, s, 1, len)) { SDL_free(s); return -1; } s[len]='\0'; while (len--) { if (s[len]<32) s[len]='.'; } SNDDBG(("%s%s", label[(type>7) ? 0 : type], s)); SDL_free(s); return 0; } #endif #define MIDIEVENT(at,t,ch,pa,pb) \ newlist = (MidiEventList *) SDL_malloc(sizeof(MidiEventList));\ if (!newlist) {song->oom = 1; return NULL;} \ newlist->event.time = at; \ newlist->event.type = t; \ newlist->event.channel = ch; \ newlist->event.a = pa; \ newlist->event.b = pb; \ newlist->next = NULL; \ return newlist; #define MAGIC_EOT ((MidiEventList *)(-1)) /* Read a MIDI event, returning a freshly allocated element that can be linked to the event list */ static MidiEventList *read_midi_event(MidiSong *song) { static Uint8 laststatus, lastchan; static Uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */ Uint8 me, type, a,b,c; Sint32 len; MidiEventList *newlist; for (;;) { song->at += getvl(song->rw); if (SDL_RWread(song->rw, &me, 1, 1) != 1) { SNDDBG(("read_midi_event: SDL_RWread() failure\n")); return NULL; } if(me==0xF0 || me == 0xF7) /* SysEx event */ { len=getvl(song->rw); SDL_RWseek(song->rw, len, RW_SEEK_CUR); } else if(me==0xFF) /* Meta event */ { SDL_RWread(song->rw, &type, 1, 1); len=getvl(song->rw); if (type>0 && type<16) { #if (defined DEBUG_CHATTER) dumpstring(song->rw, len, type); #else SDL_RWseek(song->rw, len, RW_SEEK_CUR); #endif } else switch(type) { case 0x2F: /* End of Track */ return MAGIC_EOT; case 0x51: /* Tempo */ SDL_RWread(song->rw, &a, 1, 1); SDL_RWread(song->rw, &b, 1, 1); SDL_RWread(song->rw, &c, 1, 1); MIDIEVENT(song->at, ME_TEMPO, c, a, b); default: SNDDBG(("(Meta event type 0x%02x, length %d)\n", type, len)); SDL_RWseek(song->rw, len, RW_SEEK_CUR); break; } } else { a=me; if (a & 0x80) /* status byte */ { lastchan=a & 0x0F; laststatus=(a>>4) & 0x07; SDL_RWread(song->rw, &a, 1, 1); a &= 0x7F; } switch(laststatus) { case 0: /* Note off */ SDL_RWread(song->rw, &b, 1, 1); b &= 0x7F; MIDIEVENT(song->at, ME_NOTEOFF, lastchan, a,b); case 1: /* Note on */ SDL_RWread(song->rw, &b, 1, 1); b &= 0x7F; MIDIEVENT(song->at, ME_NOTEON, lastchan, a,b); case 2: /* Key Pressure */ SDL_RWread(song->rw, &b, 1, 1); b &= 0x7F; MIDIEVENT(song->at, ME_KEYPRESSURE, lastchan, a, b); case 3: /* Control change */ SDL_RWread(song->rw, &b, 1, 1); b &= 0x7F; { int control=255; switch(a) { case 7: control=ME_MAINVOLUME; break; case 10: control=ME_PAN; break; case 11: control=ME_EXPRESSION; break; case 64: control=ME_SUSTAIN; b = (b >= 64); break; case 120: control=ME_ALL_SOUNDS_OFF; break; case 121: control=ME_RESET_CONTROLLERS; break; case 123: control=ME_ALL_NOTES_OFF; break; /* These should be the SCC-1 tone bank switch commands. I don't know why there are two, or why the latter only allows switching to bank 0. Also, some MIDI files use 0 as some sort of continuous controller. This will cause lots of warnings about undefined tone banks. */ case 0: control=ME_TONE_BANK; break; case 32: if (b!=0) { SNDDBG(("(Strange: tone bank change 0x%02x)\n", b)); } #if 0 /* `Bank Select LSB' is not worked at GS. Please ignore it. */ else control=ME_TONE_BANK; #endif break; case 100: nrpn=0; rpn_msb[lastchan]=b; break; case 101: nrpn=0; rpn_lsb[lastchan]=b; break; case 99: nrpn=1; rpn_msb[lastchan]=b; break; case 98: nrpn=1; rpn_lsb[lastchan]=b; break; case 6: if (nrpn) { SNDDBG(("(Data entry (MSB) for NRPN %02x,%02x: %d)\n", rpn_msb[lastchan], rpn_lsb[lastchan], b)); break; } switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan]) { case 0x0000: /* Pitch bend sensitivity */ control=ME_PITCH_SENS; break; case 0x7F7F: /* RPN reset */ /* reset pitch bend sensitivity to 2 */ MIDIEVENT(song->at, ME_PITCH_SENS, lastchan, 2, 0); default: SNDDBG(("(Data entry (MSB) for RPN %02x,%02x: %d)\n", rpn_msb[lastchan], rpn_lsb[lastchan], b)); break; } break; default: SNDDBG(("(Control %d: %d)\n", a, b)); break; } if (control != 255) { MIDIEVENT(song->at, control, lastchan, b, 0); } } break; case 4: /* Program change */ a &= 0x7f; MIDIEVENT(song->at, ME_PROGRAM, lastchan, a, 0); case 5: /* Channel pressure - NOT IMPLEMENTED */ break; case 6: /* Pitch wheel */ SDL_RWread(song->rw, &b, 1, 1); b &= 0x7F; MIDIEVENT(song->at, ME_PITCHWHEEL, lastchan, a, b); default: SNDDBG(("*** Can't happen: status 0x%02X, channel 0x%02X\n", laststatus, lastchan)); break; } } } return newlist; } #undef MIDIEVENT /* Read a midi track into the linked list, either merging with any previous tracks or appending to them. */ static int read_track(MidiSong *song, int append) { MidiEventList *meep; MidiEventList *next, *newlist; Sint32 len; Sint64 next_pos, pos; char tmp[4]; meep = song->evlist; if (append && meep) { /* find the last event in the list */ for (; meep->next; meep=meep->next) ; song->at = meep->event.time; } else song->at=0; /* Check the formalities */ if (SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_RWread(song->rw, &len, 4, 1) != 1) { SNDDBG(("Can't read track header.\n")); return -1; } len=(Sint32)SDL_SwapBE32((Uint32)len); next_pos = SDL_RWtell(song->rw) + len; if (SDL_memcmp(tmp, "MTrk", 4)) { SNDDBG(("Corrupt MIDI file.\n")); return -2; } for (;;) { if (!(newlist=read_midi_event(song))) /* Some kind of error */ return -2; if (newlist==MAGIC_EOT) /* End-of-track Hack. */ { /* If the track ends before the size of the * track data, skip any junk at the end. */ pos = SDL_RWtell(song->rw); if (pos < next_pos) SDL_RWseek(song->rw, next_pos - pos, RW_SEEK_CUR); return 0; } next=meep->next; while (next && (next->event.time < newlist->event.time)) { meep=next; next=meep->next; } newlist->next=next; meep->next=newlist; song->event_count++; /* Count the event. (About one?) */ meep=newlist; } } /* Free the linked event list from memory. */ static void free_midi_list(MidiSong *song) { MidiEventList *meep, *next; meep = song->evlist; while (meep) { next=meep->next; SDL_free(meep); meep=next; } song->evlist = NULL; } /* Allocate an array of MidiEvents and fill it from the linked list of events, marking used instruments for loading. Convert event times to samples: handle tempo changes. Strip unnecessary events from the list. Free the linked list. */ static MidiEvent *groom_list(MidiSong *song, Sint32 divisions,Sint32 *eventsp, Sint32 *samplesp) { MidiEvent *groomed_list, *lp; MidiEventList *meep; Sint32 i, our_event_count, tempo, skip_this_event, new_value; Sint32 sample_cum, samples_to_do, at, st, dt, counting_time; int current_bank[MAXCHAN], current_set[MAXCHAN], current_program[MAXCHAN]; /* Or should each bank have its own current program? */ for (i=0; idefault_program; } tempo=500000; compute_sample_increment(song, tempo, divisions); /* This may allocate a bit more than we need */ groomed_list=lp=SDL_malloc(sizeof(MidiEvent) * (song->event_count+1)); if (!groomed_list) { song->oom=1; free_midi_list(song); return NULL; } meep=song->evlist; our_event_count=0; st=at=sample_cum=0; counting_time=2; /* We strip any silence before the first NOTE ON. */ for (i = 0; i < song->event_count; i++) { skip_this_event=0; if (meep->event.type==ME_TEMPO) { skip_this_event=1; } else if (meep->event.channel >= MAXCHAN) skip_this_event=1; else switch (meep->event.type) { case ME_PROGRAM: if (ISDRUMCHANNEL(song, meep->event.channel)) { if (song->drumset[meep->event.a]) /* Is this a defined drumset? */ new_value=meep->event.a; else { SNDDBG(("Drum set %d is undefined\n", meep->event.a)); new_value=meep->event.a=0; } if (current_set[meep->event.channel] != new_value) current_set[meep->event.channel]=new_value; else skip_this_event=1; } else { new_value=meep->event.a; if ((current_program[meep->event.channel] != SPECIAL_PROGRAM) && (current_program[meep->event.channel] != new_value)) current_program[meep->event.channel] = new_value; else skip_this_event=1; } break; case ME_NOTEON: if (counting_time) counting_time=1; if (ISDRUMCHANNEL(song, meep->event.channel)) { /* Mark this instrument to be loaded */ if (!(song->drumset[current_set[meep->event.channel]] ->instrument[meep->event.a])) song->drumset[current_set[meep->event.channel]] ->instrument[meep->event.a] = MAGIC_LOAD_INSTRUMENT; } else { if (current_program[meep->event.channel]==SPECIAL_PROGRAM) break; /* Mark this instrument to be loaded */ if (!(song->tonebank[current_bank[meep->event.channel]] ->instrument[current_program[meep->event.channel]])) song->tonebank[current_bank[meep->event.channel]] ->instrument[current_program[meep->event.channel]] = MAGIC_LOAD_INSTRUMENT; } break; case ME_TONE_BANK: if (ISDRUMCHANNEL(song, meep->event.channel)) { skip_this_event=1; break; } if (song->tonebank[meep->event.a]) /* Is this a defined tone bank? */ new_value=meep->event.a; else { SNDDBG(("Tone bank %d is undefined\n", meep->event.a)); new_value=meep->event.a=0; } if (current_bank[meep->event.channel]!=new_value) current_bank[meep->event.channel]=new_value; else skip_this_event=1; break; } /* Recompute time in samples*/ if ((dt=meep->event.time - at) && !counting_time) { if (song->sample_increment > 2147483647/dt || song->sample_correction > 2147483647/dt) { goto _overflow; } samples_to_do = song->sample_increment * dt; sample_cum += song->sample_correction * dt; if (sample_cum & 0xFFFF0000) { samples_to_do += ((sample_cum >> 16) & 0xFFFF); sample_cum &= 0x0000FFFF; } if (st >= 2147483647 - samples_to_do) { _overflow: SNDDBG(("Overflow in sample counter\n")); free_midi_list(song); SDL_free(groomed_list); return NULL; } st += samples_to_do; } else if (counting_time==1) counting_time=0; if (meep->event.type==ME_TEMPO) { tempo= meep->event.channel + meep->event.b * 256 + meep->event.a * 65536; compute_sample_increment(song, tempo, divisions); } if (!skip_this_event) { /* Add the event to the list */ *lp=meep->event; lp->time=st; lp++; our_event_count++; } at=meep->event.time; meep=meep->next; } /* Add an End-of-Track event */ lp->time=st; lp->type=ME_EOT; our_event_count++; free_midi_list(song); *eventsp=our_event_count; *samplesp=st; return groomed_list; } MidiEvent *read_midi_file(MidiSong *song, Sint32 *count, Sint32 *sp) { Sint32 len, divisions; Sint16 format, tracks, divisions_tmp; int i; char tmp[4]; song->event_count=0; song->at=0; song->evlist = NULL; if (SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_RWread(song->rw, &len, 4, 1) != 1) { SNDDBG(("Not a MIDI file!\n")); return NULL; } if (SDL_memcmp(tmp, "RIFF", 4) == 0) { /* RMID ?? */ if (SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_memcmp(tmp, "RMID", 4) != 0 || SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_memcmp(tmp, "data", 4) != 0 || SDL_RWread(song->rw, tmp, 1, 4) != 4 || /* SMF must begin from here onwards: */ SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_RWread(song->rw, &len, 4, 1) != 1) { SNDDBG(("Not an RMID file!\n")); return NULL; } } len=(Sint32)SDL_SwapBE32((Uint32)len); if (SDL_memcmp(tmp, "MThd", 4) || len < 6) { SNDDBG(("Not a MIDI file!\n")); return NULL; } format=tracks=divisions_tmp = -1; SDL_RWread(song->rw, &format, 2, 1); SDL_RWread(song->rw, &tracks, 2, 1); SDL_RWread(song->rw, &divisions_tmp, 2, 1); format=SDL_SwapBE16(format); tracks=SDL_SwapBE16(tracks); divisions_tmp=SDL_SwapBE16(divisions_tmp); if (divisions_tmp<0) { /* SMPTE time -- totally untested. Got a MIDI file that uses this? */ divisions= (Sint32)(-(divisions_tmp/256)) * (Sint32)(divisions_tmp & 0xFF); } else divisions=(Sint32)(divisions_tmp); if (len > 6) { SNDDBG(("MIDI file header size %u bytes", len)); SDL_RWseek(song->rw, len-6, RW_SEEK_CUR); /* skip the excess */ } if (format<0 || format >2) { SNDDBG(("Unknown MIDI file format %d\n", format)); return NULL; } if (tracks<1) { SNDDBG(("Bad number of tracks %d\n", tracks)); return NULL; } if (format==0 && tracks!=1) { SNDDBG(("%d tracks with Type-0 MIDI (must be 1.)\n", tracks)); return NULL; } SNDDBG(("Format: %d Tracks: %d Divisions: %d\n", format, tracks, divisions)); /* Put a do-nothing event first in the list for easier processing */ song->evlist=SDL_calloc(1, sizeof(MidiEventList)); if (!song->evlist) { song->oom=1; return NULL; } song->event_count++; switch(format) { case 0: if (read_track(song, 0)) { free_midi_list(song); return NULL; } break; case 1: for (i=0; i -------------------------------------------------------------------------- Frequently Asked Questions with answers: -------------------------------------------------------------------------- Q: What is it? A: Where? Well Chris, TiMidity is a software-only synthesizer, MIDI renderer, MIDI to WAVE converter, realtime MIDI player for UNIX machines, even (I've heard) a Netscape helper application. It takes a MIDI file and writes a WAVE or raw PCM data or plays it on your digital audio device. It sounds much more realistic than FM synthesis, but you need a ~100Mhz processor to listen to 32kHz stereo music in the background while you work. 11kHz mono can be played on a low-end 486, and, to some, it still sounds better than FM. -------------------------------------------------------------------------- Q: I don't have a GUS, can I use TiMidity? A: Yes. That's the point. You don't need a Gravis Ultrasound to use TiMidity, you just need GUS-compatible patches, which are freely available on the Internet. See below for pointers. -------------------------------------------------------------------------- Q: I have a GUS, can I use TiMidity? A: The DOS port doesn't have GUS support, and TiMidity won't be taking advantage of the board's internal synthesizer under other operating systems either. So it kind of defeats the purpose. But you can use it. -------------------------------------------------------------------------- Q: I tried playing a MIDI file I got off the Net but all I got was a dozen warnings saying "No instrument mapped to tone bank 0, program xx - this instrument will not be heard". What's wrong? A: The General MIDI standard specifies 128 melodic instruments and some sixty percussion sounds. If you wish to play arbitrary General MIDI files, you'll need to get more patch files. There's a program called Midia for SGI's, which also plays MIDI files and has a lot more bells and whistles than TiMidity. It uses GUS-compatible patches, too -- so you can get the 8 MB set at ftp://archive.cs.umbc.edu/pub/midia for pretty good GM compatibility. There are also many excellent patches on the Ultrasound FTP sites. I can recommend Dustin McCartney's collections gsdrum*.zip and wow*.zip in the "[.../]sound/patches/files" directory. The huge ProPats series (pp3-*.zip) contains good patches as well. General MIDI files can also be found on these sites. This site list is from the GUS FAQ: > FTP Sites Archive Directories > --------- ------------------- > Main N.American Site: archive.orst.edu pub/packages/gravis > wuarchive.wustl.edu systems/ibmpc/ultrasound > Main Asian Site: nctuccca.edu.tw PC/ultrasound > Main European Site: src.doc.ic.ac.uk packages/ultrasound > Main Australian Site: ftp.mpx.com.au /ultrasound/general > /ultrasound/submit > South African Site: ftp.sun.ac.za /pub/packages/ultrasound > Submissions: archive.epas.utoronto.ca pub/pc/ultrasound/submit > Newly Validated Files: archive.epas.utoronto.ca pub/pc/ultrasound > > Mirrors: garbo.uwasa.fi mirror/ultrasound > ftp.st.nepean.uws.edu.au pc/ultrasound > ftp.luth.se pub/msdos/ultrasound -------------------------------------------------------------------------- Q: Some files have awful clicks and pops. A: Find out which patch is responsible for the clicking (try "timidity -P ". Add "strip=tail" in the config file after its name. If this doesn't fix it, mail me the patch. -------------------------------------------------------------------------- Q: I'm playing Fantasie Impromptu in the background. When I run Netscape, the sound gets choppy and it takes ten minutes to load. What can I do? A: Here are some things to try: - Use a lower sampling rate. - Use mono output. This can improve performance by 10-30%. (Using 8-bit instead of 16-bit output makes no difference.) - Use a smaller number of simultaneous voices. - Make sure you compiled with FAST_DECAY enabled in options.h - Recompile with an Intel-optimized gcc for a 5-15% performance increase. -------------------------------------------------------------------------- SDL2_mixer-2.8.0/src/codecs/timidity/CHANGES0000644000076500000240000000616414277744147017337 0ustar valvestaffThis version of TiMidity should contain all the fixes from the September 25 2003 SDL_mixer CVS snapshot. In addition, I've made some changes of my own, e.g.: * All file access is done through SDL_RWops. This means the MIDI stream no longer has to be a file. (The config file and instruments still have to be though.) * Replacing of TiMidity's endian-handling with SDL's. * Removal of much unused or unnecessary code, such as + The "hooks" for putting a user interface onto TiMidity. + The antialias filter. It wasn't active, and even at 4 kHz I couldn't hear any difference when activating it. + Removed all traces of LOOKUP_HACK and LOOKUP_INTERPOLATION. According to the code comments they weren't very good anyway. ("degrades sound quality noticeably"). I also removed the disclaimer about the "8-bit uLaw to 16-bit PCM and the 13-bit-PCM to 8-bit uLaw tables" disclaimer, since I believe those were the tables I removed. + Removed LOOKUP_SINE since it was already commented out. I think we can count on our target audience having math co-processors nowadays. + Removed USE_LDEXP since it wasn't being used and "it doesn't make much of a difference either way". + Removed decompress hack from open_file() since it didn't look very portable. + Removed heaps of unnecessary constants. + Removed unused functions. + Assume that LINEAR_INTERPOLATION is always used, so remove all code dealing with it not being so. It's not that I think the difference in audio quality is that great, but since it wouldn't compile without code changes I assume no one's used it for quite some time... + Assume PRECALC_LOOPS is always defined. Judging by the comments it may not make much of a difference either way, so why maintain two versions of the same code? * Moving several static globals into the MidiSong struct. This includes sample rate, formate, etc. which are now all per-song. * Moved some typedefs (e.g. MidiSong) to timidity.h for easy inclusion into the MIDI decoder. * Added free_pathlist(). * Replaced TiMidity's own 8, 16 and 32-bit types with SDL's. * Made TiMidity look for its configuration file in both /etc and /usr/local/lib/timidity. (Windows version remains unchanged.) * Timidity_PlaySome() now takes three arguments. A MidiSong, a decode buffer and decode buffer size in bytes. (MidiSong is a new argument, and buffer size used to be in samples.) In addition, it will return the number of bytes decoded. * Added Timidity_Exit(). * Removed Timidity_Stop() and Timidity_Active(). Stopping playback should be handled by SDL_sound, and Timidity_PlaySome() will return 0 when the MIDI stream is finished. * Modified the ToneBank stuff to allow some data to be shared between MidiSongs. * The following files have been removed: controls.c, controls.h, filter.c, filter.h, sdl_a.c, sdl_c.c * config.h has been renamed as options.h to avoid confusion with the automatically generated config.h for SDL_sound. * Added support for loading DLS format instruments: Timidity_LoadDLS(), Timidity_FreeDLS(), Timidity_LoadDLSSong() * Added Timidity_Init_NoConfig() SDL2_mixer-2.8.0/src/codecs/timidity/playmidi.h0000644000076500000240000000236714277744147020326 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. playmidi.h */ #ifndef TIMIDITY_PLAYMIDI_H #define TIMIDITY_PLAYMIDI_H /* Midi events */ #define ME_NONE 0 #define ME_NOTEON 1 #define ME_NOTEOFF 2 #define ME_KEYPRESSURE 3 #define ME_MAINVOLUME 4 #define ME_PAN 5 #define ME_SUSTAIN 6 #define ME_EXPRESSION 7 #define ME_PITCHWHEEL 8 #define ME_PROGRAM 9 #define ME_TEMPO 10 #define ME_PITCH_SENS 11 #define ME_ALL_SOUNDS_OFF 12 #define ME_RESET_CONTROLLERS 13 #define ME_ALL_NOTES_OFF 14 #define ME_TONE_BANK 15 #define ME_LYRIC 16 #define ME_EOT 99 /* Causes the instrument's default panning to be used. */ #define NO_PANNING -1 /* Voice status options: */ #define VOICE_FREE 0 #define VOICE_ON 1 #define VOICE_SUSTAINED 2 #define VOICE_OFF 3 #define VOICE_DIE 4 /* Voice panned options: */ #define PANNED_MYSTERY 0 #define PANNED_LEFT 1 #define PANNED_RIGHT 2 #define PANNED_CENTER 3 /* Anything but PANNED_MYSTERY only uses the left volume */ #define ISDRUMCHANNEL(s, c) (((s)->drumchannels & (1<<(c)))) #endif /* TIMIDITY_PLAYMIDI_H */ SDL2_mixer-2.8.0/src/codecs/timidity/instrum.c0000644000076500000240000003634314277744147020213 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. instrum.c Code to load and unload GUS-compatible instrument patches. */ #include "SDL.h" #include "timidity.h" #include "options.h" #include "common.h" #include "instrum.h" #include "resample.h" #include "tables.h" static void free_instrument(Instrument *ip) { Sample *sp; int i; if (!ip) return; if (ip->sample) { for (i=0; isamples; i++) { sp=&(ip->sample[i]); SDL_free(sp->data); } SDL_free(ip->sample); } SDL_free(ip); } static void free_bank(MidiSong *song, int dr, int b) { int i; ToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]); for (i=0; iinstrument[i]) { if (bank->instrument[i] != MAGIC_LOAD_INSTRUMENT) free_instrument(bank->instrument[i]); bank->instrument[i] = NULL; } } static Sint32 convert_envelope_rate(MidiSong *song, Uint8 rate) { Sint32 r; r = 3 - ((rate >> 6) & 0x3); r *= 3; r = (Sint32) (rate & 0x3f) << r; /* 6.9 fixed point */ /* 15.15 fixed point. */ r = ((r * 44100) / song->rate) * song->control_ratio; #ifdef FAST_DECAY return r << 10; #else return r << 9; #endif } static Sint32 convert_envelope_offset(Uint8 offset) { /* This is not too good... Can anyone tell me what these values mean? Are they GUS-style "exponential" volumes? And what does that mean? */ /* 15.15 fixed point */ return offset << (7+15); } static Sint32 convert_tremolo_sweep(MidiSong *song, Uint8 sweep) { if (!sweep) return 0; return ((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep); } static Sint32 convert_vibrato_sweep(MidiSong *song, Uint8 sweep, Sint32 vib_control_ratio) { if (!sweep) return 0; return (Sint32) (TIM_FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT) / (double)(song->rate * sweep)); /* this was overflowing with seashore.pat ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep); */ } static Sint32 convert_tremolo_rate(MidiSong *song, Uint8 rate) { return ((SINE_CYCLE_LENGTH * song->control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * song->rate); } static Sint32 convert_vibrato_rate(MidiSong *song, Uint8 rate) { /* Return a suitable vibrato_control_ratio value */ return (VIBRATO_RATE_TUNING * song->rate) / (rate * 2 * VIBRATO_SAMPLE_INCREMENTS); } static void reverse_data(Sint16 *sp, Sint32 ls, Sint32 le) { Sint16 s, *ep=sp+le; sp+=ls; le-=ls; le/=2; while (le--) { s=*sp; *sp++=*ep; *ep--=s; } } /* If panning or note_to_use != -1, it will be used for all samples, instead of the sample-specific values in the instrument file. For note_to_use, any value <0 or >127 will be forced to 0. For other parameters, 1 means yes, 0 means no, other values are undefined. TODO: do reverse loops right */ static void load_instrument(MidiSong *song, const char *name, Instrument **out, int percussion, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) { Instrument *ip; Sample *sp; SDL_RWops *rw; char tmp[1024]; int i,j; static char *patch_ext[] = PATCH_EXT_LIST; (void)percussion; /* unused */ *out = NULL; if (!name) return; /* Open patch file */ i = -1; if ((rw=timi_openfile(name)) == NULL) { /* Try with various extensions */ for (i=0; patch_ext[i]; i++) { SDL_snprintf(tmp, sizeof(tmp), "%s%s", name, patch_ext[i]); if ((rw=timi_openfile(tmp)) != NULL) break; } } if (rw == NULL) { SNDDBG(("Instrument `%s' can't be found.\n", name)); return; } SNDDBG(("Loading instrument %s\n", (i < 0)? name : tmp)); /* Read some headers and do cursory sanity checks. There are loads of magic offsets. This could be rewritten... */ if ((239 != SDL_RWread(rw, tmp, 1, 239)) || (SDL_memcmp(tmp, "GF1PATCH110\0ID#000002", 22) && SDL_memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the differences are */ { SNDDBG(("%s: not an instrument\n", name)); goto badpat; } if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers, 0 means 1 */ { SNDDBG(("Can't handle patches with %d instruments\n", tmp[82])); goto badpat; } if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */ { SNDDBG(("Can't handle instruments with %d layers\n", tmp[151])); goto badpat; } *out=SDL_malloc(sizeof(Instrument)); ip = *out; if (!ip) goto nomem; ip->samples = tmp[198]; ip->sample = SDL_malloc(sizeof(Sample) * ip->samples); if (!ip->sample) goto nomem; for (i=0; isamples; i++) { Uint8 fractions; Sint32 tmplong; Uint16 tmpshort; Uint8 tmpchar; #define READ_CHAR(thing) \ if (1 != SDL_RWread(rw, &tmpchar, 1, 1)) goto badread; \ thing = tmpchar; #define READ_SHORT(thing) \ if (1 != SDL_RWread(rw, &tmpshort, 2, 1)) goto badread; \ thing = SDL_SwapLE16(tmpshort); #define READ_LONG(thing) \ if (1 != SDL_RWread(rw, &tmplong, 4, 1)) goto badread; \ thing = (Sint32)SDL_SwapLE32((Uint32)tmplong); SDL_RWseek(rw, 7, RW_SEEK_CUR); /* Skip the wave name */ if (1 != SDL_RWread(rw, &fractions, 1, 1)) goto badread; sp=&(ip->sample[i]); READ_LONG(sp->data_length); READ_LONG(sp->loop_start); READ_LONG(sp->loop_end); READ_SHORT(sp->sample_rate); READ_LONG(sp->low_freq); READ_LONG(sp->high_freq); READ_LONG(sp->root_freq); SDL_RWseek(rw, 2, RW_SEEK_CUR); /* Why have a "root frequency" and then * "tuning"?? */ READ_CHAR(tmp[0]); if (panning==-1) sp->panning = (tmp[0] * 8 + 4) & 0x7f; else sp->panning=(Uint8)(panning & 0x7F); /* envelope, tremolo, and vibrato */ if (18 != SDL_RWread(rw, tmp, 1, 18)) goto badread; if (!tmp[13] || !tmp[14]) { sp->tremolo_sweep_increment= sp->tremolo_phase_increment=sp->tremolo_depth=0; SNDDBG((" * no tremolo\n")); } else { sp->tremolo_sweep_increment=convert_tremolo_sweep(song, tmp[12]); sp->tremolo_phase_increment=convert_tremolo_rate(song, tmp[13]); sp->tremolo_depth=tmp[14]; SNDDBG((" * tremolo: sweep %d, phase %d, depth %d\n", sp->tremolo_sweep_increment, sp->tremolo_phase_increment, sp->tremolo_depth)); } if (!tmp[16] || !tmp[17]) { sp->vibrato_sweep_increment= sp->vibrato_control_ratio=sp->vibrato_depth=0; SNDDBG((" * no vibrato\n")); } else { sp->vibrato_control_ratio=convert_vibrato_rate(song, tmp[16]); sp->vibrato_sweep_increment= convert_vibrato_sweep(song, tmp[15], sp->vibrato_control_ratio); sp->vibrato_depth=tmp[17]; SNDDBG((" * vibrato: sweep %d, ctl %d, depth %d\n", sp->vibrato_sweep_increment, sp->vibrato_control_ratio, sp->vibrato_depth)); } READ_CHAR(sp->modes); SDL_RWseek(rw, 40, RW_SEEK_CUR); /* skip the useless scale frequency, scale factor (what's it mean?), and reserved space */ /* Mark this as a fixed-pitch instrument if such a deed is desired. */ if (note_to_use!=-1) sp->note_to_use=(Uint8)(note_to_use); else sp->note_to_use=0; /* seashore.pat in the Midia patch set has no Sustain. I don't understand why, and fixing it by adding the Sustain flag to all looped patches probably breaks something else. We do it anyway. */ if (sp->modes & MODES_LOOPING) sp->modes |= MODES_SUSTAIN; /* Strip any loops and envelopes we're permitted to */ if ((strip_loop==1) && (sp->modes & (MODES_SUSTAIN | MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE))) { SNDDBG((" - Removing loop and/or sustain\n")); sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE); } if (strip_envelope==1) { if (sp->modes & MODES_ENVELOPE) { SNDDBG((" - Removing envelope\n")); } sp->modes &= ~MODES_ENVELOPE; } else if (strip_envelope != 0) { /* Have to make a guess. */ if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE))) { /* No loop? Then what's there to sustain? No envelope needed either... */ sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE); SNDDBG((" - No loop, removing sustain and envelope\n")); } else if (!SDL_memcmp(tmp, "??????", 6) || tmp[11] >= 100) { /* Envelope rates all maxed out? Envelope end at a high "offset"? That's a weird envelope. Take it out. */ sp->modes &= ~MODES_ENVELOPE; SNDDBG((" - Weirdness, removing envelope\n")); } else if (!(sp->modes & MODES_SUSTAIN)) { /* No sustain? Then no envelope. I don't know if this is justified, but patches without sustain usually don't need the envelope either... at least the Gravis ones. They're mostly drums. I think. */ sp->modes &= ~MODES_ENVELOPE; SNDDBG((" - No sustain, removing envelope\n")); } } for (j=0; j<6; j++) { sp->envelope_rate[j]= convert_envelope_rate(song, tmp[j]); sp->envelope_offset[j]= convert_envelope_offset(tmp[6+j]); } /* Then read the sample data */ sp->data = (sample_t *) SDL_malloc(sp->data_length+4); if (!sp->data) goto nomem; if (1 != SDL_RWread(rw, sp->data, sp->data_length, 1)) goto badread; if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */ { Sint32 k=sp->data_length; Uint8 *cp=(Uint8 *)(sp->data); Uint16 *tmp16,*new16; sp->data_length *= 2; sp->loop_start *= 2; sp->loop_end *= 2; tmp16 = new16 = (Uint16 *) SDL_malloc(sp->data_length+4); if (!new16) goto nomem; while (k--) *tmp16++ = (Uint16)(*cp++) << 8; SDL_free(sp->data); sp->data = (sample_t *)new16; } #if SDL_BYTEORDER == SDL_BIG_ENDIAN else /* convert to machine byte order */ { Sint32 k=sp->data_length/2; Sint16 *tmp16=(Sint16 *)sp->data,s; while (k--) { s=SDL_SwapLE16(*tmp16); *tmp16++=s; } } #endif if (sp->modes & MODES_UNSIGNED) /* convert to signed data */ { Sint32 k=sp->data_length/2; Sint16 *tmp16=(Sint16 *)sp->data; while (k--) *tmp16++ ^= 0x8000; } /* Reverse reverse loops and pass them off as normal loops */ if (sp->modes & MODES_REVERSE) { Sint32 t; /* The GUS apparently plays reverse loops by reversing the whole sample. We do the same because the GUS does not SUCK. */ SNDDBG(("Reverse loop in %s\n", name)); reverse_data((Sint16 *)sp->data, 0, sp->data_length/2); t=sp->loop_start; sp->loop_start=sp->data_length - sp->loop_end; sp->loop_end=sp->data_length - t; sp->modes &= ~MODES_REVERSE; sp->modes |= MODES_LOOPING; /* just in case */ } #ifdef ADJUST_SAMPLE_VOLUMES if (amp!=-1) sp->volume=(float)((amp) / 100.0); else { /* Try to determine a volume scaling factor for the sample. This is a very crude adjustment, but things sound more balanced with it. Still, this should be a runtime option. */ Sint32 k=sp->data_length/2; Sint16 maxamp=0,a; Sint16 *tmp16=(Sint16 *)sp->data; while (k--) { a=*tmp16++; if (a<0) a=-a; if (a>maxamp) maxamp=a; } sp->volume=(float)(32768.0 / maxamp); SNDDBG((" * volume comp: %f\n", sp->volume)); } #else if (amp!=-1) sp->volume=(double)(amp) / 100.0; else sp->volume=1.0; #endif sp->data_length /= 2; /* These are in bytes. Convert into samples. */ sp->loop_start /= 2; sp->loop_end /= 2; /* initialize the added extra sample space (see the +4 bytes) */ sp->data[sp->data_length] = sp->data[sp->data_length+1] = 0; /* Then fractional samples */ sp->data_length <<= FRACTION_BITS; sp->loop_start <<= FRACTION_BITS; sp->loop_end <<= FRACTION_BITS; /* Adjust for fractional loop points. This is a guess. Does anyone know what "fractions" really stands for? */ sp->loop_start |= (fractions & 0x0F) << (FRACTION_BITS-4); sp->loop_end |= ((fractions>>4) & 0x0F) << (FRACTION_BITS-4); /* If this instrument will always be played on the same note, and it's not looped, we can resample it now. */ if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) { pre_resample(song, sp); if (song->oom) goto fail; } if (strip_tail==1) { /* Let's not really, just say we did. */ SNDDBG((" - Stripping tail\n")); sp->data_length = sp->loop_end; } } SDL_RWclose(rw); return; nomem: song->oom=1; goto fail; badread: SNDDBG(("Error reading sample %d\n", i)); fail: free_instrument (ip); badpat: SDL_RWclose(rw); *out = NULL; } static int fill_bank(MidiSong *song, int dr, int b) { int i, errors=0; ToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]); if (!bank) { SNDDBG(("Huh. Tried to load instruments in non-existent %s %d\n", (dr) ? "drumset" : "tone bank", b)); return 0; } for (i=0; iinstrument[i]==MAGIC_LOAD_INSTRUMENT) { if (!(bank->tone[i].name)) { SNDDBG(("No instrument mapped to %s %d, program %d%s\n", (dr)? "drum set" : "tone bank", b, i, (b!=0) ? "" : " - this instrument will not be heard")); if (b!=0) { /* Mark the corresponding instrument in the default bank / drumset for loading (if it isn't already) */ if (!dr) { if (!(song->tonebank[0]->instrument[i])) song->tonebank[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT; } else { if (!(song->drumset[0]->instrument[i])) song->drumset[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT; } } bank->instrument[i] = NULL; errors++; } else { load_instrument(song, bank->tone[i].name, &bank->instrument[i], (dr) ? 1 : 0, bank->tone[i].pan, bank->tone[i].amp, (bank->tone[i].note!=-1) ? bank->tone[i].note : ((dr) ? i : -1), (bank->tone[i].strip_loop!=-1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1), (bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1), bank->tone[i].strip_tail); if (!bank->instrument[i]) { SNDDBG(("Couldn't load instrument %s (%s %d, program %d)\n", bank->tone[i].name, (dr)? "drum set" : "tone bank", b, i)); errors++; } } } } return errors; } int load_missing_instruments(MidiSong *song) { int i=MAXBANK,errors=0; while (i--) { if (song->tonebank[i]) errors+=fill_bank(song,0,i); if (song->drumset[i]) errors+=fill_bank(song,1,i); } return errors; } void free_instruments(MidiSong *song) { int i=MAXBANK; while(i--) { if (song->tonebank[i]) free_bank(song, 0, i); if (song->drumset[i]) free_bank(song, 1, i); } } int set_default_instrument(MidiSong *song, const char *name) { load_instrument(song, name, &song->default_instrument, 0, -1, -1, -1, 0, 0, 0); if (!song->default_instrument) return -1; song->default_program = SPECIAL_PROGRAM; return 0; } SDL2_mixer-2.8.0/src/codecs/timidity/options.h0000644000076500000240000000676614277744147020220 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. */ #ifndef TIMIDITY_OPTIONS_H #define TIMIDITY_OPTIONS_H /* When a patch file can't be opened, one of these extensions is appended to the filename and the open is tried again. */ #define PATCH_EXT_LIST { ".pat", 0 } /* Acoustic Grand Piano seems to be the usual default instrument. */ #define DEFAULT_PROGRAM 0 /* 9 here is MIDI channel 10, which is the standard percussion channel. Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too. On the other hand, some files know that 16 is not a drum channel and try to play music on it. This is now a runtime option, so this isn't a critical choice anymore. */ #define DEFAULT_DRUMCHANNELS (1<<9) /* In percent. */ #define DEFAULT_AMPLIFICATION 70 /* Default polyphony */ /* #define DEFAULT_VOICES 32 */ #define DEFAULT_VOICES 256 /* 1000 here will give a control ratio of 22:1 with 22 kHz output. Higher CONTROLS_PER_SECOND values allow more accurate rendering of envelopes and tremolo. The cost is CPU time. */ #define CONTROLS_PER_SECOND 1000 /* Make envelopes twice as fast. Saves ~20% CPU time (notes decay faster) and sounds more like a GUS. */ #define FAST_DECAY /* A somewhat arbitrary output frequency range. */ #define MIN_OUTPUT_RATE 4000 #define MAX_OUTPUT_RATE 256000 /* How many bits to use for the fractional part of sample positions. This affects tonal accuracy. The entire position counter must fit in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of a sample is 1048576 samples (2 megabytes in memory). The GUS gets by with just 9 bits and a little help from its friends... "The GUS does not SUCK!!!" -- a happy user :) */ #define FRACTION_BITS 12 /* For some reason the sample volume is always set to maximum in all patch files. Define this for a crude adjustment that may help equalize instrument volumes. */ #define ADJUST_SAMPLE_VOLUMES /* The number of samples to use for ramping out a dying note. Affects click removal. */ #define MAX_DIE_TIME 20 /**************************************************************************/ /* Anything below this shouldn't need to be changed unless you're porting to a new machine with other than 32-bit, big-endian words. */ /**************************************************************************/ /* change FRACTION_BITS above, not these */ #define INTEGER_BITS (32 - FRACTION_BITS) #define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS) #define FRACTION_MASK (~ INTEGER_MASK) /* This is enforced by some computations that must fit in an int */ #define MAX_CONTROL_RATIO 255 #define MAX_AMPLIFICATION 800 /* The TiMidity configuration file */ #ifndef TIMIDITY_CFG #define TIMIDITY_CFG "timidity.cfg" #endif /* These affect general volume */ #define GUARD_BITS 3 #define AMP_BITS (15-GUARD_BITS) #define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1) #define TIM_FSCALE(a,b) (float)((a) * (double)(1<<(b))) #define TIM_FSCALENEG(a,b) (float)((a) * (1.0L / (double)(1<<(b)))) /* Vibrato and tremolo Choices of the Day */ #define SWEEP_TUNING 38 #define VIBRATO_AMPLITUDE_TUNING 1.0L #define VIBRATO_RATE_TUNING 38 #define TREMOLO_AMPLITUDE_TUNING 1.0L #define TREMOLO_RATE_TUNING 38 #define SWEEP_SHIFT 16 #define RATE_SHIFT 5 #ifndef PI #define PI 3.14159265358979323846 #endif #endif /* TIMIDITY_OPTIONS_H */ SDL2_mixer-2.8.0/src/codecs/timidity/timidity.h0000644000076500000240000001051614277744147020345 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. */ #ifndef TIMIDITY_H #define TIMIDITY_H #ifdef __cplusplus extern "C" { #endif typedef Sint16 sample_t; typedef Sint32 final_volume_t; #define VIBRATO_SAMPLE_INCREMENTS 32 /* Maximum polyphony. */ /* #define MAX_VOICES 48 */ #define MAX_VOICES 256 #define MAXCHAN 16 /* #define MAXCHAN 64 */ #define MAXBANK 128 typedef struct { Sint32 loop_start, loop_end, data_length, sample_rate, low_freq, high_freq, root_freq; Sint32 envelope_rate[6], envelope_offset[6]; float volume; sample_t *data; Sint32 tremolo_sweep_increment, tremolo_phase_increment, vibrato_sweep_increment, vibrato_control_ratio; Uint8 tremolo_depth, vibrato_depth, modes; Sint8 panning, note_to_use; } Sample; typedef struct { int bank, program, volume, sustain, panning, pitchbend, expression, mono, /* one note only on this channel -- not implemented yet */ pitchsens; /* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar processor near you */ float pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ } Channel; typedef struct { Uint8 status, channel, note, velocity; Sample *sample; Sint32 orig_frequency, frequency, sample_offset, sample_increment, envelope_volume, envelope_target, envelope_increment, tremolo_sweep, tremolo_sweep_position, tremolo_phase, tremolo_phase_increment, vibrato_sweep, vibrato_sweep_position; final_volume_t left_mix, right_mix; float left_amp, right_amp, tremolo_volume; Sint32 vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS]; int vibrato_phase, vibrato_control_ratio, vibrato_control_counter, envelope_stage, control_counter, panning, panned; } Voice; typedef struct { int samples; Sample *sample; } Instrument; /* Shared data */ typedef struct { char *name; int note, amp, pan, strip_loop, strip_envelope, strip_tail; } ToneBankElement; typedef struct { ToneBankElement *tone; Instrument *instrument[128]; } ToneBank; typedef struct { Sint32 time; Uint8 channel, type, a, b; } MidiEvent; typedef struct _MidiEventList { MidiEvent event; struct _MidiEventList *next; } MidiEventList; typedef struct { int oom; /* malloc() failed */ int playing; SDL_RWops *rw; Sint32 rate; Sint32 encoding; float master_volume; Sint32 amplification; ToneBank *tonebank[MAXBANK]; ToneBank *drumset[MAXBANK]; Instrument *default_instrument; int default_program; void (*write)(void *dp, Sint32 *lp, Sint32 c); int buffer_size; sample_t *resample_buffer; Sint32 *common_buffer; Sint32 *buffer_pointer; /* These would both fit into 32 bits, but they are often added in large multiples, so it's simpler to have two roomy ints */ /* samples per MIDI delta-t */ Sint32 sample_increment; Sint32 sample_correction; Channel channel[MAXCHAN]; Voice voice[MAX_VOICES]; int voices; Sint32 drumchannels; Sint32 buffered_count; Sint32 control_ratio; Sint32 lost_notes; Sint32 cut_notes; Sint32 samples; MidiEvent *events; MidiEvent *current_event; MidiEventList *evlist; Sint32 current_sample; Sint32 event_count; Sint32 at; Sint32 groomed_event_count; } MidiSong; /* Some of these are not defined in timidity.c but are here for convenience */ extern int Timidity_Init(const char *config_file); extern int Timidity_Init_NoConfig(void); extern void Timidity_SetVolume(MidiSong *song, int volume); extern int Timidity_PlaySome(MidiSong *song, void *stream, Sint32 len); extern MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio); extern void Timidity_Start(MidiSong *song); extern void Timidity_Seek(MidiSong *song, Uint32 ms); extern Uint32 Timidity_GetSongLength(MidiSong *song); /* returns millseconds */ extern Uint32 Timidity_GetSongTime(MidiSong *song); /* returns millseconds */ extern void Timidity_Stop(MidiSong *song); extern int Timidity_IsActive(MidiSong *song); extern void Timidity_FreeSong(MidiSong *song); extern void Timidity_Exit(void); #ifdef __cplusplus } #endif #endif /* TIMIDITY_H */ SDL2_mixer-2.8.0/src/codecs/timidity/output.h0000644000076500000240000000353014277744147020047 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. output.h */ #ifndef TIMIDITY_OUTPUT_H #define TIMIDITY_OUTPUT_H /* Data format encoding bits */ #define PE_MONO 0x01 /* versus stereo */ #define PE_SIGNED 0x02 /* versus unsigned */ #define PE_16BIT 0x04 /* versus 8-bit */ #define PE_32BIT 0x08 /* versus 8-bit or 16-bit */ /* Conversion functions -- These overwrite the Sint32 data in *lp with data in another format */ /* 8-bit signed and unsigned*/ extern void timi_s32tos8(void *dp, Sint32 *lp, Sint32 c); extern void timi_s32tou8(void *dp, Sint32 *lp, Sint32 c); /* 16-bit */ extern void timi_s32tos16(void *dp, Sint32 *lp, Sint32 c); extern void timi_s32tou16(void *dp, Sint32 *lp, Sint32 c); /* byte-exchanged 16-bit */ extern void timi_s32tos16x(void *dp, Sint32 *lp, Sint32 c); extern void timi_s32tou16x(void *dp, Sint32 *lp, Sint32 c); /* 32-bit */ extern void timi_s32tof32(void *dp, Sint32 *lp, Sint32 c); extern void timi_s32tos32(void *dp, Sint32 *lp, Sint32 c); /* byte-exchanged 32-bit */ extern void timi_s32tos32x(void *dp, Sint32 *lp, Sint32 c); /* little-endian and big-endian specific */ #if SDL_BYTEORDER == SDL_LIL_ENDIAN #define timi_s32tou16l timi_s32tou16 #define timi_s32tou16b timi_s32tou16x #define timi_s32tos16l timi_s32tos16 #define timi_s32tos16b timi_s32tos16x #define timi_s32tos32l timi_s32tos32 #define timi_s32tos32b timi_s32tos32x #else #define timi_s32tou16l timi_s32tou16x #define timi_s32tou16b timi_s32tou16 #define timi_s32tos16l timi_s32tos16x #define timi_s32tos16b timi_s32tos16 #define timi_s32tos32l timi_s32tos32x #define timi_s32tos32b timi_s32tos32 #endif #endif /* TIMIDITY_OUTPUT_H */ SDL2_mixer-2.8.0/src/codecs/timidity/README0000644000076500000240000000441614277744147017222 0ustar valvestaff[This version of timidity has been stripped for simplicity in porting to SDL, and then even further for SDL_sound] ---------------------------------*-text-*--------------------------------- From http://www.cgs.fi/~tt/discontinued.html : If you'd like to continue hacking on TiMidity, feel free. I'm hereby extending the TiMidity license agreement: you can now select the most convenient license for your needs from (1) the GNU GPL, (2) the GNU LGPL, or (3) the Perl Artistic License. -------------------------------------------------------------------------- This is the README file for TiMidity v0.2i TiMidity is a MIDI to WAVE converter that uses Gravis Ultrasound(*)-compatible patch files to generate digital audio data from General MIDI files. The audio data can be played through any sound device or stored on disk. On a fast machine, music can be played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and Win32, and porting to other systems with gcc should be easy. TiMidity Features: * 32 or more dynamically allocated fully independent voices * Compatibility with GUS patch files * Output to 16- or 8-bit PCM or uLaw audio device, file, or stdout at any sampling rate * Optional interactive mode with real-time status display under ncurses and SLang terminal control libraries. Also a user friendly motif interface since version 0.2h * Support for transparent loading of compressed MIDI files and patch files * Support for the following MIDI events: - Program change - Key pressure - Channel main volume - Tempo - Panning - Damper pedal (Sustain) - Pitch wheel - Pitch wheel sensitivity - Change drum set * TiMidity requires sampled instruments (patches) to play MIDI files. You should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same directory where you unpacked the source code archive. You'll want more patches later -- read the file "FAQ" for pointers. * Timidity is no longer supported, but can be found by searching the web. Tuukka Toivonen [(*) Any Registered Trademarks used anywhere in the documentation or source code for TiMidity are acknowledged as belonging to their respective owners.] SDL2_mixer-2.8.0/src/codecs/timidity/TODO0000644000076500000240000000256314277744147017033 0ustar valvestaff* I don't like the indentation style at all, but for the most part I've left it alone. * Much of the code looks ugly to me. * The return value from SDL_RWread() is checked inconsistenly. * Group the members of MidiSong into logical units, i.e. structs? * The debug messages are probably a bit too noisy. I've removed one particularly annoying one, but... Some of them should be turned into error messages instead. * Can the instrument handling be made more efficient? At the moment different MidiSongs may separately load the same instrument. Note that the MidiSong's audio format affects how the instrument is loaded, so it's not as easy as just letting all MidiSongs share tone and drum banks. At the moment they do share the data that is simply read from the config file, but that's just a quick hack to avoid having to read the config file every time a MIDI song is loaded. * Check if any of MidiStruct's members can safely be made into static globals again. * TiMidity++ adds a number of undocumented (?) extensions to the configuration syntax. These are not implemented here. In particular, the "map" keyword used by the "eawpats". * The other decoders generally only read as much of the file as is necessary. Could we do that in this decoder as well? (Currently it seems to convert the entire file into MIDI events first.) * Can it be optimized? SDL2_mixer-2.8.0/src/codecs/timidity/tables.h0000644000076500000240000000140214277744147017755 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. tables.h */ #ifndef TIMIDITY_TABLES_H #define TIMIDITY_TABLES_H #define timi_sine(x) (SDL_sin((2*PI/1024.0) * (x))) #define SINE_CYCLE_LENGTH 1024 #define freq_table TIMI_NAMESPACE(freq_table) #define vol_table TIMI_NAMESPACE(vol_table) #define bend_fine TIMI_NAMESPACE(bend_fine) #define bend_coarse TIMI_NAMESPACE(bend_coarse) extern const Sint32 freq_table[]; extern const double vol_table[]; extern const double bend_fine[]; extern const double bend_coarse[]; #endif /* TIMIDITY_TABLES_H */ SDL2_mixer-2.8.0/src/codecs/timidity/resample.c0000644000076500000240000003442714277744147020323 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. resample.c */ #include "SDL.h" #include "timidity.h" #include "options.h" #include "common.h" #include "instrum.h" #include "playmidi.h" #include "tables.h" #include "resample.h" #define PRECALC_LOOP_COUNT(start, end, incr) (((end) - (start) + (incr) - 1) / (incr)) /*************** resampling with fixed increment *****************/ static sample_t *rs_plain(MidiSong *song, int v, Sint32 *countptr) { /* Play sample until end, then free the voice. */ sample_t v1, v2; Voice *vp=&(song->voice[v]); sample_t *dest=song->resample_buffer, *src=vp->sample->data; Sint32 ofs=vp->sample_offset, incr=vp->sample_increment, le=vp->sample->data_length, count=*countptr; Sint32 i, j; if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */ /* Precalc how many times we should go through the loop. NOTE: Assumes that incr > 0 and that ofs <= le */ i = PRECALC_LOOP_COUNT(ofs, le, incr); if (i > count) { i = count; count = 0; } else count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } if (ofs >= le) { if (ofs == le) *dest++ = src[(ofs>>FRACTION_BITS)-1]/2; vp->status=VOICE_FREE; *countptr-=count+1; } vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } static sample_t *rs_loop(MidiSong *song, Voice *vp, Sint32 count) { /* Play sample until end-of-loop, skip back and continue. */ sample_t v1, v2; Sint32 ofs=vp->sample_offset, incr=vp->sample_increment, le=vp->sample->loop_end, ll=le - vp->sample->loop_start; sample_t *dest=song->resample_buffer, *src=vp->sample->data; Sint32 i, j; while (count) { while (ofs >= le) ofs -= ll; /* Precalc how many times we should go through the loop */ i = PRECALC_LOOP_COUNT(ofs, le, incr); if (i > count) { i = count; count = 0; } else count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } } vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } static sample_t *rs_bidir(MidiSong *song, Voice *vp, Sint32 count) { sample_t v1, v2; Sint32 ofs=vp->sample_offset, incr=vp->sample_increment, le=vp->sample->loop_end, ls=vp->sample->loop_start; sample_t *dest=song->resample_buffer, *src=vp->sample->data; Sint32 le2 = le<<1, ls2 = ls<<1, i, j; /* Play normally until inside the loop region */ if (incr > 0 && ofs < ls) { /* NOTE: Assumes that incr > 0, which is NOT always the case when doing bidirectional looping. I have yet to see a case where both ofs <= ls AND incr < 0, however. */ i = PRECALC_LOOP_COUNT(ofs, ls, incr); if (i > count) { i = count; count = 0; } else count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } } /* Then do the bidirectional looping */ while(count) { /* Precalc how many times we should go through the loop */ i = PRECALC_LOOP_COUNT(ofs, incr > 0 ? le : ls, incr); if (i > count) { i = count; count = 0; } else count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } if (ofs>=le) { /* fold the overshoot back in */ ofs = le2 - ofs; incr *= -1; } else if (ofs <= ls) { ofs = ls2 - ofs; incr *= -1; } } vp->sample_increment=incr; vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } /*********************** vibrato versions ***************************/ /* We only need to compute one half of the vibrato sine cycle */ static int vib_phase_to_inc_ptr(int phase) { if (phase < VIBRATO_SAMPLE_INCREMENTS/2) return VIBRATO_SAMPLE_INCREMENTS/2-1-phase; else if (phase >= 3*VIBRATO_SAMPLE_INCREMENTS/2) return 5*VIBRATO_SAMPLE_INCREMENTS/2-1-phase; else return phase-VIBRATO_SAMPLE_INCREMENTS/2; } static Sint32 update_vibrato(MidiSong *song, Voice *vp, int sign) { Sint32 depth; int phase, pb; double a; if (vp->vibrato_phase++ >= 2*VIBRATO_SAMPLE_INCREMENTS-1) vp->vibrato_phase=0; phase=vib_phase_to_inc_ptr(vp->vibrato_phase); if (vp->vibrato_sample_increment[phase]) { if (sign) return -vp->vibrato_sample_increment[phase]; else return vp->vibrato_sample_increment[phase]; } /* Need to compute this sample increment. */ depth=vp->sample->vibrato_depth<<7; if (vp->vibrato_sweep) { /* Need to update sweep */ vp->vibrato_sweep_position += vp->vibrato_sweep; if (vp->vibrato_sweep_position >= (1<vibrato_sweep=0; else { /* Adjust depth */ depth *= vp->vibrato_sweep_position; depth >>= SWEEP_SHIFT; } } a = TIM_FSCALE(((double)(vp->sample->sample_rate) * (double)(vp->frequency)) / ((double)(vp->sample->root_freq) * (double)(song->rate)), FRACTION_BITS); pb=(int)((timi_sine(vp->vibrato_phase * (SINE_CYCLE_LENGTH/(2*VIBRATO_SAMPLE_INCREMENTS))) * (double)(depth) * VIBRATO_AMPLITUDE_TUNING)); if (pb<0) { pb=-pb; a /= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13]; } else a *= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13]; /* If the sweep's over, we can store the newly computed sample_increment */ if (!vp->vibrato_sweep) vp->vibrato_sample_increment[phase]=(Sint32) a; if (sign) a = -a; /* need to preserve the loop direction */ return (Sint32) a; } static sample_t *rs_vib_plain(MidiSong *song, int v, Sint32 *countptr) { /* Play sample until end, then free the voice. */ sample_t v1, v2; Voice *vp=&(song->voice[v]); sample_t *dest=song->resample_buffer, *src=vp->sample->data; Sint32 le=vp->sample->data_length, ofs=vp->sample_offset, incr=vp->sample_increment, count=*countptr; int cc=vp->vibrato_control_counter; /* This has never been tested */ if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */ while (count--) { if (!cc--) { cc=vp->vibrato_control_ratio; incr=update_vibrato(song, vp, 0); } v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; if (ofs >= le) { if (ofs == le) *dest++ = src[(ofs>>FRACTION_BITS)-1]/2; vp->status=VOICE_FREE; *countptr-=count+1; break; } } vp->vibrato_control_counter=cc; vp->sample_increment=incr; vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } static sample_t *rs_vib_loop(MidiSong *song, Voice *vp, Sint32 count) { /* Play sample until end-of-loop, skip back and continue. */ sample_t v1, v2; Sint32 ofs=vp->sample_offset, incr=vp->sample_increment, le=vp->sample->loop_end, ll=le - vp->sample->loop_start; sample_t *dest=song->resample_buffer, *src=vp->sample->data; int cc=vp->vibrato_control_counter; Sint32 i, j; int vibflag=0; while (count) { /* Hopefully the loop is longer than an increment */ while(ofs >= le) ofs -= ll; /* Precalc how many times to go through the loop, taking the vibrato control ratio into account this time. */ i = PRECALC_LOOP_COUNT(ofs, le, incr); if(i > count) i = count; if(i > cc) { i = cc; vibflag = 1; } else cc -= i; count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } if(vibflag) { cc = vp->vibrato_control_ratio; incr = update_vibrato(song, vp, 0); vibflag = 0; } } vp->vibrato_control_counter=cc; vp->sample_increment=incr; vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } static sample_t *rs_vib_bidir(MidiSong *song, Voice *vp, Sint32 count) { sample_t v1, v2; Sint32 ofs=vp->sample_offset, incr=vp->sample_increment, le=vp->sample->loop_end, ls=vp->sample->loop_start; sample_t *dest=song->resample_buffer, *src=vp->sample->data; int cc=vp->vibrato_control_counter; Sint32 le2=le<<1, ls2=ls<<1, i, j; int vibflag = 0; /* Play normally until inside the loop region */ while (count && incr > 0 && ofs < ls) { i = PRECALC_LOOP_COUNT(ofs, ls, incr); if (i > count) i = count; if (i > cc) { i = cc; vibflag = 1; } else cc -= i; count -= i; for (j = 0; j < i; j++) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } if (vibflag) { cc = vp->vibrato_control_ratio; incr = update_vibrato(song, vp, 0); vibflag = 0; } } /* Then do the bidirectional looping */ while (count) { /* Precalc how many times we should go through the loop */ i = PRECALC_LOOP_COUNT(ofs, incr > 0 ? le : ls, incr); if(i > count) i = count; if(i > cc) { i = cc; vibflag = 1; } else cc -= i; count -= i; while (i--) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS)+1]; *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); ofs += incr; } if (vibflag) { cc = vp->vibrato_control_ratio; incr = update_vibrato(song, vp, (incr < 0)); vibflag = 0; } if (ofs >= le) { /* fold the overshoot back in */ ofs = le2 - ofs; incr *= -1; } else if (ofs <= ls) { ofs = ls2 - ofs; incr *= -1; } } vp->vibrato_control_counter=cc; vp->sample_increment=incr; vp->sample_offset=ofs; /* Update offset */ return song->resample_buffer; } sample_t *resample_voice(MidiSong *song, int v, Sint32 *countptr) { Sint32 ofs; Uint8 modes; Voice *vp=&(song->voice[v]); if (!(vp->sample->sample_rate)) { /* Pre-resampled data -- just update the offset and check if we're out of data. */ ofs=vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use FRACTION_BITS here... */ if (*countptr >= (vp->sample->data_length>>FRACTION_BITS) - ofs) { /* Note finished. Free the voice. */ vp->status = VOICE_FREE; /* Let the caller know how much data we had left */ *countptr = (vp->sample->data_length>>FRACTION_BITS) - ofs; } else vp->sample_offset += *countptr << FRACTION_BITS; return vp->sample->data+ofs; } /* Need to resample. Use the proper function. */ modes=vp->sample->modes; if (vp->vibrato_control_ratio) { if ((modes & MODES_LOOPING) && ((modes & MODES_ENVELOPE) || (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED))) { if (modes & MODES_PINGPONG) return rs_vib_bidir(song, vp, *countptr); else return rs_vib_loop(song, vp, *countptr); } else return rs_vib_plain(song, v, countptr); } else { if ((modes & MODES_LOOPING) && ((modes & MODES_ENVELOPE) || (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED))) { if (modes & MODES_PINGPONG) return rs_bidir(song, vp, *countptr); else return rs_loop(song, vp, *countptr); } else return rs_plain(song, v, countptr); } } void pre_resample(MidiSong *song, Sample *sp) { double a, xdiff; Sint32 incr, ofs, newlen, count; Sint16 *newdata, *dest, *src = (Sint16 *) sp->data, *vptr; Sint32 v, v1, v2, v3, v4, v5, i; #ifdef DEBUG_CHATTER static const char note_name[12][3] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; SNDDBG((" * pre-resampling for note %d (%s%d)\n", sp->note_to_use, note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12)); #endif a = ((double) (sp->root_freq) * song->rate) / ((double) (sp->sample_rate) * freq_table[(int) (sp->note_to_use)]); if(sp->data_length * a >= 0x7fffffffL) { /* Too large to compute */ SNDDBG((" *** Can't pre-resampling for note %d\n", sp->note_to_use)); return; } newlen = (Sint32)(sp->data_length * a); count = (newlen >> FRACTION_BITS) - 1; ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count; if((double)newlen + incr >= 0x7fffffffL) { /* Too large to compute */ SNDDBG((" *** Can't pre-resampling for note %d\n", sp->note_to_use)); return; } dest = newdata = (Sint16 *) SDL_malloc((newlen >> (FRACTION_BITS - 1)) + 2); if (!dest) { song->oom = 1; return; } if (--count) *dest++ = src[0]; /* Since we're pre-processing and this doesn't have to be done in real-time, we go ahead and do the full sliding cubic interpolation. */ count--; for(i = 0; i < count; i++) { vptr = src + (ofs >> FRACTION_BITS); v1 = ((vptr>=src+1)? *(vptr - 1):0); v2 = *vptr; v3 = *(vptr + 1); v4 = *(vptr + 2); v5 = v2 - v3; xdiff = TIM_FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS); v = (Sint32)(v2 + xdiff * (1.0/6.0) * (3 * (v3 - v5) - 2 * v1 - v4 + xdiff * (3 * (v1 - v2 - v5) + xdiff * (3 * v5 + v4 - v1)))); *dest++ = (Sint16)((v > 32767) ? 32767 : ((v < -32768) ? -32768 : v)); ofs += incr; } if (ofs & FRACTION_MASK) { v1 = src[ofs >> FRACTION_BITS]; v2 = src[(ofs >> FRACTION_BITS) + 1]; *dest++ = (Sint16)(v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS)); } else *dest++ = src[ofs >> FRACTION_BITS]; *dest = *(dest - 1) / 2; ++dest; *dest = *(dest - 1) / 2; sp->data_length = newlen; sp->loop_start = (Sint32)(sp->loop_start * a); sp->loop_end = (Sint32)(sp->loop_end * a); SDL_free(sp->data); sp->data = (sample_t *) newdata; sp->sample_rate = 0; } SDL2_mixer-2.8.0/src/codecs/timidity/COPYING0000644000076500000240000001373214277744147017376 0ustar valvestaff The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End SDL2_mixer-2.8.0/src/codecs/timidity/common.h0000644000076500000240000000144214277744147017777 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. common.h */ #ifndef TIMIDITY_COMMON_H #define TIMIDITY_COMMON_H extern SDL_RWops *timi_openfile(const char *name); /* pathlist funcs only to be used during Timidity_Init/Timidity_Exit */ extern int timi_add_pathlist(const char *s, size_t len); extern void timi_free_pathlist(void); /* hide private symbols by prefixing with "_timi_" */ #undef TIMI_NAMESPACE #define TIMI_NAMESPACE(x) _timi_ ## x /* debug output */ #ifdef DEBUG_CHATTER #define SNDDBG(X) SDL_Log X #else #define SNDDBG(X) #endif #endif /* TIMIDITY_COMMON_H */ SDL2_mixer-2.8.0/src/codecs/timidity/mix.c0000644000076500000240000002712014277744147017300 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. mix.c */ #include "SDL.h" #include "timidity.h" #include "options.h" #include "common.h" #include "instrum.h" #include "playmidi.h" #include "output.h" #include "tables.h" #include "resample.h" #include "mix.h" /* Returns 1 if envelope runs out */ int recompute_envelope(MidiSong *song, int v) { int stage; stage = song->voice[v].envelope_stage; if (stage>5) { /* Envelope ran out. */ song->voice[v].status = VOICE_FREE; return 1; } if (song->voice[v].sample->modes & MODES_ENVELOPE) { if (song->voice[v].status==VOICE_ON || song->voice[v].status==VOICE_SUSTAINED) { if (stage>2) { /* Freeze envelope until note turns off. Trumpets want this. */ song->voice[v].envelope_increment=0; return 0; } } } song->voice[v].envelope_stage=stage+1; if (song->voice[v].envelope_volume==song->voice[v].sample->envelope_offset[stage] || (stage > 2 && song->voice[v].envelope_volume < song->voice[v].sample->envelope_offset[stage])) return recompute_envelope(song, v); song->voice[v].envelope_target = song->voice[v].sample->envelope_offset[stage]; song->voice[v].envelope_increment = song->voice[v].sample->envelope_rate[stage]; if (song->voice[v].envelope_target < song->voice[v].envelope_volume) song->voice[v].envelope_increment = -song->voice[v].envelope_increment; return 0; } void apply_envelope_to_amp(MidiSong *song, int v) { float lamp = song->voice[v].left_amp, ramp; Sint32 la,ra; if (song->voice[v].panned == PANNED_MYSTERY) { ramp = song->voice[v].right_amp; if (song->voice[v].tremolo_phase_increment) { lamp *= song->voice[v].tremolo_volume; ramp *= song->voice[v].tremolo_volume; } if (song->voice[v].sample->modes & MODES_ENVELOPE) { lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; ramp *= (float)vol_table[song->voice[v].envelope_volume>>23]; } la = (Sint32)TIM_FSCALE(lamp,AMP_BITS); if (la>MAX_AMP_VALUE) la=MAX_AMP_VALUE; ra = (Sint32)TIM_FSCALE(ramp,AMP_BITS); if (ra>MAX_AMP_VALUE) ra=MAX_AMP_VALUE; song->voice[v].left_mix = la; song->voice[v].right_mix = ra; } else { if (song->voice[v].tremolo_phase_increment) lamp *= song->voice[v].tremolo_volume; if (song->voice[v].sample->modes & MODES_ENVELOPE) lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; la = (Sint32)TIM_FSCALE(lamp,AMP_BITS); if (la>MAX_AMP_VALUE) la=MAX_AMP_VALUE; song->voice[v].left_mix = la; } } static int update_envelope(MidiSong *song, int v) { song->voice[v].envelope_volume += song->voice[v].envelope_increment; /* Why is there no ^^ operator?? */ if (((song->voice[v].envelope_increment < 0) && (song->voice[v].envelope_volume <= song->voice[v].envelope_target)) || ((song->voice[v].envelope_increment > 0) && (song->voice[v].envelope_volume >= song->voice[v].envelope_target))) { song->voice[v].envelope_volume = song->voice[v].envelope_target; if (recompute_envelope(song, v)) return 1; } return 0; } static void update_tremolo(MidiSong *song, int v) { Sint32 depth = song->voice[v].sample->tremolo_depth << 7; if (song->voice[v].tremolo_sweep) { /* Update sweep position */ song->voice[v].tremolo_sweep_position += song->voice[v].tremolo_sweep; if (song->voice[v].tremolo_sweep_position >= (1 << SWEEP_SHIFT)) song->voice[v].tremolo_sweep=0; /* Swept to max amplitude */ else { /* Need to adjust depth */ depth *= song->voice[v].tremolo_sweep_position; depth >>= SWEEP_SHIFT; } } song->voice[v].tremolo_phase += song->voice[v].tremolo_phase_increment; /* if (song->voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<voice[v].tremolo_volume = (float) (1.0 - TIM_FSCALENEG((timi_sine(song->voice[v].tremolo_phase >> RATE_SHIFT) + 1.0) * depth * TREMOLO_AMPLITUDE_TUNING, 17)); /* I'm not sure about the +1.0 there -- it makes tremoloed voices' volumes on average the lower the higher the tremolo amplitude. */ } /* Returns 1 if the note died */ static int update_signal(MidiSong *song, int v) { if (song->voice[v].envelope_increment && update_envelope(song, v)) return 1; if (song->voice[v].tremolo_phase_increment) update_tremolo(song, v); apply_envelope_to_amp(song, v); return 0; } #define MIXATION(a) *lp++ += (a)*s; static void mix_mystery_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { Voice *vp = song->voice + v; final_volume_t left=vp->left_mix, right=vp->right_mix; int cc; sample_t s; if (!(cc = vp->control_counter)) { cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; right = vp->right_mix; } while (count) if (cc < count) { count -= cc; while (cc--) { s = *sp++; MIXATION(left); MIXATION(right); } cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; right = vp->right_mix; } else { vp->control_counter = cc - count; while (count--) { s = *sp++; MIXATION(left); MIXATION(right); } return; } } static void mix_center_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { Voice *vp = song->voice + v; final_volume_t left=vp->left_mix; int cc; sample_t s; if (!(cc = vp->control_counter)) { cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } while (count) if (cc < count) { count -= cc; while (cc--) { s = *sp++; MIXATION(left); MIXATION(left); } cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } else { vp->control_counter = cc - count; while (count--) { s = *sp++; MIXATION(left); MIXATION(left); } return; } } static void mix_single_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { Voice *vp = song->voice + v; final_volume_t left=vp->left_mix; int cc; sample_t s; if (!(cc = vp->control_counter)) { cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } while (count) if (cc < count) { count -= cc; while (cc--) { s = *sp++; MIXATION(left); lp++; } cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } else { vp->control_counter = cc - count; while (count--) { s = *sp++; MIXATION(left); lp++; } return; } } static void mix_mono_signal(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { Voice *vp = song->voice + v; final_volume_t left=vp->left_mix; int cc; sample_t s; if (!(cc = vp->control_counter)) { cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } while (count) if (cc < count) { count -= cc; while (cc--) { s = *sp++; MIXATION(left); } cc = song->control_ratio; if (update_signal(song, v)) return; /* Envelope ran out */ left = vp->left_mix; } else { vp->control_counter = cc - count; while (count--) { s = *sp++; MIXATION(left); } return; } } static void mix_mystery(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { final_volume_t left = song->voice[v].left_mix, right = song->voice[v].right_mix; sample_t s; while (count--) { s = *sp++; MIXATION(left); MIXATION(right); } } static void mix_center(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { final_volume_t left = song->voice[v].left_mix; sample_t s; while (count--) { s = *sp++; MIXATION(left); MIXATION(left); } } static void mix_single(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { final_volume_t left = song->voice[v].left_mix; sample_t s; while (count--) { s = *sp++; MIXATION(left); lp++; } } static void mix_mono(MidiSong *song, sample_t *sp, Sint32 *lp, int v, int count) { final_volume_t left = song->voice[v].left_mix; sample_t s; while (count--) { s = *sp++; MIXATION(left); } } /* Ramp a note out in c samples */ static void ramp_out(MidiSong *song, sample_t *sp, Sint32 *lp, int v, Sint32 c) { /* should be final_volume_t, but Uint8 gives trouble. */ Sint32 left, right, li, ri; sample_t s=0; /* silly warning about uninitialized s */ left=song->voice[v].left_mix; li=-(left/c); if (!li) li=-1; /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ if (!(song->encoding & PE_MONO)) { if (song->voice[v].panned==PANNED_MYSTERY) { right=song->voice[v].right_mix; ri=-(right/c); while (c--) { left += li; if (left<0) left=0; right += ri; if (right<0) right=0; s=*sp++; MIXATION(left); MIXATION(right); } } else if (song->voice[v].panned==PANNED_CENTER) { while (c--) { left += li; if (left<0) return; s=*sp++; MIXATION(left); MIXATION(left); } } else if (song->voice[v].panned==PANNED_LEFT) { while (c--) { left += li; if (left<0) return; s=*sp++; MIXATION(left); lp++; } } else if (song->voice[v].panned==PANNED_RIGHT) { while (c--) { left += li; if (left<0) return; s=*sp++; lp++; MIXATION(left); } } } else { /* Mono output. */ while (c--) { left += li; if (left<0) return; s=*sp++; MIXATION(left); } } } /**************** interface function ******************/ void mix_voice(MidiSong *song, Sint32 *buf, int v, Sint32 c) { Voice *vp = song->voice + v; sample_t *sp; if (vp->status==VOICE_DIE) { if (c>=MAX_DIE_TIME) c=MAX_DIE_TIME; sp=resample_voice(song, v, &c); if(c > 0) ramp_out(song, sp, buf, v, c); vp->status=VOICE_FREE; } else { sp=resample_voice(song, v, &c); if (song->encoding & PE_MONO) { /* Mono output. */ if (vp->envelope_increment || vp->tremolo_phase_increment) mix_mono_signal(song, sp, buf, v, c); else mix_mono(song, sp, buf, v, c); } else { if (vp->panned == PANNED_MYSTERY) { if (vp->envelope_increment || vp->tremolo_phase_increment) mix_mystery_signal(song, sp, buf, v, c); else mix_mystery(song, sp, buf, v, c); } else if (vp->panned == PANNED_CENTER) { if (vp->envelope_increment || vp->tremolo_phase_increment) mix_center_signal(song, sp, buf, v, c); else mix_center(song, sp, buf, v, c); } else { /* It's either full left or full right. In either case, every other sample is 0. Just get the offset right: */ if (vp->panned == PANNED_RIGHT) buf++; if (vp->envelope_increment || vp->tremolo_phase_increment) mix_single_signal(song, sp, buf, v, c); else mix_single(song, sp, buf, v, c); } } } } SDL2_mixer-2.8.0/src/codecs/timidity/playmidi.c0000644000076500000240000005120714277744147020316 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. playmidi.c -- random stuff in need of rearrangement */ #include "SDL.h" #include "timidity.h" #include "options.h" #include "common.h" #include "instrum.h" #include "playmidi.h" #include "output.h" #include "mix.h" #include "tables.h" static void adjust_amplification(MidiSong *song) { song->master_volume = (float)(song->amplification) / 100.0f; } static void reset_voices(MidiSong *song) { int i; for (i=0; ivoice[i].status=VOICE_FREE; } /* Process the Reset All Controllers event */ static void reset_controllers(MidiSong *song, int c) { song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */ song->channel[c].expression=127; /* SCC-1 does this. */ song->channel[c].sustain=0; song->channel[c].pitchbend=0x2000; song->channel[c].pitchfactor=0; /* to be computed */ } static void reset_midi(MidiSong *song) { int i; for (i=0; ichannel[i].program=song->default_program; song->channel[i].panning=NO_PANNING; song->channel[i].pitchsens=2; song->channel[i].bank=0; /* tone bank or drum set */ } reset_voices(song); } static void select_sample(MidiSong *song, int v, Instrument *ip) { Sint32 f, cdiff, diff; int s,i; Sample *sp, *closest; s=ip->samples; sp=ip->sample; if (s==1) { song->voice[v].sample=sp; return; } f=song->voice[v].orig_frequency; for (i=0; ilow_freq <= f && sp->high_freq >= f) { song->voice[v].sample=sp; return; } } /* No suitable sample found! We'll select the sample whose root frequency is closest to the one we want. (Actually we should probably convert the low, high, and root frequencies to MIDI note values and compare those.) */ cdiff=0x7FFFFFFF; closest=sp=ip->sample; for(i=0; iroot_freq - f; if (diff<0) diff=-diff; if (diffvoice[v].sample=closest; } static void recompute_freq(MidiSong *song, int v) { int sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */ pb=song->channel[song->voice[v].channel].pitchbend; double a; if (!song->voice[v].sample->sample_rate) return; if (song->voice[v].vibrato_control_ratio) { /* This instrument has vibrato. Invalidate any precomputed sample_increments. */ int i=VIBRATO_SAMPLE_INCREMENTS; while (i--) song->voice[v].vibrato_sample_increment[i]=0; } if (pb==0x2000 || pb<0 || pb>0x3FFF) song->voice[v].frequency = song->voice[v].orig_frequency; else { pb-=0x2000; if (!(song->channel[song->voice[v].channel].pitchfactor)) { /* Damn. Somebody bent the pitch. */ Sint32 i=pb*song->channel[song->voice[v].channel].pitchsens; if (pb<0) i=-i; song->channel[song->voice[v].channel].pitchfactor= (float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]); } if (pb>0) song->voice[v].frequency= (Sint32)(song->channel[song->voice[v].channel].pitchfactor * (double)(song->voice[v].orig_frequency)); else song->voice[v].frequency= (Sint32)((double)(song->voice[v].orig_frequency) / song->channel[song->voice[v].channel].pitchfactor); } a = TIM_FSCALE(((double)(song->voice[v].sample->sample_rate) * (double)(song->voice[v].frequency)) / ((double)(song->voice[v].sample->root_freq) * (double)(song->rate)), FRACTION_BITS); if (sign) a = -a; /* need to preserve the loop direction */ song->voice[v].sample_increment = (Sint32)(a); } static void recompute_amp(MidiSong *song, int v) { Sint32 tempamp; /* TODO: use fscale */ tempamp= (song->voice[v].velocity * song->channel[song->voice[v].channel].volume * song->channel[song->voice[v].channel].expression); /* 21 bits */ if (!(song->encoding & PE_MONO)) { if (song->voice[v].panning > 60 && song->voice[v].panning < 68) { song->voice[v].panned=PANNED_CENTER; song->voice[v].left_amp= TIM_FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, 21); } else if (song->voice[v].panning<5) { song->voice[v].panned = PANNED_LEFT; song->voice[v].left_amp= TIM_FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, 20); } else if (song->voice[v].panning>123) { song->voice[v].panned = PANNED_RIGHT; song->voice[v].left_amp= /* left_amp will be used */ TIM_FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, 20); } else { song->voice[v].panned = PANNED_MYSTERY; song->voice[v].left_amp= TIM_FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, 27); song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning); song->voice[v].left_amp *= (float)(127 - song->voice[v].panning); } } else { song->voice[v].panned = PANNED_CENTER; song->voice[v].left_amp= TIM_FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, 21); } } static void start_note(MidiSong *song, MidiEvent *e, int i) { Instrument *ip; int j; if (ISDRUMCHANNEL(song, e->channel)) { if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a])) { if (!(ip=song->drumset[0]->instrument[e->a])) return; /* No instrument? Then we can't play. */ } if (ip->samples != 1) { SNDDBG(("Strange: percussion instrument with %d samples!", ip->samples)); } if (ip->sample->note_to_use) /* Do we have a fixed pitch? */ song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)]; else song->voice[i].orig_frequency = freq_table[e->a & 0x7F]; /* drums are supposed to have only one sample */ song->voice[i].sample = ip->sample; } else { if (song->channel[e->channel].program == SPECIAL_PROGRAM) ip=song->default_instrument; else if (!(ip=song->tonebank[song->channel[e->channel].bank]-> instrument[song->channel[e->channel].program])) { if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program])) return; /* No instrument? Then we can't play. */ } if (ip->sample->note_to_use) /* Fixed-pitch instrument? */ song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)]; else song->voice[i].orig_frequency = freq_table[e->a & 0x7F]; select_sample(song, i, ip); } song->voice[i].status = VOICE_ON; song->voice[i].channel = e->channel; song->voice[i].note = e->a; song->voice[i].velocity = e->b; song->voice[i].sample_offset = 0; song->voice[i].sample_increment = 0; /* make sure it isn't negative */ song->voice[i].tremolo_phase = 0; song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment; song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment; song->voice[i].tremolo_sweep_position = 0; song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment; song->voice[i].vibrato_sweep_position = 0; song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio; song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0; for (j=0; jvoice[i].vibrato_sample_increment[j] = 0; if (song->channel[e->channel].panning != NO_PANNING) song->voice[i].panning = song->channel[e->channel].panning; else song->voice[i].panning = song->voice[i].sample->panning; recompute_freq(song, i); recompute_amp(song, i); if (song->voice[i].sample->modes & MODES_ENVELOPE) { /* Ramp up from 0 */ song->voice[i].envelope_stage = 0; song->voice[i].envelope_volume = 0; song->voice[i].control_counter = 0; recompute_envelope(song, i); apply_envelope_to_amp(song, i); } else { song->voice[i].envelope_increment = 0; apply_envelope_to_amp(song, i); } } static void kill_note(MidiSong *song, int i) { song->voice[i].status = VOICE_DIE; } /* Only one instance of a note can be playing on a single channel. */ static void note_on(MidiSong *song) { int i = song->voices, lowest=-1; Sint32 lv=0x7FFFFFFF, v; MidiEvent *e = song->current_event; while (i--) { if (song->voice[i].status == VOICE_FREE) lowest=i; /* Can't get a lower volume than silence */ else if (song->voice[i].channel==e->channel && (song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono)) kill_note(song, i); } if (lowest != -1) { /* Found a free voice. */ start_note(song,e,lowest); return; } /* Look for the decaying note with the lowest volume */ i = song->voices; while (i--) { if ((song->voice[i].status != VOICE_ON) && (song->voice[i].status != VOICE_DIE)) { v = song->voice[i].left_mix; if ((song->voice[i].panned == PANNED_MYSTERY) && (song->voice[i].right_mix > v)) v = song->voice[i].right_mix; if (vcut_notes++; song->voice[lowest].status=VOICE_FREE; start_note(song,e,lowest); } else song->lost_notes++; } static void finish_note(MidiSong *song, int i) { if (song->voice[i].sample->modes & MODES_ENVELOPE) { /* We need to get the envelope out of Sustain stage */ song->voice[i].envelope_stage = 3; song->voice[i].status = VOICE_OFF; recompute_envelope(song, i); apply_envelope_to_amp(song, i); } else { /* Set status to OFF so resample_voice() will let this voice out of its loop, if any. In any case, this voice dies when it hits the end of its data (ofs>=data_length). */ song->voice[i].status = VOICE_OFF; } } static void note_off(MidiSong *song) { int i = song->voices; MidiEvent *e = song->current_event; while (i--) if (song->voice[i].status == VOICE_ON && song->voice[i].channel == e->channel && song->voice[i].note == e->a) { if (song->channel[e->channel].sustain) { song->voice[i].status = VOICE_SUSTAINED; } else finish_note(song, i); return; } } /* Process the All Notes Off event */ static void all_notes_off(MidiSong *song) { int i = song->voices; int c = song->current_event->channel; SNDDBG(("All notes off on channel %d", c)); while (i--) if (song->voice[i].status == VOICE_ON && song->voice[i].channel == c) { if (song->channel[c].sustain) song->voice[i].status = VOICE_SUSTAINED; else finish_note(song, i); } } /* Process the All Sounds Off event */ static void all_sounds_off(MidiSong *song) { int i = song->voices; int c = song->current_event->channel; while (i--) if (song->voice[i].channel == c && song->voice[i].status != VOICE_FREE && song->voice[i].status != VOICE_DIE) { kill_note(song, i); } } static void adjust_pressure(MidiSong *song) { MidiEvent *e = song->current_event; int i = song->voices; while (i--) if (song->voice[i].status == VOICE_ON && song->voice[i].channel == e->channel && song->voice[i].note == e->a) { song->voice[i].velocity = e->b; recompute_amp(song, i); apply_envelope_to_amp(song, i); return; } } static void drop_sustain(MidiSong *song) { int i = song->voices; int c = song->current_event->channel; while (i--) if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c) finish_note(song, i); } static void adjust_pitchbend(MidiSong *song) { int c = song->current_event->channel; int i = song->voices; while (i--) if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c) { recompute_freq(song, i); } } static void adjust_volume(MidiSong *song) { int c = song->current_event->channel; int i = song->voices; while (i--) if (song->voice[i].channel == c && (song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED)) { recompute_amp(song, i); apply_envelope_to_amp(song, i); } } static void seek_forward(MidiSong *song, Sint32 until_time) { reset_voices(song); while (song->current_event->time < until_time) { switch(song->current_event->type) { /* All notes stay off. Just handle the parameter changes. */ case ME_PITCH_SENS: song->channel[song->current_event->channel].pitchsens = song->current_event->a; song->channel[song->current_event->channel].pitchfactor = 0; break; case ME_PITCHWHEEL: song->channel[song->current_event->channel].pitchbend = song->current_event->a + song->current_event->b * 128; song->channel[song->current_event->channel].pitchfactor = 0; break; case ME_MAINVOLUME: song->channel[song->current_event->channel].volume = song->current_event->a; break; case ME_PAN: song->channel[song->current_event->channel].panning = song->current_event->a; break; case ME_EXPRESSION: song->channel[song->current_event->channel].expression = song->current_event->a; break; case ME_PROGRAM: if (ISDRUMCHANNEL(song, song->current_event->channel)) /* Change drum set */ song->channel[song->current_event->channel].bank = song->current_event->a; else song->channel[song->current_event->channel].program = song->current_event->a; break; case ME_SUSTAIN: song->channel[song->current_event->channel].sustain = song->current_event->a; break; case ME_RESET_CONTROLLERS: reset_controllers(song, song->current_event->channel); break; case ME_TONE_BANK: song->channel[song->current_event->channel].bank = song->current_event->a; break; case ME_EOT: song->current_sample = song->current_event->time; return; } song->current_event++; } /*song->current_sample=song->current_event->time;*/ if (song->current_event != song->events) song->current_event--; song->current_sample=until_time; } static void skip_to(MidiSong *song, Sint32 until_time) { if (song->current_sample > until_time) song->current_sample = 0; reset_midi(song); song->buffered_count = 0; song->buffer_pointer = song->common_buffer; song->current_event = song->events; if (until_time) seek_forward(song, until_time); } static void do_compute_data(MidiSong *song, Sint32 count) { int i; SDL_memset(song->buffer_pointer, 0, (song->encoding & PE_MONO) ? (count * 4) : (count * 8)); for (i = 0; i < song->voices; i++) { if(song->voice[i].status != VOICE_FREE) mix_voice(song, song->buffer_pointer, i, count); } song->current_sample += count; } /* count=0 means flush remaining buffered data to output device, then flush the device itself */ static void compute_data(MidiSong *song, void *stream, Sint32 count) { int channels; if ( song->encoding & PE_MONO ) channels = 1; else channels = 2; if (!count) { if (song->buffered_count) song->write(stream, song->common_buffer, channels * song->buffered_count); song->buffer_pointer = song->common_buffer; song->buffered_count = 0; return; } while ((count + song->buffered_count) >= song->buffer_size) { do_compute_data(song, song->buffer_size - song->buffered_count); count -= song->buffer_size - song->buffered_count; song->write(stream, song->common_buffer, channels * song->buffer_size); song->buffer_pointer = song->common_buffer; song->buffered_count = 0; } if (count>0) { do_compute_data(song, count); song->buffered_count += count; song->buffer_pointer += (song->encoding & PE_MONO) ? count : count*2; } } void Timidity_Start(MidiSong *song) { song->playing = 1; adjust_amplification(song); skip_to(song, 0); } void Timidity_Stop(MidiSong *song) { song->playing = 0; } int Timidity_IsActive(MidiSong *song) { return song->playing; } void Timidity_Seek(MidiSong *song, Uint32 ms) { skip_to(song, (ms * (song->rate / 100)) / 10); } Uint32 Timidity_GetSongLength(MidiSong *song) { MidiEvent *last_event = &song->events[song->groomed_event_count - 1]; /* We want last_event->time * 1000 / song->rate */ Uint32 retvalue = (last_event->time / song->rate) * 1000; retvalue += (last_event->time % song->rate) * 1000 / song->rate; return retvalue; } Uint32 Timidity_GetSongTime(MidiSong *song) { Uint32 retvalue = (song->current_sample / song->rate) * 1000; retvalue += (song->current_sample % song->rate) * 1000 / song->rate; return retvalue; } int Timidity_PlaySome(MidiSong *song, void *stream, Sint32 len) { Sint32 start_sample, end_sample, samples; int bytes_per_sample; if (!song->playing) return 0; bytes_per_sample = 1; bytes_per_sample *= ((song->encoding & PE_32BIT) ? 4 : ((song->encoding & PE_16BIT) ? 2 : 1)); bytes_per_sample *= ((song->encoding & PE_MONO) ? 1 : 2); samples = len / bytes_per_sample; start_sample = song->current_sample; end_sample = song->current_sample+samples; while ( song->current_sample < end_sample ) { /* Handle all events that should happen at this time */ while (song->current_event->time <= song->current_sample) { switch(song->current_event->type) { /* Effects affecting a single note */ case ME_NOTEON: if (!(song->current_event->b)) /* Velocity 0? */ note_off(song); else note_on(song); break; case ME_NOTEOFF: note_off(song); break; case ME_KEYPRESSURE: adjust_pressure(song); break; /* Effects affecting a single channel */ case ME_PITCH_SENS: song->channel[song->current_event->channel].pitchsens = song->current_event->a; song->channel[song->current_event->channel].pitchfactor = 0; break; case ME_PITCHWHEEL: song->channel[song->current_event->channel].pitchbend = song->current_event->a + song->current_event->b * 128; song->channel[song->current_event->channel].pitchfactor = 0; /* Adjust pitch for notes already playing */ adjust_pitchbend(song); break; case ME_MAINVOLUME: song->channel[song->current_event->channel].volume = song->current_event->a; adjust_volume(song); break; case ME_PAN: song->channel[song->current_event->channel].panning = song->current_event->a; break; case ME_EXPRESSION: song->channel[song->current_event->channel].expression = song->current_event->a; adjust_volume(song); break; case ME_PROGRAM: if (ISDRUMCHANNEL(song, song->current_event->channel)) { /* Change drum set */ song->channel[song->current_event->channel].bank = song->current_event->a; } else song->channel[song->current_event->channel].program = song->current_event->a; break; case ME_SUSTAIN: song->channel[song->current_event->channel].sustain = song->current_event->a; if (!song->current_event->a) drop_sustain(song); break; case ME_RESET_CONTROLLERS: reset_controllers(song, song->current_event->channel); break; case ME_ALL_NOTES_OFF: all_notes_off(song); break; case ME_ALL_SOUNDS_OFF: all_sounds_off(song); break; case ME_TONE_BANK: song->channel[song->current_event->channel].bank = song->current_event->a; break; case ME_EOT: /* Give the last notes a couple of seconds to decay */ SNDDBG(("Playing time: ~%d seconds\n", song->current_sample/song->rate+2)); SNDDBG(("Notes cut: %d\n", song->cut_notes)); SNDDBG(("Notes lost totally: %d\n", song->lost_notes)); song->playing = 0; return (song->current_sample - start_sample) * bytes_per_sample; } song->current_event++; } if (song->current_event->time > end_sample) compute_data(song, stream, end_sample-song->current_sample); else compute_data(song, stream, song->current_event->time-song->current_sample); } return samples * bytes_per_sample; } void Timidity_SetVolume(MidiSong *song, int volume) { int i; if (volume > MAX_AMPLIFICATION) song->amplification = MAX_AMPLIFICATION; else if (volume < 0) song->amplification = 0; else song->amplification = volume; adjust_amplification(song); for (i = 0; i < song->voices; i++) if (song->voice[i].status != VOICE_FREE) { recompute_amp(song, i); apply_envelope_to_amp(song, i); } } SDL2_mixer-2.8.0/src/codecs/timidity/readmidi.h0000644000076500000240000000077314277744147020273 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. readmidi.h */ #ifndef TIMIDITY_READMIDI_H #define TIMIDITY_READMIDI_H #define read_midi_file TIMI_NAMESPACE(read_midi_file) extern MidiEvent *read_midi_file(MidiSong *song, Sint32 *count, Sint32 *sp); #endif /* TIMIDITY_READMIDI_H */ SDL2_mixer-2.8.0/src/codecs/timidity/output.c0000644000076500000240000000467714277744147020057 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. output.c Audio output (to file / device) functions. */ #include "SDL.h" #include "options.h" #include "output.h" /*****************************************************************/ /* Some functions to convert signed 32-bit data to other formats */ void timi_s32tos8(void *dp, Sint32 *lp, Sint32 c) { Sint8 *cp=(Sint8 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-8-GUARD_BITS); if (l>127) l=127; else if (l<-128) l=-128; *cp++ = (Sint8) (l); } } void timi_s32tou8(void *dp, Sint32 *lp, Sint32 c) { Uint8 *cp=(Uint8 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-8-GUARD_BITS); if (l>127) l=127; else if (l<-128) l=-128; *cp++ = 0x80 ^ ((Uint8) l); } } void timi_s32tos16(void *dp, Sint32 *lp, Sint32 c) { Sint16 *sp=(Sint16 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-16-GUARD_BITS); if (l > 32767) l=32767; else if (l<-32768) l=-32768; *sp++ = (Sint16)(l); } } void timi_s32tou16(void *dp, Sint32 *lp, Sint32 c) { Uint16 *sp=(Uint16 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-16-GUARD_BITS); if (l > 32767) l=32767; else if (l<-32768) l=-32768; *sp++ = 0x8000 ^ (Uint16)(l); } } void timi_s32tos16x(void *dp, Sint32 *lp, Sint32 c) { Sint16 *sp=(Sint16 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-16-GUARD_BITS); if (l > 32767) l=32767; else if (l<-32768) l=-32768; *sp++ = SDL_Swap16((Sint16)(l)); } } void timi_s32tou16x(void *dp, Sint32 *lp, Sint32 c) { Uint16 *sp=(Uint16 *)(dp); Sint32 l; while (c--) { l=(*lp++)>>(32-16-GUARD_BITS); if (l > 32767) l=32767; else if (l<-32768) l=-32768; *sp++ = SDL_Swap16(0x8000 ^ (Uint16)(l)); } } void timi_s32tof32(void *dp, Sint32 *lp, Sint32 c) { float *sp=(float *)(dp); while (c--) { *sp++ = (float)(*lp++) / 2147483647.0f; } } void timi_s32tos32(void *dp, Sint32 *lp, Sint32 c) { Sint32 *sp=(Sint32 *)(dp); while (c--) { *sp++ = (*lp++); } } void timi_s32tos32x(void *dp, Sint32 *lp, Sint32 c) { Sint32 *sp=(Sint32 *)(dp); while (c--) { *sp++ = SDL_Swap32(*lp++); } } SDL2_mixer-2.8.0/src/codecs/timidity/timidity.c0000644000076500000240000004342614551252471020333 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. */ #include "SDL.h" #include "../../utils.h" /* for SDL_strtokr() */ #include "timidity.h" #include "options.h" #include "common.h" #include "instrum.h" #include "playmidi.h" #include "readmidi.h" #include "output.h" #include "tables.h" static ToneBank *master_tonebank[MAXBANK], *master_drumset[MAXBANK]; static char def_instr_name[256] = ""; #define MAXWORDS 10 #define MAX_RCFCOUNT 50 /* Quick-and-dirty fgets() replacement. */ static char *RWgets(SDL_RWops *rw, char *s, int size) { int num_read = 0; char *p = s; --size;/* so that we nul terminate properly */ for (; num_read < size; ++p) { if (SDL_RWread(rw, p, 1, 1) != 1) break; num_read++; /* Unlike fgets(), don't store newline. Under Windows/DOS we'll * probably get an extra blank line for every line that's being * read, but that should be ok. */ if (*p == '\n' || *p == '\r') { *p = '\0'; return s; } } *p = '\0'; return (num_read != 0) ? s : NULL; } static int read_config_file(const char *name, int rcf_count) { SDL_RWops *rw; char tmp[1024]; char *w[MAXWORDS], *cp; char *endp; ToneBank *bank; int i, j, k, line, r, words; if (rcf_count >= MAX_RCFCOUNT) { SNDDBG(("Probable source loop in configuration files\n")); return -1; } if (!(rw=timi_openfile(name))) return -1; bank = NULL; line = 0; r = -1; /* start by assuming failure, */ while (RWgets(rw, tmp, sizeof(tmp))) { line++; words=0; w[0]=SDL_strtokr(tmp, " \t\240", &endp); if (!w[0]) continue; /* Originally the TiMidity++ extensions were prefixed like this */ if (SDL_strcmp(w[0], "#extension") == 0) { w[0]=SDL_strtokr(0, " \t\240", &endp); if (!w[0]) continue; } if (*w[0] == '#') continue; while (words < MAXWORDS - 1) /* -1 : next arg */ { while (*endp == ' ' || *endp == '\t' || *endp == '\240') endp++; if (*endp == '\0' || *endp == '#') break; if (*endp == '"' || *endp == '\'') { /* quoted string */ char *terminator = SDL_strchr(endp + 1, *endp); if (terminator != NULL) { /* terminated */ if (terminator[1] == ' ' || terminator[1] == '\t' || terminator[1] == '\240' || terminator[1] == '\0') { char *extraQuote = SDL_strchr(endp + 1, *endp == '"' ? '\'' : '"'); if (extraQuote != NULL && extraQuote < terminator) { SNDDBG(("%s: line %d: Quote characters are not allowed inside a quoted string", name, line)); goto fail; } w[++words] = endp + 1; endp = terminator + 1; *terminator = '\0'; } else { /* no space after quoted string */ SNDDBG(("%s: line %d: There must be at least one whitespace between string terminator (%c) and the next parameter", name, line, *endp)); goto fail; } } else { /* not terminated */ SNDDBG(("%s: line %d: The quoted string is not terminated", name, line)); goto fail; } } else { /* not quoted string */ w[++words] = endp; while (!(*endp == ' ' || *endp == '\t' || *endp == '\240' || *endp == '\0')) { if (*endp == '"' || *endp == '\'') { /* no space before quoted string */ SNDDBG(("%s: line %d: There must be at least one whitespace between previous parameter and a beginning of the quoted string (%c)", name, line, *endp)); goto fail; } endp++; } if (*endp != '\0') { /* unless at the end-of-string (i.e. EOF) */ *endp = '\0'; /* terminate the token */ endp++; } } } w[++words] = NULL; /* TiMidity++ adds a number of extensions to the config file format. * Many of them are completely irrelevant to SDL_sound, but at least * we shouldn't choke on them. * * Unfortunately the documentation for these extensions is often quite * vague, gramatically strange or completely absent. */ if (!SDL_strcmp(w[0], "comm") /* "comm" program second */ || !SDL_strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */ || !SDL_strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */ || !SDL_strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */ || !SDL_strcmp(w[0], "opt") /* "opt" timidity-options */ ) { /* + "comm" sets some kind of comment -- the documentation is too * vague for me to understand at this time. * + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data * over a network, rather than from the file system. * + "opt" specifies default options for TiMidity++. * * Quite useless for us, so they can safely remain no-ops. */ } else if (!SDL_strcmp(w[0], "timeout")) /* "timeout" program second */ { /* Specifies a timeout value of the program. A number of seconds * before TiMidity kills the note. No urgent need for it. */ SNDDBG(("FIXME: Implement \"timeout\" in TiMidity config.\n")); } else if (!SDL_strcmp(w[0], "copydrumset") /* "copydrumset" drumset */ || !SDL_strcmp(w[0], "copybank")) /* "copybank" bank */ { /* Copies all the settings of the specified drumset or bank to * the current drumset or bank. May be useful later, but not a * high priority. */ SNDDBG(("FIXME: Implement \"%s\" in TiMidity config.\n", w[0])); } else if (!SDL_strcmp(w[0], "undef")) /* "undef" progno */ { /* Undefines the tone "progno" of the current tone bank (or * drum set?). Not a high priority. */ SNDDBG(("FIXME: Implement \"undef\" in TiMidity config.\n")); } else if (!SDL_strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */ { /* Sets the alternate assign for drum set. Whatever that's * supposed to mean. */ SNDDBG(("FIXME: Implement \"altassign\" in TiMidity config.\n")); } else if (!SDL_strcmp(w[0], "soundfont") || !SDL_strcmp(w[0], "font")) { /* "soundfont" sf_file "remove" * "soundfont" sf_file ["order=" order] ["cutoff=" cutoff] * ["reso=" reso] ["amp=" amp] * "font" "exclude" bank preset keynote * "font" "order" order bank preset keynote */ SNDDBG(("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0])); } else if (!SDL_strcmp(w[0], "progbase")) { /* The documentation for this makes absolutely no sense to me, but * apparently it sets some sort of base offset for tone numbers. */ SNDDBG(("FIXME: Implement \"progbase\" in TiMidity config.\n")); } else if (!SDL_strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */ { /* This one is used by the "eawpats". Looks like it's used * for remapping one instrument to another somehow. */ SNDDBG(("FIXME: Implement \"map\" in TiMidity config.\n")); } /* Standard TiMidity config */ else if (!SDL_strcmp(w[0], "dir")) { if (words < 2) { SNDDBG(("%s: line %d: No directory given\n", name, line)); goto fail; } for (i=1; i(MAXBANK-1)) { SNDDBG(("%s: line %d: Drum set must be between 0 and %d\n", name, line, MAXBANK-1)); goto fail; } if (!master_drumset[i]) { master_drumset[i] = SDL_calloc(1, sizeof(ToneBank)); if (!master_drumset[i]) goto fail; master_drumset[i]->tone = SDL_calloc(128, sizeof(ToneBankElement)); if (!master_drumset[i]->tone) goto fail; } bank=master_drumset[i]; } else if (!SDL_strcmp(w[0], "bank")) { if (words < 2) { SNDDBG(("%s: line %d: No bank number given\n", name, line)); goto fail; } i=SDL_atoi(w[1]); if (i<0 || i>(MAXBANK-1)) { SNDDBG(("%s: line %d: Tone bank must be between 0 and %d\n", name, line, MAXBANK-1)); goto fail; } if (!master_tonebank[i]) { master_tonebank[i] = SDL_calloc(1, sizeof(ToneBank)); if (!master_tonebank[i]) goto fail; master_tonebank[i]->tone = SDL_calloc(128, sizeof(ToneBankElement)); if (!master_tonebank[i]->tone) goto fail; } bank=master_tonebank[i]; } else { size_t sz; if ((words < 2) || (*w[0] < '0' || *w[0] > '9')) { SNDDBG(("%s: line %d: syntax error\n", name, line)); goto fail; } i=SDL_atoi(w[0]); if (i<0 || i>127) { SNDDBG(("%s: line %d: Program must be between 0 and 127\n", name, line)); goto fail; } if (!bank) { SNDDBG(("%s: line %d: Must specify tone bank or drum set before assignment\n", name, line)); goto fail; } SDL_free(bank->tone[i].name); sz = SDL_strlen(w[1])+1; bank->tone[i].name = SDL_malloc(sz); if (!bank->tone[i].name) goto fail; SDL_memcpy(bank->tone[i].name,w[1],sz); bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan= bank->tone[i].strip_loop=bank->tone[i].strip_envelope= bank->tone[i].strip_tail=-1; for (j=2; j '9')) { SNDDBG(("%s: line %d: amplification must be between 0 and %d\n", name, line, MAX_AMPLIFICATION)); goto fail; } bank->tone[i].amp=k; } else if (!SDL_strcmp(w[j], "note")) { k=SDL_atoi(cp); if ((k<0 || k>127) || (*cp < '0' || *cp > '9')) { SNDDBG(("%s: line %d: note must be between 0 and 127\n", name, line)); goto fail; } bank->tone[i].note=k; } else if (!SDL_strcmp(w[j], "pan")) { if (!SDL_strcmp(cp, "center")) k=64; else if (!SDL_strcmp(cp, "left")) k=0; else if (!SDL_strcmp(cp, "right")) k=127; else k=((SDL_atoi(cp)+100) * 100) / 157; if ((k<0 || k>127) || (k==0 && *cp!='-' && (*cp < '0' || *cp > '9'))) { SNDDBG(("%s: line %d: panning must be left, right, center, or between -100 and 100\n", name, line)); goto fail; } bank->tone[i].pan=k; } else if (!SDL_strcmp(w[j], "keep")) { if (!SDL_strcmp(cp, "env")) bank->tone[i].strip_envelope=0; else if (!SDL_strcmp(cp, "loop")) bank->tone[i].strip_loop=0; else { SNDDBG(("%s: line %d: keep must be env or loop\n", name, line)); goto fail; } } else if (!SDL_strcmp(w[j], "strip")) { if (!SDL_strcmp(cp, "env")) bank->tone[i].strip_envelope=1; else if (!SDL_strcmp(cp, "loop")) bank->tone[i].strip_loop=1; else if (!SDL_strcmp(cp, "tail")) bank->tone[i].strip_tail=1; else { SNDDBG(("%s: line %d: strip must be env, loop, or tail\n", name, line)); goto fail; } } else { SNDDBG(("%s: line %d: bad patch option %s\n", name, line, w[j])); goto fail; } } } } r = 0; /* we're good. */ fail: SDL_RWclose(rw); return r; } #if defined(_WIN32)||defined(__CYGWIN__)||defined(__OS2__) /* FIXME: What about C:FOO ? */ static SDL_INLINE char *get_last_dirsep (const char *p) { char *p1 = SDL_strrchr(p, '/'); char *p2 = SDL_strrchr(p, '\\'); if (!p1) return p2; if (!p2) return p1; return (p1 > p2)? p1 : p2; } #else /* assumed UNIX-ish : */ static SDL_INLINE char *get_last_dirsep (const char *p) { return SDL_strrchr(p, '/'); } #endif static int init_alloc_banks(void) { /* Allocate memory for the standard tonebank and drumset */ master_tonebank[0] = SDL_calloc(1, sizeof(ToneBank)); if (!master_tonebank[0]) goto _nomem; master_tonebank[0]->tone = SDL_calloc(128, sizeof(ToneBankElement)); if (!master_tonebank[0]->tone) goto _nomem; master_drumset[0] = SDL_calloc(1, sizeof(ToneBank)); if (!master_drumset[0]) goto _nomem; master_drumset[0]->tone = SDL_calloc(128, sizeof(ToneBankElement)); if (!master_drumset[0]->tone) goto _nomem; return 0; _nomem: SNDDBG(("Out of memory\n")); Timidity_Exit (); return -2; } static int init_begin_config(const char *cf) { const char *p = get_last_dirsep(cf); if (p != NULL) return timi_add_pathlist(cf, p - cf + 1); /* including DIRSEP */ return 0; } static int init_with_config(const char *cf) { int rc = init_begin_config(cf); if (rc != 0) { Timidity_Exit (); return rc; } rc = read_config_file(cf, 0); if (rc != 0) { Timidity_Exit (); } return rc; } int Timidity_Init_NoConfig(void) { master_tonebank[0] = NULL; master_drumset[0] = NULL; return init_alloc_banks(); } int Timidity_Init(const char *config_file) { int rc = Timidity_Init_NoConfig(); if (rc != 0) { return rc; } if (config_file == NULL || *config_file == '\0') { return init_with_config(TIMIDITY_CFG); } return init_with_config(config_file); } static void do_song_load(SDL_RWops *rw, SDL_AudioSpec *audio, MidiSong **out) { MidiSong *song; int i; *out = NULL; if (rw == NULL) return; /* Allocate memory for the song */ song = (MidiSong *)SDL_calloc(1, sizeof(*song)); if (song == NULL) return; for (i = 0; i < MAXBANK; i++) { if (master_tonebank[i]) { song->tonebank[i] = SDL_calloc(1, sizeof(ToneBank)); if (!song->tonebank[i]) goto fail; song->tonebank[i]->tone = master_tonebank[i]->tone; } if (master_drumset[i]) { song->drumset[i] = SDL_calloc(1, sizeof(ToneBank)); if (!song->drumset[i]) goto fail; song->drumset[i]->tone = master_drumset[i]->tone; } } song->amplification = DEFAULT_AMPLIFICATION; song->voices = DEFAULT_VOICES; song->drumchannels = DEFAULT_DRUMCHANNELS; song->rw = rw; song->rate = audio->freq; song->encoding = 0; if ((audio->format & 0xFF) == 16) song->encoding |= PE_16BIT; else if ((audio->format & 0xFF) == 32) song->encoding |= PE_32BIT; if (audio->format & 0x8000) song->encoding |= PE_SIGNED; if (audio->channels == 1) song->encoding |= PE_MONO; else if (audio->channels > 2) { SDL_SetError("Surround sound not supported"); goto fail; } switch (audio->format) { case AUDIO_S8: song->write = timi_s32tos8; break; case AUDIO_U8: song->write = timi_s32tou8; break; case AUDIO_S16LSB: song->write = timi_s32tos16l; break; case AUDIO_S16MSB: song->write = timi_s32tos16b; break; case AUDIO_U16LSB: song->write = timi_s32tou16l; break; case AUDIO_U16MSB: song->write = timi_s32tou16b; break; case AUDIO_S32LSB: song->write = timi_s32tos32l; break; case AUDIO_S32MSB: song->write = timi_s32tos32b; break; case AUDIO_F32SYS: song->write = timi_s32tof32; break; default: SDL_SetError("Unsupported audio format"); goto fail; } song->buffer_size = audio->samples; song->resample_buffer = SDL_malloc(audio->samples * sizeof(sample_t)); if (!song->resample_buffer) goto fail; song->common_buffer = SDL_malloc(audio->samples * 2 * sizeof(Sint32)); if (!song->common_buffer) goto fail; song->control_ratio = audio->freq / CONTROLS_PER_SECOND; if (song->control_ratio < 1) song->control_ratio = 1; else if (song->control_ratio > MAX_CONTROL_RATIO) song->control_ratio = MAX_CONTROL_RATIO; song->lost_notes = 0; song->cut_notes = 0; song->events = read_midi_file(song, &(song->groomed_event_count), &song->samples); /* Make sure everything is okay */ if (!song->events) goto fail; song->default_instrument = NULL; song->default_program = DEFAULT_PROGRAM; if (*def_instr_name) set_default_instrument(song, def_instr_name); load_missing_instruments(song); if (! song->oom) *out = song; else { fail: Timidity_FreeSong(song); } } MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio) { MidiSong *song; do_song_load(rw, audio, &song); return song; } void Timidity_FreeSong(MidiSong *song) { int i; if (!song) return; free_instruments(song); for (i = 0; i < 128; i++) { SDL_free(song->tonebank[i]); SDL_free(song->drumset[i]); } SDL_free(song->common_buffer); SDL_free(song->resample_buffer); SDL_free(song->events); SDL_free(song); } void Timidity_Exit(void) { int i, j; for (i = 0; i < MAXBANK; i++) { if (master_tonebank[i]) { ToneBankElement *e = master_tonebank[i]->tone; if (e != NULL) { for (j = 0; j < 128; j++) { SDL_free(e[j].name); } SDL_free(e); } SDL_free(master_tonebank[i]); master_tonebank[i] = NULL; } if (master_drumset[i]) { ToneBankElement *e = master_drumset[i]->tone; if (e != NULL) { for (j = 0; j < 128; j++) { SDL_free(e[j].name); } SDL_free(e); } SDL_free(master_drumset[i]); master_drumset[i] = NULL; } } timi_free_pathlist(); } SDL2_mixer-2.8.0/src/codecs/timidity/instrum.h0000644000076500000240000000215714277744147020214 0ustar valvestaff/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License, available in COPYING. instrum.h */ #ifndef TIMIDITY_INSTRUM_H #define TIMIDITY_INSTRUM_H /* Bits in modes: */ #define MODES_16BIT (1<<0) #define MODES_UNSIGNED (1<<1) #define MODES_LOOPING (1<<2) #define MODES_PINGPONG (1<<3) #define MODES_REVERSE (1<<4) #define MODES_SUSTAIN (1<<5) #define MODES_ENVELOPE (1<<6) /* A hack to delay instrument loading until after reading the entire MIDI file. */ #define MAGIC_LOAD_INSTRUMENT ((Instrument *) (-1)) #define SPECIAL_PROGRAM -1 #define load_missing_instruments TIMI_NAMESPACE(load_missing_instruments) #define free_instruments TIMI_NAMESPACE(free_instruments) #define set_default_instrument TIMI_NAMESPACE(set_default_instrument) extern int load_missing_instruments(MidiSong *song); extern void free_instruments(MidiSong *song); extern int set_default_instrument(MidiSong *song, const char *name); #endif /* TIMIDITY_INSTRUM_H */ SDL2_mixer-2.8.0/src/codecs/music_opus.h0000644000076500000240000000215314551252471017026 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports Ogg Opus music streams */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_Opus; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_minimp3.c0000644000076500000240000001735314553251241017413 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_MP3_MINIMP3 #include "music_minimp3.h" #include "mp3utils.h" #define MINIMP3_IMPLEMENTATION #define MINIMP3_NO_STDIO #include "minimp3/minimp3_ex.h" typedef struct { struct mp3file_t file; int play_count; int freesrc; mp3dec_ex_t dec; mp3dec_io_t io; int volume; int status; SDL_AudioStream *stream; mp3d_sample_t *buffer; int buffer_size; uint64_t second_length; int channels; Mix_MusicMetaTags tags; } MiniMP3_Music; static size_t MiniMP3_ReadCB(void *buf, size_t size, void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; return MP3_RWread(&music->file, buf, 1, size); } static int MiniMP3_SeekCB(uint64_t position, void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; if (MP3_RWseek(&music->file, position, RW_SEEK_SET) < 0) { return -1; } return 0; } static int MINIMP3_Seek(void *context, double position); static void *MINIMP3_CreateFromRW(SDL_RWops *src, int freesrc) { MiniMP3_Music *music; music = (MiniMP3_Music *)SDL_calloc(1, sizeof(MiniMP3_Music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; if (MP3_RWinit(&music->file, src) < 0) { SDL_free(music); return NULL; } meta_tags_init(&music->tags); if (mp3_read_tags(&music->tags, &music->file, SDL_FALSE) < 0) { SDL_free(music); Mix_SetError("music_minimp3: corrupt mp3 file (bad tags)."); return NULL; } music->io.read = MiniMP3_ReadCB; music->io.read_data = music; music->io.seek = MiniMP3_SeekCB; music->io.seek_data = music; MP3_RWseek(&music->file, 0, RW_SEEK_SET); if (mp3dec_ex_open_cb(&music->dec, &music->io, MP3D_SEEK_TO_SAMPLE) != 0) { mp3dec_ex_close(&music->dec); SDL_free(music); Mix_SetError("music_minimp3: corrupt mp3 file (bad stream)."); return NULL; } music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)music->dec.info.channels, (int)music->dec.info.hz, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { SDL_OutOfMemory(); mp3dec_ex_close(&music->dec); SDL_free(music); return NULL; } music->channels = music->dec.info.channels; music->second_length = music->channels * music->dec.info.hz; music->buffer_size = music_spec.samples * sizeof(mp3d_sample_t) * music->channels; music->buffer = (mp3d_sample_t*)SDL_calloc(1, music->buffer_size); if (!music->buffer) { mp3dec_ex_close(&music->dec); SDL_OutOfMemory(); SDL_free(music); return NULL; } music->freesrc = freesrc; return music; } static void MINIMP3_SetVolume(void *context, int volume) { MiniMP3_Music *music = (MiniMP3_Music *)context; music->volume = volume; } static int MINIMP3_GetVolume(void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; return music->volume; } /* Starts the playback. */ static int MINIMP3_Play(void *context, int play_count) { MiniMP3_Music *music = (MiniMP3_Music *)context; music->play_count = play_count; return MINIMP3_Seek(music, 0.0); } static void MINIMP3_Stop(void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; SDL_AudioStreamClear(music->stream); } static int MINIMP3_GetSome(void *context, void *data, int bytes, SDL_bool *done) { MiniMP3_Music *music = (MiniMP3_Music *)context; int filled, amount; if (music->stream) { filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } amount = (int)mp3dec_ex_read(&music->dec, music->buffer, music_spec.samples * music->channels); if (amount > 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, (int)amount * sizeof(mp3d_sample_t)) < 0) { return -1; } } else { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (MINIMP3_Play(music, play_count) < 0) { return -1; } } } return 0; } static int MINIMP3_GetAudio(void *context, void *data, int bytes) { MiniMP3_Music *music = (MiniMP3_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, MINIMP3_GetSome); } static int MINIMP3_Seek(void *context, double position) { MiniMP3_Music *music = (MiniMP3_Music *)context; uint64_t destpos = (uint64_t)(position * music->second_length); if (destpos % music->channels != 0) { destpos -= destpos % music->channels; } mp3dec_ex_seek(&music->dec, destpos); return 0; } static double MINIMP3_Tell(void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; return (double)music->dec.cur_sample / music->second_length; } static double MINIMP3_Duration(void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; return (double)music->dec.samples / music->second_length; } static const char* MINIMP3_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { MiniMP3_Music *music = (MiniMP3_Music *)context; return meta_tags_get(&music->tags, tag_type); } static void MINIMP3_Delete(void *context) { MiniMP3_Music *music = (MiniMP3_Music *)context; mp3dec_ex_close(&music->dec); meta_tags_clear(&music->tags); if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->file.src); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_MINIMP3 = { "MINIMP3", MIX_MUSIC_MINIMP3, MUS_MP3, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ MINIMP3_CreateFromRW, NULL, /* CreateFromFile */ MINIMP3_SetVolume, MINIMP3_GetVolume, MINIMP3_Play, NULL, /* IsPlaying */ MINIMP3_GetAudio, NULL, /* Jump */ MINIMP3_Seek, MINIMP3_Tell, MINIMP3_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ MINIMP3_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ MINIMP3_Stop, MINIMP3_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_MP3_MINIMP3 */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_modplug.h0000644000076500000240000000217114551252471017507 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MOD files with libmodplug */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_MODPLUG; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_cmd.c0000644000076500000240000001605714553225265016611 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_config.h" /* This file supports an external command for playing music */ #ifdef MUSIC_CMD #ifdef __APPLE__ #define _DARWIN_C_SOURCE #endif #include #include #include #include #include #include #include #include #include #if defined(__linux__) && defined(__arm__) # include #endif #include "music_cmd.h" typedef struct { char *file; char *cmd; pid_t pid; int play_count; } MusicCMD; /* Load a music stream from the given file */ static void *MusicCMD_CreateFromFile(const char *file) { MusicCMD *music; if (!music_cmd) { Mix_SetError("You must call Mix_SetMusicCMD() first"); return NULL; } /* Allocate and fill the music structure */ music = (MusicCMD *)SDL_calloc(1, sizeof *music); if (music == NULL) { Mix_OutOfMemory(); return NULL; } music->file = SDL_strdup(file); music->cmd = SDL_strdup(music_cmd); music->pid = 0; /* We're done */ return music; } /* Parse a command line buffer into arguments */ static int ParseCommandLine(char *cmdline, char **argv) { char *bufp; int argc; argc = 0; for (bufp = cmdline; *bufp;) { /* Skip leading whitespace */ while (isspace(*bufp)) { ++bufp; } /* Skip over argument */ if (*bufp == '"') { ++bufp; if (*bufp) { if (argv) { argv[argc] = bufp; } ++argc; } /* Skip over word */ while (*bufp && (*bufp != '"')) { ++bufp; } } else { if (*bufp) { if (argv) { argv[argc] = bufp; } ++argc; } /* Skip over word */ while (*bufp && ! isspace(*bufp)) { ++bufp; } } if (*bufp) { if (argv) { *bufp = '\0'; } ++bufp; } } if (argv) { argv[argc] = NULL; } return argc; } static char **parse_args(char *command, char *last_arg) { int argc; char **argv; /* Parse the command line */ argc = ParseCommandLine(command, NULL); if (last_arg) { ++argc; } argv = (char **)SDL_malloc((argc+1)*(sizeof *argv)); if (argv == NULL) { return NULL; } argc = ParseCommandLine(command, argv); /* Add last command line argument */ if (last_arg) { argv[argc++] = last_arg; } argv[argc] = NULL; /* We're ready! */ return argv; } /* Start playback of a given music stream */ static int MusicCMD_Play(void *context, int play_count) { MusicCMD *music = (MusicCMD *)context; music->play_count = play_count; #ifdef HAVE_FORK music->pid = fork(); #elif defined(HAVE_VFORK) music->pid = vfork(); #else music->pid = -1; #endif switch(music->pid) { /* Failed fork() system call */ case -1: return Mix_SetError("fork() failed"); /* Child process - executes here */ case 0: { char **argv; /* Unblock signals in case we're called from a thread */ { sigset_t mask; sigemptyset(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); } /* Execute the command */ argv = parse_args(music->cmd, music->file); if (argv != NULL) { execvp(argv[0], argv); /* exec() failed */ perror(argv[0]); } _exit(-1); } break; /* Parent process - executes here */ default: break; } return 0; } /* Return non-zero if a stream is currently playing */ static SDL_bool MusicCMD_IsPlaying(void *context) { MusicCMD *music = (MusicCMD *)context; int status; if (music->pid > 0) { waitpid(music->pid, &status, WNOHANG); if (kill(music->pid, 0) == 0) { return SDL_TRUE; } /* We might want to loop */ if (music->play_count != 1) { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } MusicCMD_Play(music, play_count); return SDL_TRUE; } } return SDL_FALSE; } /* Pause playback of a given music stream */ static void MusicCMD_Pause(void *context) { MusicCMD *music = (MusicCMD *)context; if (music->pid > 0) { kill(music->pid, SIGSTOP); } } /* Resume playback of a given music stream */ static void MusicCMD_Resume(void *context) { MusicCMD *music = (MusicCMD *)context; if (music->pid > 0) { kill(music->pid, SIGCONT); } } /* Stop playback of a stream previously started with MusicCMD_Start() */ static void MusicCMD_Stop(void *context) { MusicCMD *music = (MusicCMD *)context; int status; if (music->pid > 0) { while (kill(music->pid, 0) == 0) { kill(music->pid, SIGTERM); sleep(1); waitpid(music->pid, &status, WNOHANG); } music->pid = 0; } } /* Close the given music stream */ void MusicCMD_Delete(void *context) { MusicCMD *music = (MusicCMD *)context; SDL_free(music->file); SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_CMD = { "CMD", MIX_MUSIC_CMD, MUS_CMD, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ NULL, /* CreateFromRW */ MusicCMD_CreateFromFile, NULL, /* SetVolume */ NULL, /* GetVolume */ MusicCMD_Play, MusicCMD_IsPlaying, NULL, /* GetAudio */ NULL, /* Jump */ NULL, /* Seek */ NULL, /* Tell */ NULL, /* Duration */ NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ NULL, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ MusicCMD_Pause, MusicCMD_Resume, MusicCMD_Stop, MusicCMD_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_CMD */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_nativemidi.h0000644000076500000240000000217214551252471020172 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MIDI files with OS APIs */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_NATIVEMIDI; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_fluidsynth.c0000644000076500000240000002732514553225265020237 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. James Le Cuirot chewi@aura-online.co.uk */ #ifdef MUSIC_MID_FLUIDSYNTH #include "SDL_loadso.h" #include "SDL_rwops.h" #include "music_fluidsynth.h" #include typedef struct { int loaded; void *handle; #if (FLUIDSYNTH_VERSION_MAJOR >= 2) void (*delete_fluid_player)(fluid_player_t*); void (*delete_fluid_synth)(fluid_synth_t*); #else int (*delete_fluid_player)(fluid_player_t*); int (*delete_fluid_synth)(fluid_synth_t*); #endif void (*delete_fluid_settings)(fluid_settings_t*); int (*fluid_player_add)(fluid_player_t*, const char*); int (*fluid_player_add_mem)(fluid_player_t*, const void*, size_t); int (*fluid_player_get_status)(fluid_player_t*); int (*fluid_player_play)(fluid_player_t*); int (*fluid_player_set_loop)(fluid_player_t*, int); int (*fluid_player_stop)(fluid_player_t*); int (*fluid_settings_setnum)(fluid_settings_t*, const char*, double); int (*fluid_settings_getnum)(fluid_settings_t*, const char*, double*); fluid_settings_t* (*fluid_synth_get_settings)(fluid_synth_t*); void (*fluid_synth_set_gain)(fluid_synth_t*, float); int (*fluid_synth_sfload)(fluid_synth_t*, const char*, int); int (*fluid_synth_write_s16)(fluid_synth_t*, int, void*, int, int, void*, int, int); int (*fluid_synth_write_float)(fluid_synth_t*, int, void*, int, int, void*, int, int); fluid_player_t* (*new_fluid_player)(fluid_synth_t*); fluid_settings_t* (*new_fluid_settings)(void); fluid_synth_t* (*new_fluid_synth)(fluid_settings_t*); } fluidsynth_loader; static fluidsynth_loader fluidsynth; #ifdef FLUIDSYNTH_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ fluidsynth.FUNC = (SIG) SDL_LoadFunction(fluidsynth.handle, #FUNC); \ if (fluidsynth.FUNC == NULL) { SDL_UnloadObject(fluidsynth.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ fluidsynth.FUNC = FUNC; #endif static int FLUIDSYNTH_Load() { if (fluidsynth.loaded == 0) { #ifdef FLUIDSYNTH_DYNAMIC fluidsynth.handle = SDL_LoadObject(FLUIDSYNTH_DYNAMIC); if (fluidsynth.handle == NULL) { return -1; } #endif #if (FLUIDSYNTH_VERSION_MAJOR >= 2) FUNCTION_LOADER(delete_fluid_player, void (*)(fluid_player_t*)) FUNCTION_LOADER(delete_fluid_synth, void (*)(fluid_synth_t*)) #else FUNCTION_LOADER(delete_fluid_player, int (*)(fluid_player_t*)) FUNCTION_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*)) #endif FUNCTION_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*)) FUNCTION_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*)) FUNCTION_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t)) FUNCTION_LOADER(fluid_player_get_status, int (*)(fluid_player_t*)) FUNCTION_LOADER(fluid_player_play, int (*)(fluid_player_t*)) FUNCTION_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int)) FUNCTION_LOADER(fluid_player_stop, int (*)(fluid_player_t*)) FUNCTION_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double)) FUNCTION_LOADER(fluid_settings_getnum, int (*)(fluid_settings_t*, const char*, double*)) FUNCTION_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*)) FUNCTION_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float)) FUNCTION_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int)) FUNCTION_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int)) FUNCTION_LOADER(fluid_synth_write_float, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int)) FUNCTION_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*)) FUNCTION_LOADER(new_fluid_settings, fluid_settings_t* (*)(void)) FUNCTION_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*)) } ++fluidsynth.loaded; return 0; } static void FLUIDSYNTH_Unload() { if (fluidsynth.loaded == 0) { return; } if (fluidsynth.loaded == 1) { #ifdef FLUIDSYNTH_DYNAMIC SDL_UnloadObject(fluidsynth.handle); #endif } --fluidsynth.loaded; } typedef struct { fluid_synth_t *synth; fluid_settings_t *settings; fluid_player_t *player; int (*synth_write)(fluid_synth_t*, int, void*, int, int, void*, int, int); SDL_AudioStream *stream; void *buffer; int buffer_size; int volume; } FLUIDSYNTH_Music; static void FLUIDSYNTH_Delete(void *context); static int SDLCALL fluidsynth_check_soundfont(const char *path, void *data) { SDL_RWops *rw = SDL_RWFromFile(path, "rb"); (void)data; if (rw) { SDL_RWclose(rw); return 1; } else { Mix_SetError("Failed to access the SoundFont %s", path); return 0; } } static int SDLCALL fluidsynth_load_soundfont(const char *path, void *data) { /* If this fails, it's too late to try Timidity so pray that at least one works. */ fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1); return 1; } static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec) { (void)spec; if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) { return -1; } return 0; } static FLUIDSYNTH_Music *FLUIDSYNTH_LoadMusic(void *data) { SDL_RWops *src = (SDL_RWops *)data; FLUIDSYNTH_Music *music; double samplerate; /* as set by the lib. */ const Uint8 channels = 2; int src_format = AUDIO_S16SYS; void *rw_mem; size_t rw_size; int ret; if (!(music = SDL_calloc(1, sizeof(FLUIDSYNTH_Music)))) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; music->buffer_size = music_spec.samples * sizeof(Sint16) * channels; music->synth_write = fluidsynth.fluid_synth_write_s16; if (music_spec.format & 0x0020) { /* 32 bit. */ src_format = AUDIO_F32SYS; music->buffer_size <<= 1; music->synth_write = fluidsynth.fluid_synth_write_float; } if (!(music->buffer = SDL_malloc((size_t)music->buffer_size))) { SDL_OutOfMemory(); goto fail; } if (!(music->settings = fluidsynth.new_fluid_settings())) { Mix_SetError("Failed to create FluidSynth settings"); goto fail; } fluidsynth.fluid_settings_setnum(music->settings, "synth.sample-rate", (double) music_spec.freq); fluidsynth.fluid_settings_getnum(music->settings, "synth.sample-rate", &samplerate); if (!(music->synth = fluidsynth.new_fluid_synth(music->settings))) { Mix_SetError("Failed to create FluidSynth synthesizer"); goto fail; } if (!Mix_EachSoundFont(fluidsynth_load_soundfont, music->synth)) { goto fail; } if (!(music->player = fluidsynth.new_fluid_player(music->synth))) { Mix_SetError("Failed to create FluidSynth player"); goto fail; } rw_mem = SDL_LoadFile_RW(src, &rw_size, SDL_FALSE); if (!rw_mem) { SDL_OutOfMemory(); goto fail; } ret = fluidsynth.fluid_player_add_mem(music->player, rw_mem, rw_size); SDL_free(rw_mem); if (ret != FLUID_OK) { Mix_SetError("FluidSynth failed to load in-memory song"); goto fail; } if (!(music->stream = SDL_NewAudioStream(src_format, channels, (int) samplerate, music_spec.format, music_spec.channels, music_spec.freq))) { goto fail; } return music; fail: FLUIDSYNTH_Delete(music); return NULL; } static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc) { FLUIDSYNTH_Music *music; music = FLUIDSYNTH_LoadMusic(src); if (music && freesrc) { SDL_RWclose(src); } return music; } static void FLUIDSYNTH_SetVolume(void *context, int volume) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; /* FluidSynth's default gain is 0.2. Make 1.0 the maximum gain value to avoid sound overload. */ music->volume = volume; fluidsynth.fluid_synth_set_gain(music->synth, volume * 1.0f / MIX_MAX_VOLUME); } static int FLUIDSYNTH_GetVolume(void *context) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; return music->volume; } static int FLUIDSYNTH_Play(void *context, int play_count) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; fluidsynth.fluid_player_set_loop(music->player, play_count); fluidsynth.fluid_player_play(music->player); return 0; } static SDL_bool FLUIDSYNTH_IsPlaying(void *context) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; return fluidsynth.fluid_player_get_status(music->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE; } static int FLUIDSYNTH_GetSome(void *context, void *data, int bytes, SDL_bool *done) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; int filled; (void)done; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (music->synth_write(music->synth, music_spec.samples, music->buffer, 0, 2, music->buffer, 1, 2) != FLUID_OK) { return Mix_SetError("Error generating FluidSynth audio"); } if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) { return -1; } return 0; } static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes) { return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, FLUIDSYNTH_GetSome); } static void FLUIDSYNTH_Stop(void *context) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; fluidsynth.fluid_player_stop(music->player); } static void FLUIDSYNTH_Delete(void *context) { FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context; if (music->player) { fluidsynth.delete_fluid_player(music->player); } if (music->synth) { fluidsynth.delete_fluid_synth(music->synth); } if (music->settings) { fluidsynth.delete_fluid_settings(music->settings); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH = { "FLUIDSYNTH", MIX_MUSIC_FLUIDSYNTH, MUS_MID, SDL_FALSE, SDL_FALSE, FLUIDSYNTH_Load, FLUIDSYNTH_Open, FLUIDSYNTH_CreateFromRW, NULL, /* CreateFromFile */ FLUIDSYNTH_SetVolume, FLUIDSYNTH_GetVolume, FLUIDSYNTH_Play, FLUIDSYNTH_IsPlaying, FLUIDSYNTH_GetAudio, NULL, /* Jump */ NULL, /* Seek */ NULL, /* Tell */ NULL, /* Duration */ NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ NULL, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ FLUIDSYNTH_Stop, FLUIDSYNTH_Delete, NULL, /* Close */ FLUIDSYNTH_Unload }; #endif /* MUSIC_MID_FLUIDSYNTH */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_flac.c0000644000076500000240000006703414553225265016754 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file is used to support SDL_LoadMUS playback of FLAC files. ~ Austen Dicken (admin@cvpcs.org) */ #ifdef MUSIC_FLAC_LIBFLAC #include "SDL_loadso.h" #include "SDL_assert.h" #include "music_flac.h" #include "utils.h" #include typedef struct { int loaded; void *handle; FLAC__StreamDecoder *(*FLAC__stream_decoder_new)(void); void (*FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); FLAC__StreamDecoderInitStatus (*FLAC__stream_decoder_init_stream)( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, FLAC__StreamDecoderSeekCallback seek_callback, FLAC__StreamDecoderTellCallback tell_callback, FLAC__StreamDecoderLengthCallback length_callback, FLAC__StreamDecoderEofCallback eof_callback, FLAC__StreamDecoderWriteCallback write_callback, FLAC__StreamDecoderMetadataCallback metadata_callback, FLAC__StreamDecoderErrorCallback error_callback, void *client_data); FLAC__StreamDecoderInitStatus (*FLAC__stream_decoder_init_ogg_stream)( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, FLAC__StreamDecoderSeekCallback seek_callback, FLAC__StreamDecoderTellCallback tell_callback, FLAC__StreamDecoderLengthCallback length_callback, FLAC__StreamDecoderEofCallback eof_callback, FLAC__StreamDecoderWriteCallback write_callback, FLAC__StreamDecoderMetadataCallback metadata_callback, FLAC__StreamDecoderErrorCallback error_callback, void *client_data); FLAC__bool (*FLAC__stream_decoder_finish)(FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_flush)(FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_process_single)( FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_process_until_end_of_metadata)( FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_process_until_end_of_stream)( FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_seek_absolute)( FLAC__StreamDecoder *decoder, FLAC__uint64 sample); FLAC__StreamDecoderState (*FLAC__stream_decoder_get_state)( const FLAC__StreamDecoder *decoder); FLAC__uint64 (*FLAC__stream_decoder_get_total_samples)( const FLAC__StreamDecoder *decoder); FLAC__bool (*FLAC__stream_decoder_set_metadata_respond)( FLAC__StreamDecoder *decoder, FLAC__MetadataType type); } flac_loader; static flac_loader flac; #ifdef FLAC_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ flac.FUNC = (SIG) SDL_LoadFunction(flac.handle, #FUNC); \ if (flac.FUNC == NULL) { SDL_UnloadObject(flac.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ flac.FUNC = FUNC; \ if (flac.FUNC == NULL) { Mix_SetError("Missing FLAC.framework"); return -1; } #endif static int FLAC_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (flac.loaded == 0) { #ifdef FLAC_DYNAMIC flac.handle = SDL_LoadObject(FLAC_DYNAMIC); if (flac.handle == NULL) { return -1; } #endif FUNCTION_LOADER(FLAC__stream_decoder_new, FLAC__StreamDecoder *(*)(void)) FUNCTION_LOADER(FLAC__stream_decoder_delete, void (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_init_stream, FLAC__StreamDecoderInitStatus (*)( FLAC__StreamDecoder *, FLAC__StreamDecoderReadCallback, FLAC__StreamDecoderSeekCallback, FLAC__StreamDecoderTellCallback, FLAC__StreamDecoderLengthCallback, FLAC__StreamDecoderEofCallback, FLAC__StreamDecoderWriteCallback, FLAC__StreamDecoderMetadataCallback, FLAC__StreamDecoderErrorCallback, void *)) FUNCTION_LOADER(FLAC__stream_decoder_init_ogg_stream, FLAC__StreamDecoderInitStatus (*)( FLAC__StreamDecoder *, FLAC__StreamDecoderReadCallback, FLAC__StreamDecoderSeekCallback, FLAC__StreamDecoderTellCallback, FLAC__StreamDecoderLengthCallback, FLAC__StreamDecoderEofCallback, FLAC__StreamDecoderWriteCallback, FLAC__StreamDecoderMetadataCallback, FLAC__StreamDecoderErrorCallback, void *)) FUNCTION_LOADER(FLAC__stream_decoder_finish, FLAC__bool (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_flush, FLAC__bool (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_process_single, FLAC__bool (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_process_until_end_of_metadata, FLAC__bool (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_process_until_end_of_stream, FLAC__bool (*)(FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_seek_absolute, FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__uint64)) FUNCTION_LOADER(FLAC__stream_decoder_get_state, FLAC__StreamDecoderState (*)(const FLAC__StreamDecoder *decoder)) FUNCTION_LOADER(FLAC__stream_decoder_get_total_samples, FLAC__uint64 (*)(const FLAC__StreamDecoder *)) FUNCTION_LOADER(FLAC__stream_decoder_set_metadata_respond, FLAC__bool (*)(FLAC__StreamDecoder *, FLAC__MetadataType)) } ++flac.loaded; return 0; } static void FLAC_Unload(void) { if (flac.loaded == 0) { return; } if (flac.loaded == 1) { #ifdef FLAC_DYNAMIC SDL_UnloadObject(flac.handle); #endif } --flac.loaded; } typedef struct { int volume; int play_count; FLAC__StreamDecoder *flac_decoder; unsigned sample_rate; unsigned channels; unsigned bits_per_sample; SDL_RWops *src; int freesrc; SDL_AudioStream *stream; int loop; FLAC__int64 pcm_pos; FLAC__int64 full_length; SDL_bool loop_flag; FLAC__int64 loop_start; FLAC__int64 loop_end; FLAC__int64 loop_len; Mix_MusicMetaTags tags; } FLAC_Music; static int FLAC_Seek(void *context, double position); static FLAC__StreamDecoderReadStatus flac_read_music_cb( const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { FLAC_Music *data = (FLAC_Music*)client_data; (void)decoder; /* make sure there is something to be reading */ if (*bytes > 0) { *bytes = SDL_RWread (data->src, buffer, sizeof(FLAC__byte), *bytes); if (*bytes == 0) { /* error or no data was read (EOF) */ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } else { /* data was read, continue */ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } } else { return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } } static FLAC__StreamDecoderSeekStatus flac_seek_music_cb( const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { FLAC_Music *data = (FLAC_Music*)client_data; (void)decoder; if (SDL_RWseek(data->src, (Sint64)absolute_byte_offset, RW_SEEK_SET) < 0) { return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; } else { return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } } static FLAC__StreamDecoderTellStatus flac_tell_music_cb( const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { FLAC_Music *data = (FLAC_Music*)client_data; Sint64 pos = SDL_RWtell(data->src); (void)decoder; if (pos < 0) { return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; } else { *absolute_byte_offset = (FLAC__uint64)pos; return FLAC__STREAM_DECODER_TELL_STATUS_OK; } } static FLAC__StreamDecoderLengthStatus flac_length_music_cb( const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { FLAC_Music *data = (FLAC_Music*)client_data; Sint64 pos = SDL_RWtell(data->src); Sint64 length = SDL_RWseek(data->src, 0, RW_SEEK_END); (void)decoder; if (SDL_RWseek(data->src, pos, RW_SEEK_SET) != pos || length < 0) { /* there was an error attempting to return the stream to the original * position, or the length was invalid. */ return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; } else { *stream_length = (FLAC__uint64)length; return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } } static FLAC__bool flac_eof_music_cb( const FLAC__StreamDecoder *decoder, void *client_data) { FLAC_Music *data = (FLAC_Music*)client_data; Sint64 pos = SDL_RWtell(data->src); Sint64 end = SDL_RWseek(data->src, 0, RW_SEEK_END); (void)decoder; /* was the original position equal to the end (a.k.a. the seek didn't move)? */ if (pos == end) { /* must be EOF */ return true; } else { /* not EOF, return to the original position */ SDL_RWseek(data->src, pos, RW_SEEK_SET); return false; } } static FLAC__StreamDecoderWriteStatus flac_write_music_cb( const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { FLAC_Music *music = (FLAC_Music *)client_data; Sint16 *data; unsigned int i, j, channels; int shift_amount = 0, amount; (void)decoder; if (!music->stream) { return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } switch (music->bits_per_sample) { case 16: shift_amount = 0; break; case 20: shift_amount = 4; break; case 24: shift_amount = 8; break; default: Mix_SetError("FLAC decoder doesn't support %d bits_per_sample", music->bits_per_sample); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } if (music->channels == 3) { /* We'll just drop the center channel for now */ channels = 2; } else { channels = music->channels; } data = SDL_stack_alloc(Sint16, (frame->header.blocksize * channels)); if (!data) { Mix_SetError("Couldn't allocate %d bytes stack memory", (int)(frame->header.blocksize * channels * sizeof(*data))); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } if (music->channels == 3) { Sint16 *dst = data; for (i = 0; i < frame->header.blocksize; ++i) { Sint16 FL = (Sint16)(buffer[0][i] >> shift_amount); Sint16 FR = (Sint16)(buffer[1][i] >> shift_amount); Sint16 FCmix = (Sint16)((buffer[2][i] >> shift_amount) * 0.5f); int sample; sample = (FL + FCmix); if (sample > SDL_MAX_SINT16) { *dst = SDL_MAX_SINT16; } else if (sample < SDL_MIN_SINT16) { *dst = SDL_MIN_SINT16; } else { *dst = (Sint16)sample; } ++dst; sample = (FR + FCmix); if (sample > SDL_MAX_SINT16) { *dst = SDL_MAX_SINT16; } else if (sample < SDL_MIN_SINT16) { *dst = SDL_MIN_SINT16; } else { *dst = (Sint16)sample; } ++dst; } } else { for (i = 0; i < channels; ++i) { Sint16 *dst = data + i; for (j = 0; j < frame->header.blocksize; ++j) { *dst = (Sint16)(buffer[i][j] >> shift_amount); dst += channels; } } } amount = (int)(frame->header.blocksize * channels * sizeof(*data)); music->pcm_pos += (FLAC__int64) frame->header.blocksize; if (music->loop && (music->play_count != 1) && (music->pcm_pos >= music->loop_end)) { amount -= (music->pcm_pos - music->loop_end) * channels * (int)sizeof(*data); music->loop_flag = SDL_TRUE; } SDL_AudioStreamPut(music->stream, data, amount); SDL_stack_free(data); return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void flac_metadata_music_cb( const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { FLAC_Music *music = (FLAC_Music *)client_data; const FLAC__StreamMetadata_VorbisComment *vc; int channels; unsigned rate; char *param, *argument, *value; SDL_bool is_loop_length = SDL_FALSE; (void)decoder; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { music->sample_rate = metadata->data.stream_info.sample_rate; music->channels = metadata->data.stream_info.channels; music->bits_per_sample = metadata->data.stream_info.bits_per_sample; /*printf("FLAC: Sample rate = %d, channels = %d, bits_per_sample = %d\n", music->sample_rate, music->channels, music->bits_per_sample);*/ /* SDL's channel mapping and FLAC channel mapping are the same, except for 3 channels: SDL is FL FR LFE and FLAC is FL FR FC */ if (music->channels == 3) { channels = 2; } else { channels = (int)music->channels; } /* We check for NULL stream later when we get data */ SDL_assert(!music->stream); music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)channels, (int)music->sample_rate, music_spec.format, music_spec.channels, music_spec.freq); } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC__uint32 i; vc = &metadata->data.vorbis_comment; rate = music->sample_rate; for (i = 0; i < vc->num_comments; ++i) { param = SDL_strdup((const char *) vc->comments[i].entry); argument = param; value = SDL_strchr(param, '='); if (value == NULL) { value = param + SDL_strlen(param); } else { *(value++) = '\0'; } /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from * string if it is present at position 4. */ if (_Mix_IsLoopTag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) { SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); } if (SDL_strcasecmp(argument, "LOOPSTART") == 0) music->loop_start = _Mix_ParseTime(value, rate); else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { music->loop_len = SDL_strtoll(value, NULL, 10); is_loop_length = SDL_TRUE; } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { music->loop_end = _Mix_ParseTime(value, rate); is_loop_length = SDL_FALSE; } else if (SDL_strcasecmp(argument, "TITLE") == 0) { meta_tags_set(&music->tags, MIX_META_TITLE, value); } else if (SDL_strcasecmp(argument, "ARTIST") == 0) { meta_tags_set(&music->tags, MIX_META_ARTIST, value); } else if (SDL_strcasecmp(argument, "ALBUM") == 0) { meta_tags_set(&music->tags, MIX_META_ALBUM, value); } else if (SDL_strcasecmp(argument, "COPYRIGHT") == 0) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, value); } SDL_free(param); } if (is_loop_length) { music->loop_end = music->loop_start + music->loop_len; } else { music->loop_len = music->loop_end - music->loop_start; } /* Ignore invalid loop tag */ if (music->loop_start < 0 || music->loop_len < 0 || music->loop_end < 0) { music->loop_start = 0; music->loop_len = 0; music->loop_end = 0; } } } static void flac_error_music_cb( const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { (void)decoder; (void)client_data; /* print an SDL error based on the error status */ switch (status) { case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: Mix_SetError("Error processing the FLAC file [LOST_SYNC]."); break; case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: Mix_SetError("Error processing the FLAC file [BAD_HEADER]."); break; case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: Mix_SetError("Error processing the FLAC file [CRC_MISMATCH]."); break; case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM: Mix_SetError("Error processing the FLAC file [UNPARSEABLE]."); break; default: Mix_SetError("Error processing the FLAC file [UNKNOWN]."); break; } } /* Load an FLAC stream from an SDL_RWops object */ static void *FLAC_CreateFromRW(SDL_RWops *src, int freesrc) { FLAC_Music *music; int init_stage = 0; int was_error = 1; FLAC__int64 full_length; int is_ogg_flac; Uint8 magic[4]; if (SDL_RWread(src, magic, 1, 4) != 4) { Mix_SetError("Couldn't read first 4 bytes of audio data"); return NULL; } SDL_RWseek(src, -4, RW_SEEK_CUR); is_ogg_flac = (SDL_memcmp(magic, "OggS", 4) == 0); music = (FLAC_Music *)SDL_calloc(1, sizeof(*music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->src = src; music->volume = MIX_MAX_VOLUME; music->flac_decoder = flac.FLAC__stream_decoder_new(); if (music->flac_decoder) { FLAC__StreamDecoderInitStatus ret; init_stage++; /* stage 1! */ flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); if (is_ogg_flac) { ret = flac.FLAC__stream_decoder_init_ogg_stream( music->flac_decoder, flac_read_music_cb, flac_seek_music_cb, flac_tell_music_cb, flac_length_music_cb, flac_eof_music_cb, flac_write_music_cb, flac_metadata_music_cb, flac_error_music_cb, music); } else { ret = flac.FLAC__stream_decoder_init_stream( music->flac_decoder, flac_read_music_cb, flac_seek_music_cb, flac_tell_music_cb, flac_length_music_cb, flac_eof_music_cb, flac_write_music_cb, flac_metadata_music_cb, flac_error_music_cb, music); } if (ret == FLAC__STREAM_DECODER_INIT_STATUS_OK) { init_stage++; /* stage 2! */ if (flac.FLAC__stream_decoder_process_until_end_of_metadata(music->flac_decoder)) { was_error = 0; } else { Mix_SetError("FLAC__stream_decoder_process_until_end_of_metadata() failed"); } } else { Mix_SetError("FLAC__stream_decoder_init_stream() failed"); } } else { Mix_SetError("FLAC__stream_decoder_new() failed"); } if (was_error) { switch (init_stage) { case 2: flac.FLAC__stream_decoder_finish(music->flac_decoder); /* fallthrough */ case 1: flac.FLAC__stream_decoder_delete(music->flac_decoder); /* fallthrough */ case 0: SDL_free(music); break; } return NULL; } /* loop_start, loop_end and loop_len get set by metadata callback if tags * are present in metadata. */ full_length = (FLAC__int64) flac.FLAC__stream_decoder_get_total_samples(music->flac_decoder); if ((music->loop_end > 0) && (music->loop_end <= full_length) && (music->loop_start < music->loop_end)) { music->loop = 1; } music->full_length = full_length; music->freesrc = freesrc; return music; } static const char* FLAC_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { FLAC_Music *music = (FLAC_Music *)context; return meta_tags_get(&music->tags, tag_type); } /* Set the volume for an FLAC stream */ static void FLAC_SetVolume(void *context, int volume) { FLAC_Music *music = (FLAC_Music *)context; music->volume = volume; } /* Get the volume for an FLAC stream */ static int FLAC_GetVolume(void *context) { FLAC_Music *music = (FLAC_Music *)context; return music->volume; } /* Start playback of a given FLAC stream */ static int FLAC_Play(void *context, int play_count) { FLAC_Music *music = (FLAC_Music *)context; music->play_count = play_count; return FLAC_Seek(music, 0.0); } static void FLAC_Stop(void *context) { FLAC_Music *music = (FLAC_Music *)context; SDL_AudioStreamClear(music->stream); } /* Read some FLAC stream data and convert it for output */ static int FLAC_GetSome(void *context, void *data, int bytes, SDL_bool *done) { FLAC_Music *music = (FLAC_Music *)context; int filled; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } if (!flac.FLAC__stream_decoder_process_single(music->flac_decoder)) { return Mix_SetError("FLAC__stream_decoder_process_single() failed"); } if (music->loop_flag) { music->pcm_pos = music->loop_start; if (flac.FLAC__stream_decoder_seek_absolute(music->flac_decoder, (FLAC__uint64)music->loop_start) == FLAC__STREAM_DECODER_SEEK_ERROR) { flac.FLAC__stream_decoder_flush(music->flac_decoder); return Mix_SetError("FLAC__stream_decoder_seek_absolute() failed"); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } music->play_count = play_count; music->loop_flag = SDL_FALSE; } } if (flac.FLAC__stream_decoder_get_state(music->flac_decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (FLAC_Play(music, play_count) < 0) { return -1; } } } return 0; } /* Play some of a stream previously started with FLAC_play() */ static int FLAC_GetAudio(void *context, void *data, int bytes) { FLAC_Music *music = (FLAC_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, FLAC_GetSome); } /* Jump (seek) to a given position (position is in seconds) */ static int FLAC_Seek(void *context, double position) { FLAC_Music *music = (FLAC_Music *)context; FLAC__uint64 seek_sample = (FLAC__uint64) (music->sample_rate * position); SDL_AudioStreamClear(music->stream); music->pcm_pos = (FLAC__int64) seek_sample; if (!flac.FLAC__stream_decoder_seek_absolute(music->flac_decoder, seek_sample)) { if (flac.FLAC__stream_decoder_get_state(music->flac_decoder) == FLAC__STREAM_DECODER_SEEK_ERROR) { flac.FLAC__stream_decoder_flush(music->flac_decoder); } return Mix_SetError("Seeking of FLAC stream failed: libFLAC seek failed."); } return 0; } static double FLAC_Tell(void *context) { FLAC_Music *music = (FLAC_Music *)context; return (double)music->pcm_pos / music->sample_rate; } /* Return music duration in seconds */ static double FLAC_Duration(void *context) { FLAC_Music *music = (FLAC_Music *)context; return (double)music->full_length / music->sample_rate; } static double FLAC_LoopStart(void *music_p) { FLAC_Music *music = (FLAC_Music *)music_p; if (music->loop > 0) { return (double)music->loop_start / music->sample_rate; } return -1.0; } static double FLAC_LoopEnd(void *music_p) { FLAC_Music *music = (FLAC_Music *)music_p; if (music->loop > 0) { return (double)music->loop_end / music->sample_rate; } return -1.0; } static double FLAC_LoopLength(void *music_p) { FLAC_Music *music = (FLAC_Music *)music_p; if (music->loop > 0) { return (double)music->loop_len / music->sample_rate; } return -1.0; } /* Close the given FLAC_Music object */ static void FLAC_Delete(void *context) { FLAC_Music *music = (FLAC_Music *)context; if (music) { meta_tags_clear(&music->tags); if (music->flac_decoder) { flac.FLAC__stream_decoder_finish(music->flac_decoder); flac.FLAC__stream_decoder_delete(music->flac_decoder); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->freesrc) { SDL_RWclose(music->src); } SDL_free(music); } } Mix_MusicInterface Mix_MusicInterface_FLAC = { "FLAC", MIX_MUSIC_FLAC, MUS_FLAC, SDL_FALSE, SDL_FALSE, FLAC_Load, NULL, /* Open */ FLAC_CreateFromRW, NULL, /* CreateFromFile */ FLAC_SetVolume, FLAC_GetVolume, FLAC_Play, NULL, /* IsPlaying */ FLAC_GetAudio, NULL, /* Jump */ FLAC_Seek, FLAC_Tell, FLAC_Duration, FLAC_LoopStart, FLAC_LoopEnd, FLAC_LoopLength, FLAC_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ FLAC_Stop, /* Stop */ FLAC_Delete, NULL, /* Close */ FLAC_Unload }; #endif /* MUSIC_FLAC_LIBFLAC */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/load_aiff.h0000644000076500000240000000277214551252471016553 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This is the source needed to decode an AIFF file into a waveform. It's pretty straightforward once you get going. The only externally-callable function is Mix_LoadAIFF_RW(), which is meant to act as identically to SDL_LoadWAV_RW() as possible. This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) */ /* Don't call this directly; use Mix_LoadWAV_RW() for now. */ SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_gme.c0000644000076500000240000003117714553251241016607 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_GME #include "SDL_loadso.h" #include "music_gme.h" #include typedef struct { int loaded; void *handle; gme_err_t (*gme_open_data)(void const* data, long size, Music_Emu** out, int sample_rate); int (*gme_track_count)(Music_Emu const*); gme_err_t (*gme_start_track)(Music_Emu*, int index); int (*gme_track_ended)(Music_Emu const*); void (*gme_set_tempo)(Music_Emu*, double tempo); int (*gme_voice_count)(Music_Emu const*); void (*gme_mute_voice)(Music_Emu*, int index, int mute); void (*gme_set_fade)(Music_Emu*, int start_msec); void (*gme_set_autoload_playback_limit)(Music_Emu*, int do_autoload_limit); gme_err_t (*gme_track_info)(Music_Emu const*, gme_info_t** out, int track); void (*gme_free_info)(gme_info_t*); gme_err_t (*gme_seek)(Music_Emu*, int msec); int (*gme_tell)(Music_Emu const*); gme_err_t (*gme_play)(Music_Emu*, int count, short out[]); void (*gme_delete)(Music_Emu*); } gme_loader; static gme_loader gme; #ifdef GME_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ gme.FUNC = (SIG) SDL_LoadFunction(gme.handle, #FUNC); \ if (gme.FUNC == NULL) { SDL_UnloadObject(gme.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ gme.FUNC = FUNC; \ if (gme.FUNC == NULL) { Mix_SetError("Missing gme.framework"); return -1; } #endif static int GME_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (gme.loaded == 0) { #ifdef GME_DYNAMIC gme.handle = SDL_LoadObject(GME_DYNAMIC); if (gme.handle == NULL) { return -1; } #endif FUNCTION_LOADER(gme_open_data, gme_err_t (*)(void const*,long,Music_Emu**,int)) FUNCTION_LOADER(gme_track_count, int (*)(Music_Emu const*)) FUNCTION_LOADER(gme_start_track, gme_err_t (*)( Music_Emu*,int)) FUNCTION_LOADER(gme_track_ended, int (*)( Music_Emu const*)) FUNCTION_LOADER(gme_set_tempo, void (*)(Music_Emu*,double)) FUNCTION_LOADER(gme_voice_count, int (*)(Music_Emu const*)) FUNCTION_LOADER(gme_mute_voice, void (*)(Music_Emu*,int,int)) FUNCTION_LOADER(gme_set_fade, void (*)(Music_Emu*,int)) FUNCTION_LOADER(gme_track_info, gme_err_t (*)(Music_Emu const*, gme_info_t**, int)) FUNCTION_LOADER(gme_free_info, void (*)(gme_info_t*)) FUNCTION_LOADER(gme_seek, gme_err_t (*)(Music_Emu*,int)) FUNCTION_LOADER(gme_tell, int (*)(Music_Emu const*)) FUNCTION_LOADER(gme_play, gme_err_t (*)(Music_Emu*, int, short[])) FUNCTION_LOADER(gme_delete, void (*)(Music_Emu*)) #if defined(GME_DYNAMIC) gme.gme_set_autoload_playback_limit = (void (*)(Music_Emu*,int)) SDL_LoadFunction(gme.handle, "gme_set_autoload_playback_limit"); if (!gme.gme_set_autoload_playback_limit) { SDL_ClearError(); /* gme_set_autoload_playback_limit is optional. */ } #elif (GME_VERSION >= 0x000603) gme.gme_set_autoload_playback_limit = gme_set_autoload_playback_limit; #else gme.gme_set_autoload_playback_limit = NULL; #endif } ++gme.loaded; return 0; } static void GME_Unload(void) { if (gme.loaded == 0) { return; } if (gme.loaded == 1) { #ifdef GME_DYNAMIC SDL_UnloadObject(gme.handle); #endif } --gme.loaded; } /* This file supports Game Music Emulator music streams */ typedef struct { int play_count; Music_Emu* game_emu; int freesrc; SDL_bool has_track_length; int track_length; int intro_length; int loop_length; int volume; double tempo; double gain; SDL_AudioStream *stream; void *buffer; size_t buffer_size; Mix_MusicMetaTags tags; } GME_Music; static void GME_Delete(void *context); /* Set the volume for a GME stream */ static void GME_SetVolume(void *music_p, int volume) { GME_Music *music = (GME_Music*)music_p; double v = SDL_floor(((double)volume * music->gain) + 0.5); music->volume = (int)v; } /* Get the volume for a GME stream */ static int GME_GetVolume(void *music_p) { GME_Music *music = (GME_Music*)music_p; double v = SDL_floor(((double)(music->volume) / music->gain) + 0.5); return (int)v; } static int initialize_from_track_info(GME_Music *music, int track) { gme_info_t *musInfo; SDL_bool has_loop_length = SDL_TRUE; const char *err; err = gme.gme_track_info(music->game_emu, &musInfo, track); if (err != 0) { return Mix_SetError("GME: %s", err); } music->track_length = musInfo->length; music->intro_length = musInfo->intro_length; music->loop_length = musInfo->loop_length; music->has_track_length = SDL_TRUE; if (music->track_length <= 0 ) { music->track_length = (int)(2.5 * 60 * 1000); music->has_track_length = SDL_FALSE; } if (music->intro_length < 0 ) { music->intro_length = 0; } if (music->loop_length <= 0 ) { if (music->track_length > 0) { music->loop_length = music->track_length; } else { music->loop_length = (int)(2.5 * 60 * 1000); } has_loop_length = SDL_FALSE; } if (!music->has_track_length && has_loop_length) { music->track_length = music->intro_length + music->loop_length; music->has_track_length = SDL_TRUE; } meta_tags_set(&music->tags, MIX_META_TITLE, musInfo->song); meta_tags_set(&music->tags, MIX_META_ARTIST, musInfo->author); meta_tags_set(&music->tags, MIX_META_ALBUM, musInfo->game); meta_tags_set(&music->tags, MIX_META_COPYRIGHT, musInfo->copyright); gme.gme_free_info(musInfo); return 0; } static void *GME_CreateFromRW(struct SDL_RWops *src, int freesrc) { void *mem = 0; size_t size; GME_Music *music; const char *err; if (src == NULL) { Mix_SetError("GME: Empty source given"); return NULL; } music = (GME_Music *)SDL_calloc(1, sizeof(GME_Music)); music->tempo = 1.0; music->gain = 1.0; music->stream = SDL_NewAudioStream(AUDIO_S16SYS, 2, music_spec.freq, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { GME_Delete(music); return NULL; } music->buffer_size = music_spec.samples * sizeof(Sint16) * 2/*channels*/ * music_spec.channels; music->buffer = SDL_malloc(music->buffer_size); if (!music->buffer) { SDL_OutOfMemory(); GME_Delete(music); return NULL; } SDL_RWseek(src, 0, RW_SEEK_SET); mem = SDL_LoadFile_RW(src, &size, SDL_FALSE); if (mem) { err = gme.gme_open_data(mem, (long)size, &music->game_emu, music_spec.freq); SDL_free(mem); if (err != 0) { GME_Delete(music); Mix_SetError("GME: %s", err); return NULL; } } else { SDL_OutOfMemory(); GME_Delete(music); return NULL; } /* Set this flag BEFORE calling the gme_start_track() to fix an inability to loop forever */ if (gme.gme_set_autoload_playback_limit) { gme.gme_set_autoload_playback_limit(music->game_emu, 0); } err = gme.gme_start_track(music->game_emu, 0); if (err != 0) { GME_Delete(music); Mix_SetError("GME: %s", err); return NULL; } gme.gme_set_tempo(music->game_emu, music->tempo); music->volume = MIX_MAX_VOLUME; meta_tags_init(&music->tags); if (initialize_from_track_info(music, 0) < 0) { GME_Delete(music); return NULL; } music->freesrc = freesrc; return music; } /* Start playback of a given Game Music Emulators stream */ static int GME_Play(void *music_p, int play_count) { GME_Music *music = (GME_Music*)music_p; int fade_start; if (music) { SDL_AudioStreamClear(music->stream); music->play_count = play_count; fade_start = play_count > 0 ? music->intro_length + (music->loop_length * play_count) : -1; /* libgme >= 0.6.4 has gme_set_fade_msecs(), * but gme_set_fade() sets msecs to 8000 by * default and we are OK with that. */ gme.gme_set_fade(music->game_emu, fade_start); gme.gme_seek(music->game_emu, 0); } return 0; } static int GME_GetSome(void *context, void *data, int bytes, SDL_bool *done) { GME_Music *music = (GME_Music*)context; int filled; const char *err = NULL; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (gme.gme_track_ended(music->game_emu)) { /* All done */ *done = SDL_TRUE; return 0; } err = gme.gme_play(music->game_emu, (int)(music->buffer_size / 2), (short*)music->buffer); if (err != NULL) { Mix_SetError("GME: %s", err); return 0; } if (SDL_AudioStreamPut(music->stream, music->buffer, (int)music->buffer_size) < 0) { return -1; } return 0; } /* Play some of a stream previously started with GME_Play() */ static int GME_PlayAudio(void *music_p, void *data, int bytes) { GME_Music *music = (GME_Music*)music_p; return music_pcm_getaudio(music_p, data, bytes, music->volume, GME_GetSome); } /* Close the given Game Music Emulators stream */ static void GME_Delete(void *context) { GME_Music *music = (GME_Music*)context; if (music) { meta_tags_clear(&music->tags); if (music->game_emu && music->freesrc) { gme.gme_delete(music->game_emu); music->game_emu = NULL; } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } SDL_free(music); } } // TODO: this should accept a track number, not assume the current track! static const char* GME_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { GME_Music *music = (GME_Music *)context; return meta_tags_get(&music->tags, tag_type); } /* Jump (seek) to a given position (time is in seconds) */ static int GME_Seek(void *music_p, double time) { GME_Music *music = (GME_Music*)music_p; gme.gme_seek(music->game_emu, (int)(SDL_floor((time * 1000.0) + 0.5))); return 0; } static double GME_Tell(void *music_p) { GME_Music *music = (GME_Music*)music_p; return (double)(gme.gme_tell(music->game_emu)) / 1000.0; } static double GME_Duration(void *music_p) { GME_Music *music = (GME_Music*)music_p; if (music->has_track_length) { return (double)(music->track_length) / 1000.0; } return -1.0; } static int GME_GetNumTracks(void *music_p) { GME_Music *music = (GME_Music *)music_p; return gme.gme_track_count(music->game_emu); } static int GME_StartTrack(void *music_p, int track) { GME_Music *music = (GME_Music *)music_p; const char *err; if ((track < 0) || (track >= gme.gme_track_count(music->game_emu))) { track = gme.gme_track_count(music->game_emu) - 1; } err = gme.gme_start_track(music->game_emu, track); if (err != 0) { return Mix_SetError("GME: %s", err); } GME_Play(music, music->play_count); return initialize_from_track_info(music, track); } Mix_MusicInterface Mix_MusicInterface_GME = { "GME", MIX_MUSIC_GME, MUS_GME, SDL_FALSE, SDL_FALSE, GME_Load, NULL, /* Open */ GME_CreateFromRW, NULL, /* CreateFromFile */ GME_SetVolume, GME_GetVolume, GME_Play, NULL, /* IsPlaying */ GME_PlayAudio, NULL, /* Jump */ GME_Seek, GME_Tell, GME_Duration, NULL, NULL, NULL, GME_GetMetaTag, GME_GetNumTracks, GME_StartTrack, NULL, /* Pause */ NULL, /* Resume */ NULL, /* Stop */ GME_Delete, NULL, /* Close */ GME_Unload }; #endif /* MUSIC_GME */ SDL2_mixer-2.8.0/src/codecs/load_voc.h0000644000076500000240000000311714551252471016427 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This is the source needed to decode a Creative Labs VOC file into a waveform. It's pretty straightforward once you get going. The only externally-callable function is Mix_LoadVOC_RW(), which is meant to act as identically to SDL_LoadWAV_RW() as possible. This file by Ryan C. Gordon (icculus@icculus.org). Heavily borrowed from sox v12.17.1's voc.c. (http://www.freshmeat.net/projects/sox/) */ /* Don't call this directly; use Mix_LoadWAV_RW() for now. */ SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/mp3utils.c0000644000076500000240000012006214551252471016413 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* Functions to discard MP3 tags - * written by O.Sezer , put into public domain. */ /* Functions to parse some of MP3 tags - * written by V.Novichkov , put into public domain. */ #include "SDL_stdinc.h" #include "SDL_error.h" #include "SDL_rwops.h" #include "mp3utils.h" #include "SDL_log.h" #ifdef ENABLE_ID3V2_TAG /*********************** SDL_RW WITH BOOKKEEPING ************************/ int MP3_RWinit(struct mp3file_t *fil, SDL_RWops *src) { /* Don't use SDL_RWsize() here -- see SDL bug #5509 */ fil->src = src; fil->start = SDL_RWtell(src); fil->length = SDL_RWseek(src, 0, RW_SEEK_END) - fil->start; fil->pos = 0; if (fil->start < 0 || fil->length < 0) { return SDL_Error(SDL_EFSEEK); } SDL_RWseek(src, fil->start, RW_SEEK_SET); return 0; } size_t MP3_RWread(struct mp3file_t *fil, void *ptr, size_t size, size_t maxnum) { size_t remaining = (size_t)(fil->length - fil->pos); size_t ret; maxnum *= size; if (maxnum > remaining) maxnum = remaining; ret = SDL_RWread(fil->src, ptr, 1, maxnum); fil->pos += (Sint64)ret; return ret; } Sint64 MP3_RWseek(struct mp3file_t *fil, Sint64 offset, int whence) { Sint64 ret; switch (whence) { case RW_SEEK_CUR: offset += fil->pos; break; case RW_SEEK_END: offset += fil->length; break; } if (offset < 0) return -1; if (offset > fil->length) offset = fil->length; ret = SDL_RWseek(fil->src, fil->start + offset, RW_SEEK_SET); if (ret < 0) return ret; fil->pos = offset; return offset; } Sint64 MP3_RWtell(struct mp3file_t *fil) { return fil->pos; } #endif /* ENABLE_ID3V2_TAG */ #ifdef ENABLE_ALL_MP3_TAGS static SDL_INLINE Sint32 read_sint32le(const Uint8 *data) { Uint32 result = (Uint32)data[0]; result |= (Uint32)data[1] << 8; result |= (Uint32)data[2] << 16; result |= (Uint32)data[3] << 24; return (Sint32)result; } #endif /* ENABLE_ALL_MP3_TAGS */ #ifdef ENABLE_ID3V2_TAG static SDL_INLINE Sint32 read_sint24be(const Uint8 *data) { Uint32 result = (Uint32)data[2]; result |= (Uint32)data[1] << 8; result |= (Uint32)data[0] << 16; return (Sint32)result; } static SDL_INLINE Sint32 read_sint32be(const Uint8 *data) { Uint32 result = (Uint32)data[3]; result |= (Uint32)data[2] << 8; result |= (Uint32)data[1] << 16; result |= (Uint32)data[0] << 24; return (Sint32)result; } #endif /* ENABLE_ID3V2_TAG */ #ifdef ENABLE_ALL_MP3_TAGS #define TAGS_INPUT_BUFFER_SIZE 128 /******************************************************** * ID3v1 * ********************************************************/ #define ID3v1_TAG_SIZE 128 #define ID3v1_SIZE_OF_FIELD 30 #define ID3v1_FIELD_TITLE 3 #define ID3v1_FIELD_ARTIST 33 #define ID3v1_FIELD_ALBUM 63 #define ID3v1_FIELD_COPYRIGHT 97 static SDL_INLINE SDL_bool is_id3v1(const Uint8 *data, size_t length) { /* http://id3.org/ID3v1 : 3 bytes "TAG" identifier and 125 bytes tag data */ if (length < ID3v1_TAG_SIZE || SDL_memcmp(data,"TAG", 3) != 0) { return SDL_FALSE; } return SDL_TRUE; } #endif #ifdef ENABLE_ID3V2_TAG /* Parse ISO-8859-1 string and convert it into UTF-8 */ static char *parse_id3v1_ansi_string(const Uint8 *buffer, size_t src_len) { char *src_buffer = (char*)SDL_malloc(src_len + 1); char *ret; if (!src_buffer) { return NULL; /* Out of memory */ } SDL_memset(src_buffer, 0, src_len + 1); SDL_memcpy(src_buffer, buffer, src_len); ret = SDL_iconv_string("UTF-8", "ISO-8859-1", src_buffer, src_len + 1); SDL_free(src_buffer); return ret; } #endif /* ENABLE_ID3V2_TAG */ #ifdef ENABLE_ALL_MP3_TAGS static void id3v1_set_tag(Mix_MusicMetaTags *out_tags, Mix_MusicMetaTag tag, const Uint8 *buffer, size_t len) { char *src_buf = parse_id3v1_ansi_string(buffer, len); if (src_buf) { meta_tags_set(out_tags, tag, src_buf); SDL_free(src_buf); } } /* Parse content of ID3v1 tag */ static void parse_id3v1(Mix_MusicMetaTags *out_tags, const Uint8 *buffer) { id3v1_set_tag(out_tags, MIX_META_TITLE, buffer + ID3v1_FIELD_TITLE, ID3v1_SIZE_OF_FIELD); id3v1_set_tag(out_tags, MIX_META_ARTIST, buffer + ID3v1_FIELD_ARTIST, ID3v1_SIZE_OF_FIELD); id3v1_set_tag(out_tags, MIX_META_ALBUM, buffer + ID3v1_FIELD_ALBUM, ID3v1_SIZE_OF_FIELD); id3v1_set_tag(out_tags, MIX_META_COPYRIGHT, buffer + ID3v1_FIELD_COPYRIGHT, ID3v1_SIZE_OF_FIELD); } #endif /* ENABLE_ALL_MP3_TAGS */ #ifdef ENABLE_ID3V2_TAG /******************************************************** * ID3v2 * ********************************************************/ #define ID3v2_BUFFER_SIZE 1024 #define ID3v2_HEADER_SIZE 10 #define ID3v2_FIELD_VERSION_MAJOR 3 #define ID3v2_FIELD_VERSION_MINOR 4 #define ID3v2_FIELD_HEAD_FLAGS 5 #define ID3v2_FIELD_TAG_LENGTH 6 #define ID3v2_FIELD_EXTRA_HEADER_LENGTH 10 #define ID3v2_FLAG_HAS_FOOTER 0x10 #define ID3v2_FLAG_HAS_EXTRA_HEAD 0x40 #define ID3v2_3_FRAME_HEADER_SIZE 10 #define ID3v2_2_FRAME_HEADER_SIZE 6 #define ID3v2_FIELD_FRAME_SIZE 4 #define ID3v2_FIELD_FRAME_SIZEv2 3 #define ID3v2_FIELD_FLAGS 8 static SDL_bool is_id3v2(const Uint8 *data, size_t length) { /* ID3v2 header is 10 bytes: http://id3.org/id3v2.4.0-structure */ /* bytes 0-2: "ID3" identifier */ if (length < ID3v2_HEADER_SIZE || SDL_memcmp(data, "ID3",3) != 0) { return SDL_FALSE; } /* bytes 3-4: version num (major,revision), each byte always less than 0xff. */ if (data[3] == 0xff || data[4] == 0xff) { return SDL_FALSE; } /* bytes 6-9 are the ID3v2 tag size: a 32 bit 'synchsafe' integer, i.e. the * highest bit 7 in each byte zeroed. i.e.: 7 bit information in each byte -> * effectively a 28 bit value. */ if (data[6] >= 0x80 || data[7] >= 0x80 || data[8] >= 0x80 || data[9] >= 0x80) { return SDL_FALSE; } return SDL_TRUE; } static SDL_INLINE Sint32 id3v2_synchsafe_decode(const Uint8 *data) { return ((data[0] << 21) + (data[1] << 14) + (data[2] << 7) + data[3]); } static long get_id3v2_len(const Uint8 *data, long length) { /* size is a 'synchsafe' integer (see above) */ long size = id3v2_synchsafe_decode(data + 6); size += ID3v2_HEADER_SIZE; /* header size */ /* ID3v2 header[5] is flags (bits 4-7 only, 0-3 are zero). * bit 4 set: footer is present (a copy of the header but * with "3DI" as ident.) */ if (data[5] & 0x10) { size += ID3v2_HEADER_SIZE; /* footer size */ } /* optional padding (always zeroes) */ while (size < length && data[size] == 0) { ++size; } return size; } /* Decode a string in the frame according to an encoding marker */ static char *id3v2_decode_string(const Uint8 *string, size_t size) { char *str_buffer = NULL; char *src_buffer = NULL; size_t copy_size = size; if (size == 0) { SDL_Log("id3v2_decode_string: Bad string size: a string should have at least 1 byte"); return NULL; } if (size < 2) { return NULL; } if (string[0] == '\x01') { /* UTF-16 string with a BOM */ if (size <= 5) { if (size < 5) { SDL_Log("id3v2_decode_string: Bad BOM-UTF16 string size: %u < 5", (unsigned int)size); } return NULL; } copy_size = size - 3 + 2; /* exclude 3 bytes of encoding hint, append 2 bytes for a NULL termination */ src_buffer = (char*)SDL_malloc(copy_size); if (!src_buffer) { return NULL; /* Out of memory */ } SDL_memset(src_buffer, 0, copy_size); SDL_memcpy(src_buffer, (string + 3), copy_size - 2); if (SDL_memcmp(string, "\x01\xFE\xFF", 3) == 0) { /* UTF-16BE*/ str_buffer = SDL_iconv_string("UTF-8", "UCS-2BE", src_buffer, copy_size); } else if (SDL_memcmp(string, "\x01\xFF\xFE", 3) == 0) { /* UTF-16LE*/ str_buffer = SDL_iconv_string("UTF-8", "UCS-2LE", src_buffer, copy_size); } SDL_free(src_buffer); } else if (string[0] == '\x02') { /* UTF-16BEstring without a BOM */ if (size <= 3) { if (size < 3) { SDL_Log("id3v2_decode_string: Bad UTF16BE string size: %u < 3", (unsigned int)size); } return NULL; /* Blank string*/ } copy_size = size - 1 + 2; /* exclude 1 byte of encoding hint, append 2 bytes for a NULL termination */ src_buffer = (char*)SDL_malloc(copy_size); if (!src_buffer) { return NULL; /* Out of memory */ } SDL_memset(src_buffer, 0, copy_size); SDL_memcpy(src_buffer, (string + 1), copy_size - 2); str_buffer = SDL_iconv_string("UTF-8", "UCS-2BE", src_buffer, copy_size); SDL_free(src_buffer); } else if (string[0] == '\x03') { /* UTF-8 string */ if (size <= 2) { return NULL; /* Blank string*/ } str_buffer = (char*)SDL_malloc(size); if (!str_buffer) { return NULL; /* Out of memory */ } SDL_strlcpy(str_buffer, (const char*)(string + 1), size); } else if (string[0] == '\x00') { /* Latin-1 string */ if (size <= 2) { return NULL; /* Blank string*/ } str_buffer = parse_id3v1_ansi_string((string + 1), size - 1); } return str_buffer; } /* Write a tag string into internal meta-tags storage */ static void write_id3v2_string(Mix_MusicMetaTags *out_tags, Mix_MusicMetaTag tag, const Uint8 *string, size_t size) { char *str_buffer = id3v2_decode_string(string, size); if (str_buffer) { meta_tags_set(out_tags, tag, str_buffer); SDL_free(str_buffer); } } /* Identify a meta-key and decode the string (Note: input buffer should have at least 4 characters!) */ static void handle_id3v2_string(Mix_MusicMetaTags *out_tags, const char *key, const Uint8 *string, size_t size) { if (SDL_memcmp(key, "TIT2", 4) == 0) { write_id3v2_string(out_tags, MIX_META_TITLE, string, size); } else if (SDL_memcmp(key, "TPE1", 4) == 0) { write_id3v2_string(out_tags, MIX_META_ARTIST, string, size); } else if (SDL_memcmp(key, "TALB", 4) == 0) { write_id3v2_string(out_tags, MIX_META_ALBUM, string, size); } else if (SDL_memcmp(key, "TCOP", 4) == 0) { write_id3v2_string(out_tags, MIX_META_COPYRIGHT, string, size); } /* TODO: Extract "Copyright message" from TXXX value: a KEY=VALUE string divided by a zero byte:*/ /* else if (SDL_memcmp(key, "TXXX", 4) == 0) { write_id3v2_string(out_tags, MIX_META_COPYRIGHT, string, size); } */ } /* Identify a meta-key and decode the string (Note: input buffer should have at least 4 characters!) */ static void handle_id3v2x2_string(Mix_MusicMetaTags *out_tags, const char *key, const Uint8 *string, size_t size) { if (SDL_memcmp(key, "TT2", 3) == 0) { write_id3v2_string(out_tags, MIX_META_TITLE, string, size); } else if (SDL_memcmp(key, "TP1", 3) == 0) { write_id3v2_string(out_tags, MIX_META_ARTIST, string, size); } else if (SDL_memcmp(key, "TAL", 3) == 0) { write_id3v2_string(out_tags, MIX_META_ALBUM, string, size); } else if (SDL_memcmp(key, "TCR", 3) == 0) { write_id3v2_string(out_tags, MIX_META_COPYRIGHT, string, size); } } /* Parse a frame in ID3v2.2 format */ static size_t id3v22_parse_frame(Mix_MusicMetaTags *out_tags, struct mp3file_t *src, Uint8 *buffer) { size_t size; char key[4]; size_t read_size; Sint64 frame_begin = MP3_RWtell(src); read_size = MP3_RWread(src, buffer, 1, ID3v2_2_FRAME_HEADER_SIZE); if (read_size < ID3v2_2_FRAME_HEADER_SIZE) { SDL_Log("id3v22_parse_frame (1): Unexpected end of the file while frame header reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)ID3v2_2_FRAME_HEADER_SIZE); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Buffer size that left is too small */ } if (SDL_memcmp(buffer, "\0\0\0", 3) == 0) { MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; } SDL_memcpy(key, buffer, 3); /* Tag title (key) */ size = (size_t)read_sint24be(buffer + ID3v2_FIELD_FRAME_SIZEv2); if (size < ID3v2_BUFFER_SIZE) { read_size = MP3_RWread(src, buffer, 1, size); if (read_size < size) { SDL_Log("id3v22_parse_frame (2): Unexpected end of the file while frame data reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)size); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Can't read frame data, possibly, a file size was reached */ } } else { read_size = MP3_RWread(src, buffer, 1, ID3v2_BUFFER_SIZE); if (read_size < ID3v2_BUFFER_SIZE) { SDL_Log("id3v22_parse_frame (3): Unexpected end of the file while frame data reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)ID3v2_BUFFER_SIZE); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Can't read frame data, possibly, a file size was reached */ } MP3_RWseek(src, frame_begin + (Sint64)size, RW_SEEK_SET); } handle_id3v2x2_string(out_tags, key, buffer, read_size); return (size_t)(size + ID3v2_2_FRAME_HEADER_SIZE); /* data size + size of the header */ } /* Parse a frame in ID3v2.3 and ID3v2.4 formats */ static size_t id3v2x_parse_frame(Mix_MusicMetaTags *out_tags, struct mp3file_t *src, Uint8 *buffer, Uint8 version) { Uint32 size; char key[4]; Uint8 flags[2]; size_t read_size; Sint64 frame_begin = MP3_RWtell(src); read_size = MP3_RWread(src, buffer, 1, ID3v2_3_FRAME_HEADER_SIZE); if (read_size < ID3v2_3_FRAME_HEADER_SIZE) { SDL_Log("id3v2x_parse_frame (1): Unexpected end of the file while frame header reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)ID3v2_3_FRAME_HEADER_SIZE); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Can't read frame header, possibly, a file size was reached */ } if (SDL_memcmp(buffer, "\0\0\0\0", 4) == 0) { MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; } SDL_memcpy(key, buffer, 4); /* Tag title (key) */ if (version == 4) { size = (Uint32)id3v2_synchsafe_decode(buffer + ID3v2_FIELD_FRAME_SIZE); } else { size = (Uint32)read_sint32be(buffer + ID3v2_FIELD_FRAME_SIZE); } SDL_memcpy(flags, buffer + ID3v2_FIELD_FLAGS, 2); if (size < ID3v2_BUFFER_SIZE) { read_size = MP3_RWread(src, buffer, 1, size); if (read_size < size) { SDL_Log("id3v2x_parse_frame (2): Unexpected end of the file while frame data reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)size); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Can't read frame data, possibly, a file size was reached */ } } else { read_size = MP3_RWread(src, buffer, 1, ID3v2_BUFFER_SIZE); if (read_size < ID3v2_BUFFER_SIZE) { SDL_Log("id3v2x_parse_frame (3): Unexpected end of the file while frame data reading (had to read %u bytes, %u bytes wanted)", (unsigned int)read_size, (unsigned int)ID3v2_BUFFER_SIZE); MP3_RWseek(src, frame_begin, RW_SEEK_SET); return 0; /* Can't read frame data, possibly, a file size was reached */ } MP3_RWseek(src, frame_begin + (Sint64)size, RW_SEEK_SET); } handle_id3v2_string(out_tags, key, buffer, size); return (size_t)(size + ID3v2_3_FRAME_HEADER_SIZE); /* data size + size of the header */ } /* Parse content of ID3v2 */ static SDL_bool parse_id3v2(Mix_MusicMetaTags *out_tags, struct mp3file_t *src) { Uint8 version_major, flags; long total_length, tag_len, tag_extended_len = 0; Uint8 buffer[ID3v2_BUFFER_SIZE]; size_t read_size; size_t frame_length; Sint64 file_size; total_length = 0; file_size = src->length; MP3_RWseek(src, 0, RW_SEEK_SET); read_size = MP3_RWread(src, buffer, 1, ID3v2_HEADER_SIZE); /* Retrieve the header */ if (read_size < ID3v2_HEADER_SIZE) { SDL_Log("parse_id3v2: fail to read a header (%u < 10)", (unsigned int)read_size); return SDL_FALSE; /* Unsupported version of the tag */ } total_length += ID3v2_HEADER_SIZE; version_major = buffer[ID3v2_FIELD_VERSION_MAJOR]; /* Major version */ /* version_minor = buffer[ID3v2_VERSION_MINOR]; * Minor version, UNUSED */ flags = buffer[ID3v2_FIELD_HEAD_FLAGS]; /* Flags */ tag_len = id3v2_synchsafe_decode(buffer + ID3v2_FIELD_TAG_LENGTH); /* Length of a tag */ if (version_major != 2 && version_major != 3 && version_major != 4) { SDL_Log("parse_id3v2: Unsupported version %d", version_major); return SDL_FALSE; /* Unsupported version of the tag */ } if ((version_major > 2) && ((flags & ID3v2_FLAG_HAS_EXTRA_HEAD) == ID3v2_FLAG_HAS_EXTRA_HEAD)) { MP3_RWread(src, buffer + ID3v2_FIELD_EXTRA_HEADER_LENGTH, 1, 4); MP3_RWseek(src, -4, RW_SEEK_CUR); tag_extended_len = id3v2_synchsafe_decode(buffer + ID3v2_FIELD_EXTRA_HEADER_LENGTH); /* Length of an extended header */ } if (tag_extended_len) { tag_len -= tag_extended_len; /* Subtract the size of extended header */ MP3_RWseek(src, tag_extended_len, RW_SEEK_CUR); /* Skip extended header and it's size value */ } total_length += tag_len; if (flags & ID3v2_FLAG_HAS_FOOTER) { total_length += ID3v2_HEADER_SIZE; /* footer size */ } if ((MP3_RWtell(src) + tag_len) > file_size) { SDL_Log("parse_id3v2: Tag size bigger than actual file size"); return SDL_FALSE; /* Tag size is bigger than actual buffer data */ } while ((MP3_RWtell(src) >= 0) && (MP3_RWtell(src) < (total_length))) { if (version_major == 2) { frame_length = id3v22_parse_frame(out_tags, src, buffer); } else { frame_length = id3v2x_parse_frame(out_tags, src, buffer, version_major); } if (!frame_length) { break; } } return SDL_TRUE; } #endif /* ENABLE_ID3V2_TAG */ #ifdef ENABLE_ALL_MP3_TAGS /******************************************************** * APE v1 and v2 * ********************************************************/ #define APE_BUFFER_SIZE 256 #define APE_V1 1000U #define APE_V2 2000U #define APE_HEADER_SIZE 32 #define APE_HEAD_FIELD_VERSION 8 #define APE_HEAD_FIELD_TAGSIZE 12 #define APE_HEAD_FIELD_ITEMS_COUNT 16 #define APE_HEAD_FIELD_FLAGS 20 #define APE_HEAD_FIELD_RESERVED 24 #define APE_FRAME_TAG_KEY 4 static SDL_bool is_apetag(const Uint8 *data, size_t length) { /* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification * Header/footer is 32 bytes: bytes 0-7 ident, bytes 8-11 version, * bytes 12-17 size. bytes 24-31 are reserved: must be all zeroes. */ Uint32 v; if (length < 32 || SDL_memcmp(data,"APETAGEX",8) != 0) { return SDL_FALSE; } v = (Uint32) read_sint32le(data + 8); /* version */ if (v != APE_V2 && v != APE_V1) { return SDL_FALSE; } v = 0; /* reserved bits : */ if (SDL_memcmp(&data[24],&v,4) != 0 || SDL_memcmp(&data[28],&v,4) != 0) { return SDL_FALSE; } return SDL_TRUE; } static long get_ape_len(const Uint8 *data, Uint32 *version) { Uint32 flags; long size = (long) read_sint32le(data + APE_HEAD_FIELD_TAGSIZE); *version = (Uint32) read_sint32le(data + APE_HEAD_FIELD_VERSION); flags = (Uint32) read_sint32le(data + APE_HEAD_FIELD_FLAGS); if (*version == APE_V2 && (flags & (1U<<31))) { size += APE_HEADER_SIZE; /* header present. */ } return size; } static char *ape_find_value(char *key) { char *end = (key + APE_BUFFER_SIZE - 4); while (*key && key != end) { key++; } if (key >= end) { return NULL; } return key + 1; } static Uint32 ape_handle_tag(Mix_MusicMetaTags *out_tags, Uint8 *data, size_t valsize) { /* http://wiki.hydrogenaud.io/index.php?title=APE_Tag_Item * Tag entry has unclear size because of no size value for a key field * However, we only know next sizes: * - 4 bytes is a [length] of value field * - 4 bytes of value-specific flags * - unknown lenght of a key field. To detect it's size * it's need to find a zero byte looking at begin of the key field * - 1 byte of a null-terminator * - [length] bytes a value content */ char *key = (char*)(data + APE_FRAME_TAG_KEY); char *value = NULL; Uint32 key_len; /* Length of the key field */ value = ape_find_value(key); if (!value) { return 0; } key_len = (Uint32)(value - key); if (valsize > APE_BUFFER_SIZE - key_len) { data[APE_BUFFER_SIZE] = '\0'; } else { value[valsize] = '\0'; } if (SDL_strncasecmp(key, "Title", 6) == 0) { meta_tags_set(out_tags, MIX_META_TITLE, (const char*)(value)); } else if (SDL_strncasecmp(key, "Album", 6) == 0) { meta_tags_set(out_tags, MIX_META_ALBUM, (const char*)(value)); } else if (SDL_strncasecmp(key, "Artist", 7) == 0) { meta_tags_set(out_tags, MIX_META_ARTIST, (const char*)(value)); } else if (SDL_strncasecmp(key, "Copyright", 10) == 0) { meta_tags_set(out_tags, MIX_META_COPYRIGHT, (const char*)(value)); } return 4 + (Uint32)valsize + key_len; } /* Parse content of APE tag */ static SDL_bool parse_ape(Mix_MusicMetaTags *out_tags, struct mp3file_t *src, Sint64 ape_head_pos, Uint32 version) { Uint8 buffer[APE_BUFFER_SIZE + 1]; Uint32 v, i, tag_size, tag_items_count, tag_item_size; Uint32 zero8[2] = {0, 0}; Sint64 file_size, cur_tag; size_t read_size; file_size = src->length; MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); read_size = MP3_RWread(src, buffer, 1, APE_HEADER_SIZE); /* Retrieve the header */ if (read_size < APE_HEADER_SIZE) { MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); return SDL_FALSE; } v = (Uint32)read_sint32le(buffer + APE_HEAD_FIELD_VERSION); /* version */ if (v != APE_V2 && v != APE_V1) { return SDL_FALSE; } tag_size = (Uint32)read_sint32le(buffer + APE_HEAD_FIELD_TAGSIZE); /* tag size */ if (version == APE_V1) { /* If version 1, we are at footer */ if (ape_head_pos - (tag_size - APE_HEADER_SIZE) < 0) { MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); return SDL_FALSE; } MP3_RWseek(src, ape_head_pos - (tag_size - APE_HEADER_SIZE), RW_SEEK_SET); } else if ((ape_head_pos + tag_size + APE_HEADER_SIZE) > file_size) { MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); return SDL_FALSE; } tag_items_count = (Uint32)read_sint32le(buffer + APE_HEAD_FIELD_ITEMS_COUNT); /* count tag items */ /*flags = (Uint32)read_sint32be(buffer + APE_HEAD_FIELD_FLAGS);*/ /* global flags, unused */ /* reserved bits : */ if (SDL_memcmp(buffer + APE_HEAD_FIELD_RESERVED, zero8, 8) != 0) { return SDL_FALSE; } for(i = 0; i < tag_items_count; i++) { cur_tag = MP3_RWtell(src); if (cur_tag < 0) { break; } read_size = MP3_RWread(src, buffer, 1, 4); /* Retrieve the size */ if (read_size < 4) { MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); return SDL_FALSE; } v = (Uint32)read_sint32le(buffer); /* size of the tag's value field */ /* (we still need to find key size by a null termination) */ /* Retrieve the tag's data with an aproximal size as we can */ if (v + 40 < APE_BUFFER_SIZE) { read_size = MP3_RWread(src, buffer, 1, v + 40); } else { read_size = MP3_RWread(src, buffer, 1, APE_BUFFER_SIZE); } buffer[read_size] = '\0'; tag_item_size = ape_handle_tag(out_tags, buffer, v); if (tag_item_size == 0) { break; } MP3_RWseek(src, cur_tag + tag_item_size + 4, RW_SEEK_SET); } MP3_RWseek(src, ape_head_pos, RW_SEEK_SET); return SDL_TRUE; } /******************************************************** * Lyrics3 skip * ********************************************************/ /* Header : "LYRICSBEGIN" -- 11 bytes * Size field: (decimal) (v2 only) 6 bytes * End marker: "LYRICS200" (v2) - 9 bytes * End marker: "LYRICSEND" (v1) - 9 bytes * * The maximum length of Lyrics3v1 is 5100 bytes. */ #define LYRICS3v1_SEARCH_BUFFER 5120 /* 5100 + 20 of tag begin and end keywords */ #define LYRICS3v1_HEAD_SIZE 11 #define LYRICS3v1_TAIL_SIZE 9 #define LYRICS3v2_TAG_SIZE_VALUE 6 #define LYRICS3_FOOTER_SIZE 15 static SDL_INLINE int is_lyrics3tag(const Uint8 *data, size_t length) { /* http://id3.org/Lyrics3 * http://id3.org/Lyrics3v2 */ if (length < LYRICS3_FOOTER_SIZE) return 0; if (SDL_memcmp(data+LYRICS3v2_TAG_SIZE_VALUE,"LYRICS200",9) == 0) return 2; /* v2 */ if (SDL_memcmp(data+LYRICS3v2_TAG_SIZE_VALUE,"LYRICSEND",9) == 0) return 1; /* v1 */ return 0; } static long get_lyrics3v1_len(struct mp3file_t *m) { const char *p; long i, len; char buf[LYRICS3v1_SEARCH_BUFFER + 1]; /* needs manual search: http://id3.org/Lyrics3 */ if (m->length < 20) return -1; len = (m->length > LYRICS3v1_SEARCH_BUFFER)? LYRICS3v1_SEARCH_BUFFER : (long)m->length; MP3_RWseek(m, -len, RW_SEEK_END); MP3_RWread(m, buf, 1, (size_t)(len -= LYRICS3v1_TAIL_SIZE)); /* exclude footer */ /* strstr() won't work here. */ p = buf; for (i = len - LYRICS3v1_HEAD_SIZE; i >= 0; --i, ++p) { if (SDL_memcmp(p, "LYRICSBEGIN", LYRICS3v1_HEAD_SIZE) == 0) break; } if (i < 0) return -1; return len - (long)(p - buf) + LYRICS3v1_TAIL_SIZE /* footer */; } static SDL_INLINE long get_lyrics3v2_len(const Uint8 *data, size_t length) { /* 6 bytes before the end marker is size in decimal format - * does not include the 9 bytes end marker and size field. */ if (length != LYRICS3v2_TAG_SIZE_VALUE) return 0; return SDL_strtol((const char *)data, NULL, 10) + LYRICS3_FOOTER_SIZE; } static SDL_INLINE SDL_bool verify_lyrics3v2(const Uint8 *data, size_t length) { if (length < LYRICS3v1_HEAD_SIZE) return SDL_FALSE; if (SDL_memcmp(data,"LYRICSBEGIN",LYRICS3v1_HEAD_SIZE) == 0) return SDL_TRUE; return SDL_FALSE; } /******************************************************** * MusicMatch * ********************************************************/ #define MUSICMATCH_HEADER_SIZE 256 #define MUSICMATCH_VERSION_INFO_SIZE 256 #define MUSICMATCH_FOOTER_SIZE 48 #define MUSICMATCH_OFFSETS_SIZE 20 #define MMTAG_PARANOID static SDL_bool is_musicmatch(const Uint8 *data, long length) { /* From docs/musicmatch.txt in id3lib: https://sourceforge.net/projects/id3lib/ Overall tag structure: +-----------------------------+ | Header | | (256 bytes, OPTIONAL) | +-----------------------------+ | Image extension (4 bytes) | +-----------------------------+ | Image binary | | (var. length >= 4 bytes) | +-----------------------------+ | Unused (4 bytes) | +-----------------------------+ | Version info (256 bytes) | +-----------------------------+ | Audio meta-data | | (var. length >= 7868 bytes) | +-----------------------------+ | Data offsets (20 bytes) | +-----------------------------+ | Footer (48 bytes) | +-----------------------------+ */ if (length < MUSICMATCH_FOOTER_SIZE) return SDL_FALSE; /* sig: 19 bytes company name + 13 bytes space */ if (SDL_memcmp(data,"Brava Software Inc. ",32) != 0) { return SDL_FALSE; } /* 4 bytes version: x.xx */ if (!SDL_isdigit(data[32]) || data[33] != '.' || !SDL_isdigit(data[34]) ||!SDL_isdigit(data[35])) { return SDL_FALSE; } #ifdef MMTAG_PARANOID /* [36..47]: 12 bytes trailing space */ for (length = 36; length < MUSICMATCH_FOOTER_SIZE; ++length) { if (data[length] != ' ') return SDL_FALSE; } #endif return SDL_TRUE; } static long get_musicmatch_len(struct mp3file_t *m) { const Sint32 metasizes[4] = { 7868, 7936, 8004, 8132 }; const unsigned char syncstr[10] = {'1','8','2','7','3','6','4','5',0,0}; unsigned char buf[256]; Sint32 i, j, imgext_ofs, version_ofs; long len; MP3_RWseek(m, -68, RW_SEEK_END); MP3_RWread(m, buf, 1, 20); imgext_ofs = (Sint32)((buf[3] <<24) | (buf[2] <<16) | (buf[1] <<8) | buf[0] ); version_ofs = (Sint32)((buf[15]<<24) | (buf[14]<<16) | (buf[13]<<8) | buf[12]); if (version_ofs <= imgext_ofs) return -1; if (version_ofs <= 0 || imgext_ofs <= 0) return -1; /* Try finding the version info section: * Because metadata section comes after it, and because metadata section * has different sizes across versions (format ver. <= 3.00: always 7868 * bytes), we can _not_ directly calculate using deltas from the offsets * section. */ for (i = 0; i < 4; ++i) { /* 48: footer, 20: offsets, 256: version info */ len = metasizes[i] + MUSICMATCH_FOOTER_SIZE + MUSICMATCH_OFFSETS_SIZE + MUSICMATCH_VERSION_INFO_SIZE; if (m->length < len) return -1; MP3_RWseek(m, -len, RW_SEEK_END); MP3_RWread(m, buf, 1, MUSICMATCH_VERSION_INFO_SIZE); /* [0..9]: sync string, [30..255]: 0x20 */ #ifdef MMTAG_PARANOID for (j = 30; j < MUSICMATCH_VERSION_INFO_SIZE; ++j) { if (buf[j] != ' ') break; } if (j < MUSICMATCH_VERSION_INFO_SIZE) continue; #endif if (SDL_memcmp(buf, syncstr, 10) == 0) { break; } } if (i == 4) return -1; /* no luck. */ #ifdef MMTAG_PARANOID /* unused section: (4 bytes of 0x00) */ MP3_RWseek(m, -(len + 4), RW_SEEK_END); MP3_RWread(m, buf, 1, 4); j = 0; if (SDL_memcmp(buf, &j, 4) != 0) return -1; #endif len += (version_ofs - imgext_ofs); if (m->length < len) return -1; MP3_RWseek(m, -len, RW_SEEK_END); MP3_RWread(m, buf, 1, 8); j = (Sint32)((buf[7] <<24) | (buf[6] <<16) | (buf[5] <<8) | buf[4]); if (j < 0) return -1; /* verify image size: */ /* without this, we may land at a wrong place. */ if (j + 12 != version_ofs - imgext_ofs) return -1; /* try finding the optional header */ if (m->length < len + MUSICMATCH_HEADER_SIZE) return len; MP3_RWseek(m, -(len + MUSICMATCH_HEADER_SIZE), RW_SEEK_END); MP3_RWread(m, buf, 1, MUSICMATCH_HEADER_SIZE); /* [0..9]: sync string, [30..255]: 0x20 */ if (SDL_memcmp(buf, syncstr, 10) != 0) { return len; } #ifdef MMTAG_PARANOID for (j = 30; j < MUSICMATCH_HEADER_SIZE; ++j) { if (buf[j] != ' ') return len; } #endif return len + MUSICMATCH_HEADER_SIZE; /* header is present. */ } #define TAG_FOUND 1 #define TAG_INVALID -1 #define TAG_NOT_FOUND 0 static int probe_id3v1(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, Uint8 *buf, SDL_bool tag_handled, int atend) { if (fil->length >= ID3v1_TAG_SIZE) { MP3_RWseek(fil, -ID3v1_TAG_SIZE, RW_SEEK_END); if (MP3_RWread(fil, buf, 1, ID3v1_TAG_SIZE) != ID3v1_TAG_SIZE) return TAG_INVALID; if (is_id3v1(buf, ID3v1_TAG_SIZE)) { if (!atend) { /* possible false positive? */ if (is_musicmatch(buf + 128 - 48, 48) || is_apetag (buf + 128 - 32, 32) || is_lyrics3tag(buf + 128 - 15, 15)) { return TAG_NOT_FOUND; } } if (!tag_handled) { parse_id3v1(out_tags, buf); } fil->length -= ID3v1_TAG_SIZE; return TAG_FOUND; /* FIXME: handle possible double-ID3v1 tags?? */ } } return TAG_NOT_FOUND; } static int probe_mmtag(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, Uint8 *buf) { long len; (void)out_tags; /* TODO: Implement reading tag contents. */ if (fil->length >= 68) { MP3_RWseek(fil, -MUSICMATCH_FOOTER_SIZE, RW_SEEK_END); if (MP3_RWread(fil, buf, 1, MUSICMATCH_FOOTER_SIZE) != MUSICMATCH_FOOTER_SIZE) return TAG_INVALID; if (is_musicmatch(buf, MUSICMATCH_FOOTER_SIZE)) { len = get_musicmatch_len(fil); if (len < 0) return TAG_INVALID; if (len >= fil->length) return TAG_INVALID; fil->length -= len; return TAG_FOUND; } } return TAG_NOT_FOUND; } static int probe_apetag(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, Uint8 *buf, SDL_bool tag_handled) { Sint64 ape_tag_pos; size_t readsize; long len; Uint32 v; /* APE tag may be at the end: read the footer */ if (fil->length >= APE_HEADER_SIZE) { MP3_RWseek(fil, -APE_HEADER_SIZE, RW_SEEK_END); readsize = MP3_RWread(fil, buf, 1, APE_HEADER_SIZE); if (readsize != APE_HEADER_SIZE) { return TAG_INVALID; } /* APE tag may be at end or before ID3v1 tag */ if (is_apetag(buf, APE_HEADER_SIZE)) { len = get_ape_len(buf, &v); if (len >= fil->length) { return TAG_INVALID; } if (v == APE_V2) { /* verify header : */ MP3_RWseek(fil, -len, RW_SEEK_END); ape_tag_pos = MP3_RWtell(fil); readsize = MP3_RWread(fil, buf, 1, APE_HEADER_SIZE); if (readsize != APE_HEADER_SIZE) { return TAG_INVALID; } if (!is_apetag(buf, APE_HEADER_SIZE)) { fil->length -= len; return TAG_NOT_FOUND; } if (!tag_handled) { parse_ape(out_tags, fil, ape_tag_pos, APE_V2); } } else if (!tag_handled) { SDL_bool ape_tag_valid; MP3_RWseek(fil, -APE_HEADER_SIZE, RW_SEEK_END); ape_tag_pos = MP3_RWtell(fil); ape_tag_valid = parse_ape(out_tags, fil, ape_tag_pos, APE_V1); if (!ape_tag_valid) { fil->length -= len; return TAG_NOT_FOUND; } } fil->length -= len; return TAG_FOUND; } } return TAG_NOT_FOUND; } static int probe_lyrics3(struct mp3file_t *fil, Uint8 *buf) { long len; int ver; if (fil->length >= LYRICS3_FOOTER_SIZE) { MP3_RWseek(fil, -LYRICS3_FOOTER_SIZE, RW_SEEK_END); if (MP3_RWread(fil, buf, 1, LYRICS3_FOOTER_SIZE) != LYRICS3_FOOTER_SIZE) return TAG_INVALID; ver = is_lyrics3tag(buf, LYRICS3_FOOTER_SIZE); if (ver == 2) { len = get_lyrics3v2_len(buf, LYRICS3v2_TAG_SIZE_VALUE); if (len >= fil->length) return TAG_INVALID; if (len < LYRICS3_FOOTER_SIZE) return TAG_INVALID; MP3_RWseek(fil, -len, RW_SEEK_END); if (MP3_RWread(fil, buf, 1, LYRICS3v1_HEAD_SIZE) != LYRICS3v1_HEAD_SIZE) return TAG_INVALID; if (!verify_lyrics3v2(buf, LYRICS3v1_HEAD_SIZE)) return -1; fil->length -= len; return TAG_FOUND; } else if (ver == 1) { len = get_lyrics3v1_len(fil); if (len < 0) return TAG_INVALID; fil->length -= len; return TAG_FOUND; } } return TAG_NOT_FOUND; } int mp3_read_tags(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, SDL_bool keep_id3v2) { Uint8 buf[TAGS_INPUT_BUFFER_SIZE]; long len; size_t readsize; int c_id3, c_ape, c_lyr, c_mm; int rc = -1; SDL_bool tag_handled = SDL_FALSE; /* MP3 standard has no metadata format, so everyone invented * their own thing, even with extensions, until ID3v2 became * dominant: Hence the impossible mess here. * * Note: I don't yet care about freaky broken mp3 files with * double tags. -- O.S. */ MP3_RWseek(fil, 0, RW_SEEK_SET); readsize = MP3_RWread(fil, buf, 1, TAGS_INPUT_BUFFER_SIZE); if (!readsize) goto fail; /* ID3v2 tag is at the start */ if (is_id3v2(buf, readsize)) { len = get_id3v2_len(buf, (long)readsize); if (len >= fil->length) goto fail; tag_handled = parse_id3v2(out_tags, fil); if (!keep_id3v2) { fil->start += len; fil->length -= len; } } /* APE tag _might_ be at the start (discouraged * but not forbidden, either.) read the header. */ else if (is_apetag(buf, readsize)) { Uint32 v; len = get_ape_len(buf, &v); if (len >= fil->length) goto fail; if (v == APE_V1 || v == APE_V2) { tag_handled = parse_ape(out_tags, fil, 0, v); } fil->start += len; fil->length -= len; } /* it's not impossible that _old_ MusicMatch tag * placing itself after ID3v1. */ if ((c_mm = probe_mmtag(out_tags, fil, buf)) < 0) { goto fail; } /* ID3v1 tag is at the end */ if ((c_id3 = probe_id3v1(out_tags, fil, buf, tag_handled, !c_mm)) < 0) { goto fail; } /* we do not know the order of ape or lyrics3 * or musicmatch tags, hence the loop here.. */ c_ape = 0; c_lyr = 0; for (;;) { if (!c_lyr) { /* care about mp3s with double Lyrics3 tags? */ if ((c_lyr = probe_lyrics3(fil, buf)) == TAG_INVALID) goto fail; if (c_lyr) continue; } if (!c_mm) { if ((c_mm = probe_mmtag(out_tags, fil, buf)) == TAG_INVALID) goto fail; if (c_mm) continue; } if (!c_ape) { if ((c_ape = probe_apetag(out_tags, fil, buf, tag_handled)) == TAG_INVALID) goto fail; if (c_ape) continue; } break; } /* for (;;) */ rc = (fil->length > 0)? 0 : -1; fail: MP3_RWseek(fil, 0, RW_SEEK_SET); return rc; } #endif /* ENABLE_ALL_MP3_TAGS */ #ifdef ENABLE_ID3V2_TAG int read_id3v2_from_mem(Mix_MusicMetaTags *out_tags, Uint8 *data, size_t length) { SDL_RWops *src = SDL_RWFromConstMem(data, (int)length); SDL_bool is_valid; struct mp3file_t fil; if (src) { fil.src = src; fil.start = 0; fil.length = (Sint64)length; fil.pos = 0; if (!is_id3v2(data, length)) { SDL_RWclose(src); return -1; } if (get_id3v2_len(data, (long)length) > (long)length) { SDL_RWclose(src); return -1; } is_valid = parse_id3v2(out_tags, &fil); SDL_RWclose(src); return is_valid ? 0 : -1; } return -1; } #endif /* ENABLE_ID3V2_TAG */ SDL2_mixer-2.8.0/src/codecs/music_mpg123.h0000644000076500000240000000216414551252471017053 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MP3 files with mpg123 */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_MPG123; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_ogg.c0000644000076500000240000004037714553225265016624 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if defined(MUSIC_OGG) && !defined(OGG_USE_STB) /* This file supports Ogg Vorbis music streams */ #include "SDL_loadso.h" #include "music_ogg.h" #include "utils.h" #define OV_EXCLUDE_STATIC_CALLBACKS #if defined(OGG_HEADER) #include OGG_HEADER #elif defined(OGG_USE_TREMOR) #include #else #include #endif typedef struct { int loaded; void *handle; int (*ov_clear)(OggVorbis_File *vf); vorbis_info *(*ov_info)(OggVorbis_File *vf,int link); vorbis_comment *(*ov_comment)(OggVorbis_File *vf,int link); int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i); #ifdef OGG_USE_TREMOR long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream); int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos); ogg_int64_t (*ov_time_tell)(OggVorbis_File *vf); ogg_int64_t (*ov_time_total)(OggVorbis_File *vf, int i); #else long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream); int (*ov_time_seek)(OggVorbis_File *vf,double pos); double (*ov_time_tell)(OggVorbis_File *vf); double (*ov_time_total)(OggVorbis_File *vf, int i); #endif int (*ov_pcm_seek)(OggVorbis_File *vf, ogg_int64_t pos); ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf); } vorbis_loader; static vorbis_loader vorbis; #ifdef OGG_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ vorbis.FUNC = (SIG) SDL_LoadFunction(vorbis.handle, #FUNC); \ if (vorbis.FUNC == NULL) { SDL_UnloadObject(vorbis.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ vorbis.FUNC = FUNC; \ if (vorbis.FUNC == NULL) { Mix_SetError("Missing vorbis.framework or tremor.framework"); return -1; } #endif static int OGG_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (vorbis.loaded == 0) { #ifdef OGG_DYNAMIC vorbis.handle = SDL_LoadObject(OGG_DYNAMIC); if (vorbis.handle == NULL) { return -1; } #endif FUNCTION_LOADER(ov_clear, int (*)(OggVorbis_File *)) FUNCTION_LOADER(ov_info, vorbis_info *(*)(OggVorbis_File *,int)) FUNCTION_LOADER(ov_comment, vorbis_comment *(*)(OggVorbis_File *,int)) FUNCTION_LOADER(ov_open_callbacks, int (*)(void *,OggVorbis_File *,const char *,long,ov_callbacks)) FUNCTION_LOADER(ov_pcm_total, ogg_int64_t (*)(OggVorbis_File *,int)) #ifdef OGG_USE_TREMOR FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int *)) FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,ogg_int64_t)) FUNCTION_LOADER(ov_time_tell, ogg_int64_t (*)(OggVorbis_File *)) FUNCTION_LOADER(ov_time_total, ogg_int64_t (*)(OggVorbis_File *, int)) #else FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int,int,int,int *)) FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,double)) FUNCTION_LOADER(ov_time_tell, double (*)(OggVorbis_File *)) FUNCTION_LOADER(ov_time_total, double (*)(OggVorbis_File *, int)) #endif FUNCTION_LOADER(ov_pcm_seek, int (*)(OggVorbis_File *,ogg_int64_t)) FUNCTION_LOADER(ov_pcm_tell, ogg_int64_t (*)(OggVorbis_File *)) } ++vorbis.loaded; return 0; } static void OGG_Unload(void) { if (vorbis.loaded == 0) { return; } if (vorbis.loaded == 1) { #ifdef OGG_DYNAMIC SDL_UnloadObject(vorbis.handle); #endif } --vorbis.loaded; } typedef struct { SDL_RWops *src; int freesrc; int play_count; int volume; OggVorbis_File vf; vorbis_info vi; int section; SDL_AudioStream *stream; char *buffer; int buffer_size; int loop; ogg_int64_t loop_start; ogg_int64_t loop_end; ogg_int64_t loop_len; Mix_MusicMetaTags tags; } OGG_music; static int set_ov_error(const char *function, int error) { #define HANDLE_ERROR_CASE(X) case X: Mix_SetError("%s: %s", function, #X); break; switch (error) { HANDLE_ERROR_CASE(OV_FALSE) HANDLE_ERROR_CASE(OV_EOF) HANDLE_ERROR_CASE(OV_HOLE) HANDLE_ERROR_CASE(OV_EREAD) HANDLE_ERROR_CASE(OV_EFAULT) HANDLE_ERROR_CASE(OV_EIMPL) HANDLE_ERROR_CASE(OV_EINVAL) HANDLE_ERROR_CASE(OV_ENOTVORBIS) HANDLE_ERROR_CASE(OV_EBADHEADER) HANDLE_ERROR_CASE(OV_EVERSION) HANDLE_ERROR_CASE(OV_ENOTAUDIO) HANDLE_ERROR_CASE(OV_EBADPACKET) HANDLE_ERROR_CASE(OV_EBADLINK) HANDLE_ERROR_CASE(OV_ENOSEEK) default: Mix_SetError("%s: unknown error %d\n", function, error); break; } return -1; } static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); } static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence) { return (SDL_RWseek((SDL_RWops*)datasource, offset, whence) < 0)? -1 : 0; } static long sdl_tell_func(void *datasource) { return (long)SDL_RWtell((SDL_RWops*)datasource); } static int sdl_close_func(void *datasource) { (void)datasource; return 0; } static int OGG_Seek(void *context, double time); static void OGG_Delete(void *context); static int OGG_UpdateSection(OGG_music *music) { vorbis_info *vi; vi = vorbis.ov_info(&music->vf, -1); if (!vi) { return Mix_SetError("ov_info returned NULL"); } if (vi->channels == music->vi.channels && vi->rate == music->vi.rate) { return 0; } SDL_memcpy(&music->vi, vi, sizeof(*vi)); if (music->buffer) { SDL_free(music->buffer); music->buffer = NULL; } if (music->stream) { SDL_FreeAudioStream(music->stream); music->stream = NULL; } music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)vi->channels, (int)vi->rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -1; } music->buffer_size = music_spec.samples * (int)sizeof(Sint16) * vi->channels; music->buffer = (char *)SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { return -1; } return 0; } /* Load an OGG stream from an SDL_RWops object */ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) { OGG_music *music; ov_callbacks callbacks; vorbis_comment *vc; long rate; ogg_int64_t full_length; SDL_bool is_loop_length = SDL_FALSE; int i; music = (OGG_music *)SDL_calloc(1, sizeof *music); if (!music) { SDL_OutOfMemory(); return NULL; } music->src = src; music->volume = MIX_MAX_VOLUME; music->section = -1; callbacks.read_func = sdl_read_func; callbacks.seek_func = sdl_seek_func; callbacks.close_func = sdl_close_func; callbacks.tell_func = sdl_tell_func; if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) { Mix_SetError("Not an Ogg Vorbis audio stream"); SDL_free(music); return NULL; } if (OGG_UpdateSection(music) < 0) { OGG_Delete(music); return NULL; } rate = music->vi.rate; vc = vorbis.ov_comment(&music->vf, -1); if (vc != NULL) { for (i = 0; i < vc->comments; i++) { char *param = SDL_strdup(vc->user_comments[i]); char *argument = param; char *value = SDL_strchr(param, '='); if (value == NULL) { value = param + SDL_strlen(param); } else { *(value++) = '\0'; } /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from * string if it is present at position 4. */ if (_Mix_IsLoopTag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) { SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); } if (SDL_strcasecmp(argument, "LOOPSTART") == 0) music->loop_start = _Mix_ParseTime(value, rate); else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { music->loop_len = SDL_strtoll(value, NULL, 10); is_loop_length = SDL_TRUE; } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { music->loop_end = _Mix_ParseTime(value, rate); is_loop_length = SDL_FALSE; } else if (SDL_strcasecmp(argument, "TITLE") == 0) { meta_tags_set(&music->tags, MIX_META_TITLE, value); } else if (SDL_strcasecmp(argument, "ARTIST") == 0) { meta_tags_set(&music->tags, MIX_META_ARTIST, value); } else if (SDL_strcasecmp(argument, "ALBUM") == 0) { meta_tags_set(&music->tags, MIX_META_ALBUM, value); } else if (SDL_strcasecmp(argument, "COPYRIGHT") == 0) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, value); } SDL_free(param); } if (is_loop_length) { music->loop_end = music->loop_start + music->loop_len; } else { music->loop_len = music->loop_end - music->loop_start; } /* Ignore invalid loop tag */ if (music->loop_start < 0 || music->loop_len < 0 || music->loop_end < 0) { music->loop_start = 0; music->loop_len = 0; music->loop_end = 0; } } full_length = vorbis.ov_pcm_total(&music->vf, -1); if ((music->loop_end > 0) && (music->loop_end <= full_length) && (music->loop_start < music->loop_end)) { music->loop = 1; } music->freesrc = freesrc; return music; } static const char* OGG_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { OGG_music *music = (OGG_music *)context; return meta_tags_get(&music->tags, tag_type); } /* Set the volume for an OGG stream */ static void OGG_SetVolume(void *context, int volume) { OGG_music *music = (OGG_music *)context; music->volume = volume; } /* Get the volume for an OGG stream */ static int OGG_GetVolume(void *context) { OGG_music *music = (OGG_music *)context; return music->volume; } /* Start playback of a given OGG stream */ static int OGG_Play(void *context, int play_count) { OGG_music *music = (OGG_music *)context; music->play_count = play_count; return OGG_Seek(music, 0.0); } static void OGG_Stop(void *context) { OGG_music *music = (OGG_music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with OGG_play() */ static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { OGG_music *music = (OGG_music *)context; SDL_bool looped = SDL_FALSE; int filled, amount, result; int section; ogg_int64_t pcmPos; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } section = music->section; #ifdef OGG_USE_TREMOR amount = (int)vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, §ion); #else amount = (int)vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, SDL_BYTEORDER == SDL_BIG_ENDIAN, 2, 1, §ion); #endif if (amount < 0) { return set_ov_error("ov_read", amount); } if (section != music->section) { music->section = section; if (OGG_UpdateSection(music) < 0) { return -1; } } pcmPos = vorbis.ov_pcm_tell(&music->vf); if (music->loop && (music->play_count != 1) && (pcmPos >= music->loop_end)) { amount -= (int)((pcmPos - music->loop_end) * music->vi.channels) * (int)sizeof(Sint16); result = vorbis.ov_pcm_seek(&music->vf, music->loop_start); if (result < 0) { return set_ov_error("ov_pcm_seek", result); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } music->play_count = play_count; } looped = SDL_TRUE; } if (amount > 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else if (!looped) { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (OGG_Play(music, play_count) < 0) { return -1; } } } return 0; } static int OGG_GetAudio(void *context, void *data, int bytes) { OGG_music *music = (OGG_music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, OGG_GetSome); } /* Jump (seek) to a given position (time is in seconds) */ static int OGG_Seek(void *context, double time) { OGG_music *music = (OGG_music *)context; int result; #ifdef OGG_USE_TREMOR result = vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0)); #else result = vorbis.ov_time_seek(&music->vf, time); #endif if (result < 0) { return set_ov_error("ov_time_seek", result); } return 0; } static double OGG_Tell(void *context) { OGG_music *music = (OGG_music *)context; #ifdef OGG_USE_TREMOR return vorbis.ov_time_tell(&music->vf) / 1000.0; #else return vorbis.ov_time_tell(&music->vf); #endif } /* Return music duration in seconds */ static double OGG_Duration(void *context) { OGG_music *music = (OGG_music *)context; #ifdef OGG_USE_TREMOR return vorbis.ov_time_total(&music->vf, -1) / 1000.0; #else return vorbis.ov_time_total(&music->vf, -1); #endif } static double OGG_LoopStart(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_start / music->vi.rate; } return -1.0; } static double OGG_LoopEnd(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_end / music->vi.rate; } return -1.0; } static double OGG_LoopLength(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_len / music->vi.rate; } return -1.0; } /* Close the given OGG stream */ static void OGG_Delete(void *context) { OGG_music *music = (OGG_music *)context; meta_tags_clear(&music->tags); vorbis.ov_clear(&music->vf); if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->src); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_OGG = { "OGG", MIX_MUSIC_OGG, MUS_OGG, SDL_FALSE, SDL_FALSE, OGG_Load, NULL, /* Open */ OGG_CreateFromRW, NULL, /* CreateFromFile */ OGG_SetVolume, OGG_GetVolume, OGG_Play, NULL, /* IsPlaying */ OGG_GetAudio, NULL, /* Jump */ OGG_Seek, OGG_Tell, OGG_Duration, OGG_LoopStart, OGG_LoopEnd, OGG_LoopLength, OGG_GetMetaTag, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ OGG_Stop, OGG_Delete, NULL, /* Close */ OGG_Unload }; #endif /* MUSIC_OGG */ SDL2_mixer-2.8.0/src/codecs/music_timidity.h0000644000076500000240000000217114551252471017674 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MIDI files with timidity */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_TIMIDITY; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_ogg_stb.c0000644000076500000240000003402614553225265017466 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if defined(MUSIC_OGG) && defined(OGG_USE_STB) /* This file supports Ogg Vorbis music streams using a modified stb_vorbis module */ #include "music_ogg.h" #include "utils.h" #include "SDL_assert.h" #define STB_VORBIS_SDL 1 /* for SDL_mixer-specific stuff. */ #define STB_VORBIS_NO_STDIO 1 #define STB_VORBIS_NO_CRT 1 #define STB_VORBIS_NO_PUSHDATA_API 1 #define STB_VORBIS_MAX_CHANNELS 8 /* For 7.1 surround sound */ #define STB_FORCEINLINE SDL_FORCE_INLINE #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define STB_VORBIS_BIG_ENDIAN 1 #endif #define STBV_CDECL SDLCALL /* for SDL_qsort() */ #ifdef assert #undef assert #endif #ifdef memset #undef memset #endif #ifdef memcpy #undef memcpy #endif #define assert SDL_assert #define memset SDL_memset #define memcmp SDL_memcmp #define memcpy SDL_memcpy #define qsort SDL_qsort #define malloc SDL_malloc #define realloc SDL_realloc #define free SDL_free #define pow SDL_pow #define floor SDL_floor #define ldexp(v, e) SDL_scalbn((v), (e)) #define abs(x) SDL_abs(x) #define cos(x) SDL_cos(x) #define sin(x) SDL_sin(x) #define log(x) SDL_log(x) #define exp(x) SDL_exp(x) #include "stb_vorbis/stb_vorbis.h" typedef struct { SDL_RWops *src; int freesrc; int play_count; int volume; stb_vorbis *vf; stb_vorbis_info vi; int section; SDL_AudioStream *stream; char *buffer; int buffer_size; int loop; Sint64 loop_start; Sint64 loop_end; Sint64 loop_len; Sint64 full_length; Mix_MusicMetaTags tags; } OGG_music; static int set_ov_error(const char *function, int error) { #define HANDLE_ERROR_CASE(X) case X: Mix_SetError("%s: %s", function, #X); break; switch (error) { HANDLE_ERROR_CASE(VORBIS_need_more_data) HANDLE_ERROR_CASE(VORBIS_invalid_api_mixing) HANDLE_ERROR_CASE(VORBIS_outofmem) HANDLE_ERROR_CASE(VORBIS_feature_not_supported) HANDLE_ERROR_CASE(VORBIS_too_many_channels) HANDLE_ERROR_CASE(VORBIS_file_open_failure) HANDLE_ERROR_CASE(VORBIS_seek_without_length) HANDLE_ERROR_CASE(VORBIS_unexpected_eof) HANDLE_ERROR_CASE(VORBIS_seek_invalid) HANDLE_ERROR_CASE(VORBIS_invalid_setup) HANDLE_ERROR_CASE(VORBIS_invalid_stream) HANDLE_ERROR_CASE(VORBIS_missing_capture_pattern) HANDLE_ERROR_CASE(VORBIS_invalid_stream_structure_version) HANDLE_ERROR_CASE(VORBIS_continued_packet_flag_invalid) HANDLE_ERROR_CASE(VORBIS_incorrect_stream_serial_number) HANDLE_ERROR_CASE(VORBIS_invalid_first_page) HANDLE_ERROR_CASE(VORBIS_bad_packet_type) HANDLE_ERROR_CASE(VORBIS_cant_find_last_page) HANDLE_ERROR_CASE(VORBIS_seek_failed) HANDLE_ERROR_CASE(VORBIS_ogg_skeleton_not_supported) default: Mix_SetError("%s: unknown error %d\n", function, error); break; } return -1; } static int OGG_Seek(void *context, double time); static void OGG_Delete(void *context); static int OGG_UpdateSection(OGG_music *music) { stb_vorbis_info vi; vi = stb_vorbis_get_info(music->vf); if (vi.channels == music->vi.channels && vi.sample_rate == music->vi.sample_rate) { return 0; } SDL_memcpy(&music->vi, &vi, sizeof(vi)); if (music->buffer) { SDL_free(music->buffer); music->buffer = NULL; } if (music->stream) { SDL_FreeAudioStream(music->stream); music->stream = NULL; } music->stream = SDL_NewAudioStream(AUDIO_F32SYS, (Uint8)vi.channels, (int)vi.sample_rate, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -1; } music->buffer_size = music_spec.samples * (int)sizeof(float) * vi.channels; if (music->buffer_size <= 0) { return -1; } music->buffer = (char *)SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { return -1; } return 0; } /* Load an OGG stream from an SDL_RWops object */ static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc) { OGG_music *music; stb_vorbis_comment vc; long rate; SDL_bool is_loop_length = SDL_FALSE; int i, error; music = (OGG_music *)SDL_calloc(1, sizeof *music); if (!music) { SDL_OutOfMemory(); return NULL; } music->src = src; music->volume = MIX_MAX_VOLUME; music->section = -1; music->vf = stb_vorbis_open_rwops(src, 0, &error, NULL); if (music->vf == NULL) { set_ov_error("stb_vorbis_open_rwops", error); SDL_free(music); return NULL; } if (OGG_UpdateSection(music) < 0) { OGG_Delete(music); return NULL; } music->vi = stb_vorbis_get_info(music->vf); if ((int)music->vi.sample_rate <= 0) { Mix_SetError("Invalid sample rate value"); OGG_Delete(music); return NULL; } rate = music->vi.sample_rate; music->full_length = stb_vorbis_stream_length_in_samples(music->vf); if (music->full_length <= 0) { Mix_SetError("No samples in ogg/vorbis stream."); OGG_Delete(music); return NULL; } vc = stb_vorbis_get_comment(music->vf); if (vc.comment_list != NULL) { for (i = 0; i < vc.comment_list_length; i++) { char *param = SDL_strdup(vc.comment_list[i]); char *argument = param; char *value = SDL_strchr(param, '='); if (value == NULL) { value = param + SDL_strlen(param); } else { *(value++) = '\0'; } /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from * string if it is present at position 4. */ if (_Mix_IsLoopTag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) { SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); } if (SDL_strcasecmp(argument, "LOOPSTART") == 0) music->loop_start = _Mix_ParseTime(value, rate); else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { music->loop_len = SDL_strtoll(value, NULL, 10); is_loop_length = SDL_TRUE; } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { music->loop_end = _Mix_ParseTime(value, rate); is_loop_length = SDL_FALSE; } else if (SDL_strcasecmp(argument, "TITLE") == 0) { meta_tags_set(&music->tags, MIX_META_TITLE, value); } else if (SDL_strcasecmp(argument, "ARTIST") == 0) { meta_tags_set(&music->tags, MIX_META_ARTIST, value); } else if (SDL_strcasecmp(argument, "ALBUM") == 0) { meta_tags_set(&music->tags, MIX_META_ALBUM, value); } else if (SDL_strcasecmp(argument, "COPYRIGHT") == 0) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, value); } SDL_free(param); } if (is_loop_length) { music->loop_end = music->loop_start + music->loop_len; } else { music->loop_len = music->loop_end - music->loop_start; } /* Ignore invalid loop tag */ if (music->loop_start < 0 || music->loop_len < 0 || music->loop_end < 0) { music->loop_start = 0; music->loop_len = 0; music->loop_end = 0; } } if ((music->loop_end > 0) && (music->loop_end <= music->full_length) && (music->loop_start < music->loop_end)) { music->loop = 1; } OGG_Seek(music, 0.0); music->freesrc = freesrc; return music; } static const char* OGG_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { OGG_music *music = (OGG_music *)context; return meta_tags_get(&music->tags, tag_type); } /* Set the volume for an OGG stream */ static void OGG_SetVolume(void *context, int volume) { OGG_music *music = (OGG_music *)context; music->volume = volume; } /* Get the volume for an OGG stream */ static int OGG_GetVolume(void *context) { OGG_music *music = (OGG_music *)context; return music->volume; } /* Start playback of a given OGG stream */ static int OGG_Play(void *context, int play_count) { OGG_music *music = (OGG_music *)context; music->play_count = play_count; return OGG_Seek(music, 0.0); } static void OGG_Stop(void *context) { OGG_music *music = (OGG_music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with OGG_play() */ static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { OGG_music *music = (OGG_music *)context; SDL_bool looped = SDL_FALSE; int filled, amount, result; int section; Sint64 pcmPos; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } section = music->section; amount = stb_vorbis_get_samples_float_interleaved(music->vf, music->vi.channels, (float *)music->buffer, music_spec.samples * music->vi.channels); amount *= music->vi.channels * sizeof(float); if (section != music->section) { music->section = section; if (OGG_UpdateSection(music) < 0) { return -1; } } pcmPos = stb_vorbis_get_playback_sample_offset(music->vf); if (music->loop && (music->play_count != 1) && (pcmPos >= music->loop_end)) { amount -= (int)((pcmPos - music->loop_end) * music->vi.channels) * (int)sizeof(float); result = stb_vorbis_seek(music->vf, (Uint32)music->loop_start); if (!result) { return set_ov_error("stb_vorbis_seek", stb_vorbis_get_error(music->vf)); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } music->play_count = play_count; } looped = SDL_TRUE; } if (amount > 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else if (!looped) { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (OGG_Play(music, play_count) < 0) { return -1; } } } return 0; } static int OGG_GetAudio(void *context, void *data, int bytes) { OGG_music *music = (OGG_music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, OGG_GetSome); } /* Jump (seek) to a given position (time is in seconds) */ static int OGG_Seek(void *context, double time) { OGG_music *music = (OGG_music *)context; int result; result = stb_vorbis_seek(music->vf, (unsigned int)(time * music->vi.sample_rate)); if (!result) { return set_ov_error("stb_vorbis_seek", stb_vorbis_get_error(music->vf)); } return 0; } static double OGG_Tell(void *context) { OGG_music *music = (OGG_music *)context; return (double)stb_vorbis_get_playback_sample_offset(music->vf) / music->vi.sample_rate; } /* Return music duration in seconds */ static double OGG_Duration(void *context) { OGG_music *music = (OGG_music *)context; return (double)music->full_length / music->vi.sample_rate; } static double OGG_LoopStart(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_start / music->vi.sample_rate; } return -1.0; } static double OGG_LoopEnd(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_end / music->vi.sample_rate; } return -1.0; } static double OGG_LoopLength(void *music_p) { OGG_music *music = (OGG_music *)music_p; if (music->loop > 0) { return (double)music->loop_len / music->vi.sample_rate; } return -1.0; } /* Close the given OGG stream */ static void OGG_Delete(void *context) { OGG_music *music = (OGG_music *)context; meta_tags_clear(&music->tags); stb_vorbis_close(music->vf); if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->src); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_OGG = { "OGG", MIX_MUSIC_OGG, MUS_OGG, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ OGG_CreateFromRW, NULL, /* CreateFromFile */ OGG_SetVolume, OGG_GetVolume, OGG_Play, NULL, /* IsPlaying */ OGG_GetAudio, NULL, /* Jump */ OGG_Seek, OGG_Tell, OGG_Duration, OGG_LoopStart, OGG_LoopEnd, OGG_LoopLength, OGG_GetMetaTag, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ OGG_Stop, OGG_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_OGG */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/stb_vorbis/0000755000076500000240000000000014553251273016643 5ustar valvestaffSDL2_mixer-2.8.0/src/codecs/stb_vorbis/README.txt0000644000076500000240000000043414277744147020354 0ustar valvestaffstb ------------------- single-file public domain (or MIT licensed) libraries for C/C++ ------------------- https://github.com/nothings/stb ------------------- stv_vorbis.c (renamed into h) version 1.22 decode ogg vorbis files from file/memory to float/16-bit signed output SDL2_mixer-2.8.0/src/codecs/stb_vorbis/stb_vorbis.h0000644000076500000240000060520114551252471021173 0ustar valvestaff// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. // // Originally sponsored by RAD Game Tools. Seeking implementation // sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, // Elias Software, Aras Pranckevicius, and Sean Barrett. // // LICENSE // // See end of file for license information. // // Limitations: // // - floor 0 not supported (used in old ogg vorbis files pre-2004) // - lossless sample-truncation at beginning ignored // - cannot concatenate multiple vorbis streams // - sample positions are 32-bit, limiting seekable 192Khz // files to around 6 hours (Ogg supports 64-bit) // // Feature contributors: // Dougall Johnson (sample-exact seeking) // Vitaly Novichkov (sample-accurate tell) // // Bugfix/warning contributors: // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart // github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson David Reid // github:Clownacy Pedro J. Estebanez Remi Verschelde // AnthoFoxo github:morlat Gabriel Ravier // Alice Rowan // // Partial history: // 1.22 - 2021-07-11 - various small fixes // 1.21 - 2021-07-02 - fix bug for files with no comments // 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) // 1.16 - 2019-03-04 - fix warnings // 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files // 1.11 - 2017-07-23 - fix MinGW compilation // 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame // 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const // 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) // some crash fixes when out of memory or with corrupt files // fix some inappropriately signed shifts // 1.05 - 2015-04-19 - don't define __forceinline if it's redundant // 1.04 - 2014-08-27 - fix missing const-correct case in API // 1.03 - 2014-08-07 - warning fixes // 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows // 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) // 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; // (API change) report sample rate for decode-full-file funcs // // See end of file for full version history. ////////////////////////////////////////////////////////////////////////////// // // HEADER BEGINS HERE // #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H #define STB_VORBIS_INCLUDE_STB_VORBIS_H #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifdef __cplusplus extern "C" { #endif /////////// THREAD SAFETY // Individual stb_vorbis* handles are not thread-safe; you cannot decode from // them from multiple threads at the same time. However, you can have multiple // stb_vorbis* handles and decode from them independently in multiple thrads. /////////// MEMORY ALLOCATION // normally stb_vorbis uses malloc() to allocate memory at startup, // and alloca() to allocate temporary memory during a frame on the // stack. (Memory consumption will depend on the amount of setup // data in the file and how you set the compile flags for speed // vs. size. In my test files the maximal-size usage is ~150KB.) // // You can modify the wrapper functions in the source (setup_malloc, // setup_temp_malloc, temp_malloc) to change this behavior, or you // can use a simpler allocation model: you pass in a buffer from // which stb_vorbis will allocate _all_ its memory (including the // temp memory). "open" may fail with a VORBIS_outofmem if you // do not pass in enough data; there is no way to determine how // much you do need except to succeed (at which point you can // query get_info to find the exact amount required. yes I know // this is lame). // // If you pass in a non-NULL buffer of the type below, allocation // will occur from it as described above. Otherwise just pass NULL // to use malloc()/alloca() typedef struct { char *alloc_buffer; int alloc_buffer_length_in_bytes; } stb_vorbis_alloc; /////////// FUNCTIONS USEABLE WITH ALL INPUT MODES typedef struct stb_vorbis stb_vorbis; typedef struct { unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int setup_temp_memory_required; unsigned int temp_memory_required; int max_frame_size; } stb_vorbis_info; typedef struct { char *vendor; int comment_list_length; char **comment_list; } stb_vorbis_comment; // get general information about the file extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); #ifndef STB_VORBIS_NO_COMMENTS // get ogg comments extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); #endif // get the last error detected (clears it, too) extern int stb_vorbis_get_error(stb_vorbis *f); // close an ogg vorbis file and free all memory in use extern void stb_vorbis_close(stb_vorbis *f); // this function returns the offset (in samples) from the beginning of the // file that will be returned by the next decode, if it is known, or -1 // otherwise. after a flush_pushdata() call, this may take a while before // it becomes valid again. // NOT WORKING YET after a seek with PULLDATA API extern int stb_vorbis_get_sample_offset(stb_vorbis *f); // this function returns the count of returned samples from the beginning of the // file. Functions "stb_vorbis_get_samples_*", "stb_vorbis_seek_*()" will // affect the returned value. Use this call to get the accurate sample position // during playback. extern int stb_vorbis_get_playback_sample_offset(stb_vorbis *f); // returns the current seek point within the file, or offset from the beginning // of the memory buffer. In pushdata mode it returns 0. extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); /////////// PUSHDATA API #ifndef STB_VORBIS_NO_PUSHDATA_API // this API allows you to get blocks of data from any source and hand // them to stb_vorbis. you have to buffer them; stb_vorbis will tell // you how much it used, and you have to give it the rest next time; // and stb_vorbis may not have enough data to work with and you will // need to give it the same data again PLUS more. Note that the Vorbis // specification does not bound the size of an individual frame. extern stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer); // create a vorbis decoder by passing in the initial data block containing // the ogg&vorbis headers (you don't need to do parse them, just provide // the first N bytes of the file--you're told if it's not enough, see below) // on success, returns an stb_vorbis *, does not set error, returns the amount of // data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; // on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed // if returns NULL and *error is VORBIS_need_more_data, then the input block was // incomplete and you need to pass in a larger block from the start of the file extern int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ); // decode a frame of audio sample data if possible from the passed-in data block // // return value: number of bytes we used from datablock // // possible cases: // 0 bytes used, 0 samples output (need more data) // N bytes used, 0 samples output (resynching the stream, keep going) // N bytes used, M samples output (one frame of data) // note that after opening a file, you will ALWAYS get one N-bytes,0-sample // frame, because Vorbis always "discards" the first frame. // // Note that on resynch, stb_vorbis will rarely consume all of the buffer, // instead only datablock_length_in_bytes-3 or less. This is because it wants // to avoid missing parts of a page header if they cross a datablock boundary, // without writing state-machiney code to record a partial detection. // // The number of channels returned are stored in *channels (which can be // NULL--it is always the same as the number of channels reported by // get_info). *output will contain an array of float* buffers, one per // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. // // *output points into stb_vorbis's internal output buffer storage; these // buffers are owned by stb_vorbis and application code should not free // them or modify their contents. They are transient and will be overwritten // once you ask for more data to get decoded, so be sure to grab any data // you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and // once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it // will begin decoding the _next_ frame. // // if you want to seek using pushdata, you need to seek in your file, then // call stb_vorbis_flush_pushdata(), then start calling decoding, then once // decoding is returning you data, call stb_vorbis_get_sample_offset, and // if you don't like the result, seek your file again and repeat. #endif ////////// PULLING INPUT API #ifndef STB_VORBIS_NO_PULLDATA_API // This API assumes stb_vorbis is allowed to pull data from a source-- // either a block of memory containing the _entire_ vorbis stream, or a // FILE * that you or it create, or possibly some other reading mechanism // if you go modify the source to replace the FILE * case with some kind // of callback to your code. (But if you don't support seeking, you may // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif #if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO extern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between // calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. // on failure, returns NULL and sets *error. note that stb_vorbis must "own" // this stream; if you seek it in between calls to stb_vorbis, it will become // confused. #endif #ifdef STB_VORBIS_SDL extern stb_vorbis * stb_vorbis_open_rwops_section(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length); extern stb_vorbis * stb_vorbis_open_rwops(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc); #endif extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // these functions seek in the Vorbis file to (approximately) 'sample_number'. // after calling seek_frame(), the next call to get_frame_*() will include // the specified sample. after calling stb_vorbis_seek(), the next call to // stb_vorbis_get_samples_* will start with the specified sample. If you // do not need to seek to EXACTLY the target sample when using get_samples_*, // you can also use seek_frame(). extern int stb_vorbis_seek_start(stb_vorbis *f); // this function is equivalent to stb_vorbis_seek(f,0) extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); // these functions return the total length of the vorbis stream extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); // decode the next frame and return the number of samples. the number of // channels returned are stored in *channels (which can be NULL--it is always // the same as the number of channels reported by get_info). *output will // contain an array of float* buffers, one per channel. These outputs will // be overwritten on the next call to stb_vorbis_get_frame_*. // // You generally should not intermix calls to stb_vorbis_get_frame_*() // and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif // decode the next frame and return the number of *samples* per channel. // Note that for interleaved data, you pass in the number of shorts (the // size of your array), but the return value is the number of samples per // channel, not the total number of samples. // // The data is coerced to the number of channels you request according to the // channel coercion rules (see below). You must pass in the size of your // buffer(s) so that stb_vorbis will not overwrite the end of the buffer. // The maximum buffer size needed can be gotten from get_info(); however, // the Vorbis I specification implies an absolute maximum of 4096 samples // per channel. // Channel coercion rules: // Let M be the number of channels requested, and N the number of channels present, // and Cn be the nth channel; let stereo L be the sum of all L and center channels, // and stereo R be the sum of all R and center channels (channel assignment from the // vorbis spec). // M N output // 1 k sum(Ck) for all k // 2 * stereo L, stereo R // k l k > l, the first l channels, then 0s // k l k <= l, the first k channels // Note that this is not _good_ surround etc. mixing at all! It's just so // you get something useful. extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. // Returns the number of samples stored per channel; it may be less than requested // at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. Applies the coercion rules above // to produce 'channels' channels. Returns the number of samples stored per channel; // it may be less than requested at the end of the file. If there are no more // samples in the file, returns 0. #endif //////// ERROR CODES enum STBVorbisError { VORBIS__no_error, VORBIS_need_more_data=1, // not a real error VORBIS_invalid_api_mixing, // can't mix API modes VORBIS_outofmem, // not enough memory VORBIS_feature_not_supported, // uses floor 0 VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small VORBIS_file_open_failure, // fopen() failed VORBIS_seek_without_length, // can't seek in unknown-length file VORBIS_unexpected_eof=10, // file is truncated? VORBIS_seek_invalid, // seek past EOF // decoding errors (corrupt/invalid stream) -- you probably // don't care about the exact details of these // vorbis errors: VORBIS_invalid_setup=20, VORBIS_invalid_stream, // ogg errors: VORBIS_missing_capture_pattern=30, VORBIS_invalid_stream_structure_version, VORBIS_continued_packet_flag_invalid, VORBIS_incorrect_stream_serial_number, VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, VORBIS_seek_failed, VORBIS_ogg_skeleton_not_supported }; #ifdef __cplusplus } #endif #endif // STB_VORBIS_INCLUDE_STB_VORBIS_H // // HEADER ENDS HERE // ////////////////////////////////////////////////////////////////////////////// #ifndef STB_VORBIS_HEADER_ONLY // global configuration settings (e.g. set these in the project/makefile), // or just set them in this file at the top (although ideally the first few // should be visible when the header file is compiled too, although it's not // crucial) // STB_VORBIS_NO_PUSHDATA_API // does not compile the code for the various stb_vorbis_*_pushdata() // functions // #define STB_VORBIS_NO_PUSHDATA_API // STB_VORBIS_NO_PULLDATA_API // does not compile the code for the non-pushdata APIs // #define STB_VORBIS_NO_PULLDATA_API // STB_VORBIS_NO_STDIO // does not compile the code for the APIs that use FILE *s internally // or externally (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_STDIO // STB_VORBIS_NO_INTEGER_CONVERSION // does not compile the code for converting audio sample data from // float to integer (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_INTEGER_CONVERSION // STB_VORBIS_NO_FAST_SCALED_FLOAT // does not use a fast float-to-int trick to accelerate float-to-int on // most platforms which requires endianness be defined correctly. //#define STB_VORBIS_NO_FAST_SCALED_FLOAT // STB_VORBIS_MAX_CHANNELS [number] // globally define this to the maximum number of channels you need. // The spec does not put a restriction on channels except that // the count is stored in a byte, so 255 is the hard limit. // Reducing this saves about 16 bytes per value, so using 16 saves // (255-16)*16 or around 4KB. Plus anything other memory usage // I forgot to account for. Can probably go as low as 8 (7.1 audio), // 6 (5.1 audio), or 2 (stereo only). #ifndef STB_VORBIS_MAX_CHANNELS #define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? #endif // STB_VORBIS_PUSHDATA_CRC_COUNT [number] // after a flush_pushdata(), stb_vorbis begins scanning for the // next valid page, without backtracking. when it finds something // that looks like a page, it streams through it and verifies its // CRC32. Should that validation fail, it keeps scanning. But it's // possible that _while_ streaming through to check the CRC32 of // one candidate page, it sees another candidate page. This #define // determines how many "overlapping" candidate pages it can search // at once. Note that "real" pages are typically ~4KB to ~8KB, whereas // garbage pages could be as big as 64KB, but probably average ~16KB. // So don't hose ourselves by scanning an apparent 64KB page and // missing a ton of real ones in the interim; so minimum of 2 #ifndef STB_VORBIS_PUSHDATA_CRC_COUNT #define STB_VORBIS_PUSHDATA_CRC_COUNT 4 #endif // STB_VORBIS_FAST_HUFFMAN_LENGTH [number] // sets the log size of the huffman-acceleration table. Maximum // supported value is 24. with larger numbers, more decodings are O(1), // but the table size is larger so worse cache missing, so you'll have // to probe (and try multiple ogg vorbis files) to find the sweet spot. #ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH #define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 #endif // STB_VORBIS_FAST_BINARY_LENGTH [number] // sets the log size of the binary-search acceleration table. this // is used in similar fashion to the fast-huffman size to set initial // parameters for the binary search // STB_VORBIS_FAST_HUFFMAN_INT // The fast huffman tables are much more efficient if they can be // stored as 16-bit results instead of 32-bit results. This restricts // the codebooks to having only 65535 possible outcomes, though. // (At least, accelerated by the huffman table.) #ifndef STB_VORBIS_FAST_HUFFMAN_INT #define STB_VORBIS_FAST_HUFFMAN_SHORT #endif // STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // If the 'fast huffman' search doesn't succeed, then stb_vorbis falls // back on binary searching for the correct one. This requires storing // extra tables with the huffman codes in sorted order. Defining this // symbol trades off space for speed by forcing a linear search in the // non-fast case, except for "sparse" codebooks. // #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // STB_VORBIS_DIVIDES_IN_RESIDUE // stb_vorbis precomputes the result of the scalar residue decoding // that would otherwise require a divide per chunk. you can trade off // space for time by defining this symbol. // #define STB_VORBIS_DIVIDES_IN_RESIDUE // STB_VORBIS_DIVIDES_IN_CODEBOOK // vorbis VQ codebooks can be encoded two ways: with every case explicitly // stored, or with all elements being chosen from a small range of values, // and all values possible in all elements. By default, stb_vorbis expands // this latter kind out to look like the former kind for ease of decoding, // because otherwise an integer divide-per-vector-element is required to // unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can // trade off storage for speed. //#define STB_VORBIS_DIVIDES_IN_CODEBOOK #ifdef STB_VORBIS_CODEBOOK_SHORTS #error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" #endif // STB_VORBIS_DIVIDE_TABLE // this replaces small integer divides in the floor decode loop with // table lookups. made less than 1% difference, so disabled by default. // STB_VORBIS_NO_INLINE_DECODE // disables the inlining of the scalar codebook fast-huffman decode. // might save a little codespace; useful for debugging // #define STB_VORBIS_NO_INLINE_DECODE // STB_VORBIS_NO_DEFER_FLOOR // Normally we only decode the floor without synthesizing the actual // full curve. We can instead synthesize the curve immediately. This // requires more memory and is very likely slower, so I don't think // you'd ever want to do it except for debugging. // #define STB_VORBIS_NO_DEFER_FLOOR // STB_VORBIS_NO_COMMENTS // Disables reading and storing user comments. // #define STB_VORBIS_NO_COMMENTS ////////////////////////////////////////////////////////////////////////////// #ifdef STB_VORBIS_NO_PULLDATA_API #define STB_VORBIS_NO_INTEGER_CONVERSION #define STB_VORBIS_NO_STDIO #endif #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT // only need endianness for fast-float-to-int, which we don't // use for pushdata #ifndef STB_VORBIS_BIG_ENDIAN #define STB_VORBIS_ENDIAN 0 #else #define STB_VORBIS_ENDIAN 1 #endif #endif #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifndef STB_VORBIS_NO_CRT #include #include #include #include #else // STB_VORBIS_NO_CRT #ifndef NULL #define NULL 0 #endif #ifndef malloc #define malloc(s) 0 #endif #ifndef free #define free(p) ((void) 0) #endif #ifndef realloc #define realloc(p, s) 0 #endif #endif // STB_VORBIS_NO_CRT #include #ifndef STB_FORCEINLINE #if defined(_MSC_VER) #define STB_FORCEINLINE __forceinline #elif (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2))) || defined(__clang__) #define STB_FORCEINLINE static __inline __attribute__((always_inline)) #else #define STB_FORCEINLINE static inline #endif #endif #if STB_VORBIS_MAX_CHANNELS > 256 #error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" #endif #if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 #error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" #endif #if 0 #include #define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) #else #define CHECK(f) do {} while(0) #endif #define MAX_BLOCKSIZE_LOG 13 // from specification #define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) #ifdef STB_VORBIS_SDL typedef Uint8 uint8; typedef Sint8 int8; typedef Uint16 uint16; typedef Sint16 int16; typedef Uint32 uint32; typedef Sint32 int32; #else typedef unsigned char uint8; typedef signed char int8; typedef unsigned short uint16; typedef signed short int16; typedef unsigned int uint32; typedef signed int int32; #endif #ifdef __has_feature #if __has_feature(undefined_behavior_sanitizer) #define HAS_UBSAN #endif #endif #ifdef HAS_UBSAN #define STB_NO_SANITIZE(s) __attribute__((no_sanitize(s))) #else #define STB_NO_SANITIZE(s) #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif typedef float codetype; #ifdef _MSC_VER #define STBV_NOTUSED(v) (void)(v) #else #define STBV_NOTUSED(v) (void)sizeof(v) #endif // @NOTE // // Some arrays below are tagged "//varies", which means it's actually // a variable-sized piece of data, but rather than malloc I assume it's // small enough it's better to just allocate it all together with the // main thing // // Most of the variables are specified with the smallest size I could pack // them into. It might give better performance to make them all full-sized // integers. It should be safe to freely rearrange the structures or change // the sizes larger--nothing relies on silently truncating etc., nor the // order of variables. #define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) #define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) typedef struct { int dimensions, entries; uint8 *codeword_lengths; float minimum_value; float delta_value; uint8 value_bits; uint8 lookup_type; uint8 sequence_p; uint8 sparse; uint32 lookup_values; codetype *multiplicands; uint32 *codewords; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #else int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #endif uint32 *sorted_codewords; int *sorted_values; int sorted_entries; } Codebook; typedef struct { uint8 order; uint16 rate; uint16 bark_map_size; uint8 amplitude_bits; uint8 amplitude_offset; uint8 number_of_books; uint8 book_list[16]; // varies } Floor0; typedef struct { uint8 partitions; uint8 partition_class_list[32]; // varies uint8 class_dimensions[16]; // varies uint8 class_subclasses[16]; // varies uint8 class_masterbooks[16]; // varies int16 subclass_books[16][8]; // varies uint16 Xlist[31*8+2]; // varies uint8 sorted_order[31*8+2]; uint8 neighbors[31*8+2][2]; uint8 floor1_multiplier; uint8 rangebits; int values; } Floor1; typedef union { Floor0 floor0; Floor1 floor1; } Floor; typedef struct { uint32 begin, end; uint32 part_size; uint8 classifications; uint8 classbook; uint8 **classdata; int16 (*residue_books)[8]; } Residue; typedef struct { uint8 magnitude; uint8 angle; uint8 mux; } MappingChannel; typedef struct { MappingChannel *chan; uint16 coupling_steps; uint8 submaps; uint8 submap_floor[16]; // varies uint8 submap_residue[16]; // varies } Mapping; typedef struct { uint8 blockflag; uint8 mapping; uint16 windowtype; uint16 transformtype; } Mode; typedef struct { uint32 goal_crc; // expected crc if match int bytes_left; // bytes left in packet uint32 crc_so_far; // running crc int bytes_done; // bytes processed in _current_ chunk uint32 sample_loc; // granule pos encoded in page } CRCscan; typedef struct { uint32 page_start, page_end; uint32 last_decoded_sample; } ProbedPage; struct stb_vorbis { // user-accessible info unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int temp_memory_required; unsigned int setup_temp_memory_required; #ifndef STB_VORBIS_NO_COMMENTS char *vendor; int comment_list_length; char **comment_list; #endif // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; uint32 f_start; int close_on_free; #endif #ifdef STB_VORBIS_SDL SDL_RWops *rwops; uint32 rwops_start; int close_on_free; #endif uint8 *stream; uint8 *stream_start; uint8 *stream_end; uint32 stream_len; uint8 push_mode; // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; // p_first is the page on which the first audio packet ends // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management stb_vorbis_alloc alloc; int setup_offset; int temp_offset; // run-time results int eof; enum STBVorbisError error; // user-useful data // header info int blocksize[2]; int blocksize_0, blocksize_1; int codebook_count; Codebook *codebooks; int floor_count; uint16 floor_types[64]; // varies Floor *floor_config; int residue_count; uint16 residue_types[64]; // varies Residue *residue_config; int mapping_count; Mapping *mapping; int mode_count; Mode mode_config[64]; // varies uint32 total_samples; // decode buffer float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; float *outputs [STB_VORBIS_MAX_CHANNELS]; float *previous_window[STB_VORBIS_MAX_CHANNELS]; int previous_length; #ifndef STB_VORBIS_NO_DEFER_FLOOR int16 *finalY[STB_VORBIS_MAX_CHANNELS]; #else float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; #endif uint32 current_loc; // sample location of next frame to decode int current_loc_valid; int32 current_playback_loc; // sample location of played samples int current_playback_loc_valid; // per-blocksize precomputed data // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; uint16 *bit_reverse[2]; // current page/packet/segment streaming info uint32 serial; // stream serial number for verification int last_page; int segment_count; uint8 segments[255]; uint8 page_flag; uint8 bytes_in_seg; uint8 first_decode; int next_seg; int last_seg; // flag that we're on the last segment int last_seg_which; // what was the segment number of the last seg? uint32 acc; int valid_bits; int packet_bytes; int end_seg_with_known_loc; uint32 known_loc_for_packet; int discard_samples_deferred; uint32 samples_output; // push mode scanning int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching #ifndef STB_VORBIS_NO_PUSHDATA_API CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; #endif // sample-access int channel_buffer_start; int channel_buffer_end; // hack: decode work buffer (used in inverse_mdct and decode_residues) void *work_buffer; // temporary buffers void *temp_lengths; void *temp_codewords; void *temp_values; void *temp_mults; }; #if defined(STB_VORBIS_NO_PUSHDATA_API) #define IS_PUSH_MODE(f) FALSE #elif defined(STB_VORBIS_NO_PULLDATA_API) #define IS_PUSH_MODE(f) TRUE #else #define IS_PUSH_MODE(f) ((f)->push_mode) #endif typedef struct stb_vorbis vorb; static int error(vorb *f, enum STBVorbisError e) { f->error = e; if (!f->eof && e != VORBIS_need_more_data) { f->error=e; // breakpoint for debugging } return 0; } // these functions are used for allocating temporary memory // while decoding. if you can afford the stack space, use // alloca(); otherwise, provide a temp buffer and it will // allocate out of those. #define array_size_required(count,size) (count*(sizeof(void *)+(size))) #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : f->work_buffer) #define temp_free(f,p) do {} while (0) #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) #define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) // given a sufficiently large block of memory, make an array of pointers to subblocks of it static void *make_block_array(void *mem, int count, int size) { if (!mem) return NULL; else { int i; void ** p = (void **) mem; char *q = (char *) (p + count); for (i=0; i < count; ++i) { p[i] = q; q += size; } return p; } } static void *setup_malloc(vorb *f, int sz) { if (sz <= 0 || INT_MAX - 7 < sz) return NULL; sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; if (f->setup_offset + sz > f->temp_offset) return NULL; f->setup_offset += sz; return p; } return sz ? malloc(sz) : NULL; } static void setup_free(vorb *f, void *p) { if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack free(p); } static void *setup_temp_malloc(vorb *f, int sz) { if (sz <= 0 || INT_MAX - 7 < sz) return NULL; sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; return (char *) f->alloc.alloc_buffer + f->temp_offset; } return malloc(sz); } static void setup_temp_free(vorb *f, void **_p, int sz) { void *p = *_p; *_p = NULL; if (f->alloc.alloc_buffer) { f->temp_offset += (sz+7)&~7; return; } free(p); } #define CRC32_POLY 0x04c11db7 // from spec static uint32 crc_table[256]; static void crc32_init(void) { int i,j; uint32 s; for(i=0; i < 256; i++) { for (s=(uint32) i << 24, j=0; j < 8; ++j) s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); crc_table[i] = s; } } STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte) { return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; } // used in setup, and for huffman that doesn't go fast path static unsigned int bit_reverse(unsigned int n) { n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); return (n >> 16) | (n << 16); } static float square(float x) { return x*x; } // this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 // as required by the specification. fast(?) implementation from stb.h // @OPTIMIZE: called multiple times per-packet with "constants"; move to setup static int ilog(int32 n) { static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; if (n < 0) return 0; // signed n returns 0 // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) if (n < (1 << 14)) if (n < (1 << 4)) return 0 + log2_4[n ]; else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; else return 10 + log2_4[n >> 10]; else if (n < (1 << 24)) if (n < (1 << 19)) return 15 + log2_4[n >> 15]; else return 20 + log2_4[n >> 20]; else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; else return 30 + log2_4[n >> 30]; } #ifndef M_PI #define M_PI 3.14159265358979323846264f // from CRC #endif // code length assigned to a value with no huffman encoding #define NO_CODE 255 /////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// // // these functions are only called at setup, and only a few times // per file static float float32_unpack(uint32 x) { // from the specification uint32 mantissa = x & 0x1fffff; uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; return (float) ldexp((float)res, (int)exp-788); } // zlib & jpeg huffman tables assume that the output symbols // can either be arbitrarily arranged, or have monotonically // increasing frequencies--they rely on the lengths being sorted; // this makes for a very simple generation algorithm. // vorbis allows a huffman table with non-sorted lengths. This // requires a more sophisticated construction, since symbols in // order do not map to huffman codes "in order". static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) { if (!c->sparse) { c->codewords [symbol] = huff_code; } else { c->codewords [count] = huff_code; c->codeword_lengths[count] = len; values [count] = symbol; } } static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { int i,k,m=0; uint32 available[32]; memset(available, 0, sizeof(available)); // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves for (i=1; i <= len[k]; ++i) available[i] = 1U << (32-i); // note that the above code treats the first case specially, // but it's really the same as the following code, so they // could probably be combined (except the initial code is 0, // and I use 0 in available[] to mean 'empty') for (i=k+1; i < n; ++i) { uint32 res; int z = len[i], y; if (z == NO_CODE) continue; assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have // more than one free leaf at a given level, isn't totally // trivial to prove, but it seems true and the assert never // fires, so! while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); } } } return TRUE; } // accelerated huffman table allows fast O(1) match of all symbols // of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH static void compute_accelerated_huffman(Codebook *c) { int i, len; for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) c->fast_huffman[i] = -1; len = c->sparse ? c->sorted_entries : c->entries; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT if (len > 32767) len = 32767; // largest possible value we can encode! #endif for (i=0; i < len; ++i) { if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; // set table entries for all bit combinations in the higher bits while (z < FAST_HUFFMAN_TABLE_SIZE) { c->fast_huffman[z] = i; z += 1 << c->codeword_lengths[i]; } } } } #ifndef STBV_CDECL #ifdef _MSC_VER #define STBV_CDECL __cdecl #else #define STBV_CDECL #endif #endif static int STBV_CDECL uint32_compare(const void *p, const void *q) { uint32 x = * (uint32 *) p; uint32 y = * (uint32 *) q; return x < y ? -1 : x > y; } static int include_in_sort(Codebook *c, uint8 len) { if (c->sparse) { assert(len != NO_CODE); return TRUE; } if (len == NO_CODE) return FALSE; if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; return FALSE; } // if the fast table above doesn't work, we want to binary // search them... need to reverse the bits static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) { int i, len; // build a list of all the entries // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. // this is kind of a frivolous optimization--I don't see any performance improvement, // but it's like 4 extra lines of code, so. if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) if (include_in_sort(c, lengths[i])) c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { for (i=0; i < c->sorted_entries; ++i) c->sorted_codewords[i] = bit_reverse(c->codewords[i]); } qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); c->sorted_codewords[c->sorted_entries] = 0xffffffff; len = c->sparse ? c->sorted_entries : c->entries; // now we need to indicate how they correspond; we could either // #1: sort a different data structure that says who they correspond to // #2: for each sorted entry, search the original list to find who corresponds // #3: for each original entry, find the sorted entry // #1 requires extra storage, #2 is slow, #3 can use binary search! for (i=0; i < len; ++i) { int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; if (include_in_sort(c,huff_len)) { uint32 code = bit_reverse(c->codewords[i]); int x=0, n=c->sorted_entries; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } assert(c->sorted_codewords[x] == code); if (c->sparse) { c->sorted_values[x] = values[i]; c->codeword_lengths[x] = huff_len; } else { c->sorted_values[x] = i; } } } } // only run while parsing the header (3 times) static int vorbis_validate(uint8 *data) { static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; return memcmp(data, vorbis, 6) == 0; } // called from setup only, once per code book // (formula implied by specification) // // suppress an UBSan error caused by invalid input data. // upstream: https://github.com/nothings/stb/issues/1168. STB_NO_SANITIZE("float-cast-overflow") static int lookup1_values(int entries, int dim) { int r = (int) floor(exp((float) log((float) entries) / dim)); if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; ++r; // floor() to avoid _ftol() when non-CRT if (pow((float) r+1, dim) <= entries) return -1; if ((int) floor(pow((float) r, dim)) > entries) return -1; return r; } // called twice per file static void compute_twiddle_factors(int n, float *A, float *B, float *C) { int n4 = n >> 2, n8 = n >> 3; int k,k2; for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } } static void compute_window(int n, float *window) { int n2 = n >> 1, i; for (i=0; i < n2; ++i) window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); } static void compute_bitreverse(int n, uint16 *rev) { int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions int i, n8 = n >> 3; for (i=0; i < n8; ++i) rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; } static int init_blocksize(vorb *f, int b, int n) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); if (!f->window[b]) return error(f, VORBIS_outofmem); compute_window(n, f->window[b]); f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); compute_bitreverse(n, f->bit_reverse[b]); return TRUE; } static void neighbors(uint16 *x, int n, int *plow, int *phigh) { int low = -1; int high = 65536; int i; for (i=0; i < n; ++i) { if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } } } // this has been repurposed so y is now the original index instead of y typedef struct { uint16 x,id; } stbv__floor_ordering; static int STBV_CDECL point_compare(const void *p, const void *q) { stbv__floor_ordering *a = (stbv__floor_ordering *) p; stbv__floor_ordering *b = (stbv__floor_ordering *) q; return a->x < b->x ? -1 : a->x > b->x; } // /////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// #ifdef STB_VORBIS_SDL #define USE_MEMORY(z) FALSE #elif defined(STB_VORBIS_NO_STDIO) #define USE_MEMORY(z) TRUE #else #define USE_MEMORY(z) ((z)->stream) #endif static uint8 get8(vorb *z) { #ifdef STB_VORBIS_SDL uint8 c; if (SDL_RWread(z->rwops, &c, 1, 1) != 1) { z->eof = TRUE; return 0; } return c; #else if (USE_MEMORY(z)) { if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } return *z->stream++; } #endif #ifndef STB_VORBIS_NO_STDIO { int c = fgetc(z->f); if (c == EOF) { z->eof = TRUE; return 0; } return c; } #endif } static uint32 get32(vorb *f) { uint32 x; x = get8(f); x += get8(f) << 8; x += get8(f) << 16; x += (uint32) get8(f) << 24; return x; } static int getn(vorb *z, uint8 *data, int n) { #ifdef STB_VORBIS_SDL if (SDL_RWread(z->rwops, data, n, 1) == 1) return 1; z->eof = 1; return 0; #else if (USE_MEMORY(z)) { if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } memcpy(data, z->stream, n); z->stream += n; return 1; } #endif #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { z->eof = 1; return 0; } #endif } static void skip(vorb *z, int n) { #ifdef STB_VORBIS_SDL SDL_RWseek(z->rwops, n, RW_SEEK_CUR); #else if (USE_MEMORY(z)) { z->stream += n; if (z->stream >= z->stream_end) z->eof = 1; return; } #endif #ifndef STB_VORBIS_NO_STDIO { long x = ftell(z->f); fseek(z->f, x+n, SEEK_SET); } #endif } static int set_file_offset(stb_vorbis *f, unsigned int loc) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif f->eof = 0; #ifdef STB_VORBIS_SDL if (loc + f->rwops_start < loc || loc >= 0x80000000) { loc = 0x7fffffff; f->eof = 1; } else { loc += f->rwops_start; } if (SDL_RWseek(f->rwops, loc, RW_SEEK_SET) != -1) return 1; f->eof = 1; SDL_RWseek(f->rwops, f->rwops_start, RW_SEEK_END); return 0; #else if (USE_MEMORY(f)) { if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { f->stream = f->stream_end; f->eof = 1; return 0; } else { f->stream = f->stream_start + loc; return 1; } } #endif #ifndef STB_VORBIS_NO_STDIO if (loc + f->f_start < loc || loc >= 0x80000000) { loc = 0x7fffffff; f->eof = 1; } else { loc += f->f_start; } if (!fseek(f->f, loc, SEEK_SET)) return 1; f->eof = 1; fseek(f->f, f->f_start, SEEK_END); return 0; #endif } static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; static int capture_pattern(vorb *f) { if (0x4f != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x53 != get8(f)) return FALSE; return TRUE; } #define PAGEFLAG_continued_packet 1 #define PAGEFLAG_first_page 2 #define PAGEFLAG_last_page 4 static int start_page_no_capturepattern(vorb *f) { uint32 loc0,loc1,n; if (f->first_decode && !IS_PUSH_MODE(f)) { f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag f->page_flag = get8(f); // absolute granule position loc0 = get32(f); loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard get32(f); //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); // page sequence number n = get32(f); f->last_page = n; // CRC32 get32(f); // page_segments f->segment_count = get8(f); if (!getn(f, f->segments, f->segment_count)) return error(f, VORBIS_unexpected_eof); // assume we _don't_ know any the sample position of any segments f->end_seg_with_known_loc = -2; if (loc0 != ~0U || loc1 != ~0U) { int i; // determine which packet is the last one that will complete for (i=f->segment_count-1; i >= 0; --i) if (f->segments[i] < 255) break; // 'i' is now the index of the _last_ segment of a packet that ends if (i >= 0) { f->end_seg_with_known_loc = i; f->known_loc_for_packet = loc0; } } if (f->first_decode) { int i,len; len = 0; for (i=0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; f->p_first.page_end = f->p_first.page_start + len; f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; } static int start_page(vorb *f) { if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); return start_page_no_capturepattern(f); } static int start_packet(vorb *f) { while (f->next_seg == -1) { if (!start_page(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_continued_packet_flag_invalid); } f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; // f->next_seg is now valid return TRUE; } static int maybe_start_packet(vorb *f) { if (f->next_seg == -1) { int x = get8(f); if (f->eof) return FALSE; // EOF at page boundary is not an error! if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (!start_page_no_capturepattern(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) { // set up enough state that we can read this packet if we want, // e.g. during recovery f->last_seg = FALSE; f->bytes_in_seg = 0; return error(f, VORBIS_continued_packet_flag_invalid); } } return start_packet(f); } static int next_segment(vorb *f) { int len; if (f->last_seg) return 0; if (f->next_seg == -1) { f->last_seg_which = f->segment_count-1; // in case start_page fails if (!start_page(f)) { f->last_seg = 1; return 0; } if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); } len = f->segments[f->next_seg++]; if (len < 255) { f->last_seg = TRUE; f->last_seg_which = f->next_seg-1; } if (f->next_seg >= f->segment_count) f->next_seg = -1; assert(f->bytes_in_seg == 0); f->bytes_in_seg = len; return len; } #define EOP (-1) #define INVALID_BITS (-1) static int get8_packet_raw(vorb *f) { if (!f->bytes_in_seg) { // CLANG! if (f->last_seg) return EOP; else if (!next_segment(f)) return EOP; } assert(f->bytes_in_seg > 0); --f->bytes_in_seg; ++f->packet_bytes; return get8(f); } static int get8_packet(vorb *f) { int x = get8_packet_raw(f); f->valid_bits = 0; return x; } #ifndef STB_VORBIS_NO_COMMENTS static int get32_packet(vorb *f) { uint32 x; x = get8_packet(f); x += get8_packet(f) << 8; x += get8_packet(f) << 16; x += (uint32) get8_packet(f) << 24; return x; } #endif static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP); } // @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important // as the huffman decoder? static uint32 get_bits(vorb *f, int n) { uint32 z; if (f->valid_bits < 0) return 0; if (f->valid_bits < n) { if (n > 24) { // the accumulator technique below would not work correctly in this case z = get_bits(f, 24); z += get_bits(f, n-24) << 24; return z; } if (f->valid_bits == 0) f->acc = 0; while (f->valid_bits < n) { int z = get8_packet_raw(f); if (z == EOP) { f->valid_bits = INVALID_BITS; return 0; } f->acc += z << f->valid_bits; f->valid_bits += 8; } } assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; return z; } // @OPTIMIZE: primary accumulator for huffman // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, // e.g. cache them locally and decode locally STB_FORCEINLINE void prep_huffman(vorb *f) { if (f->valid_bits <= 24) { if (f->valid_bits == 0) f->acc = 0; do { int z; if (f->last_seg && !f->bytes_in_seg) return; z = get8_packet_raw(f); if (z == EOP) return; f->acc += (unsigned) z << f->valid_bits; f->valid_bits += 8; } while (f->valid_bits <= 24); } } enum { VORBIS_packet_id = 1, VORBIS_packet_comment = 3, VORBIS_packet_setup = 5 }; static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { int i; prep_huffman(f); if (c->codewords == NULL && c->sorted_codewords == NULL) return -1; // cases to use binary search: sorted_codewords && !c->codewords // sorted_codewords && c->entries > 8 if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { // binary search uint32 code = bit_reverse(f->acc); int x=0, n=c->sorted_entries, len; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } // x is now the sorted index if (!c->sparse) x = c->sorted_values[x]; // x is now sorted index if sparse, or symbol otherwise len = c->codeword_lengths[x]; if (f->valid_bits >= len) { f->acc >>= len; f->valid_bits -= len; return x; } f->valid_bits = 0; return -1; } // if small, linear search assert(!c->sparse); for (i=0; i < c->entries; ++i) { if (c->codeword_lengths[i] == NO_CODE) continue; /* unsigned left shift for 32-bit codewords. * https://github.com/nothings/stb/issues/1168 */ if (c->codewords[i] == (f->acc & ((1U << c->codeword_lengths[i])-1))) { if (f->valid_bits >= c->codeword_lengths[i]) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; return i; } f->valid_bits = 0; return -1; } } error(f, VORBIS_invalid_stream); f->valid_bits = 0; return -1; } #ifndef STB_VORBIS_NO_INLINE_DECODE #define DECODE_RAW(var, f,c) \ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ prep_huffman(f); \ var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ var = c->fast_huffman[var]; \ if (var >= 0) { \ int n = c->codeword_lengths[var]; \ f->acc >>= n; \ f->valid_bits -= n; \ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ } else { \ var = codebook_decode_scalar_raw(f,c); \ } #else static int codebook_decode_scalar(vorb *f, Codebook *c) { int i; if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) prep_huffman(f); // fast huffman table lookup i = f->acc & FAST_HUFFMAN_TABLE_MASK; i = c->fast_huffman[i]; if (i >= 0) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } return i; } return codebook_decode_scalar_raw(f,c); } #define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); #endif #define DECODE(var,f,c) \ DECODE_RAW(var,f,c) \ if (c->sparse && var >= 0) var = c->sorted_values[var]; #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) #else #define DECODE_VQ(var,f,c) DECODE(var,f,c) #endif // CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case // where we avoid one addition #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_BASE(c) (0) static int codebook_decode_start(vorb *f, Codebook *c) { int z = -1; // type 0 is only legal in a scalar context if (c->lookup_type == 0) error(f, VORBIS_invalid_stream); else { DECODE_VQ(z,f,c); if (c->sparse) assert(z < c->sorted_entries); if (z < 0) { // check for EOP if (!f->bytes_in_seg) if (f->last_seg) return z; error(f, VORBIS_invalid_stream); } } return z; } static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { int i,z = codebook_decode_start(f,c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { float last = CODEBOOK_ELEMENT_BASE(c); int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i] += val; if (c->sequence_p) last = val + c->minimum_value; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; if (c->sequence_p) { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i] += val; last = val + c->minimum_value; } } else { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; } } return TRUE; } static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { int i,z = codebook_decode_start(f,c); float last = CODEBOOK_ELEMENT_BASE(c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i*step] += val; if (c->sequence_p) last = val; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i*step] += val; if (c->sequence_p) last = val; } return TRUE; } static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) { int c_inter = *c_inter_p; int p_inter = *p_inter_p; int i,z, effective = c->dimensions; // type 0 is only legal in a scalar context if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); while (total_decode > 0) { float last = CODEBOOK_ELEMENT_BASE(c); DECODE_VQ(z,f,c); #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK assert(!c->sparse || z < c->sorted_entries); #endif if (z < 0) { if (!f->bytes_in_seg) if (f->last_seg) return FALSE; return error(f, VORBIS_invalid_stream); } // if this will take us off the end of the buffers, stop short! // we check by computing the length of the virtual interleaved // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // and the length we'll be using (effective) if (c_inter + p_inter*ch + effective > len * ch) { effective = len*ch - (p_inter*ch + c_inter); } #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < effective; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } if (c->sequence_p) last = val; div *= c->lookup_values; } } else #endif { z *= c->dimensions; if (c->sequence_p) { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } last = val; } } else { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } } } } total_decode -= effective; } *c_inter_p = c_inter; *p_inter_p = p_inter; return TRUE; } static int predict_point(int x, int x0, int x1, int y0, int y1) { int dy = y1 - y0; int adx = x1 - x0; // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? int err = abs(dy) * (x - x0); int off = err / adx; return dy < 0 ? y0 - off : y0 + off; } // the following table is block-copied from the specification static float inverse_db_table[256] = { 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f }; // @OPTIMIZE: if you want to replace this bresenham line-drawing routine, // note that you must produce bit-identical output to decode correctly; // this specific sequence of operations is specified in the spec (it's // drawing integer-quantized frequency-space lines that the encoder // expects to be exactly the same) // ... also, isn't the whole point of Bresenham's algorithm to NOT // have to divide in the setup? sigh. #ifndef STB_VORBIS_NO_DEFER_FLOOR #define LINE_OP(a,b) a *= b #else #define LINE_OP(a,b) a = b #endif #ifdef STB_VORBIS_DIVIDE_TABLE #define DIVTAB_NUMER 32 #define DIVTAB_DENOM 64 int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB #endif STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n) { int dy = y1 - y0; int adx = x1 - x0; int ady = abs(dy); int base; int x=x0,y=y0; int err = 0; int sy; #ifdef STB_VORBIS_DIVIDE_TABLE if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { if (dy < 0) { base = -integer_divide_table[ady][adx]; sy = base-1; } else { base = integer_divide_table[ady][adx]; sy = base+1; } } else { base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; } #else base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; #endif ady -= abs(base) * adx; if (x1 > n) x1 = n; if (x < x1) { LINE_OP(output[x], inverse_db_table[(uint32)y&255]); for (++x; x < x1; ++x) { err += ady; if (err >= adx) { err -= adx; y += sy; } else y += base; LINE_OP(output[x], inverse_db_table[(uint32)y&255]); } } } static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) { int k; if (rtype == 0) { int step = n / book->dimensions; for (k=0; k < step; ++k) if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) return FALSE; } else { for (k=0; k < n; ) { if (!codebook_decode(f, book, target+offset, n-k)) return FALSE; k += book->dimensions; offset += book->dimensions; } } return TRUE; } // n is 1/2 of the blocksize -- // specification: "Correct per-vector decode length is [n]/2" static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; Residue *r = f->residue_config + rn; int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; unsigned int actual_size = rtype == 2 ? n*2 : n; unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); #else int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); #endif CHECK(f); for (i=0; i < ch; ++i) if (!do_not_decode[i]) memset(residue_buffers[i], 0, sizeof(float) * n); if (rtype == 2 && ch != 1) { for (j=0; j < ch; ++j) if (!do_not_decode[j]) break; if (j == ch) goto done; for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set = 0; if (ch == 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = (z & 1), p_inter = z>>1; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #else // saves 1% if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #endif } else { z += r->part_size; c_inter = z & 1; p_inter = z >> 1; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; } else { z += r->part_size; c_inter = z % ch; p_inter = z / ch; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } } goto done; } CHECK(f); for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set=0; while (pcount < part_read) { if (pass == 0) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { Codebook *c = f->codebooks+r->classbook; int temp; DECODE(temp,f,c); if (temp == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[j][class_set] = r->classdata[temp]; #else for (i=classwords-1; i >= 0; --i) { classifications[j][i+pcount] = temp % r->classifications; temp /= r->classifications; } #endif } } } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[j][class_set][i]; #else int c = classifications[j][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { float *target = residue_buffers[j]; int offset = r->begin + pcount * r->part_size; int n = r->part_size; Codebook *book = f->codebooks + b; if (!residue_decode(f, book, target, offset, n, rtype)) goto done; } } } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } done: CHECK(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE temp_free(f,part_classdata); #else temp_free(f,classifications); #endif temp_alloc_restore(f,temp_alloc_point); } #if 0 // slow way for debugging void inverse_mdct_slow(float *buffer, int n) { int i,j; int n2 = n >> 1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) // formula from paper: //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); // formula from wikipedia //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); // these are equivalent, except the formula from the paper inverts the multiplier! // however, what actually works is NO MULTIPLIER!?! //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); buffer[i] = acc; } free(x); } #elif 0 // same as above, but just barely able to run in real time on modern machines void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { float mcos[16384]; int i,j; int n2 = n >> 1, nmask = (n << 2) -1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < 4*n; ++i) mcos[i] = (float) cos(M_PI / 2 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; buffer[i] = acc; } free(x); } #elif 0 // transform to use a slow dct-iv; this is STILL basically trivial, // but only requires half as many ops void dct_iv_slow(float *buffer, int n) { float mcos[16384]; float x[2048]; int i,j; int n2 = n >> 1, nmask = (n << 3) - 1; memcpy(x, buffer, sizeof(*x) * n); for (i=0; i < 8*n; ++i) mcos[i] = (float) cos(M_PI / 4 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n; ++j) acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; buffer[i] = acc; } } void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; float temp[4096]; memcpy(temp, buffer, n2 * sizeof(float)); dct_iv_slow(temp, n2); // returns -c'-d, a-b' for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d } #endif #ifndef LIBVORBIS_MDCT #define LIBVORBIS_MDCT 0 #endif #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison typedef struct { int n; int log2n; float *trig; int *bitrev; float scale; } mdct_lookup; extern void mdct_init(mdct_lookup *lookup, int n); extern void mdct_clear(mdct_lookup *l); extern void mdct_backward(mdct_lookup *init, float *in, float *out); mdct_lookup M1,M2; void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { mdct_lookup *M; if (M1.n == n) M = &M1; else if (M2.n == n) M = &M2; else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; } mdct_backward(M, buffer, buffer); } #endif // the following were split out into separate functions while optimizing; // they could be pushed back up but eh. __forceinline showed no change; // they're probably already being inlined. static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) { float *ee0 = e + i_off; float *ee2 = ee0 + k_off; int i; assert((n & 3) == 0); for (i=(n>>2); i > 0; --i) { float k00_20, k01_21; k00_20 = ee0[ 0] - ee2[ 0]; k01_21 = ee0[-1] - ee2[-1]; ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-2] - ee2[-2]; k01_21 = ee0[-3] - ee2[-3]; ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-4] - ee2[-4]; k01_21 = ee0[-5] - ee2[-5]; ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-6] - ee2[-6]; k01_21 = ee0[-7] - ee2[-7]; ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; A += 8; ee0 -= 8; ee2 -= 8; } } static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) { int i; float k00_20, k01_21; float *e0 = e + d0; float *e2 = e0 + k_off; for (i=lim >> 2; i > 0; --i) { k00_20 = e0[-0] - e2[-0]; k01_21 = e0[-1] - e2[-1]; e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-2] - e2[-2]; k01_21 = e0[-3] - e2[-3]; e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-4] - e2[-4]; k01_21 = e0[-5] - e2[-5]; e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-6] - e2[-6]; k01_21 = e0[-7] - e2[-7]; e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; e0 -= 8; e2 -= 8; A += k1; } } static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) { int i; float A0 = A[0]; float A1 = A[0+1]; float A2 = A[0+a_off]; float A3 = A[0+a_off+1]; float A4 = A[0+a_off*2+0]; float A5 = A[0+a_off*2+1]; float A6 = A[0+a_off*3+0]; float A7 = A[0+a_off*3+1]; float k00,k11; float *ee0 = e +i_off; float *ee2 = ee0+k_off; for (i=n; i > 0; --i) { k00 = ee0[ 0] - ee2[ 0]; k11 = ee0[-1] - ee2[-1]; ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = (k00) * A0 - (k11) * A1; ee2[-1] = (k11) * A0 + (k00) * A1; k00 = ee0[-2] - ee2[-2]; k11 = ee0[-3] - ee2[-3]; ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = (k00) * A2 - (k11) * A3; ee2[-3] = (k11) * A2 + (k00) * A3; k00 = ee0[-4] - ee2[-4]; k11 = ee0[-5] - ee2[-5]; ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = (k00) * A4 - (k11) * A5; ee2[-5] = (k11) * A4 + (k00) * A5; k00 = ee0[-6] - ee2[-6]; k11 = ee0[-7] - ee2[-7]; ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = (k00) * A6 - (k11) * A7; ee2[-7] = (k11) * A6 + (k00) * A7; ee0 -= k0; ee2 -= k0; } } STB_FORCEINLINE void iter_54(float *z) { float k00,k11,k22,k33; float y0,y1,y2,y3; k00 = z[ 0] - z[-4]; y0 = z[ 0] + z[-4]; y2 = z[-2] + z[-6]; k22 = z[-2] - z[-6]; z[-0] = y0 + y2; // z0 + z4 + z2 + z6 z[-2] = y0 - y2; // z0 + z4 - z2 - z6 // done with y0,y2 k33 = z[-3] - z[-7]; z[-4] = k00 + k33; // z0 - z4 + z3 - z7 z[-6] = k00 - k33; // z0 - z4 - z3 + z7 // done with k33 k11 = z[-1] - z[-5]; y1 = z[-1] + z[-5]; y3 = z[-3] + z[-7]; z[-1] = y1 + y3; // z1 + z5 + z3 + z7 z[-3] = y1 - y3; // z1 + z5 - z3 - z7 z[-5] = k11 - k22; // z1 - z5 + z2 - z6 z[-7] = k11 + k22; // z1 - z5 - z2 + z6 } static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { int a_off = base_n >> 3; float A2 = A[0+a_off]; float *z = e + i_off; float *base = z - 16 * n; while (z > base) { float k00,k11; float l00,l11; k00 = z[-0] - z[ -8]; k11 = z[-1] - z[ -9]; l00 = z[-2] - z[-10]; l11 = z[-3] - z[-11]; z[ -0] = z[-0] + z[ -8]; z[ -1] = z[-1] + z[ -9]; z[ -2] = z[-2] + z[-10]; z[ -3] = z[-3] + z[-11]; z[ -8] = k00; z[ -9] = k11; z[-10] = (l00+l11) * A2; z[-11] = (l11-l00) * A2; k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; l00 = z[ -6] - z[-14]; l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; z[-12] = k11; z[-13] = -k00; z[-14] = (l11-l00) * A2; z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); z -= 16; } } static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int ld; // @OPTIMIZE: reduce register pressure by using fewer variables? int save_point = temp_alloc_save(f); float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); float *u=NULL,*v=NULL; // twiddle factors float *A = f->A[blocktype]; // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. // kernel from paper // merged: // copy and reflect spectral data // step 0 // note that it turns out that the items added together during // this step are, in fact, being added to themselves (as reflected // by step 0). inexplicable inefficiency! this became obvious // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { float *d,*e, *AA, *e_stop; d = &buf2[n2-2]; AA = A; e = &buffer[0]; e_stop = &buffer[n2]; while (e != e_stop) { d[1] = (e[0] * AA[0] - e[2]*AA[1]); d[0] = (e[0] * AA[1] + e[2]*AA[0]); d -= 2; AA += 2; e += 4; } e = &buffer[n2-3]; while (d >= buf2) { d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); d -= 2; AA += 2; e -= 4; } } // now we use symbolic names for these, so that we can // possibly swap their meaning as we change which operations // are in place u = buffer; v = buf2; // step 2 (paper output is w, now u) // this could be in place, but the data ends up in the wrong // place... _somebody_'s got to swap it, so this is nominated { float *AA = &A[n2-8]; float *d0,*d1, *e0, *e1; e0 = &v[n4]; e1 = &v[0]; d0 = &u[n4]; d1 = &u[0]; while (AA >= A) { float v40_20, v41_21; v41_21 = e0[1] - e1[1]; v40_20 = e0[0] - e1[0]; d0[1] = e0[1] + e1[1]; d0[0] = e0[0] + e1[0]; d1[1] = v41_21*AA[4] - v40_20*AA[5]; d1[0] = v40_20*AA[4] + v41_21*AA[5]; v41_21 = e0[3] - e1[3]; v40_20 = e0[2] - e1[2]; d0[3] = e0[3] + e1[3]; d0[2] = e0[2] + e1[2]; d1[3] = v41_21*AA[0] - v40_20*AA[1]; d1[2] = v40_20*AA[0] + v41_21*AA[1]; AA -= 8; d0 += 4; d1 += 4; e0 += 4; e1 += 4; } } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions // optimized step 3: // the original step3 loop can be nested r inside s or s inside r; // it's written originally as s inside r, but this is dumb when r // iterates many times, and s few. So I have two copies of it and // switch between them halfway. // this is iteration 0 of step 3 imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); // this is iteration 1 of step 3 imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); l=2; for (; l < (ld-3)>>1; ++l) { int k0 = n >> (l+2), k0_2 = k0>>1; int lim = 1 << (l+1); int i; for (i=0; i < lim; ++i) imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); } for (; l < ld-6; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; int rlim = n >> (l+6), r; int lim = 1 << (l+1); int i_off; float *A0 = A; i_off = n2-1; for (r=rlim; r > 0; --r) { imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); A0 += k1*4; i_off -= 8; } } // iterations with count: // ld-6,-5,-4 all interleaved together // the big win comes from getting rid of needless flops // due to the constants on pass 5 & 4 being all 1 and 0; // combining them to be simultaneous to improve cache made little difference imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); // output is u // step 4, 5, and 6 // cannot be in-place because of step 5 { uint16 *bitrev = f->bit_reverse[blocktype]; // weirdly, I'd have thought reading sequentially and writing // erratically would have been better than vice-versa, but in // fact that's not what my testing showed. (That is, with // j = bitreverse(i), do you read i and write j, or read j and write i.) float *d0 = &v[n4-4]; float *d1 = &v[n2-4]; while (d0 >= v) { int k4; k4 = bitrev[0]; d1[3] = u[k4+0]; d1[2] = u[k4+1]; d0[3] = u[k4+2]; d0[2] = u[k4+3]; k4 = bitrev[1]; d1[1] = u[k4+0]; d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; d0 -= 4; d1 -= 4; bitrev += 2; } } // (paper output is u, now v) // data must be in buf2 assert(v == buf2); // step 7 (paper output is v, now v) // this is now in place { float *C = f->C[blocktype]; float *d, *e; d = v; e = v + n2 - 4; while (d < e) { float a02,a11,b0,b1,b2,b3; a02 = d[0] - e[2]; a11 = d[1] + e[3]; b0 = C[1]*a02 + C[0]*a11; b1 = C[1]*a11 - C[0]*a02; b2 = d[0] + e[ 2]; b3 = d[1] - e[ 3]; d[0] = b2 + b0; d[1] = b3 + b1; e[2] = b2 - b0; e[3] = b1 - b3; a02 = d[2] - e[0]; a11 = d[3] + e[1]; b0 = C[3]*a02 + C[2]*a11; b1 = C[3]*a11 - C[2]*a02; b2 = d[2] + e[ 0]; b3 = d[3] - e[ 1]; d[2] = b2 + b0; d[3] = b3 + b1; e[0] = b2 - b0; e[1] = b1 - b3; C += 4; d += 4; e -= 4; } } // data must be in buf2 // step 8+decode (paper output is X, now buffer) // this generates pairs of data a la 8 and pushes them directly through // the decode kernel (pushing rather than pulling) to avoid having // to make another pass later // this cannot POSSIBLY be in place, so we refer to the buffers directly { float *d0,*d1,*d2,*d3; float *B = f->B[blocktype] + n2 - 8; float *e = buf2 + n2 - 8; d0 = &buffer[0]; d1 = &buffer[n2-4]; d2 = &buffer[n2]; d3 = &buffer[n-4]; while (e >= v) { float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; d2[0] = p2; d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; d2[1] = p0; d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; d2[2] = p2; d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; d2[3] = p0; d3[0] = p0; B -= 8; e -= 8; d0 += 4; d2 += 4; d1 -= 4; d3 -= 4; } } temp_free(f,buf2); temp_alloc_restore(f,save_point); } #if 0 // this is the original version of the above code, if you want to optimize it from scratch void inverse_mdct_naive(float *buffer, int n) { float s; float A[1 << 12], B[1 << 12], C[1 << 11]; int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int n3_4 = n - n4, ld; // how can they claim this only uses N words?! // oh, because they're only used sparsely, whoops float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; // set up twiddle factors for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2); B[k2+1] = (float) sin((k2+1)*M_PI/n/2); } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // Note there are bugs in that pseudocode, presumably due to them attempting // to rename the arrays nicely rather than representing the way their actual // implementation bounces buffers back and forth. As a result, even in the // "some formulars corrected" version, a direct implementation fails. These // are noted below as "paper bug". // copy and reflect spectral data for (k=0; k < n2; ++k) u[k] = buffer[k]; for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; // kernel from paper // step 1 for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; } // step 2 for (k=k4=0; k < n8; k+=1, k4+=4) { w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions for (l=0; l < ld-3; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3); int rlim = n >> (l+4), r4, r; int s2lim = 1 << (l+2), s2; for (r=r4=0; r < rlim; r4+=4,++r) { for (s2=0; s2 < s2lim; s2+=2) { u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; } } if (l+1 < ld-3) { // paper bug: ping-ponging of u&w here is omitted memcpy(w, u, sizeof(u)); } } // step 4 for (i=0; i < n8; ++i) { int j = bit_reverse(i) >> (32-ld+3); assert(j < n8); if (i == j) { // paper bug: original code probably swapped in place; if copying, // need to directly copy in this case int i8 = i << 3; v[i8+1] = u[i8+1]; v[i8+3] = u[i8+3]; v[i8+5] = u[i8+5]; v[i8+7] = u[i8+7]; } else if (i < j) { int i8 = i << 3, j8 = j << 3; v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; } } // step 5 for (k=0; k < n2; ++k) { w[k] = v[k*2+1]; } // step 6 for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { u[n-1-k2] = w[k4]; u[n-2-k2] = w[k4+1]; u[n3_4 - 1 - k2] = w[k4+2]; u[n3_4 - 2 - k2] = w[k4+3]; } // step 7 for (k=k2=0; k < n8; ++k, k2 += 2) { v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; } // step 8 for (k=k2=0; k < n4; ++k,k2 += 2) { X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; } // decode kernel to output // determined the following value experimentally // (by first figuring out what made inverse_mdct_slow work); then matching that here // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) s = 0.5; // theoretically would be n4 // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, // so it needs to use the "old" B values to behave correctly, or else // set s to 1.0 ]]] for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; } #endif static float *get_window(vorb *f, int len) { len <<= 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; return NULL; } #ifndef STB_VORBIS_NO_DEFER_FLOOR typedef int16 YTYPE; #else typedef int YTYPE; #endif static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) { int n2 = n >> 1; int s = map->chan[i].mux, floor; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; int j,q; int lx = 0, ly = finalY[0] * g->floor1_multiplier; for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) #endif { int hy = finalY[j] * g->floor1_multiplier; int hx = g->Xlist[j]; if (lx != hx) draw_line(target, lx,ly, hx,hy, n2); CHECK(f); lx = hx, ly = hy; } } if (lx < n2) { // optimization of: draw_line(target, lx,ly, n,ly, n2); for (j=lx; j < n2; ++j) LINE_OP(target[j], inverse_db_table[ly]); CHECK(f); } } return TRUE; } // The meaning of "left" and "right" // // For a given frame: // we compute samples from 0..n // window_center is n/2 // we'll window and mix the samples from left_start to left_end with data from the previous frame // all of the samples from left_end to right_start can be output without mixing; however, // this interval is 0-length except when transitioning between short and long frames // all of the samples from right_start to right_end need to be mixed with the next frame, // which we don't have, so those get saved in a buffer // frame N's right_end-right_start, the number of samples to mix with the next frame, // has to be the same as frame N+1's left_end-left_start (which they are by // construction) static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { Mode *m; int i, n, prev, next, window_center; f->channel_buffer_start = f->channel_buffer_end = 0; retry: if (f->eof) return FALSE; if (!maybe_start_packet(f)) return FALSE; // check packet type if (get_bits(f,1) != 0) { if (IS_PUSH_MODE(f)) return error(f,VORBIS_bad_packet_type); while (EOP != get8_packet(f)); goto retry; } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); i = get_bits(f, ilog(f->mode_count-1)); if (i == EOP) return FALSE; if (i >= f->mode_count) return FALSE; *mode = i; m = f->mode_config + i; if (m->blockflag) { n = f->blocksize_1; prev = get_bits(f,1); next = get_bits(f,1); } else { prev = next = 0; n = f->blocksize_0; } // WINDOWING window_center = n >> 1; if (m->blockflag && !prev) { *p_left_start = (n - f->blocksize_0) >> 2; *p_left_end = (n + f->blocksize_0) >> 2; } else { *p_left_start = 0; *p_left_end = window_center; } if (m->blockflag && !next) { *p_right_start = (n*3 - f->blocksize_0) >> 2; *p_right_end = (n*3 + f->blocksize_0) >> 2; } else { *p_right_start = window_center; *p_right_end = n; } return TRUE; } static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) { Mapping *map; int i,j,k,n,n2; int zero_channel[256]; int really_zero_channel[256]; // WINDOWING STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; // FLOORS n2 = n >> 1; CHECK(f); for (i=0; i < f->channels; ++i) { int s = map->chan[i].mux, floor; zero_channel[i] = FALSE; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; if (get_bits(f, 1)) { short *finalY; uint8 step2_flag[256]; static int range_list[4] = { 256, 128, 86, 64 }; int range = range_list[g->floor1_multiplier-1]; int offset = 2; finalY = f->finalY[i]; finalY[0] = get_bits(f, ilog(range)-1); finalY[1] = get_bits(f, ilog(range)-1); for (j=0; j < g->partitions; ++j) { int pclass = g->partition_class_list[j]; int cdim = g->class_dimensions[pclass]; int cbits = g->class_subclasses[pclass]; int csub = (1 << cbits)-1; int cval = 0; if (cbits) { Codebook *c = f->codebooks + g->class_masterbooks[pclass]; DECODE(cval,f,c); } for (k=0; k < cdim; ++k) { int book = g->subclass_books[pclass][cval & csub]; cval = cval >> cbits; if (book >= 0) { int temp; Codebook *c = f->codebooks + book; DECODE(temp,f,c); finalY[offset++] = temp; } else finalY[offset++] = 0; } } if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec step2_flag[0] = step2_flag[1] = 1; for (j=2; j < g->values; ++j) { int low, high, pred, highroom, lowroom, room, val; low = g->neighbors[j][0]; high = g->neighbors[j][1]; //neighbors(g->Xlist, j, &low, &high); pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); val = finalY[j]; highroom = range - pred; lowroom = pred; if (highroom < lowroom) room = highroom * 2; else room = lowroom * 2; if (val) { step2_flag[low] = step2_flag[high] = 1; step2_flag[j] = 1; if (val >= room) if (highroom > lowroom) finalY[j] = val - lowroom + pred; else finalY[j] = pred - val + highroom - 1; else if (val & 1) finalY[j] = pred - ((val+1)>>1); else finalY[j] = pred + (val>>1); } else { step2_flag[j] = 0; finalY[j] = pred; } } #ifdef STB_VORBIS_NO_DEFER_FLOOR do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); #else // defer final floor computation until _after_ residue for (j=0; j < g->values; ++j) { if (!step2_flag[j]) finalY[j] = -1; } #endif } else { error: zero_channel[i] = TRUE; } // So we just defer everything else to later // at this point we've decoded the floor into buffer } } CHECK(f); // at this point we've decoded all floors if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); // re-enable coupled channels if necessary memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); for (i=0; i < map->coupling_steps; ++i) if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; } CHECK(f); // RESIDUE DECODE for (i=0; i < map->submaps; ++i) { float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; int r; uint8 do_not_decode[256]; int ch = 0; for (j=0; j < f->channels; ++j) { if (map->chan[j].mux == i) { if (zero_channel[j]) { do_not_decode[ch] = TRUE; residue_buffers[ch] = NULL; } else { do_not_decode[ch] = FALSE; residue_buffers[ch] = f->channel_buffers[j]; } ++ch; } } r = map->submap_residue[i]; decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); CHECK(f); // INVERSE COUPLING for (i = map->coupling_steps-1; i >= 0; --i) { int n2 = n >> 1; float *m = f->channel_buffers[map->chan[i].magnitude]; float *a = f->channel_buffers[map->chan[i].angle ]; for (j=0; j < n2; ++j) { float a2,m2; if (m[j] > 0) if (a[j] > 0) m2 = m[j], a2 = m[j] - a[j]; else a2 = m[j], m2 = m[j] + a[j]; else if (a[j] > 0) m2 = m[j], a2 = m[j] + a[j]; else a2 = m[j], m2 = m[j] - a[j]; m[j] = m2; a[j] = a2; } } CHECK(f); // finish decoding the floors #ifndef STB_VORBIS_NO_DEFER_FLOOR for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); } } #else for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { for (j=0; j < n2; ++j) f->channel_buffers[i][j] *= f->floor_buffers[i][j]; } } #endif // INVERSE MDCT CHECK(f); for (i=0; i < f->channels; ++i) inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); CHECK(f); // this shouldn't be necessary, unless we exited on an error // and want to flush to get to the next packet flush_packet(f); if (f->first_decode) { // assume we start so first non-discarded sample is sample 0 // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; f->current_loc_valid = TRUE; f->first_decode = FALSE; } else if (f->discard_samples_deferred) { if (f->discard_samples_deferred >= right_start - left_start) { f->discard_samples_deferred -= (right_start - left_start); left_start = right_start; *p_left = left_start; } else { left_start += f->discard_samples_deferred; *p_left = left_start; f->discard_samples_deferred = 0; } } else if (f->previous_length == 0 && f->current_loc_valid) { // we're recovering from a seek... that means we're going to discard // the samples from this packet even though we know our position from // the last page header, so we need to update the position based on // the discarded samples here // but wait, the code below is going to add this in itself even // on a discard, so we don't need to do it here... } // check if we have ogg information about the sample # for this packet if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { // negative truncation, that's impossible! *len = 0; } else { *len = current_end - f->current_loc; } *len += left_start; // this doesn't seem right, but has no ill effect on my test files if (*len > right_end) *len = right_end; // this should never happen f->current_loc += *len; return TRUE; } } // otherwise, just set our sample loc // guess that the ogg granule pos refers to the _middle_ of the // last frame? // set f->current_loc to the position of left_start f->current_loc = f->known_loc_for_packet - (n2-left_start); f->current_loc_valid = TRUE; } if (f->current_loc_valid) f->current_loc += (right_start - left_start); if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); *len = right_end; // ignore samples after the window goes to 0 CHECK(f); return TRUE; } static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) { int mode, left_end, right_end; if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); } static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) { int prev,i,j; // we use right&left (the start of the right- and left-window sin()-regions) // to determine how much to return, rather than inferring from the rules // (same result, clearer code); 'left' indicates where our sin() window // starts, therefore where the previous window's right edge starts, and // therefore where to start mixing from the previous buffer. 'right' // indicates where our sin() ending-window starts, therefore that's where // we start saving, and where our returned-data ends. // mixin from previous window if (f->previous_length) { int i,j, n = f->previous_length; float *w = get_window(f, n); if (w == NULL) return 0; for (i=0; i < f->channels; ++i) { for (j=0; j < n; ++j) f->channel_buffers[i][left+j] = f->channel_buffers[i][left+j]*w[ j] + f->previous_window[i][ j]*w[n-1-j]; } } prev = f->previous_length; // last half of this data becomes previous window f->previous_length = len - right; // @OPTIMIZE: could avoid this copy by double-buffering the // output (flipping previous_window with channel_buffers), but // then previous_window would have to be 2x as large, and // channel_buffers couldn't be temp mem (although they're NOT // currently temp mem, they could be (unless we want to level // performance by spreading out the computation)) for (i=0; i < f->channels; ++i) for (j=0; right+j < len; ++j) f->previous_window[i][j] = f->channel_buffers[i][right+j]; if (!prev) // there was no previous packet, so this data isn't valid... // this isn't entirely true, only the would-have-overlapped data // isn't valid, but this seems to be what the spec requires return 0; // truncate a short frame if (len < right) right = len; f->samples_output += right-left; return right - left; } static int vorbis_pump_first_frame(stb_vorbis *f) { int len, right, left, res; res = vorbis_decode_packet(f, &len, &left, &right); if (res) vorbis_finish_frame(f, len, left, right); f->current_playback_loc = 0; f->current_playback_loc_valid = TRUE; return res; } #ifndef STB_VORBIS_NO_PUSHDATA_API static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream // instead of coding this out explicitly, we could save the current read state, // read the next packet with get8() until end-of-packet, check f->eof, then // reset the state? but that would be slower, esp. since we'd have over 256 bytes // of state to restore (primarily the page segment table) int s = f->next_seg, first = TRUE; uint8 *p = f->stream; if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag for (; s < f->segment_count; ++s) { p += f->segments[s]; if (f->segments[s] < 255) // stop at first short segment break; } // either this continues, or it ends it... if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } for (; s == -1;) { uint8 *q; int n; // check that we have the page header ready if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); // validate the page if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); if (p[4] != 0) return error(f, VORBIS_invalid_stream); if (first) { // the first segment must NOT have 'continued_packet', later ones MUST if (f->previous_length) if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); // if no previous length, we're resynching, so we can come in on a continued-packet, // which we'll just drop } else { if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); } n = p[26]; // segment counts q = p+27; // q points to segment table p = q + n; // advance past header // make sure we've read the segment table if (p > f->stream_end) return error(f, VORBIS_need_more_data); for (s=0; s < n; ++s) { p += q[s]; if (q[s] < 255) break; } if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } return TRUE; } #endif // !STB_VORBIS_NO_PUSHDATA_API static int start_decoder(vorb *f) { uint8 header[6], x,y; int len,i,j,k, max_submaps = 0; int longest_floorlist=0; // first page, first packet f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); if (f->segments[0] != 30) { // check for the Ogg skeleton fishead identifying header to refine our error if (f->segments[0] == 64 && getn(f, header, 6) && header[0] == 'f' && header[1] == 'i' && header[2] == 's' && header[3] == 'h' && header[4] == 'e' && header[5] == 'a' && get8(f) == 'd' && get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); else return error(f, VORBIS_invalid_first_page); } // read packet // check packet header if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); // vorbis_version if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); get32(f); // bitrate_maximum get32(f); // bitrate_nominal get32(f); // bitrate_minimum x = get8(f); { int log0,log1; log0 = x & 15; log1 = x >> 4; f->blocksize_0 = 1 << log0; f->blocksize_1 = 1 << log1; if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); if (log0 > log1) return error(f, VORBIS_invalid_setup); } // framing_flag x = get8(f); if (!(x & 1)) return error(f, VORBIS_invalid_first_page); // second packet! if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; #ifndef STB_VORBIS_NO_COMMENTS if (!next_segment(f)) return FALSE; if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); //file vendor len = get32_packet(f); f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); if (f->vendor == NULL) return error(f, VORBIS_outofmem); for(i=0; i < len; ++i) { f->vendor[i] = get8_packet(f); } f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); f->comment_list = NULL; if (f->comment_list_length > 0) { if (INT_MAX / sizeof(char*) < f->comment_list_length) goto no_comment; len = sizeof(char*) * f->comment_list_length; f->comment_list = (char**) setup_malloc(f, len); if (f->comment_list == NULL) { no_comment: f->comment_list_length = 0; return error(f, VORBIS_outofmem); } memset(f->comment_list, 0, len); } for(i=0; i < f->comment_list_length; ++i) { len = get32_packet(f); f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); for(j=0; j < len; ++j) { f->comment_list[i][j] = get8_packet(f); } f->comment_list[i][len] = (char)'\0'; } // framing_flag x = get8_packet(f); if (!(x & 1)) return error(f, VORBIS_invalid_setup); skip(f, f->bytes_in_seg); f->bytes_in_seg = 0; #endif // STB_VORBIS_NO_COMMENTS do { len = next_segment(f); skip(f, len); f->bytes_in_seg = 0; } while (len); // third packet! if (!start_packet(f)) return FALSE; #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; return FALSE; } } #endif crc32_init(); // always init it, to avoid multithread race conditions if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); // codebooks f->codebook_count = get_bits(f,8) + 1; if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); if (f->codebooks == NULL) return error(f, VORBIS_outofmem); memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); for (i=0; i < f->codebook_count; ++i) { uint32 *values; int ordered, sorted_count; int total=0; uint8 *lengths; Codebook *c = f->codebooks+i; CHECK(f); x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); c->dimensions = (get_bits(f, 8)<<8) + x; x = get_bits(f, 8); y = get_bits(f, 8); c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; ordered = get_bits(f,1); c->sparse = ordered ? 0 : get_bits(f,1); if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (c->sparse) { lengths = (uint8 *) setup_temp_malloc(f, c->entries); f->temp_lengths = lengths; } else lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (!lengths) return error(f, VORBIS_outofmem); if (ordered) { int current_entry = 0; int current_length = get_bits(f,5) + 1; while (current_entry < c->entries) { int limit = c->entries - current_entry; int n = get_bits(f, ilog(limit)); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (current_length >= 32) return error(f, VORBIS_invalid_setup); if (current_entry + n > (int) c->entries) return error(f, VORBIS_invalid_setup); memset(lengths + current_entry, current_length, n); current_entry += n; ++current_length; } } else { for (j=0; j < c->entries; ++j) { int present = c->sparse ? get_bits(f,1) : 1; if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (present) { lengths[j] = get_bits(f, 5) + 1; ++total; if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); } else { lengths[j] = NO_CODE; } } } if (c->sparse && total >= c->entries >> 2) { // convert sparse items to non-sparse! if (c->entries > (int) f->setup_temp_memory_required) f->setup_temp_memory_required = c->entries; c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); memcpy(c->codeword_lengths, lengths, c->entries); setup_temp_free(f, &f->temp_lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! lengths = c->codeword_lengths; c->sparse = 0; } // compute the size of the sorted tables if (c->sparse) { sorted_count = total; } else { sorted_count = 0; #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH for (j=0; j < c->entries; ++j) if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) ++sorted_count; #endif } c->sorted_entries = sorted_count; values = NULL; CHECK(f); if (!c->sparse) { c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); if (!c->codewords) return error(f, VORBIS_outofmem); } else { unsigned int size; if (c->sorted_entries) { c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); if (!c->codeword_lengths) return error(f, VORBIS_outofmem); c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); f->temp_codewords = c->codewords; if (!c->codewords) return error(f, VORBIS_outofmem); values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); f->temp_values = values; if (!values) return error(f, VORBIS_outofmem); } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; if (size > f->setup_temp_memory_required) f->setup_temp_memory_required = size; } if (!compute_codewords(c, lengths, c->entries, values)) { return error(f, VORBIS_invalid_setup); } if (c->sorted_entries) { // allocate an extra slot for sentinels c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); // allocate an extra slot at the front so that c->sorted_values[-1] is defined // so that we can catch that case without an extra if c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); ++c->sorted_values; c->sorted_values[-1] = -1; compute_sorted_huffman(c, lengths, values); } if (c->sparse) { setup_temp_free(f, &f->temp_values, sizeof(*values)*c->sorted_entries); setup_temp_free(f, &f->temp_codewords, sizeof(*c->codewords)*c->sorted_entries); setup_temp_free(f, &f->temp_lengths, c->entries); c->codewords = NULL; } compute_accelerated_huffman(c); CHECK(f); c->lookup_type = get_bits(f, 4); if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); if (c->lookup_type > 0) { uint16 *mults; c->minimum_value = float32_unpack(get_bits(f, 32)); c->delta_value = float32_unpack(get_bits(f, 32)); c->value_bits = get_bits(f, 4)+1; c->sequence_p = get_bits(f,1); if (c->lookup_type == 1) { int values = lookup1_values(c->entries, c->dimensions); if (values < 0) return error(f, VORBIS_invalid_setup); c->lookup_values = (uint32) values; } else { /* unsigned multiply to suppress (legitimate) warning. * https://github.com/nothings/stb/issues/1168 */ c->lookup_values = (unsigned)c->entries * (unsigned)c->dimensions; } if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); f->temp_mults = mults; if (mults == NULL) return error(f, VORBIS_outofmem); for (j=0; j < (int) c->lookup_values; ++j) { int q = get_bits(f, c->value_bits); if (f->valid_bits < 0) return error(f, VORBIS_invalid_setup); mults[j] = q; } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int len, sparse = c->sparse; float last=0; // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop if (sparse) { if (c->sorted_entries == 0) goto skip; c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); } else c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); len = sparse ? c->sorted_entries : c->entries; for (j=0; j < len; ++j) { unsigned int z = sparse ? c->sorted_values[j] : j; unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; if (k+1 < c->dimensions) { if (div > UINT_MAX / (unsigned int) c->lookup_values) { return error(f, VORBIS_invalid_setup); } div *= c->lookup_values; } } } c->lookup_type = 2; } else #endif { float last=0; CHECK(f); c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); for (j=0; j < (int) c->lookup_values; ++j) { float val = mults[j] * c->delta_value + c->minimum_value + last; c->multiplicands[j] = val; if (c->sequence_p) last = val; } } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK skip:; #endif setup_temp_free(f, &f->temp_mults, sizeof(mults[0])*c->lookup_values); CHECK(f); } CHECK(f); } // time domain transfers (notused) x = get_bits(f, 6) + 1; for (i=0; i < x; ++i) { uint32 z = get_bits(f, 16); if (z != 0) return error(f, VORBIS_invalid_setup); } // Floors f->floor_count = get_bits(f, 6)+1; if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); if (f->floor_config == NULL) return error(f, VORBIS_outofmem); for (i=0; i < f->floor_count; ++i) { f->floor_types[i] = get_bits(f, 16); if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); if (f->floor_types[i] == 0) { Floor0 *g = &f->floor_config[i].floor0; g->order = get_bits(f,8); g->rate = get_bits(f,16); g->bark_map_size = get_bits(f,16); g->amplitude_bits = get_bits(f,6); g->amplitude_offset = get_bits(f,8); g->number_of_books = get_bits(f,4) + 1; for (j=0; j < g->number_of_books; ++j) g->book_list[j] = get_bits(f,8); return error(f, VORBIS_feature_not_supported); } else { stbv__floor_ordering p[31*8+2]; Floor1 *g = &f->floor_config[i].floor1; int max_class = -1; g->partitions = get_bits(f, 5); for (j=0; j < g->partitions; ++j) { g->partition_class_list[j] = get_bits(f, 4); if (g->partition_class_list[j] > max_class) max_class = g->partition_class_list[j]; } for (j=0; j <= max_class; ++j) { g->class_dimensions[j] = get_bits(f, 3)+1; g->class_subclasses[j] = get_bits(f, 2); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (g->class_subclasses[j]) { g->class_masterbooks[j] = get_bits(f, 8); if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } g->floor1_multiplier = get_bits(f,2)+1; g->rangebits = get_bits(f,4); g->Xlist[0] = 0; g->Xlist[1] = 1 << g->rangebits; g->values = 2; for (j=0; j < g->partitions; ++j) { int c = g->partition_class_list[j]; for (k=0; k < g->class_dimensions[c]; ++k) { g->Xlist[g->values] = get_bits(f, g->rangebits); ++g->values; } } // precompute the sorting for (j=0; j < g->values; ++j) { p[j].x = g->Xlist[j]; p[j].id = j; } qsort(p, g->values, sizeof(p[0]), point_compare); for (j=0; j < g->values-1; ++j) if (p[j].x == p[j+1].x) return error(f, VORBIS_invalid_setup); for (j=0; j < g->values; ++j) g->sorted_order[j] = (uint8) p[j].id; // precompute the neighbors for (j=2; j < g->values; ++j) { int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; } if (g->values > longest_floorlist) longest_floorlist = g->values; } } // Residue f->residue_count = get_bits(f, 6)+1; if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); if (f->residue_config == NULL) return error(f, VORBIS_outofmem); memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); for (i=0; i < f->residue_count; ++i) { uint8 residue_cascade[64]; Residue *r = f->residue_config+i; f->residue_types[i] = get_bits(f, 16); if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); r->begin = get_bits(f, 24); r->end = get_bits(f, 24); if (r->end < r->begin) return error(f, VORBIS_invalid_setup); r->part_size = get_bits(f,24)+1; r->classifications = get_bits(f,6)+1; r->classbook = get_bits(f,8); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); for (j=0; j < r->classifications; ++j) { uint8 high_bits=0; uint8 low_bits=get_bits(f,3); if (get_bits(f,1)) high_bits = get_bits(f,5); residue_cascade[j] = high_bits*8 + low_bits; } if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); if (r->residue_books == NULL) return error(f, VORBIS_outofmem); for (j=0; j < r->classifications; ++j) { for (k=0; k < 8; ++k) { if (residue_cascade[j] & (1 << k)) { r->residue_books[j][k] = get_bits(f, 8); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } else { r->residue_books[j][k] = -1; } } } // precompute the classifications[] array to avoid inner-loop mod/divide // call it 'classdata' since we already have r->classifications r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); if (!r->classdata) return error(f, VORBIS_outofmem); memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); for (j=0; j < f->codebooks[r->classbook].entries; ++j) { int classwords = f->codebooks[r->classbook].dimensions; int temp = j; r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); for (k=classwords-1; k >= 0; --k) { r->classdata[j][k] = temp % r->classifications; temp /= r->classifications; } } } f->mapping_count = get_bits(f,6)+1; if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); if (f->mapping == NULL) return error(f, VORBIS_outofmem); memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); for (i=0; i < f->mapping_count; ++i) { Mapping *m = f->mapping + i; int mapping_type = get_bits(f,16); if (mapping_type != 0) return error(f, VORBIS_invalid_setup); m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); if (m->chan == NULL) return error(f, VORBIS_outofmem); if (get_bits(f,1)) m->submaps = get_bits(f,4)+1; else m->submaps = 1; if (m->submaps > max_submaps) max_submaps = m->submaps; if (get_bits(f,1)) { m->coupling_steps = get_bits(f,8)+1; if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); for (k=0; k < m->coupling_steps; ++k) { m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); m->chan[k].angle = get_bits(f, ilog(f->channels-1)); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); } } else m->coupling_steps = 0; // reserved field if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); if (m->submaps > 1) { for (j=0; j < f->channels; ++j) { m->chan[j].mux = get_bits(f, 4); if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); } } else // @SPECIFICATION: this case is missing from the spec for (j=0; j < f->channels; ++j) m->chan[j].mux = 0; for (j=0; j < m->submaps; ++j) { get_bits(f,8); // discard m->submap_floor[j] = get_bits(f,8); m->submap_residue[j] = get_bits(f,8); if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); } } // Modes f->mode_count = get_bits(f, 6)+1; for (i=0; i < f->mode_count; ++i) { Mode *m = f->mode_config+i; m->blockflag = get_bits(f,1); m->windowtype = get_bits(f,16); m->transformtype = get_bits(f,16); m->mapping = get_bits(f,8); if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); } flush_packet(f); f->previous_length = 0; for (i=0; i < f->channels; ++i) { f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); #endif } if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; f->blocksize[0] = f->blocksize_0; f->blocksize[1] = f->blocksize_1; #ifdef STB_VORBIS_DIVIDE_TABLE if (integer_divide_table[1][1]==0) for (i=0; i < DIVTAB_NUMER; ++i) for (j=1; j < DIVTAB_DENOM; ++j) integer_divide_table[i][j] = i / j; #endif // compute how much temporary memory is needed // 1. { uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); uint32 classify_mem; int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; unsigned int rtype = f->residue_types[i]; unsigned int actual_size = rtype == 2 ? f->blocksize_1 : f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; if (part_read > max_part_read) max_part_read = part_read; } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); #else classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif // maximum reasonable partition size is f->blocksize_1 f->temp_memory_required = classify_mem; if (imdct_mem > f->temp_memory_required) f->temp_memory_required = imdct_mem; } if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) return error(f, VORBIS_outofmem); } else { f->work_buffer = setup_malloc(f, f->temp_memory_required); if (f->work_buffer == NULL) return error(f, VORBIS_outofmem); } // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page // without PAGEFLAG_continued_packet, so this either points to the first page, or // the page after the end of the headers. It might be cleaner to point to a page // in the middle of the headers, when that's the page where the first audio packet // starts, but we'd have to also correctly skip the end of any continued packet in // stb_vorbis_seek_start. if (f->next_seg == -1) { f->first_audio_page_offset = stb_vorbis_get_file_offset(f); } else { f->first_audio_page_offset = 0; } return TRUE; } static void vorbis_deinit(stb_vorbis *p) { int i,j; #ifndef STB_VORBIS_NO_COMMENTS setup_free(p, p->vendor); for (i=0; i < p->comment_list_length; ++i) { setup_free(p, p->comment_list[i]); } setup_free(p, p->comment_list); #endif if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { Residue *r = p->residue_config+i; if (r->classdata) { for (j=0; j < p->codebooks[r->classbook].entries; ++j) setup_free(p, r->classdata[j]); setup_free(p, r->classdata); } setup_free(p, r->residue_books); } } if (p->codebooks) { CHECK(p); for (i=0; i < p->codebook_count; ++i) { Codebook *c = p->codebooks + i; setup_free(p, c->codeword_lengths); setup_free(p, c->multiplicands); if (c->codewords != p->temp_codewords) // Might be the temporary buffer-allocated array. setup_free(p, c->codewords); setup_free(p, c->sorted_codewords); // c->sorted_values[-1] is the first entry in the array setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); } setup_free(p, p->codebooks); } setup_free(p, p->floor_config); setup_free(p, p->residue_config); if (p->mapping) { for (i=0; i < p->mapping_count; ++i) setup_free(p, p->mapping[i].chan); setup_free(p, p->mapping); } CHECK(p); for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { setup_free(p, p->channel_buffers[i]); setup_free(p, p->previous_window[i]); #ifdef STB_VORBIS_NO_DEFER_FLOOR setup_free(p, p->floor_buffers[i]); #endif setup_free(p, p->finalY[i]); } for (i=0; i < 2; ++i) { setup_free(p, p->A[i]); setup_free(p, p->B[i]); setup_free(p, p->C[i]); setup_free(p, p->window[i]); setup_free(p, p->bit_reverse[i]); } if (!p->alloc.alloc_buffer) { setup_free(p, p->work_buffer); setup_temp_free(p, &p->temp_lengths, 0); setup_temp_free(p, &p->temp_codewords, 0); setup_temp_free(p, &p->temp_values, 0); setup_temp_free(p, &p->temp_mults, 0); } #ifdef STB_VORBIS_SDL if (p->close_on_free) SDL_RWclose(p->rwops); #endif #ifndef STB_VORBIS_NO_STDIO if (p->close_on_free) fclose(p->f); #endif } void stb_vorbis_close(stb_vorbis *p) { if (p == NULL) return; vorbis_deinit(p); setup_free(p,p); } static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; p->error = VORBIS__no_error; p->stream = NULL; p->codebooks = NULL; p->page_crc_tests = -1; #ifdef STB_VORBIS_SDL p->close_on_free = FALSE; p->rwops = NULL; #endif #ifndef STB_VORBIS_NO_STDIO p->close_on_free = FALSE; p->f = NULL; #endif } int stb_vorbis_get_sample_offset(stb_vorbis *f) { if (f->current_loc_valid) return f->current_loc; else return -1; } int stb_vorbis_get_playback_sample_offset(stb_vorbis *f) { if (f->current_playback_loc_valid) return f->current_playback_loc; else return -1; } stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { stb_vorbis_info d; d.channels = f->channels; d.sample_rate = f->sample_rate; d.setup_memory_required = f->setup_memory_required; d.setup_temp_memory_required = f->setup_temp_memory_required; d.temp_memory_required = f->temp_memory_required; d.max_frame_size = f->blocksize_1 >> 1; return d; } #ifndef STB_VORBIS_NO_COMMENTS stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { stb_vorbis_comment d; d.vendor = f->vendor; d.comment_list_length = f->comment_list_length; d.comment_list = f->comment_list; return d; } #endif int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; return e; } static stb_vorbis * vorbis_alloc(stb_vorbis *f) { stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); return p; } #ifndef STB_VORBIS_NO_PUSHDATA_API void stb_vorbis_flush_pushdata(stb_vorbis *f) { f->previous_length = 0; f->page_crc_tests = 0; f->discard_samples_deferred = 0; f->current_loc_valid = FALSE; f->first_decode = FALSE; f->samples_output = 0; f->channel_buffer_start = 0; f->channel_buffer_end = 0; } static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) { int i,n; for (i=0; i < f->page_crc_tests; ++i) f->scan[i].bytes_done = 0; // if we have room for more scans, search for them first, because // they may cause us to stop early if their header is incomplete if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { if (data_len < 4) return 0; data_len -= 3; // need to look for 4-byte sequence, so don't miss // one that straddles a boundary for (i=0; i < data_len; ++i) { if (data[i] == 0x4f) { if (0==memcmp(data+i, ogg_page_header, 4)) { int j,len; uint32 crc; // make sure we have the whole page header if (i+26 >= data_len || i+27+data[i+26] >= data_len) { // only read up to this page start, so hopefully we'll // have the whole page header start next time data_len = i; break; } // ok, we have it all; compute the length of the page len = 27 + data[i+26]; for (j=0; j < data[i+26]; ++j) len += data[i+27+j]; // scan everything up to the embedded crc (which we must 0) crc = 0; for (j=0; j < 22; ++j) crc = crc32_update(crc, data[i+j]); // now process 4 0-bytes for ( ; j < 26; ++j) crc = crc32_update(crc, 0); // len is the total number of bytes we need to scan n = f->page_crc_tests++; f->scan[n].bytes_left = len-j; f->scan[n].crc_so_far = crc; f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); // if the last frame on a page is continued to the next, then // we can't recover the sample_loc immediately if (data[i+27+data[i+26]-1] == 255) f->scan[n].sample_loc = ~0; else f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); f->scan[n].bytes_done = i+j; if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) break; // keep going if we still have room for more } } } } for (i=0; i < f->page_crc_tests;) { uint32 crc; int j; int n = f->scan[i].bytes_done; int m = f->scan[i].bytes_left; if (m > data_len - n) m = data_len - n; // m is the bytes to scan in the current chunk crc = f->scan[i].crc_so_far; for (j=0; j < m; ++j) crc = crc32_update(crc, data[n+j]); f->scan[i].bytes_left -= m; f->scan[i].crc_so_far = crc; if (f->scan[i].bytes_left == 0) { // does it match? if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { // Houston, we have page data_len = n+m; // consumption amount is wherever that scan ended f->page_crc_tests = -1; // drop out of page scan mode f->previous_length = 0; // decode-but-don't-output one frame f->next_seg = -1; // start a new page f->current_loc = f->scan[i].sample_loc; // set the current sample location // to the amount we'd have decoded had we decoded this page f->current_loc_valid = f->current_loc != ~0U; return data_len; } // delete entry f->scan[i] = f->scan[--f->page_crc_tests]; } else { ++i; } } return data_len; } // return value: number of bytes we used int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, // the file we're decoding const uint8 *data, int data_len, // the memory available for decoding int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ) { int i; int len,right,left; if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (f->page_crc_tests >= 0) { *samples = 0; return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); } f->stream = (uint8 *) data; f->stream_end = (uint8 *) data + data_len; f->error = VORBIS__no_error; // check that we have the entire packet in memory if (!is_whole_packet_present(f)) { *samples = 0; return 0; } if (!vorbis_decode_packet(f, &len, &left, &right)) { // save the actual error we encountered enum STBVorbisError error = f->error; if (error == VORBIS_bad_packet_type) { // flush and resynch f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } if (error == VORBIS_continued_packet_flag_invalid) { if (f->previous_length == 0) { // we may be resynching, in which case it's ok to hit one // of these; just discard the packet f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } } // if we get an error while parsing, what to do? // well, it DEFINITELY won't work to continue from where we are! stb_vorbis_flush_pushdata(f); // restore the error that actually made us bail f->error = error; *samples = 0; return 1; } // success! len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; if (channels) *channels = f->channels; *samples = len; *output = f->outputs; return (int) (f->stream - data); } stb_vorbis *stb_vorbis_open_pushdata( const unsigned char *data, int data_len, // the memory available for decoding int *data_used, // only defined if result is not NULL int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + data_len; p.push_mode = TRUE; if (!start_decoder(&p)) { if (p.eof) *error = VORBIS_need_more_data; else *error = p.error; vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); if (f) { *f = p; *data_used = (int) (f->stream - data); *error = 0; return f; } else { vorbis_deinit(&p); return NULL; } } #endif // STB_VORBIS_NO_PUSHDATA_API unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif #ifdef STB_VORBIS_SDL return (unsigned int) (SDL_RWtell(f->rwops) - f->rwops_start); #else if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); #endif #ifndef STB_VORBIS_NO_STDIO return (unsigned int) (ftell(f->f) - f->f_start); #endif } #ifndef STB_VORBIS_NO_PULLDATA_API // // DATA-PULLING API // static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { for(;;) { int n; if (f->eof) return 0; n = get8(f); if (n == 0x4f) { // page header candidate unsigned int retry_loc = stb_vorbis_get_file_offset(f); int i; // check if we're off the end of a file_section stream if (retry_loc - 25 > f->stream_len) return 0; // check the rest of the header for (i=1; i < 4; ++i) if (get8(f) != ogg_page_header[i]) break; if (f->eof) return 0; if (i == 4) { uint8 header[27]; uint32 i, crc, goal, len; for (i=0; i < 4; ++i) header[i] = ogg_page_header[i]; for (; i < 27; ++i) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; for (i=0; i < 27; ++i) crc = crc32_update(crc, header[i]); len = 0; for (i=0; i < header[26]; ++i) { int s = get8(f); crc = crc32_update(crc, s); len += s; } if (len && f->eof) return 0; for (i=0; i < len; ++i) crc = crc32_update(crc, get8(f)); // finished parsing probable page if (crc == goal) { // we could now check that it's either got the last // page flag set, OR it's followed by the capture // pattern, but I guess TECHNICALLY you could have // a file with garbage between each ogg page and recover // from it automatically? So even though that paranoia // might decrease the chance of an invalid decode by // another 2^32, not worth it since it would hose those // invalid-but-useful files? if (end) *end = stb_vorbis_get_file_offset(f); if (last) { if (header[5] & 0x04) *last = 1; else *last = 0; } set_file_offset(f, retry_loc-1); return 1; } } invalid: // not a valid page, so rewind and look for next one set_file_offset(f, retry_loc); } } } #define SAMPLE_unknown 0xffffffff // seeking is implemented with a binary search, which narrows down the range to // 64K, before using a linear search (because finding the synchronization // pattern can be expensive, and the chance we'd find the end page again is // relatively high for small ranges) // // two initial interpolation-style probes are used at the start of the search // to try to bound either side of the binary search sensibly, while still // working in O(log n) time if they fail. static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) { uint8 header[27], lacing[255]; int i,len; // record where the page starts z->page_start = stb_vorbis_get_file_offset(f); // parse the header if (!getn(f, header, 27)) return 0; if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') return 0; if (!getn(f, lacing, header[26])) return 0; // determine the length of the payload len = 0; for (i=0; i < header[26]; ++i) len += lacing[i]; // this implies where the page ends z->page_end = z->page_start + 27 + header[26] + len; // read the last-decoded sample out of the data z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); // restore file state to where we were set_file_offset(f, z->page_start); return 1; } // rarely used function to seek back to the preceding page while finding the // start of a packet static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { unsigned int previous_safe; uint32 end; // now we want to seek back 64K from the limit if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) previous_safe = limit_offset - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); while (vorbis_find_page(f, &end, NULL)) { if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) return 1; set_file_offset(f, end); } return 0; } // implements the search logic for finding a page and starting decoding. if // the function succeeds, current_loc_valid will be true and current_loc will // be less than or equal to the provided sample number (the closer the // better). static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; uint32 delta, stream_length, padding, last_sample_limit; double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample stream_length = stb_vorbis_stream_length_in_samples(f); if (stream_length == 0) return error(f, VORBIS_seek_without_length); if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); // this is the maximum difference between the window-center (which is the // actual granule position value), and the right-start (which the spec // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) last_sample_limit = 0; else last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { // (untested) the first page does not have a 'last_decoded_sample' set_file_offset(f, left.page_end); if (!get_seek_page_info(f, &left)) goto error; } right = f->p_last; assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently if (last_sample_limit <= left.last_decoded_sample) { if (stb_vorbis_seek_start(f)) { if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; } return 0; } while (left.page_end != right.page_start) { assert(left.page_end < right.page_start); // search range in bytes delta = right.page_start - left.page_end; if (delta <= 65536) { // there's only 64K left to search - handle it linearly set_file_offset(f, left.page_end); } else { if (probe < 2) { if (probe == 0) { // first probe (interpolate) double data_bytes = right.page_end - left.page_start; bytes_per_sample = data_bytes / right.last_decoded_sample; offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; } // ensure the offset is valid if (offset < left.page_end) offset = left.page_end; if (offset > right.page_start - 65536) offset = right.page_start - 65536; set_file_offset(f, (unsigned int) offset); } else { // binary search for large ranges (offset by 32K to ensure // we don't hit the right page) set_file_offset(f, left.page_end + (delta / 2) - 32768); } if (!vorbis_find_page(f, NULL, NULL)) goto error; } for (;;) { if (!get_seek_page_info(f, &mid)) goto error; if (mid.last_decoded_sample != ~0U) break; // (untested) no frames end on this page set_file_offset(f, mid.page_end); assert(mid.page_start < right.page_start); } // if we've just found the last page again then we're in a tricky file, // and we're close enough (if it wasn't an interpolation probe). if (mid.page_start == right.page_start) { if (probe >= 2 || delta <= 65536) break; } else { if (last_sample_limit < mid.last_decoded_sample) right = mid; else left = mid; } ++probe; } // seek back to start of the last packet page_start = left.page_start; set_file_offset(f, page_start); if (!start_page(f)) return error(f, VORBIS_seek_failed); end_pos = f->end_seg_with_known_loc; assert(end_pos >= 0); for (;;) { for (i = end_pos; i > 0; --i) if (f->segments[i-1] != 255) break; start_seg_with_known_loc = i; if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) break; // (untested) the final packet begins on an earlier page if (!go_to_page_before(f, page_start)) goto error; page_start = stb_vorbis_get_file_offset(f); if (!start_page(f)) goto error; end_pos = f->segment_count - 1; } // prepare to start decoding f->current_loc_valid = FALSE; f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; f->previous_length = 0; f->next_seg = start_seg_with_known_loc; for (i = 0; i < start_seg_with_known_loc; i++) skip(f, f->segments[i]); // start decoding (optimizable - this frame is generally discarded) if (!vorbis_pump_first_frame(f)) return 0; if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; error: // try to restore the file to a valid state stb_vorbis_seek_start(f); return error(f, VORBIS_seek_failed); } // the same as vorbis_decode_initial, but without advancing static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { int bits_read, bytes_read; if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) return 0; // either 1 or 2 bytes were read, figure out which so we can rewind bits_read = 1 + ilog(f->mode_count-1); if (f->mode_config[*mode].blockflag) bits_read += 2; bytes_read = (bits_read + 7) / 8; f->bytes_in_seg += bytes_read; f->packet_bytes -= bytes_read; skip(f, -bytes_read); if (f->next_seg == -1) f->next_seg = f->segment_count - 1; else f->next_seg--; f->valid_bits = 0; return 1; } int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { uint32 max_frame_samples; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); // fast page-level search if (!seek_to_sample_coarse(f, sample_number)) return 0; assert(f->current_loc_valid); assert(f->current_loc <= sample_number); // linear search for the relevant packet max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; while (f->current_loc < sample_number) { int left_start, left_end, right_start, right_end, mode, frame_samples; if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) return error(f, VORBIS_seek_failed); // calculate the number of samples returned by the next frame frame_samples = right_start - left_start; if (f->current_loc + frame_samples > sample_number) { return 1; // the next frame will contain the sample } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { // there's a chance the frame after this could contain the sample vorbis_pump_first_frame(f); } else { // this frame is too early to be relevant f->current_loc += frame_samples; f->previous_length = 0; maybe_start_packet(f); flush_packet(f); } } // the next frame should start with the sample if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); f->current_playback_loc = sample_number; return 1; } int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) { if (!stb_vorbis_seek_frame(f, sample_number)) { f->current_playback_loc_valid = FALSE; return 0; } if (sample_number != f->current_loc) { int n; uint32 frame_start = f->current_loc; stb_vorbis_get_frame_float(f, &n, NULL); assert(sample_number > frame_start); assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); f->channel_buffer_start += (sample_number - frame_start); } f->current_playback_loc_valid = TRUE; f->current_playback_loc = sample_number; return 1; } int stb_vorbis_seek_start(stb_vorbis *f) { if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } set_file_offset(f, f->first_audio_page_offset); f->previous_length = 0; f->first_decode = TRUE; f->next_seg = -1; return vorbis_pump_first_frame(f); } unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { unsigned int restore_offset, previous_safe; unsigned int last_page_loc; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { uint32 end,last; uint32 lo,hi; char header[6]; // first, store the current decode position so we can restore it restore_offset = stb_vorbis_get_file_offset(f); // now we want to seek back 64K from the end (the last page must // be at most a little less than 64K, but let's allow a little slop) if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) previous_safe = f->stream_len - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); // previous_safe is now our candidate 'earliest known place that seeking // to will lead to the final page' if (!vorbis_find_page(f, &end, &last)) { // if we can't find a page, we're hosed! f->error = VORBIS_cant_find_last_page; f->total_samples = 0xffffffff; goto done; } // check if there are more pages last_page_loc = stb_vorbis_get_file_offset(f); // stop when the last_page flag is set, not when we reach eof; // this allows us to stop short of a 'file_section' end without // explicitly checking the length of the section while (!last) { set_file_offset(f, end); if (!vorbis_find_page(f, &end, &last)) { // the last page we found didn't have the 'last page' flag // set. whoops! break; } //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } set_file_offset(f, last_page_loc); // parse the header getn(f, (unsigned char *)header, 6); // extract the absolute granule position lo = get32(f); hi = get32(f); if (lo == 0xffffffff && hi == 0xffffffff) { f->error = VORBIS_cant_find_last_page; f->total_samples = SAMPLE_unknown; goto done; } if (hi) lo = 0xfffffffe; // saturate f->total_samples = lo; f->p_last.page_start = last_page_loc; f->p_last.page_end = end; f->p_last.last_decoded_sample = lo; done: set_file_offset(f, restore_offset); } return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; } float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) { return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; } int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) { int len, right,left,i; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!vorbis_decode_packet(f, &len, &left, &right)) { f->channel_buffer_start = f->channel_buffer_end = 0; return 0; } len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; f->channel_buffer_start = left; f->channel_buffer_end = left+len; if (channels) *channels = f->channels; if (output) *output = f->outputs; return len; } #ifndef STB_VORBIS_NO_STDIO stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.f = file; p.f_start = (uint32) ftell(file); p.stream_len = length; p.close_on_free = close_on_free; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { *f = p; vorbis_pump_first_frame(f); return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { unsigned int len, start; start = (unsigned int) ftell(file); fseek(file, 0, SEEK_END); len = (unsigned int) (ftell(file) - start); fseek(file, start, SEEK_SET); return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { FILE *f; #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) if (0 != fopen_s(&f, filename, "rb")) f = NULL; #else f = fopen(filename, "rb"); #endif if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; } #endif // STB_VORBIS_NO_STDIO #ifdef STB_VORBIS_SDL stb_vorbis * stb_vorbis_open_rwops_section(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.rwops = rwops; p.rwops_start = (uint32) SDL_RWtell(rwops); p.stream_len = length; p.close_on_free = close_on_free; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { memcpy(f, &p, sizeof (stb_vorbis)); vorbis_pump_first_frame(f); return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } stb_vorbis * stb_vorbis_open_rwops(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { const unsigned int start = (unsigned int) SDL_RWtell(rwops); const unsigned int len = (unsigned int) (SDL_RWsize(rwops) - start); return stb_vorbis_open_rwops_section(rwops, close_on_free, error, alloc, len); } #endif stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; if (!data) { if (error) *error = VORBIS_unexpected_eof; return NULL; } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; p.stream_start = (uint8 *) p.stream; p.stream_len = len; p.push_mode = FALSE; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { memcpy(f, &p, sizeof (stb_vorbis)); vorbis_pump_first_frame(f); if (error) *error = VORBIS__no_error; return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #define PLAYBACK_MONO 1 #define PLAYBACK_LEFT 2 #define PLAYBACK_RIGHT 4 #define L (PLAYBACK_LEFT | PLAYBACK_MONO) #define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) #define R (PLAYBACK_RIGHT | PLAYBACK_MONO) static int8 channel_position[7][6] = { { 0 }, { C }, { L, R }, { L, C, R }, { L, R, L, R }, { L, C, R, L, R }, { L, C, R, L, R, C }, }; #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT typedef union { float f; // changed this to unsigned to suppress an UBSan error. // upstream: https://github.com/nothings/stb/issues/1168. unsigned int i; } float_conv; typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; #define FASTDEF(x) float_conv x // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (int)(temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) #define check_endianness() #else #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) #define check_endianness() #define FASTDEF(x) #endif static void copy_samples(short *dest, float *src, int len) { int i; check_endianness(); for (i=0; i < len; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); if ((unsigned int)v + 32768 > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { #define STB_BUFFER_SIZE 32 float buffer[STB_BUFFER_SIZE]; int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { if (channel_position[num_c][j] & mask) { for (i=0; i < n; ++i) buffer[i] += data[j][d_offset+o+i]; } } for (i=0; i < n; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int)v + 32768 > 65535) v = v < 0 ? -32768 : 32767; output[o+i] = v; } } #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { #define STB_BUFFER_SIZE 32 float buffer[STB_BUFFER_SIZE]; int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; buffer[i*2+1] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_LEFT) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_RIGHT) { for (i=0; i < n; ++i) { buffer[i*2+1] += data[j][d_offset+o+i]; } } } for (i=0; i < (n<<1); ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int)v + 32768 > 65535) v = v < 0 ? -32768 : 32767; output[o2+i] = v; } } #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) { int i; if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; for (i=0; i < buf_c; ++i) compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); } else { int limit = buf_c < data_c ? buf_c : data_c; for (i=0; i < limit; ++i) copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); for ( ; i < buf_c; ++i) memset(buffer[i]+b_offset, 0, sizeof(short) * samples); } } int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); return len; } static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) { int i; check_endianness(); if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { assert(buf_c == 2); for (i=0; i < buf_c; ++i) compute_stereo_samples(buffer, data_c, data, d_offset, len); } else { int limit = buf_c < data_c ? buf_c : data_c; int j; for (j=0; j < len; ++j) { for (i=0; i < limit; ++i) { FASTDEF(temp); float f = data[i][d_offset+j]; int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); if ((unsigned int)v + 32768 > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; } for ( ; i < buf_c; ++i) *buffer++ = 0; } } } int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) { float **output; int len; if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); len = stb_vorbis_get_frame_float(f, NULL, &output); if (len) { if (len*num_c > num_shorts) len = num_shorts / num_c; convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); } return len; } int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) { float **outputs; int len = num_shorts / channels; int n=0; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); buffer += k*channels; n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } f->current_playback_loc += n; return n; } int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n=0; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } f->current_playback_loc += n; return n; } #ifndef STB_VORBIS_NO_STDIO int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // NO_STDIO int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // STB_VORBIS_NO_INTEGER_CONVERSION int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { float **outputs; int len = num_floats / channels; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < len) { int i,j; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; for (j=0; j < k; ++j) { for (i=0; i < z; ++i) *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; for ( ; i < channels; ++i) *buffer++ = 0; } n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } f->current_playback_loc += n; return n; } int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) { float **outputs; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < num_samples) { int i; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= num_samples) k = num_samples - n; if (k) { for (i=0; i < z; ++i) memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); for ( ; i < channels; ++i) memset(buffer[i]+n, 0, sizeof(float) * k); } n += k; f->channel_buffer_start += k; if (n == num_samples) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } f->current_playback_loc += n; return n; } #endif // STB_VORBIS_NO_PULLDATA_API /* Version history 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 found with Mayhem by ForAllSecure 1.16 - 2019-03-04 - fix warnings 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found 1.14 - 2018-02-11 - delete bogus dealloca usage 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files 1.11 - 2017-07-23 - fix MinGW compilation 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API some more crash fixes when out of memory or with corrupt files 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files 1.05 - 2015-04-19 - don't define __forceinline if it's redundant 1.04 - 2014-08-27 - fix missing const-correct case in API 1.03 - 2014-08-07 - Warning fixes 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel (API change) report sample rate for decode-full-file funcs 0.99996 - bracket #include for macintosh compilation by Laurent Gomila 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence 0.99993 - remove assert that fired on legal files with empty tables 0.99992 - rewind-to-start 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ 0.9998 - add a full-decode function with a memory source 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition 0.9996 - query length of vorbis stream in samples/seconds 0.9995 - bugfix to another optimization that only happened in certain files 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation 0.9992 - performance improvement of IMDCT; now performs close to reference implementation 0.9991 - performance improvement of IMDCT 0.999 - (should have been 0.9990) performance improvement of IMDCT 0.998 - no-CRT support from Casey Muratori 0.997 - bugfixes for bugs found by Terje Mathisen 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen 0.992 - fixes for MinGW warning 0.991 - turn fast-float-conversion on by default 0.990 - fix push-mode seek recovery if you seek into the headers 0.98b - fix to bad release of 0.98 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode 0.97 - builds under c++ (typecasting, don't use 'class' keyword) 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code 0.95 - clamping code for 16-bit functions 0.94 - not publically released 0.93 - fixed all-zero-floor case (was decoding garbage) 0.92 - fixed a memory leak 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION 0.90 - first public release */ #endif // STB_VORBIS_HEADER_ONLY /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ SDL2_mixer-2.8.0/src/codecs/native_midi/0000755000076500000240000000000014553251273016757 5ustar valvestaffSDL2_mixer-2.8.0/src/codecs/native_midi/native_midi_macosx.c0000644000076500000240000002301414277744147022777 0ustar valvestaff/* native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library Copyright (C) 2009 Ryan C. Gordon This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_config.h" #if __MACOSX__ /* Mac OS X 10.6+, using Core MIDI. */ #include "SDL_stdinc.h" #include #include #include #include "SDL_endian.h" #include "SDL_mixer.h" #include "../../mixer.h" #include "native_midi.h" /* Native Midi song */ struct _NativeMidiSong { MusicPlayer player; MusicSequence sequence; MusicTimeStamp endTime; AudioUnit audiounit; int loops; }; static NativeMidiSong *currentsong = NULL; static int latched_volume = MIX_MAX_VOLUME; static OSStatus GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength) { /* http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html * figure out sequence length */ UInt32 ntracks, i; MusicTimeStamp sequenceLength = 0; OSStatus err; err = MusicSequenceGetTrackCount(sequence, &ntracks); if (err != noErr) return err; for (i = 0; i < ntracks; ++i) { MusicTrack track; MusicTimeStamp tracklen = 0; UInt32 tracklenlen = sizeof (tracklen); err = MusicSequenceGetIndTrack(sequence, i, &track); if (err != noErr) return err; err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &tracklen, &tracklenlen); if (err != noErr) return err; if (sequenceLength < tracklen) sequenceLength = tracklen; } *_sequenceLength = sequenceLength; return noErr; } static OSStatus GetSequenceAudioUnitMatching(MusicSequence sequence, AudioUnit *aunit, OSType type, OSType subtype) { AUGraph graph; UInt32 nodecount, i; OSStatus err; err = MusicSequenceGetAUGraph(sequence, &graph); if (err != noErr) return err; err = AUGraphGetNodeCount(graph, &nodecount); if (err != noErr) return err; for (i = 0; i < nodecount; i++) { AUNode node; AudioComponentDescription desc; if (AUGraphGetIndNode(graph, i, &node) != noErr) continue; /* better luck next time. */ if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr) continue; else if (desc.componentType != type) continue; else if (desc.componentSubType != subtype) continue; return noErr; /* found it! */ } *aunit = NULL; return kAUGraphErr_NodeNotFound; } typedef struct { AudioUnit aunit; SDL_bool soundfont_set; CFURLRef default_url; } macosx_load_soundfont_ctx; static int SDLCALL macosx_load_soundfont(const char *path, void *data) { CFURLRef url; OSStatus err; macosx_load_soundfont_ctx *ctx = data; if (ctx->soundfont_set) return SDL_FALSE; url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)path, strlen(path), false); if (!url) return SDL_FALSE; err = AudioUnitSetProperty(ctx->aunit, kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, 0, &url, sizeof(url)); CFRelease(url); if (err != noErr) { if (ctx->default_url) err = AudioUnitSetProperty(ctx->aunit, kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, 0, &ctx->default_url, sizeof(CFURLRef)); if (err != noErr) { /* uh-oh, this might leave the audio unit in an unusable state (e.g. if the soundfont was an incompatible file type) */ } return SDL_FALSE; } ctx->soundfont_set = SDL_TRUE; return SDL_TRUE; } static void SetSequenceSoundFont(MusicSequence sequence) { OSStatus err; macosx_load_soundfont_ctx ctx; ctx.soundfont_set = SDL_FALSE; ctx.default_url = NULL; CFBundleRef bundle = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.audio.units.Components")); if (bundle) ctx.default_url = CFBundleCopyResourceURL(bundle, CFSTR("gs_instruments"), CFSTR("dls"), NULL); err = GetSequenceAudioUnitMatching(sequence, &ctx.aunit, kAudioUnitType_MusicDevice, kAudioUnitSubType_DLSSynth); if (err != noErr) return; Mix_EachSoundFont(macosx_load_soundfont, &ctx); if (ctx.default_url) CFRelease(ctx.default_url); return; } int native_midi_detect(void) { return 1; /* always available. */ } NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc) { NativeMidiSong *retval = NULL; void *buf = NULL; Sint64 len = 0; CFDataRef data = NULL; if (SDL_RWseek(src, 0, RW_SEEK_END) < 0) goto fail; len = SDL_RWtell(src); if (len < 0) goto fail; if (SDL_RWseek(src, 0, RW_SEEK_SET) < 0) goto fail; buf = SDL_malloc(len); if (buf == NULL) goto fail; if (SDL_RWread(src, buf, len, 1) != 1) goto fail; retval = SDL_malloc(sizeof(NativeMidiSong)); if (retval == NULL) goto fail; SDL_memset(retval, '\0', sizeof (*retval)); if (NewMusicPlayer(&retval->player) != noErr) goto fail; if (NewMusicSequence(&retval->sequence) != noErr) goto fail; data = CFDataCreate(NULL, (const UInt8 *) buf, len); if (data == NULL) goto fail; SDL_free(buf); buf = NULL; if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr) goto fail; CFRelease(data); data = NULL; if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr) goto fail; if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr) goto fail; if (freesrc) SDL_RWclose(src); return retval; fail: if (retval) { if (retval->sequence) DisposeMusicSequence(retval->sequence); if (retval->player) DisposeMusicPlayer(retval->player); SDL_free(retval); } if (data) CFRelease(data); if (buf) SDL_free(buf); return NULL; } void native_midi_freesong(NativeMidiSong *song) { if (song != NULL) { if (currentsong == song) currentsong = NULL; MusicPlayerStop(song->player); /* needed to prevent error and memory leak when disposing sequence */ MusicPlayerSetSequence(song->player, NULL); DisposeMusicSequence(song->sequence); DisposeMusicPlayer(song->player); SDL_free(song); } } void native_midi_start(NativeMidiSong *song, int loops) { int vol; if (song == NULL) return; if (currentsong) MusicPlayerStop(currentsong->player); currentsong = song; currentsong->loops = loops; MusicPlayerPreroll(song->player); GetSequenceAudioUnitMatching(song->sequence, &song->audiounit, kAudioUnitType_Output, kAudioUnitSubType_DefaultOutput); SetSequenceSoundFont(song->sequence); vol = latched_volume; latched_volume++; /* just make this not match. */ native_midi_setvolume(vol); MusicPlayerSetTime(song->player, 0); MusicPlayerStart(song->player); } void native_midi_pause(void) { } void native_midi_resume(void) { } void native_midi_stop(void) { if (currentsong) { MusicPlayerStop(currentsong->player); currentsong = NULL; } } int native_midi_active(void) { MusicTimeStamp currentTime = 0; if (currentsong == NULL) return 0; MusicPlayerGetTime(currentsong->player, ¤tTime); if ((currentTime < currentsong->endTime) || (currentTime >= kMusicTimeStamp_EndOfTrack)) { return 1; } else if (currentsong->loops) { --currentsong->loops; MusicPlayerSetTime(currentsong->player, 0); return 1; } return 0; } void native_midi_setvolume(int volume) { if (latched_volume == volume) return; latched_volume = volume; if ((currentsong) && (currentsong->audiounit)) { const float floatvol = ((float) volume) / ((float) MIX_MAX_VOLUME); AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, floatvol, 0); } } const char *native_midi_error(void) { return ""; /* !!! FIXME */ } #endif /* Mac OS X native MIDI support */ SDL2_mixer-2.8.0/src/codecs/native_midi/native_midi_haiku.cpp0000644000076500000240000001500214277744147023144 0ustar valvestaff/* native_midi_haiku: Native Midi support on Haiku for the SDL_mixer library Copyright (C) 2010 Egor Suvorov This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_config.h" #ifdef __HAIKU__ #include #include #include #include #include #include #include #include extern "C" { #include "native_midi.h" #include "native_midi_common.h" } bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b) { return a.time < b.time; } class MidiEventsStore : public BMidi { public: MidiEventsStore() { fPlaying = false; fLoops = 0; } virtual status_t Import(SDL_RWops *src) { fEvs = CreateMIDIEventList(src, &fDivision); if (!fEvs) { return B_BAD_MIDI_DATA; } fTotal = 0; for (MIDIEvent *x = fEvs; x; x = x->next) fTotal++; fPos = fTotal; sort_events(); return B_OK; } virtual void Run() { fPlaying = true; fPos = 0; MIDIEvent *ev = fEvs; uint32 startTime = B_NOW; while (KeepRunning()) { if (!ev) { if (fLoops && fEvs) { if (fLoops > 0) --fLoops; fPos = 0; ev = fEvs; } else break; } SprayEvent(ev, ev->time + startTime); ev = ev->next; fPos++; } fPos = fTotal; fPlaying = false; } virtual ~MidiEventsStore() { if (!fEvs) return; FreeMIDIEventList(fEvs); fEvs = 0; } bool IsPlaying() { return fPlaying; } void SetLoops(int loops) { fLoops = loops; } protected: MIDIEvent *fEvs; Uint16 fDivision; int fPos, fTotal; int fLoops; bool fPlaying; void SprayEvent(MIDIEvent *ev, uint32 time) { switch (ev->status & 0xF0) { case B_NOTE_OFF: SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); break; case B_NOTE_ON: SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); break; case B_KEY_PRESSURE: SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); break; case B_CONTROL_CHANGE: SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); break; case B_PROGRAM_CHANGE: SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time); break; case B_CHANNEL_PRESSURE: SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time); break; case B_PITCH_BEND: SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); break; case 0xF: switch (ev->status) { case B_SYS_EX_START: SpraySystemExclusive(ev->extraData, ev->extraLen, time); break; case B_MIDI_TIME_CODE: case B_SONG_POSITION: case B_SONG_SELECT: case B_CABLE_MESSAGE: case B_TUNE_REQUEST: case B_SYS_EX_END: SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time); break; case B_TIMING_CLOCK: case B_START: case B_STOP: case B_CONTINUE: case B_ACTIVE_SENSING: SpraySystemRealTime(ev->status, time); break; case B_SYSTEM_RESET: if (ev->data[0] == 0x51 && ev->data[1] == 0x03) { assert(ev->extraLen == 3); int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2]; int tempo = 60000000 / val; SprayTempoChange(tempo, time); } else { SpraySystemRealTime(ev->status, time); } } break; } } void sort_events() { MIDIEvent *items = new MIDIEvent[fTotal]; MIDIEvent *x = fEvs; for (int i = 0; i < fTotal; i++) { memcpy(items + i, x, sizeof(MIDIEvent)); x = x->next; } std::sort(items, items + fTotal, compareMIDIEvent); x = fEvs; for (int i = 0; i < fTotal; i++) { MIDIEvent *ne = x->next; memcpy(x, items + i, sizeof(MIDIEvent)); x->next = ne; x = ne; } for (x = fEvs; x && x->next; x = x->next) assert(x->time <= x->next->time); delete[] items; } }; BMidiSynth synth; struct _NativeMidiSong { MidiEventsStore *store; } *currentSong = NULL; char lasterr[1024]; int native_midi_detect(void) { status_t res = synth.EnableInput(true, false); return res == B_OK; } void native_midi_setvolume(int volume) { if (volume < 0) volume = 0; if (volume > 128) volume = 128; synth.SetVolume(volume / 128.0); } NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc) { NativeMidiSong *song = new NativeMidiSong; song->store = new MidiEventsStore; status_t res = song->store->Import(src); if (res != B_OK) { snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res); delete song->store; delete song; return NULL; } else { if (freesrc) { SDL_RWclose(src); } } return song; } void native_midi_freesong(NativeMidiSong *song) { if (song == NULL) return; song->store->Stop(); song->store->Disconnect(&synth); if (currentSong == song) { currentSong = NULL; } delete song->store; delete song; song = 0; } void native_midi_start(NativeMidiSong *song, int loops) { native_midi_stop(); song->store->Connect(&synth); song->store->SetLoops(loops); song->store->Start(); currentSong = song; } void native_midi_pause(void) { } void native_midi_resume(void) { } void native_midi_stop(void) { if (currentSong == NULL) return; currentSong->store->Stop(); currentSong->store->Disconnect(&synth); while (currentSong->store->IsPlaying()) usleep(1000); currentSong = NULL; } int native_midi_active(void) { if (currentSong == NULL) return 0; return currentSong->store->IsPlaying(); } const char* native_midi_error(void) { return lasterr; } #endif /* __HAIKU__ */ SDL2_mixer-2.8.0/src/codecs/native_midi/native_midi_win32.c0000644000076500000240000002043614277744147022454 0ustar valvestaff/* native_midi: Hardware Midi support for the SDL_mixer library Copyright (C) 2000,2001 Florian 'Proff' Schulze This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_config.h" /* everything below is currently one very big bad hack ;) Proff */ #if __WIN32__ #define WIN32_LEAN_AND_MEAN #include #include #include "native_midi.h" #include "native_midi_common.h" struct _NativeMidiSong { int MusicLoaded; int MusicPlaying; int Loops; int CurrentHdr; MIDIHDR MidiStreamHdr[2]; MIDIEVENT *NewEvents; Uint16 ppqn; int Size; int NewPos; SDL_mutex *mutex; }; static UINT MidiDevice=MIDI_MAPPER; static HMIDISTRM hMidiStream; static NativeMidiSong *currentsong; static int BlockOut(NativeMidiSong *song) { MMRESULT err; int BlockSize; MIDIHDR *hdr; if ((song->MusicLoaded) && (song->NewEvents)) { /* proff 12/8/98: Added for safety*/ song->CurrentHdr = !song->CurrentHdr; hdr = &song->MidiStreamHdr[song->CurrentHdr]; midiOutUnprepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); if (song->NewPos>=song->Size) return 0; BlockSize=(song->Size-song->NewPos); if (BlockSize<=0) return 0; if (BlockSize>36000) BlockSize=36000; hdr->lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos); song->NewPos+=BlockSize; hdr->dwBufferLength=BlockSize; hdr->dwBytesRecorded=BlockSize; hdr->dwFlags=0; hdr->dwOffset=0; err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); if (err!=MMSYSERR_NOERROR) return 0; err=midiStreamOut(hMidiStream,hdr,sizeof(MIDIHDR)); return 0; } return 1; } static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist) { int eventcount; MIDIEvent *event; MIDIEVENT *newevent; eventcount=0; event=evntlist; while (event) { eventcount++; event=event->next; } song->NewEvents = SDL_malloc(eventcount*3*sizeof(DWORD)); if (!song->NewEvents) return; SDL_memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD))); eventcount=0; event=evntlist; newevent=song->NewEvents; while (event) { int status = (event->status&0xF0)>>4; switch (status) { case MIDI_STATUS_NOTE_OFF: case MIDI_STATUS_NOTE_ON: case MIDI_STATUS_AFTERTOUCH: case MIDI_STATUS_CONTROLLER: case MIDI_STATUS_PROG_CHANGE: case MIDI_STATUS_PRESSURE: case MIDI_STATUS_PITCH_WHEEL: newevent->dwDeltaTime=event->time; newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24); newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); eventcount++; break; case MIDI_STATUS_SYSEX: if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */ { int tempo = (event->extraData[0] << 16) | (event->extraData[1] << 8) | event->extraData[2]; newevent->dwDeltaTime=event->time; newevent->dwEvent=(MEVT_TEMPO<<24) | tempo; newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); eventcount++; } break; } event=event->next; } song->Size=eventcount*3*sizeof(DWORD); { int time; int temptime; song->NewPos=0; time=0; newevent=song->NewEvents; while (song->NewPosSize) { temptime=newevent->dwDeltaTime; newevent->dwDeltaTime-=time; time=temptime; if ((song->NewPos+12)>=song->Size) newevent->dwEvent |= MEVT_F_CALLBACK; newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); song->NewPos+=12; } } song->NewPos=0; song->MusicLoaded=1; } void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) { NativeMidiSong *song = (NativeMidiSong *)dwInstance; (void)hMidi; (void)dwParam2; if (!song) { return; } SDL_LockMutex(song->mutex); switch( uMsg ) { case MOM_DONE: if (song->MusicPlaying && song->MusicLoaded && (dwParam1 == (DWORD_PTR)&song->MidiStreamHdr[song->CurrentHdr])) BlockOut(song); break; case MOM_POSITIONCB: if (song->MusicPlaying && song->MusicLoaded && (dwParam1 == (DWORD_PTR)&song->MidiStreamHdr[song->CurrentHdr])) { if (song->Loops) { if (song->Loops > 0) --song->Loops; song->NewPos=0; BlockOut(song); } else { song->MusicPlaying=0; } } break; case MOM_CLOSE: song->MusicPlaying=0; break; default: break; } SDL_UnlockMutex(song->mutex); } int native_midi_detect(void) { MMRESULT merr; HMIDISTRM MidiStream; merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION); if (merr!=MMSYSERR_NOERROR) return 0; midiStreamClose(MidiStream); return 1; } NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc) { NativeMidiSong *newsong; MIDIEvent *evntlist = NULL; newsong = SDL_malloc(sizeof(NativeMidiSong)); if (!newsong) { return NULL; } SDL_memset(newsong,0,sizeof(NativeMidiSong)); /* Attempt to load the midi file */ evntlist = CreateMIDIEventList(src, &newsong->ppqn); if (!evntlist) { SDL_free(newsong); return NULL; } MIDItoStream(newsong, evntlist); FreeMIDIEventList(evntlist); newsong->mutex = SDL_CreateMutex(); if (freesrc) { SDL_RWclose(src); } return newsong; } void native_midi_freesong(NativeMidiSong *song) { if (song) { if (song->NewEvents) SDL_free(song->NewEvents); SDL_DestroyMutex(song->mutex); SDL_free(song); } } void native_midi_start(NativeMidiSong *song, int loops) { MMRESULT merr; MIDIPROPTIMEDIV mptd; native_midi_stop(); if (!hMidiStream) { merr=midiStreamOpen(&hMidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)song,CALLBACK_FUNCTION); if (merr!=MMSYSERR_NOERROR) { hMidiStream = NULL; /* should I do midiStreamClose(hMidiStream) before? */ return; } /* midiStreamStop(hMidiStream); */ currentsong=song; currentsong->NewPos=0; currentsong->MusicPlaying=1; currentsong->Loops=loops; mptd.cbStruct=sizeof(MIDIPROPTIMEDIV); mptd.dwTimeDiv=currentsong->ppqn; merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV); BlockOut(song); merr=midiStreamRestart(hMidiStream); } } void native_midi_pause(void) { if (!hMidiStream) return; midiStreamPause(hMidiStream); } void native_midi_resume(void) { if (!hMidiStream) return; midiStreamRestart(hMidiStream); } void native_midi_stop(void) { NativeMidiSong *song = currentsong; if (!hMidiStream) return; SDL_LockMutex(song->mutex); midiStreamStop(hMidiStream); midiStreamClose(hMidiStream); currentsong=NULL; hMidiStream = NULL; SDL_UnlockMutex(song->mutex); } int native_midi_active(void) { if (!hMidiStream) return 0; if (!currentsong) return 0; return currentsong->MusicPlaying; } void native_midi_setvolume(int volume) { int calcVolume; if (volume > 128) volume = 128; if (volume < 0) volume = 0; calcVolume = (65535 * volume / 128); midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume)); } const char *native_midi_error(void) { return ""; } #endif /* Windows native MIDI support */ SDL2_mixer-2.8.0/src/codecs/native_midi/native_midi_common.c0000644000076500000240000002642514551252471022773 0ustar valvestaff/* native_midi: Hardware Midi support for the SDL_mixer library Copyright (C) 2000,2001 Florian 'Proff' Schulze This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "native_midi_common.h" #include "SDL_mixer.h" /* The constant 'MThd' */ #define MIDI_MAGIC 0x4d546864 /* The constant 'RIFF' */ #define RIFF_MAGIC 0x52494646 /* A single midi track as read from the midi file */ typedef struct { Uint8 *data; /* MIDI message stream */ int len; /* length of the track data */ } MIDITrack; /* A midi file, stripped down to the absolute minimum - divison & track data */ typedef struct { int division; /* number of pulses per quarter note (ppqn) */ int nTracks; /* number of tracks */ MIDITrack *track; /* tracks */ } MIDIFile; /* Some macros that help us stay endianess-independant */ #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define BE_SHORT(x) (x) #define BE_LONG(x) (x) #else #define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #define BE_LONG(x) ((((x)&0x0000FF)<<24) | \ (((x)&0x00FF00)<<8) | \ (((x)&0xFF0000)>>8) | \ (((x)>>24)&0xFF)) #endif /* Get Variable Length Quantity */ static int GetVLQ(MIDITrack *track, int *currentPos) { int l = 0; Uint8 c; while (1) { c = track->data[*currentPos]; (*currentPos)++; l += (c & 0x7f); if (!(c & 0x80)) { return l; } l <<= 7; } } /* Create a single MIDIEvent */ static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b) { MIDIEvent *newEvent; newEvent = SDL_calloc(1, sizeof(MIDIEvent)); if (newEvent) { newEvent->time = time; newEvent->status = event; newEvent->data[0] = a; newEvent->data[1] = b; } else { Mix_OutOfMemory(); } return newEvent; } /* Convert a single midi track to a list of MIDIEvents */ static MIDIEvent *MIDITracktoStream(MIDITrack *track) { Uint32 atime = 0; Uint32 len = 0; Uint8 event,type,a,b; Uint8 laststatus = 0; Uint8 lastchan = 0; int currentPos = 0; int end = 0; MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ MIDIEvent *currentEvent = head; while (!end) { if (currentPos >= track->len) { break; /* End of data stream reached */ } atime += GetVLQ(track, ¤tPos); event = track->data[currentPos++]; /* Handle SysEx seperatly */ if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX) { if (event == 0xFF) { type = track->data[currentPos]; currentPos++; switch(type) { case 0x2f: /* End of data marker */ end = 1; case 0x51: /* Tempo change */ /* a=track->data[currentPos]; b=track->data[currentPos+1]; c=track->data[currentPos+2]; AddEvent(song, atime, MEVT_TEMPO, c, b, a); */ break; } } else { type = 0; } len = GetVLQ(track, ¤tPos); /* Create an event and attach the extra data, if any */ currentEvent->next = CreateEvent(atime, event, type, 0); currentEvent = currentEvent->next; if (NULL == currentEvent) { FreeMIDIEventList(head); return NULL; } if (len) { currentEvent->extraLen = len; currentEvent->extraData = SDL_malloc(len); SDL_memcpy(currentEvent->extraData, &(track->data[currentPos]), len); currentPos += len; } } else { a = event; if (a & 0x80) { /* It's a status byte */ /* Extract channel and status information */ lastchan = a & 0x0F; laststatus = (a>>4) & 0x0F; /* Read the next byte which should always be a data byte */ a = track->data[currentPos++] & 0x7F; } switch(laststatus) { case MIDI_STATUS_NOTE_OFF: case MIDI_STATUS_NOTE_ON: /* Note on */ case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */ case MIDI_STATUS_CONTROLLER: /* Control change */ case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */ b = track->data[currentPos++] & 0x7F; currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b); currentEvent = currentEvent->next; if (NULL == currentEvent) { FreeMIDIEventList(head); return NULL; } break; case MIDI_STATUS_PROG_CHANGE: /* Program change */ case MIDI_STATUS_PRESSURE: /* Channel pressure */ a &= 0x7f; currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0); currentEvent = currentEvent->next; if (NULL == currentEvent) { FreeMIDIEventList(head); return NULL; } break; default: /* Sysex already handled above */ break; } } } currentEvent = head->next; SDL_free(head); /* release the dummy head event */ return currentEvent; } /* * Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents. * To do so, first convert the tracks seperatly, then interweave the resulting * MIDIEvent-Lists to one big list. */ static MIDIEvent *MIDItoStream(MIDIFile *mididata) { MIDIEvent **track; MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ MIDIEvent *currentEvent = head; int trackID; if (NULL == head) { return NULL; } track = (MIDIEvent**) SDL_calloc(1, sizeof(MIDIEvent*) * mididata->nTracks); if (NULL == track) { SDL_free(head); return NULL; } /* First, convert all tracks to MIDIEvent lists */ for (trackID = 0; trackID < mididata->nTracks; trackID++) { track[trackID] = MIDITracktoStream(&mididata->track[trackID]); } /* Now, merge the lists. */ /* TODO */ while (1) { Uint32 lowestTime = 0x7FFFFFFF; /* INT_MAX */ int currentTrackID = -1; /* Find the next event */ for (trackID = 0; trackID < mididata->nTracks; trackID++) { if (track[trackID] && (track[trackID]->time < lowestTime)) { currentTrackID = trackID; lowestTime = track[currentTrackID]->time; } } /* Check if we processes all events */ if (currentTrackID == -1) { break; } currentEvent->next = track[currentTrackID]; track[currentTrackID] = track[currentTrackID]->next; currentEvent = currentEvent->next; lowestTime = 0; } /* Make sure the list is properly terminated */ currentEvent->next = 0; currentEvent = head->next; SDL_free(track); SDL_free(head); /* release the dummy head event */ return currentEvent; } static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *src) { int i = 0; Uint32 ID; Uint32 size; Uint16 format; Uint16 tracks; Uint16 division; if (!mididata) { return 0; } if (!src) { return 0; } /* Make sure this is really a MIDI file */ SDL_RWread(src, &ID, 1, 4); if (BE_LONG(ID) == RIFF_MAGIC) { SDL_RWseek(src, 16, RW_SEEK_CUR); SDL_RWread(src, &ID, 1, 4); } if (BE_LONG(ID) != MIDI_MAGIC) { return 0; } /* Header size must be 6 */ SDL_RWread(src, &size, 1, 4); size = BE_LONG(size); if (size != 6) { return 0; } /* We only support format 0 and 1, but not 2 */ SDL_RWread(src, &format, 1, 2); format = BE_SHORT(format); if (format != 0 && format != 1) { return 0; } SDL_RWread(src, &tracks, 1, 2); tracks = BE_SHORT(tracks); mididata->nTracks = tracks; /* Allocate tracks */ mididata->track = (MIDITrack*) SDL_calloc(1, sizeof(MIDITrack) * mididata->nTracks); if (NULL == mididata->track) { Mix_OutOfMemory(); goto bail; } /* Retrieve the PPQN value, needed for playback */ SDL_RWread(src, &division, 1, 2); mididata->division = BE_SHORT(division); for (i = 0; i < tracks; i++) { SDL_RWread(src, &ID, 1, 4); /* We might want to verify this is MTrk... */ SDL_RWread(src, &size, 1, 4); size = BE_LONG(size); mididata->track[i].len = size; mididata->track[i].data = SDL_malloc(size); if (NULL == mididata->track[i].data) { Mix_OutOfMemory(); goto bail; } SDL_RWread(src, mididata->track[i].data, 1, size); } return 1; bail: for (; i >= 0; i--) { if (mididata->track[i].data) { SDL_free(mididata->track[i].data); } } SDL_free(mididata->track); return 0; } MIDIEvent *CreateMIDIEventList(SDL_RWops *src, Uint16 *division) { MIDIFile *mididata = NULL; MIDIEvent *eventList; int trackID; mididata = SDL_calloc(1, sizeof(MIDIFile)); if (!mididata) { return NULL; } /* Open the file */ if (src != NULL) { /* Read in the data */ if (!ReadMIDIFile(mididata, src)) { SDL_free(mididata); return NULL; } } else { SDL_free(mididata); return NULL; } if (division) { *division = mididata->division; } eventList = MIDItoStream(mididata); if (eventList == NULL) { SDL_free(mididata); return NULL; } for (trackID = 0; trackID < mididata->nTracks; trackID++) { if (mididata->track[trackID].data) { SDL_free(mididata->track[trackID].data); } } SDL_free(mididata->track); SDL_free(mididata); return eventList; } void FreeMIDIEventList(MIDIEvent *head) { MIDIEvent *cur, *next; cur = head; while (cur) { next = cur->next; if (cur->extraData) { SDL_free(cur->extraData); } SDL_free(cur); cur = next; } } SDL2_mixer-2.8.0/src/codecs/native_midi/native_midi.h0000644000076500000240000000301214277744147021426 0ustar valvestaff/* native_midi: Hardware Midi support for the SDL_mixer library Copyright (C) 2000 Florian 'Proff' Schulze This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef NATIVE_MIDI_H_ #define NATIVE_MIDI_H_ #include "SDL_rwops.h" typedef struct _NativeMidiSong NativeMidiSong; int native_midi_detect(void); NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *src, int freesrc); void native_midi_freesong(NativeMidiSong *song); void native_midi_start(NativeMidiSong *song, int loops); void native_midi_pause(void); void native_midi_resume(void); void native_midi_stop(void); int native_midi_active(void); void native_midi_setvolume(int volume); const char *native_midi_error(void); #endif /* NATIVE_MIDI_H_ */ SDL2_mixer-2.8.0/src/codecs/native_midi/native_midi_common.h0000644000076500000240000000432414277744147023005 0ustar valvestaff/* native_midi: Hardware Midi support for the SDL_mixer library Copyright (C) 2000,2001 Florian 'Proff' Schulze This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef _NATIVE_MIDI_COMMON_H_ #define _NATIVE_MIDI_COMMON_H_ #include "SDL.h" /* Midi Status Bytes */ #define MIDI_STATUS_NOTE_OFF 0x8 #define MIDI_STATUS_NOTE_ON 0x9 #define MIDI_STATUS_AFTERTOUCH 0xA #define MIDI_STATUS_CONTROLLER 0xB #define MIDI_STATUS_PROG_CHANGE 0xC #define MIDI_STATUS_PRESSURE 0xD #define MIDI_STATUS_PITCH_WHEEL 0xE #define MIDI_STATUS_SYSEX 0xF /* We store the midi events in a linked list; this way it is easy to shuffle the tracks together later on; and we are flexible in the size of each elemnt. */ typedef struct MIDIEvent { Uint32 time; /* Time at which this midi events occurs */ Uint8 status; /* Status byte */ Uint8 data[2]; /* 1 or 2 bytes additional data for most events */ Uint32 extraLen; /* For some SysEx events, we need additional storage */ Uint8 *extraData; struct MIDIEvent *next; } MIDIEvent; /* Load a midifile to memory, converting it to a list of MIDIEvents. This function returns a linked lists of MIDIEvents, 0 if an error occured. */ MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division); /* Release a MIDIEvent list after usage. */ void FreeMIDIEventList(MIDIEvent *head); #endif /* _NATIVE_MIDI_COMMON_H_ */ SDL2_mixer-2.8.0/src/codecs/music_xmp.h0000644000076500000240000000216114551252471016643 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MOD files with libxmp */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_XMP; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_drflac.h0000644000076500000240000000216614551252471017277 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing FLAC files with dr_flac */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_DRFLAC; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_opus.c0000644000076500000240000003613514553225265017033 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_OPUS /* This file supports Ogg Opus music streams */ #include "SDL_loadso.h" #include "music_opus.h" #include "utils.h" #ifdef OPUSFILE_HEADER #include OPUSFILE_HEADER #else #include #endif typedef struct { int loaded; void *handle; const OpusTags *(*op_tags)(const OggOpusFile *,int); OggOpusFile *(*op_open_callbacks)(void *,const OpusFileCallbacks *,const unsigned char *,size_t,int *); void (*op_free)(OggOpusFile *); const OpusHead *(*op_head)(const OggOpusFile *,int); int (*op_seekable)(const OggOpusFile *); int (*op_read)(OggOpusFile *, opus_int16 *,int,int *); int (*op_pcm_seek)(OggOpusFile *,ogg_int64_t); ogg_int64_t (*op_pcm_tell)(const OggOpusFile *); ogg_int64_t (*op_pcm_total)(const OggOpusFile *, int); } opus_loader; static opus_loader opus; #ifdef OPUS_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ opus.FUNC = (SIG) SDL_LoadFunction(opus.handle, #FUNC); \ if (opus.FUNC == NULL) { SDL_UnloadObject(opus.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ opus.FUNC = FUNC; \ if (opus.FUNC == NULL) { Mix_SetError("Missing opus.framework"); return -1; } #endif static int OPUS_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (opus.loaded == 0) { #ifdef OPUS_DYNAMIC opus.handle = SDL_LoadObject(OPUS_DYNAMIC); if (opus.handle == NULL) { return -1; } #endif FUNCTION_LOADER(op_open_callbacks, OggOpusFile *(*)(void *,const OpusFileCallbacks *,const unsigned char *,size_t,int *)) FUNCTION_LOADER(op_tags, const OpusTags *(*)(const OggOpusFile *,int)) FUNCTION_LOADER(op_free, void (*)(OggOpusFile *)) FUNCTION_LOADER(op_head, const OpusHead *(*)(const OggOpusFile *,int)) FUNCTION_LOADER(op_seekable, int (*)(const OggOpusFile *)) FUNCTION_LOADER(op_read, int (*)(OggOpusFile *, opus_int16 *,int,int *)) FUNCTION_LOADER(op_pcm_seek, int (*)(OggOpusFile *,ogg_int64_t)) FUNCTION_LOADER(op_pcm_tell, ogg_int64_t (*)(const OggOpusFile *)) FUNCTION_LOADER(op_pcm_total, ogg_int64_t (*)(const OggOpusFile *, int)) } ++opus.loaded; return 0; } static void OPUS_Unload(void) { if (opus.loaded == 0) { return; } if (opus.loaded == 1) { #ifdef OPUS_DYNAMIC SDL_UnloadObject(opus.handle); #endif } --opus.loaded; } typedef struct { SDL_RWops *src; int freesrc; int play_count; int volume; OggOpusFile *of; const OpusHead *op_info; int section; SDL_AudioStream *stream; char *buffer; int buffer_size; int loop; ogg_int64_t loop_start; ogg_int64_t loop_end; ogg_int64_t loop_len; ogg_int64_t full_length; Mix_MusicMetaTags tags; } OPUS_music; static int set_op_error(const char *function, int error) { #define HANDLE_ERROR_CASE(X) case X: Mix_SetError("%s: %s", function, #X); break; switch (error) { HANDLE_ERROR_CASE(OP_FALSE) HANDLE_ERROR_CASE(OP_EOF) HANDLE_ERROR_CASE(OP_HOLE) HANDLE_ERROR_CASE(OP_EREAD) HANDLE_ERROR_CASE(OP_EFAULT) HANDLE_ERROR_CASE(OP_EIMPL) HANDLE_ERROR_CASE(OP_EINVAL) HANDLE_ERROR_CASE(OP_ENOTFORMAT) HANDLE_ERROR_CASE(OP_EBADHEADER) HANDLE_ERROR_CASE(OP_EVERSION) HANDLE_ERROR_CASE(OP_ENOTAUDIO) HANDLE_ERROR_CASE(OP_EBADPACKET) HANDLE_ERROR_CASE(OP_EBADLINK) HANDLE_ERROR_CASE(OP_ENOSEEK) HANDLE_ERROR_CASE(OP_EBADTIMESTAMP) default: Mix_SetError("%s: unknown error %d\n", function, error); break; } return -1; } static int sdl_read_func(void *datasource, unsigned char *ptr, int size) { return (int)SDL_RWread((SDL_RWops*)datasource, ptr, 1, (size_t)size); } static int sdl_seek_func(void *datasource, opus_int64 offset, int whence) { return (SDL_RWseek((SDL_RWops*)datasource, offset, whence) < 0)? -1 : 0; } static opus_int64 sdl_tell_func(void *datasource) { return SDL_RWtell((SDL_RWops*)datasource); } static int OPUS_Seek(void*, double); static void OPUS_Delete(void*); static int OPUS_UpdateSection(OPUS_music *music) { const OpusHead *op_info; op_info = opus.op_head(music->of, -1); if (!op_info) { return Mix_SetError("op_head returned NULL"); } if (music->op_info && op_info->channel_count == music->op_info->channel_count) { return 0; } music->op_info = op_info; if (music->buffer) { SDL_free(music->buffer); music->buffer = NULL; } if (music->stream) { SDL_FreeAudioStream(music->stream); music->stream = NULL; } music->stream = SDL_NewAudioStream(AUDIO_S16SYS, (Uint8)op_info->channel_count, 48000, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { return -1; } music->buffer_size = (int)music_spec.samples * (int)sizeof(opus_int16) * op_info->channel_count; music->buffer = (char *)SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { return -1; } return 0; } /* Load an Opus stream from an SDL_RWops object */ static void *OPUS_CreateFromRW(SDL_RWops *src, int freesrc) { OPUS_music *music; OpusFileCallbacks callbacks; const OpusTags* tags; int err = 0, ci; SDL_bool is_loop_length = SDL_FALSE; ogg_int64_t full_length; music = (OPUS_music *)SDL_calloc(1, sizeof *music); if (!music) { SDL_OutOfMemory(); return NULL; } music->src = src; music->volume = MIX_MAX_VOLUME; music->section = -1; SDL_zero(callbacks); callbacks.read = sdl_read_func; callbacks.seek = sdl_seek_func; callbacks.tell = sdl_tell_func; music->of = opus.op_open_callbacks(src, &callbacks, NULL, 0, &err); if (music->of == NULL) { /* set_op_error("op_open_callbacks", err);*/ Mix_SetError("Not an Opus audio stream"); SDL_free(music); return NULL; } if (!opus.op_seekable(music->of)) { OPUS_Delete(music); Mix_SetError("Opus stream not seekable"); return NULL; } if (OPUS_UpdateSection(music) < 0) { OPUS_Delete(music); return NULL; } tags = opus.op_tags(music->of, -1); if (tags != NULL) { for (ci = 0; ci < tags->comments; ci++) { char *param = SDL_strdup(tags->user_comments[ci]); char *argument = param; char *value = SDL_strchr(param, '='); if (value == NULL) { value = param + SDL_strlen(param); } else { *(value++) = '\0'; } /* Want to match LOOP-START, LOOP_START, etc. Remove - or _ from * string if it is present at position 4. */ if (_Mix_IsLoopTag(argument) && ((argument[4] == '_') || (argument[4] == '-'))) { SDL_memmove(argument + 4, argument + 5, SDL_strlen(argument) - 4); } if (SDL_strcasecmp(argument, "LOOPSTART") == 0) music->loop_start = _Mix_ParseTime(value, 48000); else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) { music->loop_len = SDL_strtoll(value, NULL, 10); is_loop_length = SDL_TRUE; } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) { music->loop_end = _Mix_ParseTime(value, 48000); is_loop_length = SDL_FALSE; } else if (SDL_strcasecmp(argument, "TITLE") == 0) { meta_tags_set(&music->tags, MIX_META_TITLE, value); } else if (SDL_strcasecmp(argument, "ARTIST") == 0) { meta_tags_set(&music->tags, MIX_META_ARTIST, value); } else if (SDL_strcasecmp(argument, "ALBUM") == 0) { meta_tags_set(&music->tags, MIX_META_ALBUM, value); } else if (SDL_strcasecmp(argument, "COPYRIGHT") == 0) { meta_tags_set(&music->tags, MIX_META_COPYRIGHT, value); } SDL_free(param); } if (is_loop_length) { music->loop_end = music->loop_start + music->loop_len; } else { music->loop_len = music->loop_end - music->loop_start; } /* Ignore invalid loop tag */ if (music->loop_start < 0 || music->loop_len < 0 || music->loop_end < 0) { music->loop_start = 0; music->loop_len = 0; music->loop_end = 0; } } full_length = opus.op_pcm_total(music->of, -1); if ((music->loop_end > 0) && (music->loop_end <= full_length) && (music->loop_start < music->loop_end)) { music->loop = 1; } music->full_length = full_length; music->freesrc = freesrc; return music; } static const char* OPUS_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { OPUS_music *music = (OPUS_music *)context; return meta_tags_get(&music->tags, tag_type); } /* Set the volume for an Opus stream */ static void OPUS_SetVolume(void *context, int volume) { OPUS_music *music = (OPUS_music *)context; music->volume = volume; } /* Get the volume for an Opus stream */ static int OPUS_GetVolume(void *context) { OPUS_music *music = (OPUS_music *)context; return music->volume; } /* Start playback of a given Opus stream */ static int OPUS_Play(void *context, int play_count) { OPUS_music *music = (OPUS_music *)context; music->play_count = play_count; return OPUS_Seek(music, 0.0); } /* Clean-up the output buffer */ static void OPUS_Stop(void *context) { OPUS_music *music = (OPUS_music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with OPUS_Play() */ static int OPUS_GetSome(void *context, void *data, int bytes, SDL_bool *done) { OPUS_music *music = (OPUS_music *)context; int filled, samples, section; int result; SDL_bool looped = SDL_FALSE; ogg_int64_t pcmPos; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } section = music->section; samples = opus.op_read(music->of, (opus_int16 *)music->buffer, music->buffer_size / (int)sizeof(opus_int16), §ion); if (samples < 0) { return set_op_error("op_read", samples); } if (section != music->section) { music->section = section; if (OPUS_UpdateSection(music) < 0) { return -1; } } pcmPos = opus.op_pcm_tell(music->of); if (music->loop && (music->play_count != 1) && (pcmPos >= music->loop_end)) { samples -= (int)((pcmPos - music->loop_end) * music->op_info->channel_count) * (int)sizeof(Sint16); result = opus.op_pcm_seek(music->of, music->loop_start); if (result < 0) { return set_op_error("ov_pcm_seek", result); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } music->play_count = play_count; } looped = SDL_TRUE; } if (samples > 0) { filled = samples * music->op_info->channel_count * 2; if (SDL_AudioStreamPut(music->stream, music->buffer, filled) < 0) { return -1; } } else if (!looped) { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (OPUS_Play(music, play_count) < 0) { return -1; } } } return 0; } static int OPUS_GetAudio(void *context, void *data, int bytes) { OPUS_music *music = (OPUS_music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, OPUS_GetSome); } /* Jump (seek) to a given position (time is in seconds) */ static int OPUS_Seek(void *context, double time) { OPUS_music *music = (OPUS_music *)context; int result = opus.op_pcm_seek(music->of, (ogg_int64_t)(time * 48000)); if (result < 0) { return set_op_error("op_pcm_seek", result); } return 0; } static double OPUS_Tell(void *context) { OPUS_music *music = (OPUS_music *)context; return (double)(opus.op_pcm_tell(music->of)) / 48000.0; } /* Return music duration in seconds */ static double OPUS_Duration(void *context) { OPUS_music *music = (OPUS_music *)context; return music->full_length / 48000.0; } static double OPUS_LoopStart(void *music_p) { OPUS_music *music = (OPUS_music *)music_p; if (music->loop > 0) { return (double)music->loop_start / 48000.0; } return -1.0; } static double OPUS_LoopEnd(void *music_p) { OPUS_music *music = (OPUS_music *)music_p; if (music->loop > 0) { return (double)music->loop_end / 48000.0; } return -1.0; } static double OPUS_LoopLength(void *music_p) { OPUS_music *music = (OPUS_music *)music_p; if (music->loop > 0) { return (double)music->loop_len / 48000.0; } return -1.0; } /* Close the given Opus stream */ static void OPUS_Delete(void *context) { OPUS_music *music = (OPUS_music *)context; meta_tags_clear(&music->tags); opus.op_free(music->of); if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->src); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_Opus = { "OPUS", MIX_MUSIC_OPUS, MUS_OPUS, SDL_FALSE, SDL_FALSE, OPUS_Load, NULL, /* Open */ OPUS_CreateFromRW, NULL, /* CreateFromFile */ OPUS_SetVolume, OPUS_GetVolume, OPUS_Play, NULL, /* IsPlaying */ OPUS_GetAudio, NULL, /* Jump */ OPUS_Seek, OPUS_Tell, OPUS_Duration, OPUS_LoopStart, OPUS_LoopEnd, OPUS_LoopLength, OPUS_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ OPUS_Stop, OPUS_Delete, NULL, /* Close */ OPUS_Unload }; #endif /* MUSIC_OPUS */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_wav.c0000644000076500000240000011551414551252471016636 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_WAV /* This file supports streaming WAV files */ #include "music_wav.h" #include "mp3utils.h" typedef struct { SDL_bool active; Uint32 start; Uint32 stop; Uint32 initial_play_count; Uint32 current_play_count; } WAVLoopPoint; typedef struct { SDL_RWops *src; int freesrc; SDL_AudioSpec spec; int volume; int play_count; Sint64 start; Sint64 stop; Sint64 samplesize; Uint8 *buffer; SDL_AudioStream *stream; unsigned int numloops; WAVLoopPoint *loops; Mix_MusicMetaTags tags; Uint16 encoding; int (*decode)(void *music, int length); } WAV_Music; /* Taken with permission from SDL_wave.h, part of the SDL library, available at: http://www.libsdl.org/ and placed under the same license as this mixer library. */ /* WAVE files are little-endian */ /*******************************************/ /* Define values for Microsoft WAVE format */ /*******************************************/ #define RIFF 0x46464952 /* "RIFF" */ #define WAVE 0x45564157 /* "WAVE" */ #define FMT 0x20746D66 /* "fmt " */ #define DATA 0x61746164 /* "data" */ #define SMPL 0x6c706d73 /* "smpl" */ #define LIST 0x5453494c /* "LIST" */ #define ID3_ 0x20336469 /* "id3 " */ #define PCM_CODE 1 /* WAVE_FORMAT_PCM */ #define ADPCM_CODE 2 /* WAVE_FORMAT_ADPCM */ #define FLOAT_CODE 3 /* WAVE_FORMAT_IEEE_FLOAT */ #define ALAW_CODE 6 /* WAVE_FORMAT_ALAW */ #define uLAW_CODE 7 /* WAVE_FORMAT_MULAW */ #define EXT_CODE 0xFFFE /* WAVE_FORMAT_EXTENSIBLE */ #define WAVE_MONO 1 #define WAVE_STEREO 2 #pragma pack(push, 1) typedef struct { /* Not saved in the chunk we read: Uint32 chunkID; Uint32 chunkLen; */ Uint16 encoding; Uint16 channels; /* 1 = mono, 2 = stereo */ Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */ Uint32 byterate; /* Average bytes per second */ Uint16 blockalign; /* Bytes per sample block */ Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */ } WaveFMT; typedef struct { WaveFMT format; Uint16 cbSize; union { Uint16 validbitspersample; /* bits of precision */ Uint16 samplesperblock; /* valid if wBitsPerSample==0 */ Uint16 reserved; /* If neither applies, set to zero. */ } Samples; Uint32 channelsmask; /* GUID subFormat 16 bytes */ Uint32 subencoding; Uint16 sub_data2; Uint16 sub_data3; Uint8 sub_data[8]; } WaveFMTEx; typedef struct { Uint32 identifier; Uint32 type; Uint32 start; Uint32 end; Uint32 fraction; Uint32 play_count; } SampleLoop; typedef struct { /* Not saved in the chunk we read: Uint32 chunkID; Uint32 chunkLen; */ Uint32 manufacturer; Uint32 product; Uint32 sample_period; Uint32 MIDI_unity_note; Uint32 MIDI_pitch_fraction; Uint32 SMTPE_format; Uint32 SMTPE_offset; Uint32 sample_loops; Uint32 sampler_data; SampleLoop loops[1]; } SamplerChunk; #pragma pack(pop) /*********************************************/ /* Define values for AIFF (IFF audio) format */ /*********************************************/ #define FORM 0x4d524f46 /* "FORM" */ #define AIFF 0x46464941 /* "AIFF" */ #define AIFC 0x43464941 /* "AIFС" */ #define FVER 0x52455646 /* "FVER" */ #define SSND 0x444e5353 /* "SSND" */ #define COMM 0x4d4d4f43 /* "COMM" */ #define AIFF_ID3_ 0x20334449 /* "ID3 " */ #define MARK 0x4B52414D /* "MARK" */ #define INST 0x54534E49 /* "INST" */ #define AUTH 0x48545541 /* "AUTH" */ #define NAME 0x454D414E /* "NAME" */ #define _c__ 0x20296328 /* "(c) " */ /* Supported compression types */ #define NONE 0x454E4F4E /* "NONE" */ #define sowt 0x74776F73 /* "sowt" */ #define raw_ 0x20776172 /* "raw " */ #define ulaw 0x77616C75 /* "ulaw" */ #define alaw 0x77616C61 /* "alaw" */ #define ULAW 0x57414C55 /* "ULAW" */ #define ALAW 0x57414C41 /* "ALAW" */ #define fl32 0x32336C66 /* "fl32" */ #define fl64 0x34366C66 /* "fl64" */ #define FL32 0x32334C46 /* "FL32" */ /* Function to load the WAV/AIFF stream */ static SDL_bool LoadWAVMusic(WAV_Music *wave); static SDL_bool LoadAIFFMusic(WAV_Music *wave); static void WAV_Delete(void *context); static int fetch_pcm(void *context, int length); /* Load a WAV stream from the given RWops object */ static void *WAV_CreateFromRW(SDL_RWops *src, int freesrc) { WAV_Music *music; Uint32 magic; SDL_bool loaded = SDL_FALSE; music = (WAV_Music *)SDL_calloc(1, sizeof(*music)); if (!music) { Mix_OutOfMemory(); return NULL; } music->src = src; music->volume = MIX_MAX_VOLUME; /* Default decoder is PCM */ music->decode = fetch_pcm; music->encoding = PCM_CODE; magic = SDL_ReadLE32(src); if (magic == RIFF || magic == WAVE) { loaded = LoadWAVMusic(music); } else if (magic == FORM) { loaded = LoadAIFFMusic(music); } else { Mix_SetError("Unknown WAVE format"); } if (!loaded) { WAV_Delete(music); return NULL; } music->buffer = (Uint8*)SDL_malloc(music->spec.size); if (!music->buffer) { Mix_OutOfMemory(); WAV_Delete(music); return NULL; } music->stream = SDL_NewAudioStream( music->spec.format, music->spec.channels, music->spec.freq, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { WAV_Delete(music); return NULL; } music->freesrc = freesrc; return music; } static void WAV_SetVolume(void *context, int volume) { WAV_Music *music = (WAV_Music *)context; music->volume = volume; } static int WAV_GetVolume(void *context) { WAV_Music *music = (WAV_Music *)context; return music->volume; } /* Start playback of a given WAV stream */ static int WAV_Play(void *context, int play_count) { WAV_Music *music = (WAV_Music *)context; unsigned int i; for (i = 0; i < music->numloops; ++i) { WAVLoopPoint *loop = &music->loops[i]; loop->active = SDL_TRUE; loop->current_play_count = loop->initial_play_count; } music->play_count = play_count; if (SDL_RWseek(music->src, music->start, RW_SEEK_SET) < 0) { return -1; } return 0; } static void WAV_Stop(void *context) { WAV_Music *music = (WAV_Music *)context; SDL_AudioStreamClear(music->stream); } static int fetch_pcm(void *context, int length) { WAV_Music *music = (WAV_Music *)context; return (int)SDL_RWread(music->src, music->buffer, 1, (size_t)length); } static Uint32 PCM_S24_to_S32_BE(Uint8 *x) { const Uint32 bits = 24; Uint32 in = (((Uint32)x[0] << 0) & 0x0000FF) | (((Uint32)x[1] << 8) & 0x00FF00) | (((Uint32)x[2] << 16) & 0xFF0000); Uint32 m = 1u << (bits - 1); return (in ^ m) - m; } static Uint32 PCM_S24_to_S32_LE(Uint8 *x) { const Uint32 bits = 24; Uint32 in = (((Uint32)x[2] << 0) & 0x0000FF) | (((Uint32)x[1] << 8) & 0x00FF00) | (((Uint32)x[0] << 16) & 0xFF0000); Uint32 m = 1u << (bits - 1); return (in ^ m) - m; } static int fetch_pcm24be(void *context, int length) { WAV_Music *music = (WAV_Music *)context; int i = 0, o = 0; length = (int)SDL_RWread(music->src, music->buffer, 1, (size_t)((length / 4) * 3)); if (length % music->samplesize != 0) { length -= length % music->samplesize; } for (i = length - 3, o = ((length - 3) / 3) * 4; i >= 0; i -= 3, o -= 4) { Uint32 decoded = PCM_S24_to_S32_BE(music->buffer + i); music->buffer[o + 0] = (decoded >> 0) & 0xFF; music->buffer[o + 1] = (decoded >> 8) & 0xFF; music->buffer[o + 2] = (decoded >> 16) & 0xFF; music->buffer[o + 3] = (decoded >> 24) & 0xFF; } return (length / 3) * 4; } static int fetch_pcm24le(void *context, int length) { WAV_Music *music = (WAV_Music *)context; int i = 0, o = 0; length = (int)SDL_RWread(music->src, music->buffer, 1, (size_t)((length / 4) * 3)); if (length % music->samplesize != 0) { length -= length % music->samplesize; } for (i = length - 3, o = ((length - 3) / 3) * 4; i >= 0; i -= 3, o -= 4) { Uint32 decoded = PCM_S24_to_S32_LE(music->buffer + i); music->buffer[o + 3] = (decoded >> 0) & 0xFF; music->buffer[o + 2] = (decoded >> 8) & 0xFF; music->buffer[o + 1] = (decoded >> 16) & 0xFF; music->buffer[o + 0] = (decoded >> 24) & 0xFF; } return (length / 3) * 4; } SDL_FORCE_INLINE double Mix_SwapDouble(double x) { union { double f; Uint64 ui64; } swapper; swapper.f = x; swapper.ui64 = SDL_Swap64(swapper.ui64); return swapper.f; } #if SDL_BYTEORDER == SDL_LIL_ENDIAN #define Mix_SwapDoubleLE(X) (X) #define Mix_SwapDoubleBE(X) Mix_SwapDouble(X) #else #define Mix_SwapDoubleLE(X) Mix_SwapDouble(X) #define Mix_SwapDoubleBE(X) (X) #endif static int fetch_float64be(void *context, int length) { WAV_Music *music = (WAV_Music *)context; int i = 0, o = 0; length = (int)SDL_RWread(music->src, music->buffer, 1, (size_t)(length)); if (length % music->samplesize != 0) { length -= length % music->samplesize; } for (i = 0, o = 0; i < length; i += 8, o += 4) { union { float f; Uint32 ui32; } sample; sample.f = (float)Mix_SwapDoubleBE(*(double*)(music->buffer + i)); music->buffer[o + 0] = (sample.ui32 >> 0) & 0xFF; music->buffer[o + 1] = (sample.ui32 >> 8) & 0xFF; music->buffer[o + 2] = (sample.ui32 >> 16) & 0xFF; music->buffer[o + 3] = (sample.ui32 >> 24) & 0xFF; } return length / 2; } static int fetch_float64le(void *context, int length) { WAV_Music *music = (WAV_Music *)context; int i = 0, o = 0; length = (int)SDL_RWread(music->src, music->buffer, 1, (size_t)(length)); if (length % music->samplesize != 0) { length -= length % music->samplesize; } for (i = 0, o = 0; i < length; i += 8, o += 4) { union { float f; Uint32 ui32; } sample; sample.f = (float)Mix_SwapDoubleLE(*(double*)(music->buffer + i)); music->buffer[o + 0] = (sample.ui32 >> 0) & 0xFF; music->buffer[o + 1] = (sample.ui32 >> 8) & 0xFF; music->buffer[o + 2] = (sample.ui32 >> 16) & 0xFF; music->buffer[o + 3] = (sample.ui32 >> 24) & 0xFF; } return length / 2; } /* G711 decode tables taken from SDL2 (src/audio/SDL_wave.c) */ #ifdef SDL_WAVE_LAW_LUT static const Sint16 alaw_lut[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, -344, -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 }; static const Sint16 mulaw_lut[256] = { -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 }; #endif static Sint16 uLAW_To_PCM16(Uint8 u_val) { #ifdef SDL_WAVE_LAW_LUT return mulaw_lut[u_val]; #else Uint8 nibble = ~u_val; Sint16 mantissa = nibble & 0xf; Uint8 exponent = (nibble >> 4) & 0x7; Sint16 step = (Sint16)(4 << (exponent + 1)); mantissa = (Sint16)(0x80 << exponent) + step * mantissa + step / 2 - 132; return nibble & 0x80 ? -mantissa : mantissa; #endif } static Sint16 ALAW_To_PCM16(Uint8 a_val) { #ifdef SDL_WAVE_LAW_LUT return alaw_lut[a_val]; #else Uint8 nibble = a_val; Uint8 exponent = (nibble & 0x7f) ^ 0x55; Sint16 mantissa = exponent & 0xf; exponent >>= 4; if (exponent > 0) { mantissa |= 0x10; } mantissa = (Sint16)(mantissa << 4) | 0x8; if (exponent > 1) { mantissa <<= exponent - 1; } return nibble & 0x80 ? mantissa : -mantissa; #endif } static int fetch_xlaw(Sint16 (*decode_sample)(Uint8), void *context, int length) { WAV_Music *music = (WAV_Music *)context; int i = 0, o = 0; length = (int)SDL_RWread(music->src, music->buffer, 1, (size_t)(length / 2)); if (length % music->samplesize != 0) { length -= length % music->samplesize; } for (i = length - 1, o = (length - 1) * 2; i >= 0; i--, o -= 2) { Uint16 decoded = (Uint16)decode_sample(music->buffer[i]); music->buffer[o] = decoded & 0xFF; music->buffer[o + 1] = (decoded >> 8) & 0xFF; } return length * 2; } static int fetch_ulaw(void *context, int length) { return fetch_xlaw(uLAW_To_PCM16, context, length); } static int fetch_alaw(void *context, int length) { return fetch_xlaw(ALAW_To_PCM16, context, length); } /* Play some of a stream previously started with WAV_Play() */ static int WAV_GetSome(void *context, void *data, int bytes, SDL_bool *done) { WAV_Music *music = (WAV_Music *)context; Sint64 pos, stop; WAVLoopPoint *loop; Sint64 loop_start = music->start; Sint64 loop_stop = music->stop; SDL_bool looped = SDL_FALSE; SDL_bool at_end = SDL_FALSE; unsigned int i; int filled, amount, result; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } pos = SDL_RWtell(music->src); stop = music->stop; loop = NULL; for (i = 0; i < music->numloops; ++i) { loop = &music->loops[i]; if (loop->active) { const int bytes_per_sample = (SDL_AUDIO_BITSIZE(music->spec.format) / 8) * music->spec.channels; loop_start = music->start + loop->start * (Uint32)bytes_per_sample; loop_stop = music->start + (loop->stop + 1) * (Uint32)bytes_per_sample; if (pos >= loop_start && pos < loop_stop) { stop = loop_stop; break; } } loop = NULL; } amount = (int)music->spec.size; if ((stop - pos) < amount) { amount = (int)(stop - pos); } amount = music->decode(music, amount); if (amount > 0) { result = SDL_AudioStreamPut(music->stream, music->buffer, amount); if (result < 0) { return -1; } } else { /* We might be looping, continue */ at_end = SDL_TRUE; } if (loop && SDL_RWtell(music->src) >= stop) { if (loop->current_play_count == 1) { loop->active = SDL_FALSE; } else { if (loop->current_play_count > 0) { --loop->current_play_count; } if (SDL_RWseek(music->src, loop_start, RW_SEEK_SET) < 0) return -1; looped = SDL_TRUE; } } if (!looped && (at_end || SDL_RWtell(music->src) >= music->stop)) { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (WAV_Play(music, play_count) < 0) { return -1; } } } /* We'll get called again in the case where we looped or have more data */ return 0; } static int WAV_GetAudio(void *context, void *data, int bytes) { WAV_Music *music = (WAV_Music *)context; return music_pcm_getaudio(context, data, bytes, music->volume, WAV_GetSome); } static int WAV_Seek(void *context, double position) { WAV_Music *music = (WAV_Music *)context; Sint64 sample_size = music->spec.freq * music->samplesize; Sint64 dest_offset = (Sint64)(position * (double)music->spec.freq * music->samplesize); Sint64 destpos = music->start + dest_offset; destpos -= dest_offset % sample_size; if (destpos > music->stop) return -1; if (SDL_RWseek(music->src, destpos, RW_SEEK_SET) < 0) return -1; return 0; } static double WAV_Tell(void *context) { WAV_Music *music = (WAV_Music *)context; Sint64 phys_pos = SDL_RWtell(music->src); return (double)(phys_pos - music->start) / (double)(music->spec.freq * music->samplesize); } /* Return music duration in seconds */ static double WAV_Duration(void *context) { WAV_Music *music = (WAV_Music *)context; Sint64 sample_size = music->spec.freq * music->samplesize; return (double)(music->stop - music->start) / sample_size; } static const char* WAV_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { WAV_Music *music = (WAV_Music *)context; return meta_tags_get(&music->tags, tag_type); } /* Close the given WAV stream */ static void WAV_Delete(void *context) { WAV_Music *music = (WAV_Music *)context; /* Clean up associated data */ meta_tags_clear(&music->tags); if (music->loops) { SDL_free(music->loops); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } if (music->freesrc) { SDL_RWclose(music->src); } SDL_free(music); } static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length) { SDL_AudioSpec *spec = &wave->spec; WaveFMTEx fmt; size_t size; int bits; if (chunk_length < sizeof(fmt.format)) { Mix_SetError("Wave format chunk too small"); return SDL_FALSE; } size = (chunk_length >= sizeof(fmt)) ? sizeof(fmt) : sizeof(fmt.format); if (!SDL_RWread(wave->src, &fmt, size, 1)) { Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length); return SDL_FALSE; } chunk_length = (Uint32)(chunk_length - size); if (chunk_length != 0 && SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR) < 0) { Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length); return SDL_FALSE; } wave->encoding = SDL_SwapLE16(fmt.format.encoding); if (wave->encoding == EXT_CODE) { if (size < sizeof(fmt)) { Mix_SetError("Wave format chunk too small"); return SDL_FALSE; } wave->encoding = (Uint16)SDL_SwapLE32(fmt.subencoding); } /* Decode the audio data format */ switch (wave->encoding) { case PCM_CODE: case FLOAT_CODE: /* We can understand this */ wave->decode = fetch_pcm; break; case uLAW_CODE: /* , this */ wave->decode = fetch_ulaw; break; case ALAW_CODE: /* , and this */ wave->decode = fetch_alaw; break; default: /* but NOT this */ Mix_SetError("Unknown WAVE data format"); return SDL_FALSE; } spec->freq = (int)SDL_SwapLE32(fmt.format.frequency); bits = (int) SDL_SwapLE16(fmt.format.bitspersample); switch (bits) { case 8: switch(wave->encoding) { case PCM_CODE: spec->format = AUDIO_U8; break; case ALAW_CODE: spec->format = AUDIO_S16; break; case uLAW_CODE: spec->format = AUDIO_S16; break; default: goto unknown_bits; } break; case 16: switch(wave->encoding) { case PCM_CODE: spec->format = AUDIO_S16; break; default: goto unknown_bits; } break; case 24: switch(wave->encoding) { case PCM_CODE: wave->decode = fetch_pcm24le; spec->format = AUDIO_S32; break; default: goto unknown_bits; } break; case 32: switch(wave->encoding) { case PCM_CODE: spec->format = AUDIO_S32; break; case FLOAT_CODE: spec->format = AUDIO_F32; break; default: goto unknown_bits; } break; case 64: switch(wave->encoding) { case FLOAT_CODE: wave->decode = fetch_float64le; spec->format = AUDIO_F32; break; default: goto unknown_bits; } break; default: unknown_bits: Mix_SetError("Unknown PCM format with %d bits", bits); return SDL_FALSE; } spec->channels = (Uint8) SDL_SwapLE16(fmt.format.channels); spec->samples = 4096; /* Good default buffer size */ wave->samplesize = spec->channels * (bits / 8); /* SDL_CalculateAudioSpec */ spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8; spec->size *= spec->channels; spec->size *= spec->samples; return SDL_TRUE; } static SDL_bool ParseDATA(WAV_Music *wave, Uint32 chunk_length) { wave->start = SDL_RWtell(wave->src); wave->stop = wave->start + chunk_length; if (SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR) < 0) return SDL_FALSE; return SDL_TRUE; } static SDL_bool AddLoopPoint(WAV_Music *wave, Uint32 play_count, Uint32 start, Uint32 stop) { WAVLoopPoint *loop; WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1) * sizeof(*wave->loops)); if (!loops) { Mix_OutOfMemory(); return SDL_FALSE; } loop = &loops[ wave->numloops ]; loop->start = start; loop->stop = stop; loop->initial_play_count = play_count; loop->current_play_count = play_count; wave->loops = loops; ++wave->numloops; return SDL_TRUE; } static SDL_bool ParseSMPL(WAV_Music *wave, Uint32 chunk_length) { SamplerChunk *chunk; Uint8 *data; Uint32 i; SDL_bool loaded = SDL_FALSE; data = (Uint8 *)SDL_malloc(chunk_length); if (!data) { Mix_OutOfMemory(); return SDL_FALSE; } if (!SDL_RWread(wave->src, data, chunk_length, 1)) { Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length); SDL_free(data); return SDL_FALSE; } chunk = (SamplerChunk *)data; for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) { const Uint32 LOOP_TYPE_FORWARD = 0; Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type); if (loop_type == LOOP_TYPE_FORWARD) { AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end)); } } loaded = SDL_TRUE; SDL_free(data); return loaded; } static void read_meta_field(Mix_MusicMetaTags *tags, Mix_MusicMetaTag tag_type, size_t *i, Uint32 chunk_length, Uint8 *data, size_t fieldOffset) { Uint32 len = 0; int isID3 = fieldOffset == 7; char *field = NULL; *i += 4; len = isID3 ? SDL_SwapBE32(*((Uint32 *)(data + *i))) : /* ID3 */ SDL_SwapLE32(*((Uint32 *)(data + *i))); /* LIST */ if (len > chunk_length) { return; /* Do nothing due to broken lenght */ } *i += fieldOffset; field = (char *)SDL_malloc(len + 1); SDL_memset(field, 0, (len + 1)); SDL_strlcpy(field, (char *)(data + *i), isID3 ? len - 1 : len); *i += len; meta_tags_set(tags, tag_type, field); SDL_free(field); } static SDL_bool ParseLIST(WAV_Music *wave, Uint32 chunk_length) { Uint8 *data; data = (Uint8 *)SDL_malloc(chunk_length); if (!data) { Mix_OutOfMemory(); return SDL_FALSE; } if (!SDL_RWread(wave->src, data, chunk_length, 1)) { Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length); SDL_free(data); return SDL_FALSE; } if (SDL_strncmp((char *)data, "INFO", 4) == 0) { size_t i = 4; for (i = 4; i < chunk_length - 4;) { if(SDL_strncmp((char *)(data + i), "INAM", 4) == 0) { read_meta_field(&wave->tags, MIX_META_TITLE, &i, chunk_length, data, 4); continue; } else if(SDL_strncmp((char *)(data + i), "IART", 4) == 0) { read_meta_field(&wave->tags, MIX_META_ARTIST, &i, chunk_length, data, 4); continue; } else if(SDL_strncmp((char *)(data + i), "IALB", 4) == 0) { read_meta_field(&wave->tags, MIX_META_ALBUM, &i, chunk_length, data, 4); continue; } else if (SDL_strncmp((char *)(data + i), "BCPR", 4) == 0) { read_meta_field(&wave->tags, MIX_META_COPYRIGHT, &i, chunk_length, data, 4); continue; } i++; } } /* done: */ SDL_free(data); return SDL_TRUE; } static SDL_bool ParseID3(WAV_Music *wave, Uint32 chunk_length) { SDL_bool loaded = SDL_TRUE; Uint8 *data; data = (Uint8 *)SDL_malloc(chunk_length); if (!data) { SDL_OutOfMemory(); return SDL_FALSE; } if (!SDL_RWread(wave->src, data, chunk_length, 1)) { Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length); loaded = SDL_FALSE; } if (loaded) { read_id3v2_from_mem(&wave->tags, data, chunk_length); } /* done: */ SDL_free(data); return loaded; } static SDL_bool LoadWAVMusic(WAV_Music *wave) { SDL_RWops *src = wave->src; Uint32 chunk_type; Uint32 chunk_length; SDL_bool found_FMT = SDL_FALSE; SDL_bool found_DATA = SDL_FALSE; /* WAV magic header */ Uint32 wavelen; Uint32 WAVEmagic; meta_tags_init(&wave->tags); /* Check the magic header */ wavelen = SDL_ReadLE32(src); WAVEmagic = SDL_ReadLE32(src); (void)wavelen; /* unused */ (void)WAVEmagic; /* unused */ /* Read the chunks */ for (; ;) { chunk_type = SDL_ReadLE32(src); chunk_length = SDL_ReadLE32(src); if (chunk_length == 0) break; switch (chunk_type) { case FMT: found_FMT = SDL_TRUE; if (!ParseFMT(wave, chunk_length)) return SDL_FALSE; break; case DATA: found_DATA = SDL_TRUE; if (!ParseDATA(wave, chunk_length)) return SDL_FALSE; break; case SMPL: if (!ParseSMPL(wave, chunk_length)) return SDL_FALSE; break; case LIST: if (!ParseLIST(wave, chunk_length)) return SDL_FALSE; break; case ID3_: if (!ParseID3(wave, chunk_length)) return SDL_FALSE; break; default: if (SDL_RWseek(src, chunk_length, RW_SEEK_CUR) < 0) return SDL_FALSE; break; } /* RIFF chunks have a 2-byte alignment. Skip padding byte. */ if (chunk_length & 1) { if (SDL_RWseek(src, 1, RW_SEEK_CUR) < 0) return SDL_FALSE; } } if (!found_FMT) { Mix_SetError("Bad WAV file (no FMT chunk)"); return SDL_FALSE; } if (!found_DATA) { Mix_SetError("Bad WAV file (no DATA chunk)"); return SDL_FALSE; } return SDL_TRUE; } /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile. * I don't pretend to fully understand it. */ static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) { /* Negative number? */ if (sanebuf[0] & 0x80) return 0; /* Less than 1? */ if (sanebuf[0] <= 0x3F) return 1; /* Way too big? */ if (sanebuf[0] > 0x40) return 0x4000000; /* Still too big? */ if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) return 800000000; return (Uint32)(((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) | (sanebuf[5] >> 1)) >> (29 - sanebuf[1])); } static SDL_bool LoadAIFFMusic(WAV_Music *wave) { SDL_RWops *src = wave->src; SDL_AudioSpec *spec = &wave->spec; SDL_bool found_SSND = SDL_FALSE; SDL_bool found_COMM = SDL_FALSE; SDL_bool found_FVER = SDL_FALSE; SDL_bool is_AIFC = SDL_FALSE; Uint32 chunk_type; Uint32 chunk_length; Sint64 next_chunk = 0; Sint64 file_length; /* AIFF magic header */ Uint32 AIFFmagic; /* SSND chunk */ Uint32 offset; Uint32 blocksize; /* COMM format chunk */ Uint16 channels = 0; Uint32 numsamples = 0; Uint16 samplesize = 0; Uint8 sane_freq[10]; Uint32 frequency = 0; Uint32 AIFCVersion1 = 0; Uint32 compressionType = 0; char *chunk_buffer; file_length = SDL_RWsize(src); /* Check the magic header */ chunk_length = SDL_ReadBE32(src); AIFFmagic = SDL_ReadLE32(src); if (AIFFmagic != AIFF && AIFFmagic != AIFC) { Mix_SetError("Unrecognized file type (not AIFF or AIFC)"); return SDL_FALSE; } if (AIFFmagic == AIFC) { is_AIFC = SDL_TRUE; } /* From what I understand of the specification, chunks may appear in * any order, and we should just ignore unknown ones. * * TODO: Better sanity-checking. E.g. what happens if the AIFF file * contains compressed sound data? */ do { chunk_type = SDL_ReadLE32(src); chunk_length = SDL_ReadBE32(src); next_chunk = SDL_RWtell(src) + chunk_length; if (chunk_length % 2) { next_chunk++; } switch (chunk_type) { case SSND: found_SSND = SDL_TRUE; offset = SDL_ReadBE32(src); blocksize = SDL_ReadBE32(src); wave->start = SDL_RWtell(src) + offset; (void)blocksize; /* unused */ break; case FVER: found_FVER = SDL_TRUE; AIFCVersion1 = SDL_ReadBE32(src); (void)AIFCVersion1; /* unused */ break; case AIFF_ID3_: if (!ParseID3(wave, chunk_length)) return SDL_FALSE; break; case MARK: case INST: /* Just skip those chunks */ break; case NAME: case AUTH: case _c__: chunk_buffer = (char*)SDL_calloc(1, chunk_length + 1); if (SDL_RWread(src, chunk_buffer, 1, chunk_length) != chunk_length) { SDL_free(chunk_buffer); return SDL_FALSE; } meta_tags_set(&wave->tags, chunk_type == NAME ? MIX_META_TITLE : chunk_type == AUTH ? MIX_META_ARTIST : chunk_type == _c__ ? MIX_META_COPYRIGHT : 0, chunk_buffer); SDL_free(chunk_buffer); break; case COMM: found_COMM = SDL_TRUE; /* Read the audio data format chunk */ channels = SDL_ReadBE16(src); numsamples = SDL_ReadBE32(src); samplesize = SDL_ReadBE16(src); SDL_RWread(src, sane_freq, sizeof(sane_freq), 1); frequency = SANE_to_Uint32(sane_freq); if (is_AIFC) { compressionType = SDL_ReadLE32(src); /* here must be a "compressionName" which is a padded string */ } break; default: /* Unknown/unsupported chunk: we just skip over */ break; } } while (next_chunk < file_length && SDL_RWseek(src, next_chunk, RW_SEEK_SET) >= 0); if (!found_SSND) { Mix_SetError("Bad AIFF/AIFF-C file (no SSND chunk)"); return SDL_FALSE; } if (!found_COMM) { Mix_SetError("Bad AIFF/AIFF-C file (no COMM chunk)"); return SDL_FALSE; } if (is_AIFC && !found_FVER) { Mix_SetError("Bad AIFF-C file (no FVER chunk)"); return SDL_FALSE; } wave->samplesize = channels * (samplesize / 8); wave->stop = wave->start + channels * numsamples * (samplesize / 8); /* Decode the audio data format */ SDL_memset(spec, 0, (sizeof *spec)); spec->freq = (int)frequency; switch (samplesize) { case 8: if (!is_AIFC) spec->format = AUDIO_S8; else switch (compressionType) { case raw_: spec->format = AUDIO_U8; break; case sowt: spec->format = AUDIO_S8; break; case ulaw: spec->format = AUDIO_S16LSB; wave->encoding = uLAW_CODE; wave->decode = fetch_ulaw; break; case alaw: spec->format = AUDIO_S16LSB; wave->encoding = ALAW_CODE; wave->decode = fetch_alaw; break; default: goto unsupported_format; } break; case 16: if (!is_AIFC) spec->format = AUDIO_S16MSB; else switch (compressionType) { case sowt: spec->format = AUDIO_S16LSB; break; case NONE: spec->format = AUDIO_S16MSB; break; case ULAW: spec->format = AUDIO_S16LSB; wave->encoding = uLAW_CODE; wave->decode = fetch_ulaw; break; case ALAW: spec->format = AUDIO_S16LSB; wave->encoding = ALAW_CODE; wave->decode = fetch_alaw; break; default: goto unsupported_format; } break; case 24: wave->encoding = PCM_CODE; wave->decode = fetch_pcm24be; if (!is_AIFC) spec->format = AUDIO_S32MSB; else switch (compressionType) { case sowt: spec->format = AUDIO_S32LSB; break; case NONE: spec->format = AUDIO_S32MSB; break; default: goto unsupported_format; } break; case 32: if (!is_AIFC) spec->format = AUDIO_S32MSB; else switch (compressionType) { case sowt: spec->format = AUDIO_S32LSB; break; case NONE: spec->format = AUDIO_S32MSB; break; case fl32: case FL32: spec->format = AUDIO_F32MSB; break; default: goto unsupported_format; } break; case 64: wave->encoding = FLOAT_CODE; wave->decode = fetch_float64be; if (!is_AIFC) spec->format = AUDIO_F32; else switch (compressionType) { case fl64: spec->format = AUDIO_F32; break; default: goto unsupported_format; } break; default: unsupported_format: Mix_SetError("Unknown samplesize in data format"); return SDL_FALSE; } spec->channels = (Uint8) channels; spec->samples = 4096; /* Good default buffer size */ spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8; spec->size *= spec->channels; spec->size *= spec->samples; return SDL_TRUE; } Mix_MusicInterface Mix_MusicInterface_WAV = { "WAVE", MIX_MUSIC_WAVE, MUS_WAV, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ WAV_CreateFromRW, NULL, /* CreateFromFile */ WAV_SetVolume, WAV_GetVolume, WAV_Play, NULL, /* IsPlaying */ WAV_GetAudio, NULL, /* Jump */ WAV_Seek, /* Seek */ WAV_Tell, /* Tell */ WAV_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ WAV_GetMetaTag, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ WAV_Stop, /* Stop */ WAV_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_WAV */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_wavpack.c0000644000076500000240000005720614553251241017474 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if defined(MUSIC_WAVPACK) #define WAVPACK_DBG 0 /* This file supports WavPack music streams */ #include "SDL_loadso.h" #include "SDL_log.h" #include "music_wavpack.h" #if defined(WAVPACK_HEADER) #include WAVPACK_HEADER #elif defined(HAVE_WAVPACK_H) #include #else #include #endif #include /* SEEK_SET, ... */ #ifndef OPEN_DSD_NATIVE #define OPEN_DSD_NATIVE 0x100 #define OPEN_DSD_AS_PCM 0x200 #define WAVPACK4_OR_OLDER #endif #ifdef WAVPACK4_OR_OLDER typedef struct { int32_t (*read_bytes)(void *id, void *data, int32_t bcount); int32_t (*write_bytes)(void *id, void *data, int32_t bcount); int64_t (*get_pos)(void *id); int (*set_pos_abs)(void *id, int64_t pos); int (*set_pos_rel)(void *id, int64_t delta, int mode); int (*push_back_byte)(void *id, int c); int64_t (*get_length)(void *id); int (*can_seek)(void *id); int (*truncate_here)(void *id); int (*close)(void *id); } WavpackStreamReader64; #endif typedef struct { int loaded; void *handle; uint32_t libversion; uint32_t (*WavpackGetLibraryVersion)(void); char *(*WavpackGetErrorMessage)(WavpackContext*); WavpackContext *(*WavpackOpenFileInputEx)(WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); WavpackContext *(*WavpackCloseFile)(WavpackContext*); int (*WavpackGetMode)(WavpackContext*); int (*WavpackGetBytesPerSample)(WavpackContext*); int (*WavpackGetNumChannels)(WavpackContext*); uint32_t (*WavpackGetNumSamples)(WavpackContext*); uint32_t (*WavpackGetSampleRate)(WavpackContext*); uint32_t (*WavpackUnpackSamples)(WavpackContext*, int32_t *buffer, uint32_t samples); int (*WavpackSeekSample)(WavpackContext*, uint32_t sample); uint32_t (*WavpackGetSampleIndex)(WavpackContext*); int (*WavpackGetTagItem)(WavpackContext*, const char *item, char *value, int size); /* WavPack 5.x functions with 64 bit support: */ WavpackContext *(*WavpackOpenFileInputEx64)(WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset); int64_t (*WavpackGetNumSamples64)(WavpackContext*); int64_t (*WavpackGetSampleIndex64)(WavpackContext*); int (*WavpackSeekSample64)(WavpackContext*, int64_t sample); } wavpack_loader; static wavpack_loader wvpk; #ifdef WAVPACK_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ wvpk.FUNC = (SIG) SDL_LoadFunction(wvpk.handle, #FUNC); \ if (wvpk.FUNC == NULL) { SDL_UnloadObject(wvpk.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ wvpk.FUNC = FUNC; \ if (wvpk.FUNC == NULL) { Mix_SetError("Missing wavpack.framework"); return -1; } #endif static int WAVPACK_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (wvpk.loaded == 0) { #ifdef WAVPACK_DYNAMIC wvpk.handle = SDL_LoadObject(WAVPACK_DYNAMIC); if (wvpk.handle == NULL) { return -1; } #endif FUNCTION_LOADER(WavpackGetLibraryVersion, uint32_t (*)(void)); FUNCTION_LOADER(WavpackGetErrorMessage, char *(*)(WavpackContext*)); FUNCTION_LOADER(WavpackOpenFileInputEx, WavpackContext *(*)(WavpackStreamReader*, void*, void*, char*, int, int)); FUNCTION_LOADER(WavpackCloseFile, WavpackContext *(*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetMode, int (*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetBytesPerSample, int (*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetNumChannels, int (*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetNumSamples, uint32_t (*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetSampleRate, uint32_t (*)(WavpackContext*)); FUNCTION_LOADER(WavpackUnpackSamples, uint32_t (*)(WavpackContext*, int32_t*, uint32_t)); FUNCTION_LOADER(WavpackSeekSample, int (*)(WavpackContext*, uint32_t)); FUNCTION_LOADER(WavpackGetSampleIndex, uint32_t (*)(WavpackContext*)); FUNCTION_LOADER(WavpackGetTagItem, int (*)(WavpackContext*, const char*, char*, int)); /* WavPack 5.x functions with 64 bit support: */ #ifdef WAVPACK_DYNAMIC wvpk.WavpackOpenFileInputEx64 = (WavpackContext *(*)(WavpackStreamReader64*, void*, void*, char*, int, int)) SDL_LoadFunction(wvpk.handle, "WavpackOpenFileInputEx64"); wvpk.WavpackGetNumSamples64 = (int64_t (*)(WavpackContext*)) SDL_LoadFunction(wvpk.handle, "WavpackGetNumSamples64"); wvpk.WavpackGetSampleIndex64 = (int64_t (*)(WavpackContext*)) SDL_LoadFunction(wvpk.handle, "WavpackGetSampleIndex64"); wvpk.WavpackSeekSample64 = (int (*)(WavpackContext*, int64_t)) SDL_LoadFunction(wvpk.handle, "WavpackSeekSample64"); if (!wvpk.WavpackOpenFileInputEx64 || !wvpk.WavpackGetNumSamples64 || !wvpk.WavpackGetSampleIndex64 || !wvpk.WavpackSeekSample64) { wvpk.WavpackOpenFileInputEx64 = NULL; wvpk.WavpackGetNumSamples64 = NULL; wvpk.WavpackGetSampleIndex64 = NULL; wvpk.WavpackSeekSample64 = NULL; SDL_ClearError(); /* WavPack 5.x functions are optional. */ } #if WAVPACK_DBG else { SDL_Log("WavPack 5.x functions available"); } #endif #elif !defined(WAVPACK4_OR_OLDER) wvpk.WavpackOpenFileInputEx64 = WavpackOpenFileInputEx64; wvpk.WavpackGetNumSamples64 = WavpackGetNumSamples64; wvpk.WavpackGetSampleIndex64 = WavpackGetSampleIndex64; wvpk.WavpackSeekSample64 = WavpackSeekSample64; #else wvpk.WavpackOpenFileInputEx64 = NULL; wvpk.WavpackGetNumSamples64 = NULL; wvpk.WavpackGetSampleIndex64 = NULL; wvpk.WavpackSeekSample64 = NULL; #endif wvpk.libversion = wvpk.WavpackGetLibraryVersion(); #if WAVPACK_DBG SDL_Log("WavPack library version: 0x%x", wvpk.libversion); #endif } ++wvpk.loaded; return 0; } static void WAVPACK_Unload(void) { if (wvpk.loaded == 0) { return; } if (wvpk.loaded == 1) { #ifdef WAVPACK_DYNAMIC SDL_UnloadObject(wvpk.handle); #endif } --wvpk.loaded; } typedef struct { SDL_RWops *src1; /* wavpack file */ SDL_RWops *src2; /* correction file */ int freesrc; int play_count; int volume; WavpackContext *ctx; int64_t numsamples; uint32_t samplerate; int bps, channels, mode; #ifdef MUSIC_WAVPACK_DSD int decimation; void *decimation_ctx; #endif SDL_AudioStream *stream; void *buffer; int32_t frames; Mix_MusicMetaTags tags; } WAVPACK_music; static int32_t sdl_read_bytes(void *id, void *data, int32_t bcount) { return (int32_t)SDL_RWread((SDL_RWops*)id, data, 1, bcount); } static uint32_t sdl_get_pos32(void *id) { return (uint32_t)SDL_RWtell((SDL_RWops*)id); } static int64_t sdl_get_pos64(void *id) { return SDL_RWtell((SDL_RWops*)id); } static int sdl_setpos_rel64(void *id, int64_t delta, int mode) { switch (mode) { /* just in case SDL_RW doesn't match stdio.. */ case SEEK_SET: mode = RW_SEEK_SET; break; case SEEK_CUR: mode = RW_SEEK_CUR; break; case SEEK_END: mode = RW_SEEK_END; break; default: return -1; } return (SDL_RWseek((SDL_RWops*)id, delta, mode) < 0)? -1 : 0; } static int sdl_setpos_rel32(void *id, int32_t delta, int mode) { return sdl_setpos_rel64(id, delta, mode); } static int sdl_setpos_abs64(void *id, int64_t pos) { return (SDL_RWseek((SDL_RWops*)id, pos, RW_SEEK_SET) < 0)? -1 : 0; } static int sdl_setpos_abs32(void *id, uint32_t pos) { return (SDL_RWseek((SDL_RWops*)id, pos, RW_SEEK_SET) < 0)? -1 : 0; } static int sdl_pushback_byte(void *id, int c) { (void)c; /* libwavpack calls ungetc(), but doesn't really modify buffer. */ return (SDL_RWseek((SDL_RWops*)id, -1, RW_SEEK_CUR) < 0)? -1 : 0; } static uint32_t sdl_get_length32(void *id) { return (uint32_t)SDL_RWsize((SDL_RWops*)id); } static int64_t sdl_get_length64(void *id) { return SDL_RWsize((SDL_RWops*)id); } static int sdl_can_seek(void *id) { return (SDL_RWseek((SDL_RWops*)id, 0, RW_SEEK_CUR) < 0)? 0 : 1; } static WavpackStreamReader sdl_reader32 = { sdl_read_bytes, sdl_get_pos32, sdl_setpos_abs32, sdl_setpos_rel32, sdl_pushback_byte, sdl_get_length32, sdl_can_seek, NULL /* write_bytes */ }; static WavpackStreamReader64 sdl_reader64 = { sdl_read_bytes, NULL, /* write_bytes */ sdl_get_pos64, sdl_setpos_abs64, sdl_setpos_rel64, sdl_pushback_byte, sdl_get_length64, sdl_can_seek, NULL, /* truncate_here */ NULL /* close */ }; static int WAVPACK_Seek(void *context, double time); static void WAVPACK_Delete(void *context); static void *WAVPACK_CreateFromRW_internal(SDL_RWops *src1, SDL_RWops *src2, int freesrc, int *freesrc2); #ifdef MUSIC_WAVPACK_DSD static void *decimation_init(int num_channels, int ratio); static int decimation_run(void *context, int32_t *samples, int num_samples); static void decimation_reset(void *context); #define FLAGS_DSD OPEN_DSD_AS_PCM #define DECIMATION(x) (x)->decimation #else #define FLAGS_DSD 0 #define DECIMATION(x) 1 #endif static void *WAVPACK_CreateFromRW(SDL_RWops *src, int freesrc) { return WAVPACK_CreateFromRW_internal(src, NULL, freesrc, NULL); } static void *WAVPACK_CreateFromFile(const char *file) { SDL_RWops *src1, *src2; WAVPACK_music *music; int freesrc2 = 1; size_t len; char *file2; src1 = SDL_RWFromFile(file, "rb"); if (!src1) { Mix_SetError("Couldn't open '%s'", file); return NULL; } len = SDL_strlen(file); file2 = SDL_stack_alloc(char, len + 2); if (!file2) src2 = NULL; else { SDL_memcpy(file2, file, len); file2[len] = 'c'; file2[len + 1] = '\0'; src2 = SDL_RWFromFile(file2, "rb"); #if WAVPACK_DBG if (src2) { SDL_Log("Loaded WavPack correction file %s", file2); } #endif SDL_stack_free(file2); } music = WAVPACK_CreateFromRW_internal(src1, src2, 1, &freesrc2); if (!music) { SDL_RWclose(src1); if (freesrc2 && src2) { SDL_RWclose(src2); } } return music; } /* Load a WavPack stream from an SDL_RWops object */ static void *WAVPACK_CreateFromRW_internal(SDL_RWops *src1, SDL_RWops *src2, int freesrc, int *freesrc2) { WAVPACK_music *music; SDL_AudioFormat format; char *tag; char err[80]; int n; music = (WAVPACK_music *)SDL_calloc(1, sizeof *music); if (!music) { SDL_OutOfMemory(); return NULL; } music->src1 = src1; music->src2 = src2; music->volume = MIX_MAX_VOLUME; music->ctx = (wvpk.WavpackOpenFileInputEx64 != NULL) ? wvpk.WavpackOpenFileInputEx64(&sdl_reader64, src1, src2, err, OPEN_NORMALIZE|OPEN_TAGS|FLAGS_DSD, 0) : wvpk.WavpackOpenFileInputEx(&sdl_reader32, src1, src2, err, OPEN_NORMALIZE|OPEN_TAGS, 0); if (!music->ctx) { Mix_SetError("%s", err); SDL_free(music); if (src2) { SDL_RWclose(src2); } return NULL; } music->numsamples = (wvpk.WavpackGetNumSamples64 != NULL) ? wvpk.WavpackGetNumSamples64(music->ctx) : wvpk.WavpackGetNumSamples(music->ctx); music->samplerate = wvpk.WavpackGetSampleRate(music->ctx); music->bps = wvpk.WavpackGetBytesPerSample(music->ctx) << 3; music->channels = wvpk.WavpackGetNumChannels(music->ctx); music->mode = wvpk.WavpackGetMode(music->ctx); if (freesrc2) { *freesrc2 = 0; /* WAVPACK_Delete() will free it. */ } #ifdef MUSIC_WAVPACK_DSD music->decimation = 1; /* for very high sample rates (including DSD, which will normally be 352,800 Hz) * decimate 4x here before sending on */ if (music->samplerate >= 256000) { music->decimation = 4; music->decimation_ctx = decimation_init(music->channels, music->decimation); if (!music->decimation_ctx) { SDL_OutOfMemory(); WAVPACK_Delete(music); return NULL; } } #endif #if WAVPACK_DBG SDL_Log("WavPack loader:\n numsamples: %" SDL_PRIs64 "\n samplerate: %d\n bitspersample: %d\n channels: %d\n mode: 0x%x\n lossy: %d\n duration: %f\n", (Sint64)music->numsamples, music->samplerate, music->bps, music->channels, music->mode, !(music->mode & MODE_LOSSLESS), music->numsamples/(double)music->samplerate); #endif /* library returns the samples in 8, 16, 24, or 32 bit depth, but * always in an int32_t[] buffer, in signed host-endian format. */ switch (music->bps) { case 8: format = AUDIO_U8; break; case 16: format = AUDIO_S16SYS; break; default: format = (music->mode & MODE_FLOAT) ? AUDIO_F32SYS : AUDIO_S32SYS; break; } music->stream = SDL_NewAudioStream(format, (Uint8)music->channels, (int)music->samplerate / DECIMATION(music), music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { WAVPACK_Delete(music); return NULL; } music->frames = music_spec.samples; music->buffer = SDL_malloc(music->frames * music->channels * sizeof(int32_t) * DECIMATION(music)); if (!music->buffer) { SDL_OutOfMemory(); WAVPACK_Delete(music); return NULL; } tag = NULL; n = wvpk.WavpackGetTagItem(music->ctx, "TITLE", NULL, 0); if (n > 0) { tag = SDL_realloc(tag, (size_t)(++n)); wvpk.WavpackGetTagItem(music->ctx, "TITLE", tag, n); meta_tags_set(&music->tags, MIX_META_TITLE, tag); } n = wvpk.WavpackGetTagItem(music->ctx, "ARTIST", NULL, 0); if (n > 0) { tag = SDL_realloc(tag, (size_t)(++n)); wvpk.WavpackGetTagItem(music->ctx, "ARTIST", tag, n); meta_tags_set(&music->tags, MIX_META_ARTIST, tag); } n = wvpk.WavpackGetTagItem(music->ctx, "ALBUM", NULL, 0); if (n > 0) { tag = SDL_realloc(tag, (size_t)(++n)); wvpk.WavpackGetTagItem(music->ctx, "ALBUM", tag, n); meta_tags_set(&music->tags, MIX_META_ALBUM, tag); } n = wvpk.WavpackGetTagItem(music->ctx, "COPYRIGHT", NULL, 0); if (n > 0) { tag = SDL_realloc(tag, (size_t)(++n)); wvpk.WavpackGetTagItem(music->ctx, "COPYRIGHT", tag, n); meta_tags_set(&music->tags, MIX_META_COPYRIGHT, tag); } SDL_free(tag); music->freesrc = freesrc; return music; } static const char* WAVPACK_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { WAVPACK_music *music = (WAVPACK_music *)context; return meta_tags_get(&music->tags, tag_type); } static void WAVPACK_SetVolume(void *context, int volume) { WAVPACK_music *music = (WAVPACK_music *)context; music->volume = volume; } static int WAVPACK_GetVolume(void *context) { WAVPACK_music *music = (WAVPACK_music *)context; return music->volume; } /* Start playback of a given WavPack stream */ static int WAVPACK_Play(void *context, int play_count) { WAVPACK_music *music = (WAVPACK_music *)context; music->play_count = play_count; return WAVPACK_Seek(music, 0.0); } static void WAVPACK_Stop(void *context) { WAVPACK_music *music = (WAVPACK_music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with WAVPACK_play() */ static int WAVPACK_GetSome(void *context, void *data, int bytes, SDL_bool *done) { WAVPACK_music *music = (WAVPACK_music *)context; int amount; amount = SDL_AudioStreamGet(music->stream, data, bytes); if (amount != 0) { return amount; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } amount = (int) wvpk.WavpackUnpackSamples(music->ctx, music->buffer, music->frames * DECIMATION(music)); #ifdef MUSIC_WAVPACK_DSD if (amount && music->decimation_ctx) { amount = decimation_run(music->decimation_ctx, music->buffer, amount); } #endif if (amount) { int32_t *src = (int32_t *)music->buffer; int c = 0; amount *= music->channels; switch (music->bps) { case 8: { Uint8 *dst = (Uint8 *)music->buffer; for (; c < amount; ++c) { *dst++ = 0x80 ^ (Uint8)*src++; } } break; case 16: { Sint16 *dst = (Sint16 *)music->buffer; for (; c < amount; ++c) { *dst++ = *src++; } } amount *= sizeof(Sint16); break; case 24: for (; c < amount; ++c) { src[c] <<= 8; } /* FALLTHRU */ default: amount *= sizeof(Sint32); break; } if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (WAVPACK_Play(music, play_count) < 0) { return -1; } } } return 0; } static int WAVPACK_GetAudio(void *context, void *data, int bytes) { WAVPACK_music *music = (WAVPACK_music *)context; return music_pcm_getaudio(music, data, bytes, music->volume, WAVPACK_GetSome); } /* Jump (seek) to a given position (time is in seconds) */ static int WAVPACK_Seek(void *context, double time) { WAVPACK_music *music = (WAVPACK_music *)context; int64_t sample = (int64_t)(time * music->samplerate); int success = (wvpk.WavpackSeekSample64 != NULL) ? wvpk.WavpackSeekSample64(music->ctx, sample) : wvpk.WavpackSeekSample(music->ctx, (uint32_t)sample); if (!success) { return Mix_SetError("%s", wvpk.WavpackGetErrorMessage(music->ctx)); } #ifdef MUSIC_WAVPACK_DSD if (music->decimation_ctx) { decimation_reset(music->decimation_ctx); } #endif return 0; } static double WAVPACK_Tell(void *context) { WAVPACK_music *music = (WAVPACK_music *)context; if (wvpk.WavpackGetSampleIndex64 != NULL) { return wvpk.WavpackGetSampleIndex64(music->ctx) / (double)music->samplerate; } return wvpk.WavpackGetSampleIndex(music->ctx) / (double)music->samplerate; } /* Return music duration in seconds */ static double WAVPACK_Duration(void *context) { WAVPACK_music *music = (WAVPACK_music *)context; return music->numsamples / (double)music->samplerate; } /* Close the given WavPack stream */ static void WAVPACK_Delete(void *context) { WAVPACK_music *music = (WAVPACK_music *)context; meta_tags_clear(&music->tags); wvpk.WavpackCloseFile(music->ctx); if (music->stream) { SDL_FreeAudioStream(music->stream); } SDL_free(music->buffer); #ifdef MUSIC_WAVPACK_DSD SDL_free(music->decimation_ctx); #endif if (music->src2) { SDL_RWclose(music->src2); } if (music->freesrc) { SDL_RWclose(music->src1); } SDL_free(music); } #ifdef MUSIC_WAVPACK_DSD /* Decimation code for playing DSD (which comes from the library already decimated 8x) */ /* Code provided by David Bryant. */ /* sinc low-pass filter, cutoff = fs/12, 80 terms */ #define NUM_TERMS 80 static const int32_t filter[NUM_TERMS] = { 50, 464, 968, 711, -1203, -5028, -9818, -13376, -12870, -6021, 7526, 25238, 41688, 49778, 43050, 18447, -21428, -67553, -105876, -120890, -100640, -41752, 47201, 145510, 224022, 252377, 208224, 86014, -97312, -301919, -470919, -541796, -461126, -199113, 239795, 813326, 1446343, 2043793, 2509064, 2763659, 2763659, 2509064, 2043793, 1446343, 813326, 239795, -199113, -461126, -541796, -470919, -301919, -97312, 86014, 208224, 252377, 224022, 145510, 47201, -41752, -100640, -120890, -105876, -67553, -21428, 18447, 43050, 49778, 41688, 25238, 7526, -6021, -12870, -13376, -9818, -5028, -1203, 711, 968, 464, 50 }; typedef struct chan_state { int32_t delay[NUM_TERMS]; int index, num_channels, ratio; } ChanState; static void *decimation_init(int num_channels, int ratio) { ChanState *sp = (ChanState *)SDL_calloc(num_channels, sizeof(ChanState)); if (sp) { int i = 0; for (; i < num_channels; ++i) { sp[i].num_channels = num_channels; sp[i].index = NUM_TERMS - ratio; sp[i].ratio = ratio; } } return sp; } /** FIXME: This isn't particularly easy on the CPU ! **/ static int decimation_run(void *context, int32_t *samples, int num_samples) { ChanState *sp = (ChanState *)context; int32_t *in_samples = samples; int32_t *out_samples = samples; const int num_channels = sp->num_channels; const int ratio = sp->ratio; int chan = 0; while (num_samples) { sp = (ChanState *)context + chan; sp->delay[sp->index++] = *in_samples++; if (sp->index == NUM_TERMS) { int64_t sum = 0; int i = 0; for (; i < NUM_TERMS; ++i) { sum += (int64_t)filter[i] * sp->delay[i]; } *out_samples++ = (int32_t)(sum >> 24); SDL_memmove(sp->delay, sp->delay + ratio, sizeof(sp->delay[0]) * (NUM_TERMS - ratio)); sp->index = NUM_TERMS - ratio; } if (++chan == num_channels) { num_samples--; chan = 0; } } return (int)(out_samples - samples) / num_channels; } static void decimation_reset(void *context) { ChanState *sp = (ChanState *)context; const int num_channels = sp->num_channels; const int ratio = sp->ratio; int i = 0; SDL_memset(sp, 0, sizeof(ChanState) * num_channels); for (; i < num_channels; ++i) { sp[i].num_channels = num_channels; sp[i].index = NUM_TERMS - ratio; sp[i].ratio = ratio; } } #endif /* MUSIC_WAVPACK_DSD */ Mix_MusicInterface Mix_MusicInterface_WAVPACK = { "WAVPACK", MIX_MUSIC_WAVPACK, MUS_WAVPACK, SDL_FALSE, SDL_FALSE, WAVPACK_Load, NULL, /* Open */ WAVPACK_CreateFromRW, WAVPACK_CreateFromFile, WAVPACK_SetVolume, WAVPACK_GetVolume, WAVPACK_Play, NULL, /* IsPlaying */ WAVPACK_GetAudio, NULL, /* Jump */ WAVPACK_Seek, WAVPACK_Tell, WAVPACK_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ WAVPACK_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ WAVPACK_Stop, WAVPACK_Delete, NULL, /* Close */ WAVPACK_Unload }; #endif /* MUSIC_WAVPACK */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/dr_libs/0000755000076500000240000000000014553251273016105 5ustar valvestaffSDL2_mixer-2.8.0/src/codecs/dr_libs/LICENSE0000644000076500000240000000504614277744147017131 0ustar valvestaffThis software is available as a choice of the following licenses. Choose whichever you prefer. =============================================================================== ALTERNATIVE 1 - Public Domain (www.unlicense.org) =============================================================================== This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== Copyright 2020 David Reid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SDL2_mixer-2.8.0/src/codecs/dr_libs/README.md0000644000076500000240000000221714277744147017400 0ustar valvestaff

Public domain, single file audio decoding libraries for C and C++.

discord twitter

Library | Description ----------------------------------------------- | ----------- [dr_flac](dr_flac.h) | FLAC audio decoder. [dr_mp3](dr_mp3.h) | MP3 audio decoder. Based off [minimp3](https://github.com/lieff/minimp3). [dr_wav](dr_wav.h) | WAV audio loader and writer. # Other Libraries Below are some of my other libraries you may be interested in. Library | Description ------------------------------------------------- | ----------- [miniaudio](https://github.com/mackron/miniaudio) | A public domain, single file library for audio playback and recording. SDL2_mixer-2.8.0/src/codecs/dr_libs/dr_flac.h0000644000076500000240000200254714551252471017661 0ustar valvestaff/* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. dr_flac - v0.12.42 - 2023-11-02 David Reid - mackron@gmail.com GitHub: https://github.com/mackron/dr_libs */ /* RELEASE NOTES - v0.12.0 ======================= Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs. Improved Client-Defined Memory Allocation ----------------------------------------- The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom allocation callbacks are specified. To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this: void* my_malloc(size_t sz, void* pUserData) { return malloc(sz); } void* my_realloc(void* p, size_t sz, void* pUserData) { return realloc(p, sz); } void my_free(void* p, void* pUserData) { free(p); } ... drflac_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = &myData; allocationCallbacks.onMalloc = my_malloc; allocationCallbacks.onRealloc = my_realloc; allocationCallbacks.onFree = my_free; drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks); The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions. Every API that opens a drflac object now takes this extra parameter. These include the following: drflac_open() drflac_open_relaxed() drflac_open_with_metadata() drflac_open_with_metadata_relaxed() drflac_open_file() drflac_open_file_with_metadata() drflac_open_memory() drflac_open_memory_with_metadata() drflac_open_and_read_pcm_frames_s32() drflac_open_and_read_pcm_frames_s16() drflac_open_and_read_pcm_frames_f32() drflac_open_file_and_read_pcm_frames_s32() drflac_open_file_and_read_pcm_frames_s16() drflac_open_file_and_read_pcm_frames_f32() drflac_open_memory_and_read_pcm_frames_s32() drflac_open_memory_and_read_pcm_frames_s16() drflac_open_memory_and_read_pcm_frames_f32() Optimizations ------------- Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which means it will be disabled when DR_FLAC_NO_CRC is used. The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in particular. 16-bit streams should also see some improvement. drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32 to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths. A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo channel reconstruction which is the last part of the decoding process. The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at compile time and the REV instruction requires ARM architecture version 6. An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling. Removed APIs ------------ The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0: drflac_read_s32() -> drflac_read_pcm_frames_s32() drflac_read_s16() -> drflac_read_pcm_frames_s16() drflac_read_f32() -> drflac_read_pcm_frames_f32() drflac_seek_to_sample() -> drflac_seek_to_pcm_frame() drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32() drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16() drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32() drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32() drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16() drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32() drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32() drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16() drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32() Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate to the old per-sample APIs. You now need to use the "pcm_frame" versions. */ /* Introduction ============ dr_flac is a single file library. To use it, do something like the following in one .c file. ```c #define DR_FLAC_IMPLEMENTATION #include "dr_flac.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 drflac* pFlac = drflac_open_file("MySong.flac", NULL); if (pFlac == NULL) { // Failed to open FLAC file } drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); ``` The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well. You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example: ```c while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { do_something(); } ``` You can seek to a specific PCM frame with `drflac_seek_to_pcm_frame()`. If you just want to quickly decode an entire FLAC file in one go you can do something like this: ```c unsigned int channels; unsigned int sampleRate; drflac_uint64 totalPCMFrameCount; drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount, NULL); if (pSampleData == NULL) { // Failed to open and decode FLAC file. } ... drflac_free(pSampleData, NULL); ``` You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs respectively, but note that these should be considered lossy. If you need access to metadata (album art, etc.), use `drflac_open_with_metadata()`, `drflac_open_file_with_metdata()` or `drflac_open_memory_with_metadata()`. The rationale for keeping these APIs separate is that they're slightly slower than the normal versions and also just a little bit harder to use. dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before `drflac_open_with_metdata()` returns. The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: `drflac_open_relaxed()` `drflac_open_with_metadata_relaxed()` It is not recommended to use these APIs for file based streams because a missing header would usually indicate a corrupt or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend a lot of time finding the first frame. Build Options ============= #define these options before including this file. #define DR_FLAC_NO_STDIO Disable `drflac_open_file()` and family. #define DR_FLAC_NO_OGG Disables support for Ogg/FLAC streams. #define DR_FLAC_BUFFER_SIZE Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8. #define DR_FLAC_NO_CRC Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. When seeking, the seek table will be used if available. Otherwise the seek will be performed using brute force. #define DR_FLAC_NO_SIMD Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. #define DR_FLAC_NO_WCHAR Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_FLAC_NO_STDIO is also defined. Notes ===== - dr_flac does not support changing the sample rate nor channel count mid stream. - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. - When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due to differences in corrupted stream recorvery logic between the two APIs. */ #ifndef dr_flac_h #define dr_flac_h #ifdef __cplusplus extern "C" { #endif #define DRFLAC_STRINGIFY(x) #x #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 #define DRFLAC_VERSION_REVISION 42 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include /* For size_t. */ /* Sized Types */ typedef signed char drflac_int8; typedef unsigned char drflac_uint8; typedef signed short drflac_int16; typedef unsigned short drflac_uint16; typedef signed int drflac_int32; typedef unsigned int drflac_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drflac_int64; typedef unsigned __int64 drflac_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 drflac_int64; typedef unsigned long long drflac_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(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; #endif typedef drflac_uint8 drflac_bool8; typedef drflac_uint32 drflac_bool32; #define DRFLAC_TRUE 1 #define DRFLAC_FALSE 0 /* End Sized Types */ /* Decorations */ #if !defined(DRFLAC_API) #if defined(DRFLAC_DLL) #if defined(_WIN32) #define DRFLAC_DLL_IMPORT __declspec(dllimport) #define DRFLAC_DLL_EXPORT __declspec(dllexport) #define DRFLAC_DLL_PRIVATE static #else #if defined(__GNUC__) && __GNUC__ >= 4 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) #else #define DRFLAC_DLL_IMPORT #define DRFLAC_DLL_EXPORT #define DRFLAC_DLL_PRIVATE static #endif #endif #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) #define DRFLAC_API DRFLAC_DLL_EXPORT #else #define DRFLAC_API DRFLAC_DLL_IMPORT #endif #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE #else #define DRFLAC_API extern #define DRFLAC_PRIVATE static #endif #endif /* End Decorations */ #if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */ #define DRFLAC_DEPRECATED __declspec(deprecated) #elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */ #define DRFLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) /* Clang */ #if __has_feature(attribute_deprecated) #define DRFLAC_DEPRECATED __attribute__((deprecated)) #else #define DRFLAC_DEPRECATED #endif #else #define DRFLAC_DEPRECATED #endif DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); DRFLAC_API const char* drflac_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); } drflac_allocation_callbacks; /* End Allocation Callbacks */ /* As data is read from the client it is placed into an internal buffer for fast access. This controls the size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. */ #ifndef DR_FLAC_BUFFER_SIZE #define DR_FLAC_BUFFER_SIZE 4096 #endif /* Architecture Detection */ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #define DRFLAC_64BIT #endif #if defined(__x86_64__) || defined(_M_X64) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define DRFLAC_ARM #endif /* End Architecture Detection */ #ifdef DRFLAC_64BIT typedef drflac_uint64 drflac_cache_t; #else typedef drflac_uint32 drflac_cache_t; #endif /* The various metadata block types. */ #define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 #define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 #define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 #define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 #define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 #define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 #define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 #define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 /* The various picture types specified in the PICTURE block. */ #define DRFLAC_PICTURE_TYPE_OTHER 0 #define DRFLAC_PICTURE_TYPE_FILE_ICON 1 #define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 #define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 #define DRFLAC_PICTURE_TYPE_COVER_BACK 4 #define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 #define DRFLAC_PICTURE_TYPE_MEDIA 6 #define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 #define DRFLAC_PICTURE_TYPE_ARTIST 8 #define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 #define DRFLAC_PICTURE_TYPE_BAND 10 #define DRFLAC_PICTURE_TYPE_COMPOSER 11 #define DRFLAC_PICTURE_TYPE_LYRICIST 12 #define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 #define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 #define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 #define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 #define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 #define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 #define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 #define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 typedef enum { drflac_container_native, drflac_container_ogg, drflac_container_unknown } drflac_container; typedef enum { drflac_seek_origin_start, drflac_seek_origin_current } drflac_seek_origin; /* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ typedef struct { drflac_uint64 firstPCMFrame; drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */ drflac_uint16 pcmFrameCount; } drflac_seekpoint; typedef struct { drflac_uint16 minBlockSizeInPCMFrames; drflac_uint16 maxBlockSizeInPCMFrames; drflac_uint32 minFrameSizeInPCMFrames; drflac_uint32 maxFrameSizeInPCMFrames; drflac_uint32 sampleRate; drflac_uint8 channels; drflac_uint8 bitsPerSample; drflac_uint64 totalPCMFrameCount; drflac_uint8 md5[16]; } drflac_streaminfo; typedef struct { /* The metadata type. Use this to know how to interpret the data below. Will be set to one of the DRFLAC_METADATA_BLOCK_TYPE_* tokens. */ drflac_uint32 type; /* A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to not modify the contents of this buffer. Use the structures below for more meaningful and structured information about the metadata. It's possible for this to be null. */ const void* pRawData; /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ drflac_uint32 rawDataSize; union { drflac_streaminfo streaminfo; struct { int unused; } padding; struct { drflac_uint32 id; const void* pData; drflac_uint32 dataSize; } application; struct { drflac_uint32 seekpointCount; const drflac_seekpoint* pSeekpoints; } seektable; struct { drflac_uint32 vendorLength; const char* vendor; drflac_uint32 commentCount; const void* pComments; } vorbis_comment; struct { char catalog[128]; drflac_uint64 leadInSampleCount; drflac_bool32 isCD; drflac_uint8 trackCount; const void* pTrackData; } cuesheet; struct { drflac_uint32 type; drflac_uint32 mimeLength; const char* mime; drflac_uint32 descriptionLength; const char* description; drflac_uint32 width; drflac_uint32 height; drflac_uint32 colorDepth; drflac_uint32 indexColorCount; drflac_uint32 pictureDataSize; const drflac_uint8* pPictureData; } picture; } data; } drflac_metadata; /* Callback for when data needs to be read from the client. Parameters ---------- pUserData (in) The user data that was passed to drflac_open() and family. pBufferOut (out) The output buffer. bytesToRead (in) The number of bytes to read. Return Value ------------ The number of bytes actually read. Remarks ------- 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 (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); /* Callback for when data needs to be seeked. Parameters ---------- pUserData (in) The user data that was passed to drflac_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. Return Value ------------ Whether or not the seek was successful. Remarks ------- The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected and handled by returning DRFLAC_FALSE. */ typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); /* Callback for when a metadata block is read. Parameters ---------- pUserData (in) The user data that was passed to drflac_open() and family. pMetadata (in) A pointer to a structure containing the data of the metadata block. Remarks ------- Use pMetadata->type to determine which metadata block is being handled and how to read the data. This will be set to one of the DRFLAC_METADATA_BLOCK_TYPE_* tokens. */ typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); /* Structure for internal use. Only used for decoders opened with drflac_open_memory. */ typedef struct { const drflac_uint8* data; size_t dataSize; size_t currentReadPos; } drflac__memory_stream; /* Structure for internal use. Used for bit streaming. */ typedef struct { /* The function to call when more data needs to be read. */ drflac_read_proc onRead; /* The function to call when the current read position needs to be moved. */ drflac_seek_proc onSeek; /* The user data to pass around to onRead and onSeek. */ void* pUserData; /* The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). */ size_t unalignedByteCount; /* The content of the unaligned bytes. */ drflac_cache_t unalignedCache; /* The index of the next valid cache line in the "L2" cache. */ drflac_uint32 nextL2Line; /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */ drflac_uint32 consumedBits; /* The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. */ drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; drflac_cache_t cache; /* CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this is reset to 0 at the beginning of each frame. */ drflac_uint16 crc16; drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */ drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */ } drflac_bs; typedef struct { /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */ drflac_uint8 subframeType; /* The number of wasted bits per sample as specified by the sub-frame header. */ drflac_uint8 wastedBitsPerSample; /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */ drflac_uint8 lpcOrder; /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. */ drflac_int32* pSamplesS32; } drflac_subframe; typedef struct { /* If the stream uses variable block sizes, this will be set to the index of the first PCM frame. If fixed block sizes are used, this will always be set to 0. This is 64-bit because the decoded PCM frame number will be 36 bits. */ drflac_uint64 pcmFrameNumber; /* If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. This is 32-bit because in fixed block sizes, the maximum frame number will be 31 bits. */ drflac_uint32 flacFrameNumber; /* The sample rate of this frame. */ drflac_uint32 sampleRate; /* The number of PCM frames in each sub-frame within this frame. */ drflac_uint16 blockSizeInPCMFrames; /* The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. */ drflac_uint8 channelAssignment; /* The number of bits per sample within this frame. */ drflac_uint8 bitsPerSample; /* The frame's CRC. */ drflac_uint8 crc8; } drflac_frame_header; typedef struct { /* The header. */ drflac_frame_header header; /* The number of PCM frames left to be read in this FLAC frame. This is initially set to the block size. As PCM frames are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. */ drflac_uint32 pcmFramesRemaining; /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */ drflac_subframe subframes[8]; } drflac_frame; typedef struct { /* The function to call when a metadata block is read. */ drflac_meta_proc onMeta; /* The user data posted to the metadata callback function. */ void* pUserDataMD; /* Memory allocation callbacks. */ drflac_allocation_callbacks allocationCallbacks; /* The sample rate. Will be set to something like 44100. */ drflac_uint32 sampleRate; /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the value specified in the STREAMINFO block. */ drflac_uint8 channels; /* The bits per sample. Will be set to something like 16, 24, etc. */ drflac_uint8 bitsPerSample; /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */ drflac_uint16 maxBlockSizeInPCMFrames; /* The total number of PCM Frames making up the stream. Can be 0 in which case it's still a valid stream, but just means the total PCM frame count is unknown. Likely the case with streams like internet radio. */ drflac_uint64 totalPCMFrameCount; /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */ drflac_container container; /* The number of seekpoints in the seektable. */ drflac_uint32 seekpointCount; /* Information about the frame the decoder is currently sitting on. */ drflac_frame currentFLACFrame; /* The index of the PCM frame the decoder is currently sitting on. This is only used for seeking. */ drflac_uint64 currentPCMFrame; /* The position of the first FLAC frame in the stream. This is only ever used for seeking. */ drflac_uint64 firstFLACFramePosInBytes; /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */ drflac__memory_stream memoryStream; /* A pointer to the decoded sample data. This is an offset of pExtraData. */ drflac_int32* pDecodedSamples; /* A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. */ drflac_seekpoint* pSeekpoints; /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */ void* _oggbs; /* Internal use only. Used for profiling and testing different seeking modes. */ drflac_bool32 _noSeekTableSeek : 1; drflac_bool32 _noBinarySearchSeek : 1; drflac_bool32 _noBruteForceSeek : 1; /* The bit streamer. The raw FLAC data is fed through this object. */ drflac_bs bs; /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */ drflac_uint8 pExtraData[1]; } drflac; /* Opens a FLAC decoder. Parameters ---------- 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. pAllocationCallbacks (in, optional) A pointer to application defined callbacks for managing memory allocations. Return Value ------------ Returns a pointer to an object representing the decoder. Remarks ------- Close the decoder with `drflac_close()`. `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. This is the lowest level function for opening a FLAC stream. You can also use `drflac_open_file()` and `drflac_open_memory()` to open the stream from a file or from a block of memory respectively. The STREAMINFO block must be present for this to succeed. Use `drflac_open_relaxed()` to open a FLAC stream where the header may not be present. Use `drflac_open_with_metadata()` if you need access to metadata. Seek Also --------- drflac_open_file() drflac_open_memory() drflac_open_with_metadata() drflac_close() */ DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC stream with relaxed validation of the header block. Parameters ---------- 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. container (in) Whether or not the FLAC stream is encapsulated using standard FLAC encapsulation or Ogg encapsulation. pUserData (in, optional) A pointer to application defined data that will be passed to onRead and onSeek. pAllocationCallbacks (in, optional) A pointer to application defined callbacks for managing memory allocations. Return Value ------------ A pointer to an object representing the decoder. Remarks ------- The same as drflac_open(), except attempts to open the stream even when a header block is not present. Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do not set this to `drflac_container_unknown` as that is for internal use only. Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never found it will continue forever. To abort, force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found. Use `drflac_open_with_metadata_relaxed()` if you need access to metadata. */ DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). Parameters ---------- 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. onMeta (in) The function to call for every metadata block. pUserData (in, optional) A pointer to application defined data that will be passed to onRead, onSeek and onMeta. pAllocationCallbacks (in, optional) A pointer to application defined callbacks for managing memory allocations. Return Value ------------ A pointer to an object representing the decoder. Remarks ------- Close the decoder with `drflac_close()`. `pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. This is slower than `drflac_open()`, so avoid this one if you don't need metadata. Internally, this will allocate and free memory on the heap for every metadata block except for STREAMINFO and PADDING blocks. The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns. This callback takes a pointer to a `drflac_metadata` object which is a union containing the data of all relevant metadata blocks. Use the `type` member to discriminate against the different metadata types. The STREAMINFO block must be present for this to succeed. Use `drflac_open_with_metadata_relaxed()` to open a FLAC stream where the header may not be present. Note that this will behave inconsistently with `drflac_open()` if the stream is an Ogg encapsulated stream and a metadata block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When `drflac_open_with_metadata()` is being used, the open routine will try to read the contents of the metadata block, whereas `drflac_open()` will simply seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on whether or not the stream is being opened with metadata. Seek Also --------- drflac_open_file_with_metadata() drflac_open_memory_with_metadata() drflac_open() drflac_close() */ DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. See Also -------- drflac_open_with_metadata() drflac_open_relaxed() */ DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* Closes the given FLAC decoder. Parameters ---------- pFlac (in) The decoder to close. Remarks ------- This will destroy the decoder object. See Also -------- drflac_open() drflac_open_with_metadata() drflac_open_file() drflac_open_file_w() drflac_open_file_with_metadata() drflac_open_file_with_metadata_w() drflac_open_memory() drflac_open_memory_with_metadata() */ DRFLAC_API void drflac_close(drflac* pFlac); /* Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. Parameters ---------- pFlac (in) The decoder. framesToRead (in) The number of PCM frames to read. pBufferOut (out, optional) A pointer to the buffer that will receive the decoded samples. Return Value ------------ Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. Remarks ------- pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. */ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); /* Reads sample data from the given FLAC decoder, output as interleaved signed 16-bit PCM. Parameters ---------- pFlac (in) The decoder. framesToRead (in) The number of PCM frames to read. pBufferOut (out, optional) A pointer to the buffer that will receive the decoded samples. Return Value ------------ Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. Remarks ------- pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. Note that this is lossy for streams where the bits per sample is larger than 16. */ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); /* Reads sample data from the given FLAC decoder, output as interleaved 32-bit floating point PCM. Parameters ---------- pFlac (in) The decoder. framesToRead (in) The number of PCM frames to read. pBufferOut (out, optional) A pointer to the buffer that will receive the decoded samples. Return Value ------------ Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. Remarks ------- pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly represent every possible number. */ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); /* Seeks to the PCM frame at the given index. Parameters ---------- pFlac (in) The decoder. pcmFrameIndex (in) The index of the PCM frame to seek to. See notes below. Return Value ------------- `DRFLAC_TRUE` if successful; `DRFLAC_FALSE` otherwise. */ DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); #ifndef DR_FLAC_NO_STDIO /* Opens a FLAC decoder from the file at the given path. Parameters ---------- pFileName (in) The path of the file to open, either absolute or relative to the current directory. pAllocationCallbacks (in, optional) A pointer to application defined callbacks for managing memory allocations. Return Value ------------ A pointer to an object representing the decoder. Remarks ------- Close the decoder with drflac_close(). Remarks ------- This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the number of files a process can have open at any given time, so keep this mind if you have many decoders open at the same time. See Also -------- drflac_open_file_with_metadata() drflac_open() drflac_close() */ DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) Parameters ---------- pFileName (in) The path of the file to open, either absolute or relative to the current directory. pAllocationCallbacks (in, optional) A pointer to application defined callbacks for managing memory allocations. onMeta (in) The callback to fire for each metadata block. pUserData (in) A pointer to the user data to pass to the metadata callback. pAllocationCallbacks (in) A pointer to application defined callbacks for managing memory allocations. Remarks ------- Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. See Also -------- drflac_open_with_metadata() drflac_open() drflac_close() */ DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); #endif /* Opens a FLAC decoder from a pre-allocated block of memory Parameters ---------- pData (in) A pointer to the raw encoded FLAC data. dataSize (in) The size in bytes of `data`. pAllocationCallbacks (in) A pointer to application defined callbacks for managing memory allocations. Return Value ------------ A pointer to an object representing the decoder. Remarks ------- 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 decoder. See Also -------- drflac_open() drflac_close() */ DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); /* Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) Parameters ---------- pData (in) A pointer to the raw encoded FLAC data. dataSize (in) The size in bytes of `data`. onMeta (in) The callback to fire for each metadata block. pUserData (in) A pointer to the user data to pass to the metadata callback. pAllocationCallbacks (in) A pointer to application defined callbacks for managing memory allocations. Remarks ------- Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. See Also ------- drflac_open_with_metadata() drflac_open() drflac_close() */ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); /* High Level APIs */ /* Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). You can pass in custom memory allocation callbacks via the pAllocationCallbacks parameter. This can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously read samples into a dynamically sized buffer on the heap until no samples are left. Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). */ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #ifndef DR_FLAC_NO_STDIO /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #endif /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); /* Frees memory that was allocated internally by dr_flac. Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this. */ DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); /* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ typedef struct { drflac_uint32 countRemaining; const char* pRunningData; } drflac_vorbis_comment_iterator; /* Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT metadata block. */ DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); /* Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The returned string is NOT null terminated. */ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); /* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */ typedef struct { drflac_uint32 countRemaining; const char* pRunningData; } drflac_cuesheet_track_iterator; /* The order of members here is important because we map this directly to the raw data within the CUESHEET metadata block. */ typedef struct { drflac_uint64 offset; drflac_uint8 index; drflac_uint8 reserved[3]; } drflac_cuesheet_track_index; typedef struct { drflac_uint64 offset; drflac_uint8 trackNumber; char ISRC[12]; drflac_bool8 isAudio; drflac_bool8 preEmphasis; drflac_uint8 indexCount; const drflac_cuesheet_track_index* pIndexPoints; } drflac_cuesheet_track; /* Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata block. */ DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); /* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus } #endif #endif /* dr_flac_h */ /************************************************************************************************************************************************************ ************************************************************************************************************************************************************ IMPLEMENTATION ************************************************************************************************************************************************************ ************************************************************************************************************************************************************/ #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) #ifndef dr_flac_c #define dr_flac_c /* Disable some annoying warnings. */ #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif #endif #ifdef __linux__ #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE #endif #ifndef __USE_BSD #define __USE_BSD #endif #include #endif #include #include /* Inline */ #ifdef _MSC_VER #define DRFLAC_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 DRFLAC_GNUC_INLINE_HINT __inline__ #else #define DRFLAC_GNUC_INLINE_HINT inline #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) #else #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRFLAC_INLINE __inline #else #define DRFLAC_INLINE #endif /* End Inline */ /* Intrinsics Support There's a bug in GCC 4.2.x which results in an incorrect compilation error when using _mm_slli_epi32() where it complains with "error: shift must be an immediate" Unfortuantely dr_flac depends on this for a few things so we're just going to disable SSE on GCC 4.2 and below. */ #if !defined(DR_FLAC_NO_SIMD) #if defined(DRFLAC_X64) || defined(DRFLAC_X86) #if defined(_MSC_VER) && !defined(__clang__) /* MSVC. */ #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */ #define DRFLAC_SUPPORT_SSE2 #endif #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */ #define DRFLAC_SUPPORT_SSE41 #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) /* Assume GNUC-style. */ #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) #define DRFLAC_SUPPORT_SSE2 #endif #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) #define DRFLAC_SUPPORT_SSE41 #endif #endif /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() #define DRFLAC_SUPPORT_SSE2 #endif #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() #define DRFLAC_SUPPORT_SSE41 #endif #endif #if defined(DRFLAC_SUPPORT_SSE41) #include #elif defined(DRFLAC_SUPPORT_SSE2) #include #endif #endif #if defined(DRFLAC_ARM) #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define DRFLAC_SUPPORT_NEON #include #endif #endif #endif /* Compile-time CPU feature support. */ #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include static void drflac__cpuid(int info[4], int fid) { __cpuid(info, fid); } #else #define DRFLAC_NO_CPUID #endif #else #if defined(__GNUC__) || defined(__clang__) static void drflac__cpuid(int info[4], int fid) { /* It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for supporting different assembly dialects. What's basically happening is that we're saving and restoring the ebx register manually. */ #if defined(DRFLAC_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" "xchg{l} {%%}ebx, %k1;" : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #else __asm__ __volatile__ ( "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #endif } #else #define DRFLAC_NO_CPUID #endif #endif #else #define DRFLAC_NO_CPUID #endif static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) { #if defined(DRFLAC_SUPPORT_SSE2) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) #if defined(DRFLAC_X64) return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ #else #if defined(DRFLAC_NO_CPUID) return DRFLAC_FALSE; #else int info[4]; drflac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ #endif #else return DRFLAC_FALSE; /* No compiler support. */ #endif } static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) #if defined(__SSE4_1__) || defined(__AVX__) return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */ #else #if defined(DRFLAC_NO_CPUID) return DRFLAC_FALSE; #else int info[4]; drflac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif #else return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */ #endif #else return DRFLAC_FALSE; /* No compiler support. */ #endif } #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) #define DRFLAC_HAS_LZCNT_INTRINSIC #endif #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #elif defined(__WATCOMC__) && defined(__386__) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ parm [ax] \ value [ax] \ modify nomemory; #pragma aux _watcom_bswap32 = \ "bswap eax" \ parm [eax] \ value [eax] \ modify nomemory; #pragma aux _watcom_bswap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ value [eax edx] \ modify nomemory; #endif /* Standard library stuff. */ #ifndef DRFLAC_ASSERT #include #define DRFLAC_ASSERT(expression) assert(expression) #endif #ifndef DRFLAC_MALLOC #define DRFLAC_MALLOC(sz) malloc((sz)) #endif #ifndef DRFLAC_REALLOC #define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) #endif #ifndef DRFLAC_FREE #define DRFLAC_FREE(p) free((p)) #endif #ifndef DRFLAC_COPY_MEMORY #define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #ifndef DRFLAC_ZERO_MEMORY #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif #ifndef DRFLAC_ZERO_OBJECT #define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) #endif #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ /* Result Codes */ typedef drflac_int32 drflac_result; #define DRFLAC_SUCCESS 0 #define DRFLAC_ERROR -1 /* A generic error. */ #define DRFLAC_INVALID_ARGS -2 #define DRFLAC_INVALID_OPERATION -3 #define DRFLAC_OUT_OF_MEMORY -4 #define DRFLAC_OUT_OF_RANGE -5 #define DRFLAC_ACCESS_DENIED -6 #define DRFLAC_DOES_NOT_EXIST -7 #define DRFLAC_ALREADY_EXISTS -8 #define DRFLAC_TOO_MANY_OPEN_FILES -9 #define DRFLAC_INVALID_FILE -10 #define DRFLAC_TOO_BIG -11 #define DRFLAC_PATH_TOO_LONG -12 #define DRFLAC_NAME_TOO_LONG -13 #define DRFLAC_NOT_DIRECTORY -14 #define DRFLAC_IS_DIRECTORY -15 #define DRFLAC_DIRECTORY_NOT_EMPTY -16 #define DRFLAC_END_OF_FILE -17 #define DRFLAC_NO_SPACE -18 #define DRFLAC_BUSY -19 #define DRFLAC_IO_ERROR -20 #define DRFLAC_INTERRUPT -21 #define DRFLAC_UNAVAILABLE -22 #define DRFLAC_ALREADY_IN_USE -23 #define DRFLAC_BAD_ADDRESS -24 #define DRFLAC_BAD_SEEK -25 #define DRFLAC_BAD_PIPE -26 #define DRFLAC_DEADLOCK -27 #define DRFLAC_TOO_MANY_LINKS -28 #define DRFLAC_NOT_IMPLEMENTED -29 #define DRFLAC_NO_MESSAGE -30 #define DRFLAC_BAD_MESSAGE -31 #define DRFLAC_NO_DATA_AVAILABLE -32 #define DRFLAC_INVALID_DATA -33 #define DRFLAC_TIMEOUT -34 #define DRFLAC_NO_NETWORK -35 #define DRFLAC_NOT_UNIQUE -36 #define DRFLAC_NOT_SOCKET -37 #define DRFLAC_NO_ADDRESS -38 #define DRFLAC_BAD_PROTOCOL -39 #define DRFLAC_PROTOCOL_UNAVAILABLE -40 #define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 #define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 #define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 #define DRFLAC_SOCKET_NOT_SUPPORTED -44 #define DRFLAC_CONNECTION_RESET -45 #define DRFLAC_ALREADY_CONNECTED -46 #define DRFLAC_NOT_CONNECTED -47 #define DRFLAC_CONNECTION_REFUSED -48 #define DRFLAC_NO_HOST -49 #define DRFLAC_IN_PROGRESS -50 #define DRFLAC_CANCELLED -51 #define DRFLAC_MEMORY_ALREADY_MAPPED -52 #define DRFLAC_AT_END -53 #define DRFLAC_CRC_MISMATCH -100 /* End Result Codes */ #define DRFLAC_SUBFRAME_CONSTANT 0 #define DRFLAC_SUBFRAME_VERBATIM 1 #define DRFLAC_SUBFRAME_FIXED 8 #define DRFLAC_SUBFRAME_LPC 32 #define DRFLAC_SUBFRAME_RESERVED 255 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 #define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 #define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 #define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 #define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) { if (pMajor) { *pMajor = DRFLAC_VERSION_MAJOR; } if (pMinor) { *pMinor = DRFLAC_VERSION_MINOR; } if (pRevision) { *pRevision = DRFLAC_VERSION_REVISION; } } DRFLAC_API const char* drflac_version_string(void) { return DRFLAC_VERSION_STRING; } /* CPU caps. */ #if defined(__has_feature) #if __has_feature(thread_sanitizer) #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) #else #define DRFLAC_NO_THREAD_SANITIZE #endif #else #define DRFLAC_NO_THREAD_SANITIZE #endif #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; #endif #ifndef DRFLAC_NO_CPUID static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; /* I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute. */ DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; if (!isCPUCapsInitialized) { /* LZCNT */ #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) int info[4] = {0}; drflac__cpuid(info, 0x80000001); drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif /* SSE2 */ drflac__gIsSSE2Supported = drflac_has_sse2(); /* SSE4.1 */ drflac__gIsSSE41Supported = drflac_has_sse41(); /* Initialized. */ isCPUCapsInitialized = DRFLAC_TRUE; } } #else static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) { #if defined(DRFLAC_SUPPORT_NEON) #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) return DRFLAC_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ #else /* TODO: Runtime check. */ return DRFLAC_FALSE; #endif #else return DRFLAC_FALSE; /* NEON is only supported on ARM architectures. */ #endif #else return DRFLAC_FALSE; /* No compiler support. */ #endif } DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { drflac__gIsNEONSupported = drflac__has_neon(); #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) drflac__gIsLZCNTSupported = DRFLAC_TRUE; #endif } #endif /* Endian Management */ static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) { #if defined(DRFLAC_X86) || defined(DRFLAC_X64) return DRFLAC_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN return DRFLAC_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) { #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap16(n); #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap16(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); #endif } static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) { #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ drflac_uint32 r; __asm__ __volatile__ ( #if defined(DRFLAC_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) #endif ); return r; #else return __builtin_bswap32(n); #endif #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap32(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF000000) >> 24) | ((n & 0x00FF0000) >> 8) | ((n & 0x0000FF00) << 8) | ((n & 0x000000FF) << 24); #endif } static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) { #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(n); #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap64(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | ((n & ((drflac_uint64)0xFF000000 )) << 8) | ((n & ((drflac_uint64)0x00FF0000 )) << 24) | ((n & ((drflac_uint64)0x0000FF00 )) << 40) | ((n & ((drflac_uint64)0x000000FF )) << 56); #endif } static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint16(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint32(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) { const drflac_uint8* pNum = (drflac_uint8*)pData; return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); } static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint64(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) { if (!drflac__is_little_endian()) { return drflac__swap_endian_uint32(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) { const drflac_uint8* pNum = (drflac_uint8*)pData; return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; } static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) { drflac_uint32 result = 0; result |= (n & 0x7F000000) >> 3; result |= (n & 0x007F0000) >> 2; result |= (n & 0x00007F00) >> 1; result |= (n & 0x0000007F) >> 0; return result; } /* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */ static drflac_uint8 drflac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; static drflac_uint16 drflac__crc16_table[] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 }; static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) { return drflac__crc8_table[crc ^ data]; } static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */ drflac_uint8 p = 0x07; for (int i = count-1; i >= 0; --i) { drflac_uint8 bit = (data & (1 << i)) >> i; if (crc & 0x80) { crc = ((crc << 1) | bit) ^ p; } else { crc = ((crc << 1) | bit); } } return crc; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 32); wholeBytes = count >> 3; leftoverBits = count - (wholeBytes*8); leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) { return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; } static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) { #ifdef DRFLAC_64BIT crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); return crc; } static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) { switch (byteCount) { #ifdef DRFLAC_64BIT case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); } return crc; } #if 0 static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */ drflac_uint16 p = 0x8005; for (int i = count-1; i >= 0; --i) { drflac_uint16 bit = (data & (1ULL << i)) >> i; if (r & 0x8000) { r = ((r << 1) | bit) ^ p; } else { r = ((r << 1) | bit); } } return crc; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) { #ifdef DRFLAC_64BIT return drflac_crc16__64bit(crc, data, count); #else return drflac_crc16__32bit(crc, data, count); #endif } #endif #ifdef DRFLAC_64BIT #define drflac__be2host__cache_line drflac__be2host_64 #else #define drflac__be2host__cache_line drflac__be2host_32 #endif /* BIT READING ATTEMPT #2 This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data from onRead() is read into. */ #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) #define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) #define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) #ifndef DR_FLAC_NO_CRC static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) { bs->crc16 = 0; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) { if (bs->crc16CacheIgnoredBytes == 0) { bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); } else { bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = 0; } } static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) { /* We should never be flushing in a situation where we are not aligned on a byte boundary. */ DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); /* The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined by the number of bits that have been consumed. */ if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { drflac__update_crc16(bs); } else { /* We only accumulate the consumed bits. */ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); /* The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated so we can handle that later. */ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } return bs->crc16; } #endif static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) { size_t bytesRead; size_t alignedL1LineCount; /* Fast path. Try loading straight from L2. */ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } /* If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's any left. */ if (bs->unalignedByteCount > 0) { return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */ } bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); bs->nextL2Line = 0; if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } /* If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably means we've just reached the end of the file. We need to move the valid data down to the end of the buffer and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to the size of the L1 so we'll need to seek backwards by any misaligned bytes. */ alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); /* We need to keep track of any unaligned bytes for later use. */ bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } if (alignedL1LineCount > 0) { size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; size_t i; for (i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } bs->nextL2Line = (drflac_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } else { /* If we get into this branch it means we weren't able to load any L1-aligned data. */ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); return DRFLAC_FALSE; } } static drflac_bool32 drflac__reload_cache(drflac_bs* bs) { size_t bytesRead; #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */ if (drflac__reload_l1_cache_from_l2(bs)) { bs->cache = drflac__be2host__cache_line(bs->cache); bs->consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif return DRFLAC_TRUE; } /* Slow path. */ /* If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the data from the unaligned cache. */ bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */ return DRFLAC_FALSE; } DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; bs->cache = drflac__be2host__cache_line(bs->unalignedCache); bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */ bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */ #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; #endif return DRFLAC_TRUE; } static void drflac__reset_cache(drflac_bs* bs) { bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */ bs->cache = 0; bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */ bs->unalignedCache = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = 0; bs->crc16CacheIgnoredBytes = 0; #endif } static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResultOut != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 32); if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly more optimal solution for this. */ #ifdef DRFLAC_64BIT *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; #else if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { /* Cannot shift by 32-bits, so need to do it differently. */ *pResultOut = (drflac_uint32)bs->cache; bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } #endif return DRFLAC_TRUE; } else { /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; drflac_uint32 resultHi; DRFLAC_ASSERT(bitCountHi > 0); DRFLAC_ASSERT(bitCountHi < 32); resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* This happens when we get to end of stream */ return DRFLAC_FALSE; } *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; return DRFLAC_TRUE; } } static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 32); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } /* Do not attempt to shift by 32 as it's undefined. */ if (bitCount < 32) { drflac_uint32 signbit; signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; } *pResult = (drflac_int32)result; return DRFLAC_TRUE; } #ifdef DRFLAC_64BIT static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { drflac_uint32 resultHi; drflac_uint32 resultLo; DRFLAC_ASSERT(bitCount <= 64); DRFLAC_ASSERT(bitCount > 32); if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { return DRFLAC_FALSE; } if (!drflac__read_uint32(bs, 32, &resultLo)) { return DRFLAC_FALSE; } *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); return DRFLAC_TRUE; } #endif /* Function below is unused, but leaving it here in case I need to quickly add it again. */ #if 0 static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) { drflac_uint64 result; drflac_uint64 signbit; DRFLAC_ASSERT(bitCount <= 64); if (!drflac__read_uint64(bs, bitCount, &result)) { return DRFLAC_FALSE; } signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; *pResultOut = (drflac_int64)result; return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 16); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_uint16)result; return DRFLAC_TRUE; } #if 0 static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) { drflac_int32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 16); if (!drflac__read_int32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_int16)result; return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 8); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_uint8)result; return DRFLAC_TRUE; } static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) { drflac_int32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 8); if (!drflac__read_int32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_int8)result; return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { bs->consumedBits += (drflac_uint32)bitsToSeek; bs->cache <<= bitsToSeek; return DRFLAC_TRUE; } else { /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */ bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); bs->cache = 0; /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */ #ifdef DRFLAC_64BIT while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { drflac_uint64 bin; if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { return DRFLAC_FALSE; } bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #else while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { drflac_uint32 bin; if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { return DRFLAC_FALSE; } bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #endif /* Whole leftover bytes. */ while (bitsToSeek >= 8) { drflac_uint8 bin; if (!drflac__read_uint8(bs, 8, &bin)) { return DRFLAC_FALSE; } bitsToSeek -= 8; } /* Leftover bits. */ if (bitsToSeek > 0) { drflac_uint8 bin; if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { return DRFLAC_FALSE; } bitsToSeek = 0; /* <-- Necessary for the assert below. */ } DRFLAC_ASSERT(bitsToSeek == 0); return DRFLAC_TRUE; } } /* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) { DRFLAC_ASSERT(bs != NULL); /* The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first thing to do is align to the next byte. */ if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { return DRFLAC_FALSE; } for (;;) { drflac_uint8 hi; #ifndef DR_FLAC_NO_CRC drflac__reset_crc16(bs); #endif if (!drflac__read_uint8(bs, 8, &hi)) { return DRFLAC_FALSE; } if (hi == 0xFF) { drflac_uint8 lo; if (!drflac__read_uint8(bs, 6, &lo)) { return DRFLAC_FALSE; } if (lo == 0x3E) { return DRFLAC_TRUE; } else { if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { return DRFLAC_FALSE; } } } } /* Should never get here. */ /*return DRFLAC_FALSE;*/ } #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) #define DRFLAC_IMPLEMENT_CLZ_LZCNT #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) #define DRFLAC_IMPLEMENT_CLZ_MSVC #endif #if defined(__WATCOMC__) && defined(__386__) #define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif #ifdef __MRC__ #include #define DRFLAC_IMPLEMENT_CLZ_MRC #endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { drflac_uint32 n; static drflac_uint32 clz_table_4[] = { 0, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }; if (x == 0) { return sizeof(x)*8; } n = clz_table_4[x >> (sizeof(x)*8 - 4)]; if (n == 0) { #ifdef DRFLAC_64BIT if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } #else if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } #endif n += clz_table_4[x >> (sizeof(x)*8 - 4)]; } return n - 1; } #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { /* Fast compile time check for ARM. */ #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) return DRFLAC_TRUE; #elif defined(__MRC__) return DRFLAC_TRUE; #else /* If the compiler itself does not support the intrinsic then we'll need to return false. */ #ifdef DRFLAC_HAS_LZCNT_INTRINSIC return drflac__gIsLZCNTSupported; #else return DRFLAC_FALSE; #endif #endif } static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { /* It's critical for competitive decoding performance that this function be highly optimal. With MSVC we can use the __lzcnt64() and __lzcnt() intrinsics to achieve good performance, however on GCC and Clang it's a little bit more annoying. The __builtin_clzl() and __builtin_clzll() intrinsics leave it undefined as to the return value when `x` is 0. We need this to be well defined as returning 32 or 64, depending on whether or not it's a 32- or 64-bit build. To work around this we would need to add a conditional to check for the x = 0 case, but this creates unnecessary inefficiency. To work around this problem I have written some inline assembly to emit the LZCNT (x86) or CLZ (ARM) instruction directly which removes the need to include the conditional. This has worked well in the past, but for some reason Clang's MSVC compatible driver, clang-cl, does not seem to be handling this in the same way as the normal Clang driver. It seems that `clang-cl` is just outputting the wrong results sometimes, maybe due to some register getting clobbered? I'm not sure if this is a bug with dr_flac's inlined assembly (most likely), a bug in `clang-cl` or just a misunderstanding on my part with inline assembly rules for `clang-cl`. If somebody can identify an error in dr_flac's inlined assembly I'm happy to get that fixed. Fortunately there is an easy workaround for this. Clang implements MSVC-specific intrinsics for compatibility. It also defines _MSC_VER for extra compatibility. We can therefore just check for _MSC_VER and use the MSVC intrinsic which, fortunately for us, Clang supports. It would still be nice to know how to fix the inlined assembly for correctness sake, however. */ #if defined(_MSC_VER) /*&& !defined(__clang__)*/ /* <-- Intentionally wanting Clang to use the MSVC __lzcnt64/__lzcnt intrinsics due to above ^. */ #ifdef DRFLAC_64BIT return (drflac_uint32)__lzcnt64(x); #else return (drflac_uint32)__lzcnt(x); #endif #else #if defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_X64) { drflac_uint64 r; __asm__ __volatile__ ( "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return (drflac_uint32)r; } #elif defined(DRFLAC_X86) { drflac_uint32 r; __asm__ __volatile__ ( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ { unsigned int r; __asm__ __volatile__ ( #if defined(DRFLAC_64BIT) "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ #else "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) #endif ); return r; } #else if (x == 0) { return sizeof(x)*8; } #ifdef DRFLAC_64BIT return (drflac_uint32)__builtin_clzll((drflac_uint64)x); #else return (drflac_uint32)__builtin_clzl((drflac_uint32)x); #endif #endif #else /* Unsupported compiler. */ #error "This compiler does not support the lzcnt intrinsic." #endif #endif } #endif #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC #include /* For BitScanReverse(). */ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) { drflac_uint32 n; if (x == 0) { return sizeof(x)*8; } #ifdef DRFLAC_64BIT _BitScanReverse64((unsigned long*)&n, x); #else _BitScanReverse((unsigned long*)&n, x); #endif return sizeof(x)*8 - n - 1; } #endif #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT /* Use the LZCNT instruction (only available on some processors since the 2010s). */ #pragma aux drflac__clz_watcom_lzcnt = \ "db 0F3h, 0Fh, 0BDh, 0C0h" /* lzcnt eax, eax */ \ parm [eax] \ value [eax] \ modify nomemory; #else /* Use the 386+-compatible implementation. */ #pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ parm [eax] nomemory \ value [eax] \ modify exact [eax] nomemory; #endif #endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT if (drflac__is_lzcnt_supported()) { return drflac__clz_lzcnt(x); } else #endif { #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC return drflac__clz_msvc(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) return drflac__clz_watcom_lzcnt(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); #elif defined(__MRC__) return __cntlzw(x); #else return drflac__clz_software(x); #endif } } static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { drflac_uint32 zeroCounter = 0; drflac_uint32 setBitOffsetPlus1; while (bs->cache == 0) { zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } if (bs->cache == 1) { /* Not catching this would lead to undefined behaviour: a shift of a 32-bit number by 32 or more is undefined */ *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } setBitOffsetPlus1 = drflac__clz(bs->cache); setBitOffsetPlus1 += 1; if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* This happens when we get to end of stream */ return DRFLAC_FALSE; } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(offsetFromStart > 0); /* Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. */ if (offsetFromStart > 0x7FFFFFFF) { drflac_uint64 bytesRemaining = offsetFromStart; if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { return DRFLAC_FALSE; } } /* The cache should be reset to force a reload of fresh data from the client. */ drflac__reset_cache(bs); return DRFLAC_TRUE; } static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { drflac_uint8 crc; drflac_uint64 result; drflac_uint8 utf8[7] = {0}; int byteCount; int i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pNumberOut != NULL); DRFLAC_ASSERT(pCRCOut != NULL); crc = *pCRCOut; if (!drflac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; *pCRCOut = crc; return DRFLAC_SUCCESS; } /*byteCount = 1;*/ if ((utf8[0] & 0xE0) == 0xC0) { byteCount = 2; } else if ((utf8[0] & 0xF0) == 0xE0) { byteCount = 3; } else if ((utf8[0] & 0xF8) == 0xF0) { byteCount = 4; } else if ((utf8[0] & 0xFC) == 0xF8) { byteCount = 5; } else if ((utf8[0] & 0xFE) == 0xFC) { byteCount = 6; } else if ((utf8[0] & 0xFF) == 0xFE) { byteCount = 7; } else { *pNumberOut = 0; return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */ } /* Read extra bytes. */ DRFLAC_ASSERT(byteCount > 1); result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { if (!drflac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; *pCRCOut = crc; return DRFLAC_SUCCESS; } static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) { #if 1 /* Needs optimizing. */ drflac_uint32 result = 0; while (x > 0) { result += 1; x >>= 1; } return result; #endif } static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) { /* https://web.archive.org/web/20220205005724/https://github.com/ietf-wg-cellar/flac-specification/blob/37a49aa48ba4ba12e8757badfc59c0df35435fec/rfc_backmatter.md */ return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; } /* The next two functions are responsible for calculating the prediction. When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. */ #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int32 prediction = 0; DRFLAC_ASSERT(order <= 32); /* 32-bit version. */ /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */ switch (order) { case 32: prediction += coefficients[31] * pDecodedSamples[-32]; case 31: prediction += coefficients[30] * pDecodedSamples[-31]; case 30: prediction += coefficients[29] * pDecodedSamples[-30]; case 29: prediction += coefficients[28] * pDecodedSamples[-29]; case 28: prediction += coefficients[27] * pDecodedSamples[-28]; case 27: prediction += coefficients[26] * pDecodedSamples[-27]; case 26: prediction += coefficients[25] * pDecodedSamples[-26]; case 25: prediction += coefficients[24] * pDecodedSamples[-25]; case 24: prediction += coefficients[23] * pDecodedSamples[-24]; case 23: prediction += coefficients[22] * pDecodedSamples[-23]; case 22: prediction += coefficients[21] * pDecodedSamples[-22]; case 21: prediction += coefficients[20] * pDecodedSamples[-21]; case 20: prediction += coefficients[19] * pDecodedSamples[-20]; case 19: prediction += coefficients[18] * pDecodedSamples[-19]; case 18: prediction += coefficients[17] * pDecodedSamples[-18]; case 17: prediction += coefficients[16] * pDecodedSamples[-17]; case 16: prediction += coefficients[15] * pDecodedSamples[-16]; case 15: prediction += coefficients[14] * pDecodedSamples[-15]; case 14: prediction += coefficients[13] * pDecodedSamples[-14]; case 13: prediction += coefficients[12] * pDecodedSamples[-13]; case 12: prediction += coefficients[11] * pDecodedSamples[-12]; case 11: prediction += coefficients[10] * pDecodedSamples[-11]; case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } return (drflac_int32)(prediction >> shift); } static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int64 prediction; DRFLAC_ASSERT(order <= 32); /* 64-bit version. */ /* This method is faster on the 32-bit build when compiling with VC++. See note below. */ #ifndef DRFLAC_64BIT if (order == 8) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; } else if (order == 7) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; } else if (order == 3) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; } else if (order == 6) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; } else if (order == 5) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; } else if (order == 4) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; } else if (order == 12) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; } else if (order == 2) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; } else if (order == 1) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; } else if (order == 10) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; } else if (order == 9) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; } else if (order == 11) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; } else { int j; prediction = 0; for (j = 0; j < (int)order; ++j) { prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; } } #endif /* VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. */ #ifdef DRFLAC_64BIT prediction = 0; switch (order) { case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; } #endif return (drflac_int32)(prediction >> shift); } #if 0 /* Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the sake of readability and should only be used as a reference. */ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { drflac_uint32 zeroCounter = 0; for (;;) { drflac_uint8 bit; if (!drflac__read_uint8(bs, 1, &bit)) { return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; } else { break; } } drflac_uint32 decodedRice; if (riceParam > 0) { if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { return DRFLAC_FALSE; } } else { decodedRice = 0; } decodedRice |= (zeroCounter << riceParam); if ((decodedRice & 0x01)) { decodedRice = ~(decodedRice >> 1); } else { decodedRice = (decodedRice >> 1); } if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; } #endif #if 0 static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_uint32 zeroCounter = 0; drflac_uint32 decodedRice; for (;;) { drflac_uint8 bit; if (!drflac__read_uint8(bs, 1, &bit)) { return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; } else { break; } } if (riceParam > 0) { if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { return DRFLAC_FALSE; } } else { decodedRice = 0; } *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; return DRFLAC_TRUE; } #endif #if 0 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_cache_t riceParamMask; drflac_uint32 zeroCounter; drflac_uint32 setBitOffsetPlus1; drflac_uint32 riceParamPart; drflac_uint32 riceLength; DRFLAC_ASSERT(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */ riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); zeroCounter = 0; while (bs->cache == 0) { zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } setBitOffsetPlus1 = drflac__clz(bs->cache); zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; riceLength = setBitOffsetPlus1 + riceParam; if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); bs->consumedBits += riceLength; bs->cache <<= riceLength; } else { drflac_uint32 bitCountLo; drflac_cache_t resultHi; bs->consumedBits += riceLength; bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }" */ /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs->consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif } else { /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* This happens when we get to end of stream */ return DRFLAC_FALSE; } } riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; } pZeroCounterOut[0] = zeroCounter; pRiceParamPartOut[0] = riceParamPart; return DRFLAC_TRUE; } #endif static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_uint32 riceParamPlus1 = riceParam + 1; /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; /* The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have no idea how this will work in practice... */ drflac_cache_t bs_cache = bs->cache; drflac_uint32 bs_consumedBits = bs->consumedBits; /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { pZeroCounterOut[0] = lzcount; /* It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled outside of this function at a higher level. */ extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { /* Getting here means the rice parameter part is wholly contained within the current cache line. */ pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { drflac_uint32 riceParamPartHi; drflac_uint32 riceParamPartLo; drflac_uint32 riceParamPartLoBitCount; /* Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache line, reload the cache, and then combine it with the head of the next cache line. */ /* Grab the high part of the rice parameter part. */ riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); /* Before reloading the cache we need to grab the size in bits of the low part. */ riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); /* Now reload the cache. */ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* This happens when we get to end of stream */ return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } /* We should now have enough information to construct the rice parameter part. */ riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; bs_cache <<= riceParamPartLoBitCount; } } else { /* Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call to drflac__clz() and we need to reload the cache. */ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); for (;;) { if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } lzcount = drflac__clz(bs_cache); zeroCounter += lzcount; if (lzcount < sizeof(bs_cache)*8) { break; } } pZeroCounterOut[0] = zeroCounter; goto extract_rice_param_part; } /* Make sure the cache is restored at the end of it all. */ bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; return DRFLAC_TRUE; } static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) { drflac_uint32 riceParamPlus1 = riceParam + 1; drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; /* The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have no idea how this will work in practice... */ drflac_cache_t bs_cache = bs->cache; drflac_uint32 bs_consumedBits = bs->consumedBits; /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { /* It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled outside of this function at a higher level. */ extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { /* Getting here means the rice parameter part is wholly contained within the current cache line. */ bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { /* Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache line, reload the cache, and then combine it with the head of the next cache line. */ /* Before reloading the cache we need to grab the size in bits of the low part. */ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); /* Now reload the cache. */ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { /* This happens when we get to end of stream */ return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } bs_cache <<= riceParamPartLoBitCount; } } else { /* Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call to drflac__clz() and we need to reload the cache. */ for (;;) { if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { /* Slow path. We need to fetch more data from the client. */ if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { break; } } goto extract_rice_param_part; } /* Make sure the cache is restored at the end of it all. */ bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0; drflac_uint32 riceParamPart0; drflac_uint32 riceParamMask; drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); (void)bitsPerSample; (void)order; (void)shift; (void)coefficients; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); i = 0; while (i < count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; pSamplesOut[i] = riceParamPart0; i += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0 = 0; drflac_uint32 zeroCountPart1 = 0; drflac_uint32 zeroCountPart2 = 0; drflac_uint32 zeroCountPart3 = 0; drflac_uint32 riceParamPart0 = 0; drflac_uint32 riceParamPart1 = 0; drflac_uint32 riceParamPart2 = 0; drflac_uint32 riceParamPart3 = 0; drflac_uint32 riceParamMask; const drflac_int32* pSamplesOutEnd; drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder == 0) { return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } riceParamMask = (drflac_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { /* Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version against an array. Not sure why, but perhaps it's making more efficient use of registers? */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; riceParamPart2 &= riceParamMask; riceParamPart3 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart1 |= (zeroCountPart1 << riceParam); riceParamPart2 |= (zeroCountPart2 << riceParam); riceParamPart3 |= (zeroCountPart3 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { while (pSamplesOut < pSamplesOutEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; riceParamPart2 &= riceParamMask; riceParamPart3 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart1 |= (zeroCountPart1 << riceParam); riceParamPart2 |= (zeroCountPart2 << riceParam); riceParamPart3 |= (zeroCountPart3 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } i = (count & ~3); while (i < count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/ /* Sample reconstruction. */ if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; } return DRFLAC_TRUE; } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) { __m128i r; /* Pack. */ r = _mm_packs_epi32(a, b); /* a3a2 a1a0 b3b2 b1b0 -> a3a2 b3b2 a1a0 b1b0 */ r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); /* a3a2 b3b2 a1a0 b1b0 -> a3b3 a2b2 a1b1 a0b0 */ r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); return r; } #endif #if defined(DRFLAC_SUPPORT_SSE41) static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) { return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); } static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) { __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); return _mm_add_epi32(x64, x32); } static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) { return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); } static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) { /* To simplify this we are assuming count < 32. This restriction allows us to work on a low side and a high side. The low side is shifted with zero bits, whereas the right side is shifted with sign bits. */ __m128i lo = _mm_srli_epi64(x, count); __m128i hi = _mm_srai_epi32(x, count); hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); /* The high part needs to have the low part cleared. */ return _mm_or_si128(lo, hi); } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts0 = 0; drflac_uint32 zeroCountParts1 = 0; drflac_uint32 zeroCountParts2 = 0; drflac_uint32 zeroCountParts3 = 0; drflac_uint32 riceParamParts0 = 0; drflac_uint32 riceParamParts1 = 0; drflac_uint32 riceParamParts2 = 0; drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; __m128i samples128_0; __m128i samples128_4; __m128i samples128_8; __m128i riceParamMask128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); /* Pre-load. */ coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); coefficients128_8 = _mm_setzero_si128(); samples128_0 = _mm_setzero_si128(); samples128_4 = _mm_setzero_si128(); samples128_8 = _mm_setzero_si128(); /* Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted so I think there's opportunity for this to be simplified. */ #if 1 { int runningOrder = order; /* 0 - 3. */ if (runningOrder >= 4) { coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; } runningOrder = 0; } /* 4 - 7 */ if (runningOrder >= 4) { coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; } runningOrder = 0; } /* 8 - 11 */ if (runningOrder == 4) { coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; } runningOrder = 0; } /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); } #else /* This causes strict-aliasing warnings with GCC. */ switch (order) { case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif /* For this version we are doing one sample at a time. */ while (pDecodedSamples < pDecodedSamplesEnd) { __m128i prediction128; __m128i zeroCountPart128; __m128i riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); /* <-- SSE2 compatible */ /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01)), _mm_set1_epi32(0xFFFFFFFF)));*/ /* <-- Only supported from SSE4.1 and is slower in my testing... */ if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); /* Horizontal add and shift. */ prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); /* Horizontal add and shift. */ prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } else { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); /* Horizontal add and shift. */ prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } /* We store samples in groups of 4. */ _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); pDecodedSamples += 4; } /* Make sure we process the last few samples. */ i = (count & ~3); while (i < (int)count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; /* Sample reconstruction. */ pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts0 = 0; drflac_uint32 zeroCountParts1 = 0; drflac_uint32 zeroCountParts2 = 0; drflac_uint32 zeroCountParts3 = 0; drflac_uint32 riceParamParts0 = 0; drflac_uint32 riceParamParts1 = 0; drflac_uint32 riceParamParts2 = 0; drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; __m128i samples128_0; __m128i samples128_4; __m128i samples128_8; __m128i prediction128; __m128i riceParamMask128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; DRFLAC_ASSERT(order <= 12); riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); prediction128 = _mm_setzero_si128(); /* Pre-load. */ coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); coefficients128_8 = _mm_setzero_si128(); samples128_0 = _mm_setzero_si128(); samples128_4 = _mm_setzero_si128(); samples128_8 = _mm_setzero_si128(); #if 1 { int runningOrder = order; /* 0 - 3. */ if (runningOrder >= 4) { coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; } runningOrder = 0; } /* 4 - 7 */ if (runningOrder >= 4) { coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; } runningOrder = 0; } /* 8 - 11 */ if (runningOrder == 4) { coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; } runningOrder = 0; } /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); } #else switch (order) { case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif /* For this version we are doing one sample at a time. */ while (pDecodedSamples < pDecodedSamplesEnd) { __m128i zeroCountPart128; __m128i riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); for (i = 0; i < 4; i += 1) { prediction128 = _mm_xor_si128(prediction128, prediction128); /* Reset to 0. */ switch (order) { case 12: case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); case 10: case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); case 8: case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); case 6: case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); case 4: case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); case 2: case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); } /* Horizontal add and shift. */ prediction128 = drflac__mm_hadd_epi64(prediction128); prediction128 = drflac__mm_srai_epi64(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); /* Our value should be sitting in prediction128[0]. We need to combine this with our SSE samples. */ samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } /* We store samples in groups of 4. */ _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); pDecodedSamples += 4; } /* Make sure we process the last few samples. */ i = (count & ~3); while (i < (int)count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; /* Sample reconstruction. */ pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */ if (lpcOrder > 0 && lpcOrder <= 12) { if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) { vst1q_s32(p+0, x.val[0]); vst1q_s32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) { vst1q_u32(p+0, x.val[0]); vst1q_u32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); vst1q_f32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) { vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) { vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); } static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) { drflac_int32 x[4]; x[3] = x3; x[2] = x2; x[1] = x1; x[0] = x0; return vld1q_s32(x); } static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) { /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ /* Reference */ /*return drflac__vdupq_n_s32x4( vgetq_lane_s32(a, 0), vgetq_lane_s32(b, 3), vgetq_lane_s32(b, 2), vgetq_lane_s32(b, 1) );*/ return vextq_s32(b, a, 1); } static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) { /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ /* Reference */ /*return drflac__vdupq_n_s32x4( vgetq_lane_s32(a, 0), vgetq_lane_s32(b, 3), vgetq_lane_s32(b, 2), vgetq_lane_s32(b, 1) );*/ return vextq_u32(b, a, 1); } static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) { /* The sum must end up in position 0. */ /* Reference */ /*return vdupq_n_s32( vgetq_lane_s32(x, 3) + vgetq_lane_s32(x, 2) + vgetq_lane_s32(x, 1) + vgetq_lane_s32(x, 0) );*/ int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); return vpadd_s32(r, r); } static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) { return vadd_s64(vget_high_s64(x), vget_low_s64(x)); } static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) { /* Reference */ /*return drflac__vdupq_n_s32x4( vgetq_lane_s32(x, 0), vgetq_lane_s32(x, 1), vgetq_lane_s32(x, 2), vgetq_lane_s32(x, 3) );*/ return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); } static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) { return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); } static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) { return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts[4]; drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; int32x4_t samples128_0; int32x4_t samples128_4; int32x4_t samples128_8; uint32x4_t riceParamMask128; int32x4_t riceParam128; int32x2_t shift64; uint32x4_t one128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ one128 = vdupq_n_u32(1); /* Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted so I think there's opportunity for this to be simplified. */ { int runningOrder = order; drflac_int32 tempC[4] = {0, 0, 0, 0}; drflac_int32 tempS[4] = {0, 0, 0, 0}; /* 0 - 3. */ if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ } coefficients128_0 = vld1q_s32(tempC); samples128_0 = vld1q_s32(tempS); runningOrder = 0; } /* 4 - 7 */ if (runningOrder >= 4) { coefficients128_4 = vld1q_s32(coefficients + 4); samples128_4 = vld1q_s32(pSamplesOut - 8); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ } coefficients128_4 = vld1q_s32(tempC); samples128_4 = vld1q_s32(tempS); runningOrder = 0; } /* 8 - 11 */ if (runningOrder == 4) { coefficients128_8 = vld1q_s32(coefficients + 8); samples128_8 = vld1q_s32(pSamplesOut - 12); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ } coefficients128_8 = vld1q_s32(tempC); samples128_8 = vld1q_s32(tempS); runningOrder = 0; } /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ coefficients128_0 = drflac__vrevq_s32(coefficients128_0); coefficients128_4 = drflac__vrevq_s32(coefficients128_4); coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } /* For this version we are doing one sample at a time. */ while (pDecodedSamples < pDecodedSamplesEnd) { int32x4_t prediction128; int32x2_t prediction64; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_0, samples128_0); /* Horizontal add and shift. */ prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); /* Horizontal add and shift. */ prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_8, samples128_8); prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); /* Horizontal add and shift. */ prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } /* We store samples in groups of 4. */ vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } /* Make sure we process the last few samples. */ i = (count & ~3); while (i < (int)count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; /* Sample reconstruction. */ pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts[4]; drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; int32x4_t samples128_0; int32x4_t samples128_4; int32x4_t samples128_8; uint32x4_t riceParamMask128; int32x4_t riceParam128; int64x1_t shift64; uint32x4_t one128; int64x2_t prediction128 = { 0 }; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ one128 = vdupq_n_u32(1); /* Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted so I think there's opportunity for this to be simplified. */ { int runningOrder = order; drflac_int32 tempC[4] = {0, 0, 0, 0}; drflac_int32 tempS[4] = {0, 0, 0, 0}; /* 0 - 3. */ if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ } coefficients128_0 = vld1q_s32(tempC); samples128_0 = vld1q_s32(tempS); runningOrder = 0; } /* 4 - 7 */ if (runningOrder >= 4) { coefficients128_4 = vld1q_s32(coefficients + 4); samples128_4 = vld1q_s32(pSamplesOut - 8); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ } coefficients128_4 = vld1q_s32(tempC); samples128_4 = vld1q_s32(tempS); runningOrder = 0; } /* 8 - 11 */ if (runningOrder == 4) { coefficients128_8 = vld1q_s32(coefficients + 8); samples128_8 = vld1q_s32(pSamplesOut - 12); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ } coefficients128_8 = vld1q_s32(tempC); samples128_8 = vld1q_s32(tempS); runningOrder = 0; } /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ coefficients128_0 = drflac__vrevq_s32(coefficients128_0); coefficients128_4 = drflac__vrevq_s32(coefficients128_4); coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } /* For this version we are doing one sample at a time. */ while (pDecodedSamples < pDecodedSamplesEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); for (i = 0; i < 4; i += 1) { int64x1_t prediction64; prediction128 = veorq_s64(prediction128, prediction128); /* Reset to 0. */ switch (order) { case 12: case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); case 10: case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); case 8: case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); case 6: case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); case 4: case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); case 2: case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); } /* Horizontal add and shift. */ prediction64 = drflac__vhaddq_s64(prediction128); prediction64 = vshl_s64(prediction64, shift64); prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); /* Our value should be sitting in prediction64[0]. We need to combine this with our SSE samples. */ samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } /* We store samples in groups of 4. */ vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } /* Make sure we process the last few samples. */ i = (count & ~3); while (i < (int)count) { /* Rice extraction. */ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { return DRFLAC_FALSE; } /* Rice reconstruction. */ riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; /* Sample reconstruction. */ pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */ if (lpcOrder > 0 && lpcOrder <= 12) { if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { #if defined(DRFLAC_SUPPORT_SSE41) if (drflac__gIsSSE41Supported) { return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported) { return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { /* Scalar fallback. */ #if 0 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } /* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); for (i = 0; i < count; ++i) { if (!drflac__seek_rice_parts(bs, riceParam)) { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */ DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { if (unencodedBitsPerSample > 0) { if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { return DRFLAC_FALSE; } } else { pSamplesOut[i] = 0; } if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; } /* Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The and parameters are used to determine how many residual values need to be decoded. */ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; drflac_uint32 samplesInPartition; drflac_uint32 partitionsRemaining; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(blockSize != 0); DRFLAC_ASSERT(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */ if (!drflac__read_uint8(bs, 2, &residualMethod)) { return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ } /* Ignore the first values. */ pDecodedSamples += lpcOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; } /* From the FLAC spec: The Rice partition order in a Rice-coded residual section must be less than or equal to 8. */ if (partitionOrder > 8) { return DRFLAC_FALSE; } /* Validation check. */ if ((blockSize / (1 << partitionOrder)) < lpcOrder) { return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } else { drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } pDecodedSamples += samplesInPartition; if (partitionsRemaining == 1) { break; } partitionsRemaining -= 1; if (partitionOrder != 0) { samplesInPartition = blockSize / (1 << partitionOrder); } } return DRFLAC_TRUE; } /* Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The and parameters are used to determine how many residual values need to be decoded. */ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; drflac_uint32 samplesInPartition; drflac_uint32 partitionsRemaining; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(blockSize != 0); if (!drflac__read_uint8(bs, 2, &residualMethod)) { return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ } if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; } /* From the FLAC spec: The Rice partition order in a Rice-coded residual section must be less than or equal to 8. */ if (partitionOrder > 8) { return DRFLAC_FALSE; } /* Validation check. */ if ((blockSize / (1 << partitionOrder)) <= order) { return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - order; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { return DRFLAC_FALSE; } } else { drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { return DRFLAC_FALSE; } } if (partitionsRemaining == 1) { break; } partitionsRemaining -= 1; samplesInPartition = blockSize / (1 << partitionOrder); } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { drflac_uint32 i; /* Only a single sample needs to be decoded here. */ drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } /* We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) we'll want to look at a more efficient way. */ for (i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { drflac_uint32 i; for (i = 0; i < blockSize; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { drflac_uint32 i; static drflac_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, {3, -3, 1, 0}, {4, -6, 4, -1} }; /* Warm up samples and coefficients. */ for (i = 0; i < lpcOrder; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { drflac_uint8 i; drflac_uint8 lpcPrecision; drflac_int8 lpcShift; drflac_int32 coefficients[32]; /* Warm up samples. */ for (i = 0; i < lpcOrder; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { return DRFLAC_FALSE; } if (lpcPrecision == 15) { return DRFLAC_FALSE; /* Invalid. */ } lpcPrecision += 1; if (!drflac__read_int8(bs, 5, &lpcShift)) { return DRFLAC_FALSE; } /* From the FLAC specification: Quantized linear predictor coefficient shift needed in bits (NOTE: this number is signed two's-complement) Emphasis on the "signed two's-complement". In practice there does not seem to be any encoders nor decoders supporting negative shifts. For now dr_flac is not going to support negative shifts as I don't have any reference files. However, when a reference file comes through I will consider adding support. */ if (lpcShift < 0) { return DRFLAC_FALSE; } DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); for (i = 0; i < lpcOrder; ++i) { if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { return DRFLAC_FALSE; } } if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) { const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(header != NULL); /* Keep looping until we find a valid sync code. */ for (;;) { drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */ drflac_uint8 reserved = 0; drflac_uint8 blockingStrategy = 0; drflac_uint8 blockSize = 0; drflac_uint8 sampleRate = 0; drflac_uint8 channelAssignment = 0; drflac_uint8 bitsPerSample = 0; drflac_bool32 isVariableBlockSize; if (!drflac__find_and_seek_to_next_sync_code(bs)) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 1, &reserved)) { return DRFLAC_FALSE; } if (reserved == 1) { continue; } crc8 = drflac_crc8(crc8, reserved, 1); if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, blockingStrategy, 1); if (!drflac__read_uint8(bs, 4, &blockSize)) { return DRFLAC_FALSE; } if (blockSize == 0) { continue; } crc8 = drflac_crc8(crc8, blockSize, 4); if (!drflac__read_uint8(bs, 4, &sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, sampleRate, 4); if (!drflac__read_uint8(bs, 4, &channelAssignment)) { return DRFLAC_FALSE; } if (channelAssignment > 10) { continue; } crc8 = drflac_crc8(crc8, channelAssignment, 4); if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { return DRFLAC_FALSE; } if (bitsPerSample == 3 || bitsPerSample == 7) { continue; } crc8 = drflac_crc8(crc8, bitsPerSample, 3); if (!drflac__read_uint8(bs, 1, &reserved)) { return DRFLAC_FALSE; } if (reserved == 1) { continue; } crc8 = drflac_crc8(crc8, reserved, 1); isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { drflac_uint64 pcmFrameNumber; drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; } } header->flacFrameNumber = 0; header->pcmFrameNumber = pcmFrameNumber; } else { drflac_uint64 flacFrameNumber = 0; drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; } } header->flacFrameNumber = (drflac_uint32)flacFrameNumber; /* <-- Safe cast. */ header->pcmFrameNumber = 0; } DRFLAC_ASSERT(blockSize > 0); if (blockSize == 1) { header->blockSizeInPCMFrames = 192; } else if (blockSize <= 5) { DRFLAC_ASSERT(blockSize >= 2); header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); } else if (blockSize == 6) { if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); header->blockSizeInPCMFrames += 1; } else if (blockSize == 7) { if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); if (header->blockSizeInPCMFrames == 0xFFFF) { return DRFLAC_FALSE; /* Frame is too big. This is the size of the frame minus 1. The STREAMINFO block defines the max block size which is 16-bits. Adding one will make it 17 bits and therefore too big. */ } header->blockSizeInPCMFrames += 1; } else { DRFLAC_ASSERT(blockSize >= 8); header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); } if (sampleRate <= 11) { header->sampleRate = sampleRateTable[sampleRate]; } else if (sampleRate == 12) { if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 8); header->sampleRate *= 1000; } else if (sampleRate == 13) { if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 16); } else if (sampleRate == 14) { if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 16); header->sampleRate *= 10; } else { continue; /* Invalid. Assume an invalid block. */ } header->channelAssignment = channelAssignment; header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; if (header->bitsPerSample == 0) { header->bitsPerSample = streaminfoBitsPerSample; } if (header->bitsPerSample != streaminfoBitsPerSample) { /* If this subframe has a different bitsPerSample then streaminfo or the first frame, reject it */ return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 8, &header->crc8)) { return DRFLAC_FALSE; } #ifndef DR_FLAC_NO_CRC if (header->crc8 != crc8) { continue; /* CRC mismatch. Loop back to the top and find the next sync code. */ } #endif return DRFLAC_TRUE; } } static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { drflac_uint8 header; int type; if (!drflac__read_uint8(bs, 8, &header)) { return DRFLAC_FALSE; } /* First bit should always be 0. */ if ((header & 0x80) != 0) { return DRFLAC_FALSE; } type = (header & 0x7E) >> 1; if (type == 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; } else if (type == 1) { pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; } else { if ((type & 0x20) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; } } else { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; } } if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { return DRFLAC_FALSE; } /* Wasted bits per sample. */ pSubframe->wastedBitsPerSample = 0; if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { return DRFLAC_FALSE; } pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) { drflac_subframe* pSubframe; drflac_uint32 subframeBitsPerSample; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { return DRFLAC_FALSE; } /* Side channels require an extra bit per sample. Took a while to figure that one out... */ subframeBitsPerSample = frame->header.bitsPerSample; if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (subframeBitsPerSample > 32) { /* libFLAC and ffmpeg reject 33-bit subframes as well */ return DRFLAC_FALSE; } /* Need to handle wasted bits per sample. */ if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: { drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_VERBATIM: { drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_FIXED: { drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_LPC: { drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; default: return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { drflac_subframe* pSubframe; drflac_uint32 subframeBitsPerSample; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { return DRFLAC_FALSE; } /* Side channels require an extra bit per sample. Took a while to figure that one out... */ subframeBitsPerSample = frame->header.bitsPerSample; if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } /* Need to handle wasted bits per sample. */ if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = NULL; switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: { if (!drflac__seek_bits(bs, subframeBitsPerSample)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_LPC: { drflac_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { return DRFLAC_FALSE; } if (lpcPrecision == 15) { return DRFLAC_FALSE; /* Invalid. */ } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { return DRFLAC_FALSE; } } break; default: return DRFLAC_FALSE; } return DRFLAC_TRUE; } static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) { drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; DRFLAC_ASSERT(channelAssignment <= 10); return lookup[channelAssignment]; } static drflac_result drflac__decode_flac_frame(drflac* pFlac) { int channelCount; int i; drflac_uint8 paddingSizeInBits; drflac_uint16 desiredCRC16; #ifndef DR_FLAC_NO_CRC drflac_uint16 actualCRC16; #endif /* This function should be called while the stream is sitting on the first byte after the frame header. */ DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { return DRFLAC_ERROR; } /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); if (channelCount != (int)pFlac->channels) { return DRFLAC_ERROR; } for (i = 0; i < channelCount; ++i) { if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { return DRFLAC_ERROR; } } paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { drflac_uint8 padding = 0; if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { return DRFLAC_AT_END; } } #ifndef DR_FLAC_NO_CRC actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ } #endif pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; return DRFLAC_SUCCESS; } static drflac_result drflac__seek_flac_frame(drflac* pFlac) { int channelCount; int i; drflac_uint16 desiredCRC16; #ifndef DR_FLAC_NO_CRC drflac_uint16 actualCRC16; #endif channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); for (i = 0; i < channelCount; ++i) { if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { return DRFLAC_ERROR; } } /* Padding. */ if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { return DRFLAC_ERROR; } /* CRC. */ #ifndef DR_FLAC_NO_CRC actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ } #endif return DRFLAC_SUCCESS; } static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) { DRFLAC_ASSERT(pFlac != NULL); for (;;) { drflac_result result; if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } result = drflac__decode_flac_frame(pFlac); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_CRC_MISMATCH) { continue; /* CRC mismatch. Skip to the next frame. */ } else { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } } static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) { drflac_uint64 firstPCMFrame; drflac_uint64 lastPCMFrame; DRFLAC_ASSERT(pFlac != NULL); firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; if (firstPCMFrame == 0) { firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; } lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; if (lastPCMFrame > 0) { lastPCMFrame -= 1; /* Needs to be zero based. */ } if (pFirstPCMFrame) { *pFirstPCMFrame = firstPCMFrame; } if (pLastPCMFrame) { *pLastPCMFrame = lastPCMFrame; } } static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) { drflac_bool32 result; DRFLAC_ASSERT(pFlac != NULL); result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); pFlac->currentPCMFrame = 0; return result; } static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) { /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */ DRFLAC_ASSERT(pFlac != NULL); return drflac__seek_flac_frame(pFlac); } static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) { drflac_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; /* Couldn't read the next frame, so just break from the loop and return. */ } } else { if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { pcmFramesRead += pcmFramesToSeek; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; /* <-- Safe cast. Will always be < currentFrame.pcmFramesRemaining < 65536. */ pcmFramesToSeek = 0; } else { pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; } } } pFlac->currentPCMFrame += pcmFramesRead; return pcmFramesRead; } static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_bool32 isMidFrame = DRFLAC_FALSE; drflac_uint64 runningPCMFrameCount; DRFLAC_ASSERT(pFlac != NULL); /* If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. */ if (pcmFrameIndex >= pFlac->currentPCMFrame) { /* Seeking forward. Need to seek from the current position. */ runningPCMFrameCount = pFlac->currentPCMFrame; /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } else { isMidFrame = DRFLAC_TRUE; } } else { /* Seeking backwards. Need to seek from the start of the file. */ runningPCMFrameCount = 0; /* Move back to the start. */ if (!drflac__seek_to_first_frame(pFlac)) { return DRFLAC_FALSE; } /* Decode the first frame in preparation for sample-exact seeking below. */ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } /* We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. */ for (;;) { drflac_uint64 pcmFrameCountInThisFLACFrame; drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { /* The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend it never existed and keep iterating. */ drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } else { /* We started seeking mid-frame which means we need to skip the frame decoding part. */ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this frame never existed and leave the running sample count untouched. */ if (!isMidFrame) { drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } else { /* We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. */ runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; isMidFrame = DRFLAC_FALSE; } /* If we are seeking to the end of the file and we've just hit it, we're done. */ if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { return DRFLAC_TRUE; } } next_iteration: /* Grab the next frame in preparation for the next iteration. */ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } } #if !defined(DR_FLAC_NO_CRC) /* We use an average compression ratio to determine our approximate start location. FLAC files are generally about 50%-70% the size of their uncompressed counterparts so we'll use this as a basis. I'm going to split the middle and use a factor of 0.6 to determine the starting location. */ #define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) { DRFLAC_ASSERT(pFlac != NULL); DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); DRFLAC_ASSERT(targetByte >= rangeLo); DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; for (;;) { /* After rangeLo == rangeHi == targetByte fails, we need to break out. */ drflac_uint64 lastTargetByte = targetByte; /* When seeking to a byte, failure probably means we've attempted to seek beyond the end of the stream. To counter this we just halve it each attempt. */ if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { /* If we couldn't even seek to the first byte in the stream we have a problem. Just abandon the whole thing. */ if (targetByte == 0) { drflac__seek_to_first_frame(pFlac); /* Try to recover. */ return DRFLAC_FALSE; } /* Halve the byte location and continue. */ targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { /* Getting here should mean that we have seeked to an appropriate byte. */ /* Clear the details of the FLAC frame so we don't misreport data. */ DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); /* Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing so it needs to stay this way for now. */ #if 1 if (!drflac__read_and_decode_next_flac_frame(pFlac)) { /* Halve the byte location and continue. */ targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #else if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { /* Halve the byte location and continue. */ targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #endif } /* We already tried this byte and there are no more to try, break out. */ if(targetByte == lastTargetByte) { return DRFLAC_FALSE; } } /* The current PCM frame needs to be updated based on the frame we just seeked to. */ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = targetByte; return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) { /* This section of code would be used if we were only decoding the FLAC frame header when calling drflac__seek_to_approximate_flac_frame_to_byte(). */ #if 0 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { /* We failed to decode this frame which may be due to it being corrupt. We'll just use the next valid FLAC frame. */ if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { return DRFLAC_FALSE; } } #endif return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; } static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) { /* This assumes pFlac->currentPCMFrame is sitting on byteRangeLo upon entry. */ drflac_uint64 targetByte; drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; drflac_uint64 pcmRangeHi = 0; drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } for (;;) { if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { /* We found a FLAC frame. We need to check if it contains the sample we're looking for. */ drflac_uint64 newPCMRangeLo; drflac_uint64 newPCMRangeHi; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); /* If we selected the same frame, it means we should be pretty close. Just decode the rest. */ if (pcmRangeLo == newPCMRangeLo) { if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { break; /* Failed to seek to closest frame. */ } if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { return DRFLAC_TRUE; } else { break; /* Failed to seek forward. */ } } pcmRangeLo = newPCMRangeLo; pcmRangeHi = newPCMRangeHi; if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { /* The target PCM frame is in this FLAC frame. */ if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { return DRFLAC_TRUE; } else { break; /* Failed to seek to FLAC frame. */ } } else { const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { /* We seeked too far forward. We need to move our target byte backward and try again. */ byteRangeHi = lastSuccessfulSeekOffset; if (byteRangeLo > byteRangeHi) { byteRangeLo = byteRangeHi; } targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); if (targetByte < byteRangeLo) { targetByte = byteRangeLo; } } else /*if (pcmRangeHi < pcmFrameIndex)*/ { /* We didn't seek far enough. We need to move our target byte forward and try again. */ /* If we're close enough we can just seek forward. */ if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { return DRFLAC_TRUE; } else { break; /* Failed to seek to FLAC frame. */ } } else { byteRangeLo = lastSuccessfulSeekOffset; if (byteRangeHi < byteRangeLo) { byteRangeHi = byteRangeLo; } targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; } } } } } else { /* Getting here is really bad. We just recover as best we can, but moving to the first frame in the stream, and then abort. */ break; } } drflac__seek_to_first_frame(pFlac); /* <-- Try to recover. */ return DRFLAC_FALSE; } static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_uint64 byteRangeLo; drflac_uint64 byteRangeHi; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; /* Our algorithm currently assumes the FLAC stream is currently sitting at the start. */ if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { return DRFLAC_FALSE; } /* If we're close enough to the start, just move to the start and seek forward. */ if (pcmFrameIndex < seekForwardThreshold) { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; } /* Our starting byte range is the byte position of the first FLAC frame and the approximate end of the file as if it were completely uncompressed. This ensures the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it. */ byteRangeLo = pFlac->firstFLACFramePosInBytes; byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } #endif /* !DR_FLAC_NO_CRC */ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_uint32 iClosestSeekpoint = 0; drflac_bool32 isMidFrame = DRFLAC_FALSE; drflac_uint64 runningPCMFrameCount; drflac_uint32 iSeekpoint; DRFLAC_ASSERT(pFlac != NULL); if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } /* Do not use the seektable if pcmFramIndex is not coverd by it. */ if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { return DRFLAC_FALSE; } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; } iClosestSeekpoint = iSeekpoint; } /* There's been cases where the seek table contains only zeros. We need to do some basic validation on the closest seekpoint. */ if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { return DRFLAC_FALSE; } if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { return DRFLAC_FALSE; } #if !defined(DR_FLAC_NO_CRC) /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */ if (pFlac->totalPCMFrameCount > 0) { drflac_uint64 byteRangeLo; drflac_uint64 byteRangeHi; byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; /* If our closest seek point is not the last one, we only need to search between it and the next one. The section below calculates an appropriate starting value for byteRangeHi which will clamp it appropriately. Note that the next seekpoint must have an offset greater than the closest seekpoint because otherwise our binary search algorithm will break down. There have been cases where a seektable consists of seek points where every byte offset is set to 0 which causes problems. If this happens we need to abort. */ if (iClosestSeekpoint < pFlac->seekpointCount-1) { drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; /* Basic validation on the seekpoints to ensure they're usable. */ if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { return DRFLAC_FALSE; /* The next seekpoint doesn't look right. The seek table cannot be trusted from here. Abort. */ } if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Make sure it's not a placeholder seekpoint. */ byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; /* byteRangeHi must be zero based. */ } } if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { return DRFLAC_TRUE; } } } } #endif /* !DR_FLAC_NO_CRC */ /* Getting here means we need to use a slower algorithm because the binary search method failed or cannot be used. */ /* If we are seeking forward and the closest seekpoint is _before_ the current sample, we just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample. */ if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { /* Optimized case. Just seek forward from where we are. */ runningPCMFrameCount = pFlac->currentPCMFrame; /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } else { isMidFrame = DRFLAC_TRUE; } } else { /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */ runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { return DRFLAC_FALSE; } /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } for (;;) { drflac_uint64 pcmFrameCountInThisFLACFrame; drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { /* The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend it never existed and keep iterating. */ drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } else { /* We started seeking mid-frame which means we need to skip the frame decoding part. */ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this frame never existed and leave the running sample count untouched. */ if (!isMidFrame) { drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } else { /* We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. */ runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; isMidFrame = DRFLAC_FALSE; } /* If we are seeking to the end of the file and we've just hit it, we're done. */ if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { return DRFLAC_TRUE; } } next_iteration: /* Grab the next frame in preparation for the next iteration. */ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } } #ifndef DR_FLAC_NO_OGG typedef struct { drflac_uint8 capturePattern[4]; /* Should be "OggS" */ drflac_uint8 structureVersion; /* Always 0. */ drflac_uint8 headerType; drflac_uint64 granulePosition; drflac_uint32 serialNumber; drflac_uint32 sequenceNumber; drflac_uint32 checksum; drflac_uint8 segmentCount; drflac_uint8 segmentTable[255]; } drflac_ogg_page_header; #endif typedef struct { drflac_read_proc onRead; drflac_seek_proc onSeek; drflac_meta_proc onMeta; drflac_container container; void* pUserData; void* pUserDataMD; drflac_uint32 sampleRate; drflac_uint8 channels; drflac_uint8 bitsPerSample; drflac_uint64 totalPCMFrameCount; drflac_uint16 maxBlockSizeInPCMFrames; drflac_uint64 runningFilePos; drflac_bool32 hasStreamInfoBlock; drflac_bool32 hasMetadataBlocks; drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */ drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */ #ifndef DR_FLAC_NO_OGG drflac_uint32 oggSerial; drflac_uint64 oggFirstBytePos; drflac_ogg_page_header oggBosHeader; #endif } drflac_init_info; static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { blockHeader = drflac__be2host_32(blockHeader); *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); *blockSize = (blockHeader & 0x00FFFFFFUL); } static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { drflac_uint32 blockHeader; *blockSize = 0; if (onRead(pUserData, &blockHeader, 4) != 4) { return DRFLAC_FALSE; } drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); return DRFLAC_TRUE; } static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { drflac_uint32 blockSizes; drflac_uint64 frameSizes = 0; drflac_uint64 importantProps; drflac_uint8 md5[16]; /* min/max block size. */ if (onRead(pUserData, &blockSizes, 4) != 4) { return DRFLAC_FALSE; } /* min/max frame size. */ if (onRead(pUserData, &frameSizes, 6) != 6) { return DRFLAC_FALSE; } /* Sample rate, channels, bits per sample and total sample count. */ if (onRead(pUserData, &importantProps, 8) != 8) { return DRFLAC_FALSE; } /* MD5 */ if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { return DRFLAC_FALSE; } blockSizes = drflac__be2host_32(blockSizes); frameSizes = drflac__be2host_64(frameSizes); importantProps = drflac__be2host_64(importantProps); pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); return DRFLAC_TRUE; } static void* drflac__malloc_default(size_t sz, void* pUserData) { (void)pUserData; return DRFLAC_MALLOC(sz); } static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; return DRFLAC_REALLOC(p, sz); } static void drflac__free_default(void* p, void* pUserData) { (void)pUserData; DRFLAC_FREE(p); } static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onMalloc != NULL) { return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); } /* Try using realloc(). */ if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); } return NULL; } static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); } /* Try emulating realloc() in terms of malloc()/free(). */ if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { void* p2; p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); if (p2 == NULL) { return NULL; } if (p != NULL) { DRFLAC_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; } if (pAllocationCallbacks->onFree != NULL) { pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) { /* We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that we'll be sitting on byte 42. */ drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; drflac_uint32 seektableSize = 0; for (;;) { drflac_metadata metadata; drflac_uint8 isLastBlock = 0; drflac_uint8 blockType = 0; drflac_uint32 blockSize; if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { return DRFLAC_FALSE; } runningFilePos += 4; metadata.type = blockType; metadata.pRawData = NULL; metadata.rawDataSize = 0; switch (blockType) { case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (blockSize < 4) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: { seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { drflac_uint32 seekpointCount; drflac_uint32 iSeekpoint; void* pRawData; seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } /* We need to read seekpoint by seekpoint and do some processing. */ for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } /* Endian swap. */ pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.seektable.seekpointCount = seekpointCount; metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (blockSize < 8) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; drflac_uint32 i; pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.vorbis_comment.pComments = pRunningData; /* Check that the comments section is valid before passing it to the callback */ for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { drflac_uint32 commentLength; if (pRunningDataEnd - pRunningData < 4) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningData += commentLength; } onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (blockSize < 396) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; size_t bufferSize; drflac_uint8 iTrack; drflac_uint8 iIndex; void* pTrackData; /* This needs to be loaded in two passes. The first pass is used to calculate the size of the memory allocation we need for storing the necessary data. The second pass will fill that buffer with usable data. */ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = NULL; /* Will be filled later. */ /* Pass 1: Calculate the size of the buffer for the track data. */ { const char* pRunningDataSaved = pRunningData; /* Will be restored at the end in preparation for the second pass. */ bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { drflac_uint8 indexCount; drflac_uint32 indexPointSize; if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } /* Skip to the index point count */ pRunningData += 35; indexCount = pRunningData[0]; pRunningData += 1; bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); /* Quick validation check. */ indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningData += indexPointSize; } pRunningData = pRunningDataSaved; } /* Pass 2: Allocate a buffer and fill the data. Validation was done in the step above so can be skipped. */ { char* pRunningTrackData; pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); if (pTrackData == NULL) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningTrackData = (char*)pTrackData; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { drflac_uint8 indexCount; DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Skip forward, but not beyond the last byte in the CUESHEET_TRACK block which is the index count. */ pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Grab the index count for the next part. */ indexCount = pRunningData[0]; pRunningData += 1; pRunningTrackData += 1; /* Extract each track index. */ for (iIndex = 0; iIndex < indexCount; ++iIndex) { drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; pRunningTrackData += sizeof(drflac_cuesheet_track_index); pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); } } metadata.data.cuesheet.pTrackData = pTrackData; } /* The original data is no longer needed. */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); pRawData = NULL; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); pTrackData = NULL; } } break; case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { if (blockSize < 32) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; /* Need space for the rest of the block */ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; /* Need space for the picture after the block */ if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_PADDING: { if (onMeta) { metadata.data.padding.unused = 0; /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ } else { onMeta(pUserDataMD, &metadata); } } } break; case DRFLAC_METADATA_BLOCK_TYPE_INVALID: { /* Invalid chunk. Just skip over this one. */ if (onMeta) { if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ } } } break; default: { /* It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we can at the very least report the chunk to the application and let it look at the raw data. */ if (onMeta) { void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; } /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ if (onMeta == NULL && blockSize > 0) { if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } } runningFilePos += blockSize; if (isLastBlock) { break; } } *pSeektablePos = seektablePos; *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */ drflac_uint8 isLastBlock; drflac_uint8 blockType; drflac_uint32 blockSize; (void)onSeek; pInit->container = drflac_container_native; /* The first metadata block should be the STREAMINFO block. */ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { if (!relaxed) { /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */ return DRFLAC_FALSE; } else { /* Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined for that frame. */ pInit->hasStreamInfoBlock = DRFLAC_FALSE; pInit->hasMetadataBlocks = DRFLAC_FALSE; if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { return DRFLAC_FALSE; /* Couldn't find a frame. */ } if (pInit->firstFrameHeader.bitsPerSample == 0) { return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */ } pInit->sampleRate = pInit->firstFrameHeader.sampleRate; pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; pInit->maxBlockSizeInPCMFrames = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */ return DRFLAC_TRUE; } } else { drflac_streaminfo streaminfo; if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { return DRFLAC_FALSE; } pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */ pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { drflac_metadata metadata; metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } return DRFLAC_TRUE; } } #ifndef DR_FLAC_NO_OGG #define DRFLAC_OGG_MAX_PAGE_SIZE 65307 #define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */ typedef enum { drflac_ogg_recover_on_crc_mismatch, drflac_ogg_fail_on_crc_mismatch } drflac_ogg_crc_mismatch_recovery; #ifndef DR_FLAC_NO_CRC static drflac_uint32 drflac__crc32_table[] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; #endif static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) { #ifndef DR_FLAC_NO_CRC return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; #else (void)data; return crc32; #endif } #if 0 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) { crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); return crc32; } static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) { crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); return crc32; } #endif static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) { /* This can be optimized. */ drflac_uint32 i; for (i = 0; i < dataSize; ++i) { crc32 = drflac_crc32_byte(crc32, pData[i]); } return crc32; } static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) { drflac_uint32 pageBodySize = 0; int i; for (i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } return pageBodySize; } static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 data[23]; drflac_uint32 i; DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { return DRFLAC_AT_END; } *pBytesRead += 23; /* It's not actually used, but set the capture pattern to 'OggS' for completeness. Not doing this will cause static analysers to complain about us trying to access uninitialized data. We could alternatively just comment out this member of the drflac_ogg_page_header structure, but I like to have it map to the structure of the underlying data. */ pHeader->capturePattern[0] = 'O'; pHeader->capturePattern[1] = 'g'; pHeader->capturePattern[2] = 'g'; pHeader->capturePattern[3] = 'S'; pHeader->structureVersion = data[0]; pHeader->headerType = data[1]; DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */ data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; for (i = 0; i < 23; ++i) { *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); } if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { return DRFLAC_AT_END; } *pBytesRead += pHeader->segmentCount; for (i = 0; i < pHeader->segmentCount; ++i) { *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); } return DRFLAC_SUCCESS; } static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { return DRFLAC_AT_END; } *pBytesRead += 4; /* We need to read byte-by-byte until we find the OggS capture pattern. */ for (;;) { if (drflac_ogg__is_capture_pattern(id)) { drflac_result result; *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); if (result == DRFLAC_SUCCESS) { return DRFLAC_SUCCESS; } else { if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return result; } } } else { /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */ id[0] = id[1]; id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { return DRFLAC_AT_END; } *pBytesRead += 1; } } } /* The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from the physical Ogg bitstream are converted and delivered in native FLAC format. */ typedef struct { drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */ drflac_ogg_page_header bosPageHeader; /* Used for seeking. */ drflac_ogg_page_header currentPageHeader; drflac_uint32 bytesRemainingInPage; drflac_uint32 pageDataSize; drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; } drflac_oggbs; /* oggbs = Ogg Bitstream */ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); oggbs->currentBytePos += bytesActuallyRead; return bytesActuallyRead; } static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { if (origin == drflac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return DRFLAC_TRUE; } else { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { return DRFLAC_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */ return DRFLAC_FALSE; } oggbs->currentBytePos += offset; return DRFLAC_TRUE; } } static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) { drflac_ogg_page_header header; for (;;) { drflac_uint32 crc32 = 0; drflac_uint32 bytesRead; drflac_uint32 pageBodySize; #ifndef DR_FLAC_NO_CRC drflac_uint32 actualCRC32; #endif if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } oggbs->currentBytePos += bytesRead; pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ } if (header.serialNumber != oggbs->serialNumber) { /* It's not a FLAC page. Skip it. */ if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { return DRFLAC_FALSE; } continue; } /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */ if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { return DRFLAC_FALSE; } oggbs->pageDataSize = pageBodySize; #ifndef DR_FLAC_NO_CRC actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { continue; /* CRC mismatch. Skip this page. */ } else { /* Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we go to the next valid page to ensure we're in a good state, but return false to let the caller know that the seek did not fully complete. */ drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); return DRFLAC_FALSE; } } #else (void)recoveryMethod; /* <-- Silence a warning. */ #endif oggbs->currentPageHeader = header; oggbs->bytesRemainingInPage = pageBodySize; return DRFLAC_TRUE; } } /* Function below is unused at the moment, but I might be re-adding it later. */ #if 0 static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) { drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; drflac_uint8 iSeg = 0; drflac_uint32 iByte = 0; while (iByte < bytesConsumedInPage) { drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { iSeg += 1; iByte += segmentSize; } } *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); return iSeg; } static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) { /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */ for (;;) { drflac_bool32 atEndOfPage = DRFLAC_FALSE; drflac_uint8 bytesRemainingInSeg; drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { atEndOfPage = DRFLAC_TRUE; } break; } bytesToEndOfPacketOrPage += segmentSize; } /* At this point we will have found either the packet or the end of the page. If were at the end of the page we'll want to load the next page and keep searching for the end of the packet. */ drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { /* We're potentially at the next packet, but we need to check the next page first to be sure because the packet may straddle pages. */ if (!drflac_oggbs__goto_next_page(oggbs)) { return DRFLAC_FALSE; } /* If it's a fresh packet it most likely means we're at the next packet. */ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { return DRFLAC_TRUE; } } else { /* We're at the next packet. */ return DRFLAC_TRUE; } } } static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) { /* The bitstream should be sitting on the first byte just after the header of the frame. */ /* What we're actually doing here is seeking to the start of the next packet. */ return drflac_oggbs__seek_to_next_packet(oggbs); } #endif static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; size_t bytesRead = 0; DRFLAC_ASSERT(oggbs != NULL); DRFLAC_ASSERT(pRunningBufferOut != NULL); /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */ while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); bytesRead += bytesRemainingToRead; oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; break; } /* If we get here it means some of the requested data is contained in the next pages. */ if (oggbs->bytesRemainingInPage > 0) { DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); bytesRead += oggbs->bytesRemainingInPage; pRunningBufferOut += oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } DRFLAC_ASSERT(bytesRemainingToRead > 0); if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { break; /* Failed to go to the next page. Might have simply hit the end of the stream. */ } } return bytesRead; } static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; int bytesSeeked = 0; DRFLAC_ASSERT(oggbs != NULL); DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ /* Seeking is always forward which makes things a lot simpler. */ if (origin == drflac_seek_origin_start) { if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { return DRFLAC_FALSE; } if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { return DRFLAC_FALSE; } return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); } DRFLAC_ASSERT(origin == drflac_seek_origin_current); while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; DRFLAC_ASSERT(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { bytesSeeked += bytesRemainingToSeek; (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ oggbs->bytesRemainingInPage -= bytesRemainingToSeek; break; } /* If we get here it means some of the requested data is contained in the next pages. */ if (oggbs->bytesRemainingInPage > 0) { bytesSeeked += (int)oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } DRFLAC_ASSERT(bytesRemainingToSeek > 0); if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ return DRFLAC_FALSE; } } return DRFLAC_TRUE; } static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; drflac_uint64 originalBytePos; drflac_uint64 runningGranulePosition; drflac_uint64 runningFrameBytePos; drflac_uint64 runningPCMFrameCount; DRFLAC_ASSERT(oggbs != NULL); originalBytePos = oggbs->currentBytePos; /* For recovery. Points to the OggS identifier. */ /* First seek to the first frame. */ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { return DRFLAC_FALSE; } oggbs->bytesRemainingInPage = 0; runningGranulePosition = 0; for (;;) { if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); return DRFLAC_FALSE; /* Never did find that sample... */ } runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { break; /* The sample is somewhere in the previous page. */ } /* At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we disregard any pages that do not begin a fresh packet. */ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { /* <-- Is it a fresh page? */ if (oggbs->currentPageHeader.segmentTable[0] >= 2) { drflac_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { /* <-- Does the page begin with a frame's sync code? */ runningGranulePosition = oggbs->currentPageHeader.granulePosition; } continue; } } } /* We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until we find the one containing the target sample. */ if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { return DRFLAC_FALSE; } if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { return DRFLAC_FALSE; } /* At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep looping over these frames until we find the one containing the sample we're after. */ runningPCMFrameCount = runningGranulePosition; for (;;) { /* There are two ways to find the sample and seek past irrelevant frames: 1) Use the native FLAC decoder. 2) Use Ogg's framing system. Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code duplication for the decoding of frame headers. Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to avoid the use of the drflac_bs object. Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. 3) Simplicity. */ drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac_uint64 pcmFrameCountInThisFrame; if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; /* If we are seeking to the end of the file and we've just hit it, we're done. */ if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { pFlac->currentPCMFrame = pcmFrameIndex; pFlac->currentFLACFrame.pcmFramesRemaining = 0; return DRFLAC_TRUE; } else { return DRFLAC_FALSE; } } if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { /* The sample should be in this FLAC frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend it never existed and keep iterating. */ drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ if (pcmFramesToDecode == 0) { return DRFLAC_TRUE; } pFlac->currentPCMFrame = runningPCMFrameCount; return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ } else { if (result == DRFLAC_CRC_MISMATCH) { continue; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } else { /* It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this frame never existed and leave the running sample count untouched. */ drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { continue; /* CRC mismatch. Pretend this frame never existed. */ } else { return DRFLAC_FALSE; } } } } } static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { drflac_ogg_page_header header; drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; drflac_uint32 bytesRead = 0; /* Pre Condition: The bit stream should be sitting just past the 4-byte OggS capture pattern. */ (void)relaxed; pInit->container = drflac_container_ogg; pInit->oggFirstBytePos = 0; /* We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. */ if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; for (;;) { int pageBodySize; /* Break if we're past the beginning of stream page. */ if ((header.headerType & 0x02) == 0) { return DRFLAC_FALSE; } /* Check if it's a FLAC header. */ pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize == 51) { /* 51 = the lacing value of the FLAC header packet. */ /* It could be a FLAC page... */ drflac_uint32 bytesRemainingInPage = pageBodySize; drflac_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { return DRFLAC_FALSE; } bytesRemainingInPage -= 1; if (packetType == 0x7F) { /* Increasingly more likely to be a FLAC page... */ drflac_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { return DRFLAC_FALSE; } bytesRemainingInPage -= 4; if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { /* Almost certainly a FLAC page... */ drflac_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { return DRFLAC_FALSE; } if (mappingVersion[0] != 1) { return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */ } /* The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to be handling it in a generic way based on the serial number and packet types. */ if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { return DRFLAC_FALSE; } /* Expecting the native FLAC signature "fLaC". */ if (onRead(pUserData, sig, 4) != 4) { return DRFLAC_FALSE; } if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { /* The remaining data in the page should be the STREAMINFO block. */ drflac_streaminfo streaminfo; drflac_uint8 isLastBlock; drflac_uint8 blockType; drflac_uint32 blockSize; if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ } if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { /* Success! */ pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { drflac_metadata metadata; metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } pInit->runningFilePos += pageBodySize; pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */ pInit->oggSerial = header.serialNumber; pInit->oggBosHeader = header; break; } else { /* Failed to read STREAMINFO block. Aww, so close... */ return DRFLAC_FALSE; } } else { /* Invalid file. */ return DRFLAC_FALSE; } } else { /* Not a FLAC header. Skip it. */ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { /* Not a FLAC header. Seek past the entire page and move on to the next. */ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } pInit->runningFilePos += pageBodySize; /* Read the header of the next page. */ if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; } /* If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the Ogg bistream object. */ pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */ return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_bool32 relaxed; drflac_uint8 id[4]; if (pInit == NULL || onRead == NULL || onSeek == NULL) { return DRFLAC_FALSE; } DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; pInit->onMeta = onMeta; pInit->container = container; pInit->pUserData = pUserData; pInit->pUserDataMD = pUserDataMD; pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; pInit->bs.pUserData = pUserData; drflac__reset_cache(&pInit->bs); /* If the container is explicitly defined then we can try opening in relaxed mode. */ relaxed = container != drflac_container_unknown; /* Skip over any ID3 tags. */ for (;;) { if (onRead(pUserData, id, 4) != 4) { return DRFLAC_FALSE; /* Ran out of data. */ } pInit->runningFilePos += 4; if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { drflac_uint8 header[6]; drflac_uint8 flags; drflac_uint32 headerSize; if (onRead(pUserData, header, 6) != 6) { return DRFLAC_FALSE; /* Ran out of data. */ } pInit->runningFilePos += 6; flags = header[1]; DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); if (flags & 0x10) { headerSize += 10; } if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { return DRFLAC_FALSE; /* Failed to seek past the tag. */ } pInit->runningFilePos += headerSize; } else { break; } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #ifndef DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */ if (relaxed) { if (container == drflac_container_native) { return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #ifndef DR_FLAC_NO_OGG if (container == drflac_container_ogg) { return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif } /* Unsupported container. */ return DRFLAC_FALSE; } static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) { DRFLAC_ASSERT(pFlac != NULL); DRFLAC_ASSERT(pInit != NULL); DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; pFlac->sampleRate = pInit->sampleRate; pFlac->channels = (drflac_uint8)pInit->channels; pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac_init_info init; drflac_uint32 allocationSize; drflac_uint32 wholeSIMDVectorCountPerChannel; drflac_uint32 decodedSamplesAllocationSize; #ifndef DR_FLAC_NO_OGG drflac_oggbs* pOggbs = NULL; #endif drflac_uint64 firstFramePos; drflac_uint64 seektablePos; drflac_uint32 seekpointCount; drflac_allocation_callbacks allocationCallbacks; drflac* pFlac; /* CPU support first. */ drflac__init_cpu_caps(); if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { allocationCallbacks = *pAllocationCallbacks; if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { return NULL; /* Invalid allocation callbacks. */ } } else { allocationCallbacks.pUserData = NULL; allocationCallbacks.onMalloc = drflac__malloc_default; allocationCallbacks.onRealloc = drflac__realloc_default; allocationCallbacks.onFree = drflac__free_default; } /* The size of the allocation for the drflac object needs to be large enough to fit the following: 1) The main members of the drflac structure 2) A block of memory large enough to store the decoded samples of the largest frame in the stream 3) If the container is Ogg, a drflac_oggbs object The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration the different SIMD instruction sets. */ allocationSize = sizeof(drflac); /* The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector we are supporting. */ if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); } else { wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; } decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ #ifndef DR_FLAC_NO_OGG /* There's additional data required for Ogg streams. */ if (init.container == drflac_container_ogg) { allocationSize += sizeof(drflac_oggbs); pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); if (pOggbs == NULL) { return NULL; /*DRFLAC_OUT_OF_MEMORY;*/ } DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; pOggbs->pUserData = pUserData; pOggbs->currentBytePos = init.oggFirstBytePos; pOggbs->firstBytePos = init.oggFirstBytePos; pOggbs->serialNumber = init.oggSerial; pOggbs->bosPageHeader = init.oggBosHeader; pOggbs->bytesRemainingInPage = 0; } #endif /* This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading and decoding the metadata. */ firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ seektablePos = 0; seekpointCount = 0; if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; void* pUserDataOverride = pUserData; #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; pUserDataOverride = (void*)pOggbs; } #endif if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { #ifndef DR_FLAC_NO_OGG drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } allocationSize += seekpointCount * sizeof(drflac_seekpoint); } pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { #ifndef DR_FLAC_NO_OGG drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } drflac__init_from_info(pFlac, &init); pFlac->allocationCallbacks = allocationCallbacks; pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); /* At this point the pOggbs object has been handed over to pInternalOggbs and can be freed. */ drflac__free_from_callbacks(pOggbs, &allocationCallbacks); pOggbs = NULL; /* The Ogg bistream needs to be layered on top of the original bitstream. */ pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } #endif pFlac->firstFLACFramePosInBytes = firstFramePos; /* NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. */ #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; } else #endif { /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */ if (seektablePos != 0) { pFlac->seekpointCount = seekpointCount; pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); DRFLAC_ASSERT(pFlac->bs.onRead != NULL); /* Seek to the seektable, then just read directly into our seektable buffer. */ if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { drflac_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { /* Endian swap. */ pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); } else { /* Failed to read the seektable. Pretend we don't have one. */ pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; break; } } /* We need to seek back to where we were. If this fails it's a critical error. */ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } else { /* Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. */ pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; } } } /* If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode the first frame. */ if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; for (;;) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { break; } else { if (result == DRFLAC_CRC_MISMATCH) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } continue; } else { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } } } return pFlac; } #ifndef DR_FLAC_NO_STDIO #include #ifndef DR_FLAC_NO_WCHAR #include /* For wcslen(), wcsrtombs() */ #endif /* Errno */ /* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ #include static drflac_result drflac_result_from_errno(int e) { switch (e) { case 0: return DRFLAC_SUCCESS; #ifdef EPERM case EPERM: return DRFLAC_INVALID_OPERATION; #endif #ifdef ENOENT case ENOENT: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef ESRCH case ESRCH: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef EINTR case EINTR: return DRFLAC_INTERRUPT; #endif #ifdef EIO case EIO: return DRFLAC_IO_ERROR; #endif #ifdef ENXIO case ENXIO: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef E2BIG case E2BIG: return DRFLAC_INVALID_ARGS; #endif #ifdef ENOEXEC case ENOEXEC: return DRFLAC_INVALID_FILE; #endif #ifdef EBADF case EBADF: return DRFLAC_INVALID_FILE; #endif #ifdef ECHILD case ECHILD: return DRFLAC_ERROR; #endif #ifdef EAGAIN case EAGAIN: return DRFLAC_UNAVAILABLE; #endif #ifdef ENOMEM case ENOMEM: return DRFLAC_OUT_OF_MEMORY; #endif #ifdef EACCES case EACCES: return DRFLAC_ACCESS_DENIED; #endif #ifdef EFAULT case EFAULT: return DRFLAC_BAD_ADDRESS; #endif #ifdef ENOTBLK case ENOTBLK: return DRFLAC_ERROR; #endif #ifdef EBUSY case EBUSY: return DRFLAC_BUSY; #endif #ifdef EEXIST case EEXIST: return DRFLAC_ALREADY_EXISTS; #endif #ifdef EXDEV case EXDEV: return DRFLAC_ERROR; #endif #ifdef ENODEV case ENODEV: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef ENOTDIR case ENOTDIR: return DRFLAC_NOT_DIRECTORY; #endif #ifdef EISDIR case EISDIR: return DRFLAC_IS_DIRECTORY; #endif #ifdef EINVAL case EINVAL: return DRFLAC_INVALID_ARGS; #endif #ifdef ENFILE case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; #endif #ifdef EMFILE case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; #endif #ifdef ENOTTY case ENOTTY: return DRFLAC_INVALID_OPERATION; #endif #ifdef ETXTBSY case ETXTBSY: return DRFLAC_BUSY; #endif #ifdef EFBIG case EFBIG: return DRFLAC_TOO_BIG; #endif #ifdef ENOSPC case ENOSPC: return DRFLAC_NO_SPACE; #endif #ifdef ESPIPE case ESPIPE: return DRFLAC_BAD_SEEK; #endif #ifdef EROFS case EROFS: return DRFLAC_ACCESS_DENIED; #endif #ifdef EMLINK case EMLINK: return DRFLAC_TOO_MANY_LINKS; #endif #ifdef EPIPE case EPIPE: return DRFLAC_BAD_PIPE; #endif #ifdef EDOM case EDOM: return DRFLAC_OUT_OF_RANGE; #endif #ifdef ERANGE case ERANGE: return DRFLAC_OUT_OF_RANGE; #endif #ifdef EDEADLK case EDEADLK: return DRFLAC_DEADLOCK; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; #endif #ifdef ENOLCK case ENOLCK: return DRFLAC_ERROR; #endif #ifdef ENOSYS case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP case ELOOP: return DRFLAC_TOO_MANY_LINKS; #endif #ifdef ENOMSG case ENOMSG: return DRFLAC_NO_MESSAGE; #endif #ifdef EIDRM case EIDRM: return DRFLAC_ERROR; #endif #ifdef ECHRNG case ECHRNG: return DRFLAC_ERROR; #endif #ifdef EL2NSYNC case EL2NSYNC: return DRFLAC_ERROR; #endif #ifdef EL3HLT case EL3HLT: return DRFLAC_ERROR; #endif #ifdef EL3RST case EL3RST: return DRFLAC_ERROR; #endif #ifdef ELNRNG case ELNRNG: return DRFLAC_OUT_OF_RANGE; #endif #ifdef EUNATCH case EUNATCH: return DRFLAC_ERROR; #endif #ifdef ENOCSI case ENOCSI: return DRFLAC_ERROR; #endif #ifdef EL2HLT case EL2HLT: return DRFLAC_ERROR; #endif #ifdef EBADE case EBADE: return DRFLAC_ERROR; #endif #ifdef EBADR case EBADR: return DRFLAC_ERROR; #endif #ifdef EXFULL case EXFULL: return DRFLAC_ERROR; #endif #ifdef ENOANO case ENOANO: return DRFLAC_ERROR; #endif #ifdef EBADRQC case EBADRQC: return DRFLAC_ERROR; #endif #ifdef EBADSLT case EBADSLT: return DRFLAC_ERROR; #endif #ifdef EBFONT case EBFONT: return DRFLAC_INVALID_FILE; #endif #ifdef ENOSTR case ENOSTR: return DRFLAC_ERROR; #endif #ifdef ENODATA case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; #endif #ifdef ETIME case ETIME: return DRFLAC_TIMEOUT; #endif #ifdef ENOSR case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; #endif #ifdef ENONET case ENONET: return DRFLAC_NO_NETWORK; #endif #ifdef ENOPKG case ENOPKG: return DRFLAC_ERROR; #endif #ifdef EREMOTE case EREMOTE: return DRFLAC_ERROR; #endif #ifdef ENOLINK case ENOLINK: return DRFLAC_ERROR; #endif #ifdef EADV case EADV: return DRFLAC_ERROR; #endif #ifdef ESRMNT case ESRMNT: return DRFLAC_ERROR; #endif #ifdef ECOMM case ECOMM: return DRFLAC_ERROR; #endif #ifdef EPROTO case EPROTO: return DRFLAC_ERROR; #endif #ifdef EMULTIHOP case EMULTIHOP: return DRFLAC_ERROR; #endif #ifdef EDOTDOT case EDOTDOT: return DRFLAC_ERROR; #endif #ifdef EBADMSG case EBADMSG: return DRFLAC_BAD_MESSAGE; #endif #ifdef EOVERFLOW case EOVERFLOW: return DRFLAC_TOO_BIG; #endif #ifdef ENOTUNIQ case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; #endif #ifdef EBADFD case EBADFD: return DRFLAC_ERROR; #endif #ifdef EREMCHG case EREMCHG: return DRFLAC_ERROR; #endif #ifdef ELIBACC case ELIBACC: return DRFLAC_ACCESS_DENIED; #endif #ifdef ELIBBAD case ELIBBAD: return DRFLAC_INVALID_FILE; #endif #ifdef ELIBSCN case ELIBSCN: return DRFLAC_INVALID_FILE; #endif #ifdef ELIBMAX case ELIBMAX: return DRFLAC_ERROR; #endif #ifdef ELIBEXEC case ELIBEXEC: return DRFLAC_ERROR; #endif #ifdef EILSEQ case EILSEQ: return DRFLAC_INVALID_DATA; #endif #ifdef ERESTART case ERESTART: return DRFLAC_ERROR; #endif #ifdef ESTRPIPE case ESTRPIPE: return DRFLAC_ERROR; #endif #ifdef EUSERS case EUSERS: return DRFLAC_ERROR; #endif #ifdef ENOTSOCK case ENOTSOCK: return DRFLAC_NOT_SOCKET; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; #endif #ifdef EMSGSIZE case EMSGSIZE: return DRFLAC_TOO_BIG; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; #endif #ifdef EADDRINUSE case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return DRFLAC_ERROR; #endif #ifdef ENETDOWN case ENETDOWN: return DRFLAC_NO_NETWORK; #endif #ifdef ENETUNREACH case ENETUNREACH: return DRFLAC_NO_NETWORK; #endif #ifdef ENETRESET case ENETRESET: return DRFLAC_NO_NETWORK; #endif #ifdef ECONNABORTED case ECONNABORTED: return DRFLAC_NO_NETWORK; #endif #ifdef ECONNRESET case ECONNRESET: return DRFLAC_CONNECTION_RESET; #endif #ifdef ENOBUFS case ENOBUFS: return DRFLAC_NO_SPACE; #endif #ifdef EISCONN case EISCONN: return DRFLAC_ALREADY_CONNECTED; #endif #ifdef ENOTCONN case ENOTCONN: return DRFLAC_NOT_CONNECTED; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return DRFLAC_ERROR; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return DRFLAC_ERROR; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return DRFLAC_TIMEOUT; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return DRFLAC_NO_HOST; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return DRFLAC_NO_HOST; #endif #ifdef EALREADY case EALREADY: return DRFLAC_IN_PROGRESS; #endif #ifdef EINPROGRESS case EINPROGRESS: return DRFLAC_IN_PROGRESS; #endif #ifdef ESTALE case ESTALE: return DRFLAC_INVALID_FILE; #endif #ifdef EUCLEAN case EUCLEAN: return DRFLAC_ERROR; #endif #ifdef ENOTNAM case ENOTNAM: return DRFLAC_ERROR; #endif #ifdef ENAVAIL case ENAVAIL: return DRFLAC_ERROR; #endif #ifdef EISNAM case EISNAM: return DRFLAC_ERROR; #endif #ifdef EREMOTEIO case EREMOTEIO: return DRFLAC_IO_ERROR; #endif #ifdef EDQUOT case EDQUOT: return DRFLAC_NO_SPACE; #endif #ifdef ENOMEDIUM case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef EMEDIUMTYPE case EMEDIUMTYPE: return DRFLAC_ERROR; #endif #ifdef ECANCELED case ECANCELED: return DRFLAC_CANCELLED; #endif #ifdef ENOKEY case ENOKEY: return DRFLAC_ERROR; #endif #ifdef EKEYEXPIRED case EKEYEXPIRED: return DRFLAC_ERROR; #endif #ifdef EKEYREVOKED case EKEYREVOKED: return DRFLAC_ERROR; #endif #ifdef EKEYREJECTED case EKEYREJECTED: return DRFLAC_ERROR; #endif #ifdef EOWNERDEAD case EOWNERDEAD: return DRFLAC_ERROR; #endif #ifdef ENOTRECOVERABLE case ENOTRECOVERABLE: return DRFLAC_ERROR; #endif #ifdef ERFKILL case ERFKILL: return DRFLAC_ERROR; #endif #ifdef EHWPOISON case EHWPOISON: return DRFLAC_ERROR; #endif default: return DRFLAC_ERROR; } } /* End Errno */ /* fopen */ static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err; #endif if (ppFile != NULL) { *ppFile = NULL; /* Safety. */ } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRFLAC_INVALID_ARGS; } #if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drflac_result_from_errno(err); } #else #if defined(_WIN32) || defined(__APPLE__) *ppFile = fopen(pFilePath, pOpenMode); #else #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) *ppFile = fopen64(pFilePath, pOpenMode); #else *ppFile = fopen(pFilePath, pOpenMode); #endif #endif if (*ppFile == NULL) { drflac_result result = drflac_result_from_errno(errno); if (result == DRFLAC_SUCCESS) { result = DRFLAC_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ } return result; } #endif return DRFLAC_SUCCESS; } /* _wfopen() isn't always available in all compilation environments. * Windows only. * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). * MinGW-64 (both 32- and 64-bit) seems to support it. * MinGW wraps it in !defined(__STRICT_ANSI__). * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. */ #if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRFLAC_HAS_WFOPEN #endif #endif #ifndef DR_FLAC_NO_WCHAR static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { *ppFile = NULL; /* Safety. */ } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRFLAC_INVALID_ARGS; } #if defined(DRFLAC_HAS_WFOPEN) { /* Use _wfopen() on Windows. */ #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drflac_result_from_errno(err); } #else *ppFile = _wfopen(pFilePath, pOpenMode); if (*ppFile == NULL) { return drflac_result_from_errno(errno); } #endif (void)pAllocationCallbacks; } #else /* Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. */ /* Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just need to abort with an error. If you encounter a compiler lacking such support, add it to this list and submit a bug report and it'll be added to the library upstream. */ #if defined(__DJGPP__) { /* Nothing to do here. This will fall through to the error check below. */ } #else { mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; char* pFilePathMB = NULL; char pOpenModeMB[32] = {0}; /* Get the length first. */ DRFLAC_ZERO_OBJECT(&mbs); lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); if (lenMB == (size_t)-1) { return drflac_result_from_errno(errno); } pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); if (pFilePathMB == NULL) { return DRFLAC_OUT_OF_MEMORY; } pFilePathTemp = pFilePath; DRFLAC_ZERO_OBJECT(&mbs); wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ { size_t i = 0; for (;;) { if (pOpenMode[i] == 0) { pOpenModeMB[i] = '\0'; break; } pOpenModeMB[i] = (char)pOpenMode[i]; i += 1; } } *ppFile = fopen(pFilePathMB, pOpenModeMB); drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } #endif if (*ppFile == NULL) { return DRFLAC_ERROR; } #endif return DRFLAC_SUCCESS; } #endif /* End fopen */ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } #ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } #endif DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } #ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } #endif #endif /* DR_FLAC_NO_STDIO */ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; size_t bytesRemaining; DRFLAC_ASSERT(memoryStream != NULL); DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; DRFLAC_ASSERT(memoryStream != NULL); DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ if (offset > (drflac_int64)memoryStream->dataSize) { return DRFLAC_FALSE; } if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { memoryStream->currentReadPos += offset; } else { return DRFLAC_FALSE; /* Trying to seek too far forward. */ } } else { if ((drflac_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { return DRFLAC_FALSE; /* Trying to seek too far forward. */ } } return DRFLAC_TRUE; } DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; /* This is an awful hack... */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else #endif { pFlac->bs.pUserData = &pFlac->memoryStream; } return pFlac; } DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; /* This is an awful hack... */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else #endif { pFlac->bs.pUserData = &pFlac->memoryStream; } return pFlac; } DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API void drflac_close(drflac* pFlac) { if (pFlac == NULL) { return; } #ifndef DR_FLAC_NO_STDIO /* If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() was used by looking at the callbacks. */ if (pFlac->bs.onRead == drflac__on_read_stdio) { fclose((FILE*)pFlac->bs.pUserData); } #ifndef DR_FLAC_NO_OGG /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */ if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); if (oggbs->onRead == drflac__on_read_stdio) { fclose((FILE*)oggbs->pUserData); } } #endif #endif drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; pOutputSamples[i*8+0] = (drflac_int32)left0; pOutputSamples[i*8+1] = (drflac_int32)right0; pOutputSamples[i*8+2] = (drflac_int32)left1; pOutputSamples[i*8+3] = (drflac_int32)right1; pOutputSamples[i*8+4] = (drflac_int32)left2; pOutputSamples[i*8+5] = (drflac_int32)right2; pOutputSamples[i*8+6] = (drflac_int32)left3; pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; pOutputSamples[i*8+0] = (drflac_int32)left0; pOutputSamples[i*8+1] = (drflac_int32)right0; pOutputSamples[i*8+2] = (drflac_int32)left1; pOutputSamples[i*8+3] = (drflac_int32)right1; pOutputSamples[i*8+4] = (drflac_int32)left2; pOutputSamples[i*8+5] = (drflac_int32)right2; pOutputSamples[i*8+6] = (drflac_int32)left3; pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; pOutputSamples[i*8+0] = (drflac_int32)temp0L; pOutputSamples[i*8+1] = (drflac_int32)temp0R; pOutputSamples[i*8+2] = (drflac_int32)temp1L; pOutputSamples[i*8+3] = (drflac_int32)temp1R; pOutputSamples[i*8+4] = (drflac_int32)temp2L; pOutputSamples[i*8+5] = (drflac_int32)temp2R; pOutputSamples[i*8+6] = (drflac_int32)temp3L; pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); pOutputSamples[i*8+0] = (drflac_int32)temp0L; pOutputSamples[i*8+1] = (drflac_int32)temp0R; pOutputSamples[i*8+2] = (drflac_int32)temp1L; pOutputSamples[i*8+3] = (drflac_int32)temp1R; pOutputSamples[i*8+4] = (drflac_int32)temp2L; pOutputSamples[i*8+5] = (drflac_int32)temp2R; pOutputSamples[i*8+6] = (drflac_int32)temp3L; pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ uint32x4_t one4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); one4 = vdupq_n_u32(1); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { int32x4_t shift4; shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; pOutputSamples[i*8+0] = (drflac_int32)tempL0; pOutputSamples[i*8+1] = (drflac_int32)tempR0; pOutputSamples[i*8+2] = (drflac_int32)tempL1; pOutputSamples[i*8+3] = (drflac_int32)tempR1; pOutputSamples[i*8+4] = (drflac_int32)tempL2; pOutputSamples[i*8+5] = (drflac_int32)tempR2; pOutputSamples[i*8+6] = (drflac_int32)tempL3; pOutputSamples[i*8+7] = (drflac_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t left; int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { /* If we've run out of samples in this frame, go to the next. */ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; /* Couldn't read the next frame, so just break from the loop and return. */ } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { /* Generic interleaving. */ drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; left3 >>= 16; right0 >>= 16; right1 >>= 16; right2 >>= 16; right3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)left0; pOutputSamples[i*8+1] = (drflac_int16)right0; pOutputSamples[i*8+2] = (drflac_int16)left1; pOutputSamples[i*8+3] = (drflac_int16)right1; pOutputSamples[i*8+4] = (drflac_int16)left2; pOutputSamples[i*8+5] = (drflac_int16)right2; pOutputSamples[i*8+6] = (drflac_int16)left3; pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; left3 >>= 16; right0 >>= 16; right1 >>= 16; right2 >>= 16; right3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)left0; pOutputSamples[i*8+1] = (drflac_int16)right0; pOutputSamples[i*8+2] = (drflac_int16)left1; pOutputSamples[i*8+3] = (drflac_int16)right1; pOutputSamples[i*8+4] = (drflac_int16)left2; pOutputSamples[i*8+5] = (drflac_int16)right2; pOutputSamples[i*8+6] = (drflac_int16)left3; pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; temp3L >>= 16; temp0R >>= 16; temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; pOutputSamples[i*8+0] = (drflac_int16)temp0L; pOutputSamples[i*8+1] = (drflac_int16)temp0R; pOutputSamples[i*8+2] = (drflac_int16)temp1L; pOutputSamples[i*8+3] = (drflac_int16)temp1R; pOutputSamples[i*8+4] = (drflac_int16)temp2L; pOutputSamples[i*8+5] = (drflac_int16)temp2R; pOutputSamples[i*8+6] = (drflac_int16)temp3L; pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = ((drflac_int32)(mid0 + side0) >> 1); temp1L = ((drflac_int32)(mid1 + side1) >> 1); temp2L = ((drflac_int32)(mid2 + side2) >> 1); temp3L = ((drflac_int32)(mid3 + side3) >> 1); temp0R = ((drflac_int32)(mid0 - side0) >> 1); temp1R = ((drflac_int32)(mid1 - side1) >> 1); temp2R = ((drflac_int32)(mid2 - side2) >> 1); temp3R = ((drflac_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; temp3L >>= 16; temp0R >>= 16; temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; pOutputSamples[i*8+0] = (drflac_int16)temp0L; pOutputSamples[i*8+1] = (drflac_int16)temp0R; pOutputSamples[i*8+2] = (drflac_int16)temp1L; pOutputSamples[i*8+3] = (drflac_int16)temp1R; pOutputSamples[i*8+4] = (drflac_int16)temp2L; pOutputSamples[i*8+5] = (drflac_int16)temp2R; pOutputSamples[i*8+6] = (drflac_int16)temp3L; pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; tempL2 >>= 16; tempL3 >>= 16; tempR0 >>= 16; tempR1 >>= 16; tempR2 >>= 16; tempR3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)tempL0; pOutputSamples[i*8+1] = (drflac_int16)tempR0; pOutputSamples[i*8+2] = (drflac_int16)tempL1; pOutputSamples[i*8+3] = (drflac_int16)tempR1; pOutputSamples[i*8+4] = (drflac_int16)tempL2; pOutputSamples[i*8+5] = (drflac_int16)tempR2; pOutputSamples[i*8+6] = (drflac_int16)tempL3; pOutputSamples[i*8+7] = (drflac_int16)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); /* At this point we have results. We can now pack and interleave these into a single __m128i object and then store the in the output buffer. */ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t left; int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { /* If we've run out of samples in this frame, go to the next. */ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; /* Couldn't read the next frame, so just break from the loop and return. */ } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { /* Generic interleaving. */ drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left * factor; pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; float32x4_t leftf; float32x4_t rightf; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left * factor; pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; float32x4_t leftf; float32x4_t rightf; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample - 8; float factor; __m128 factor128; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor128 = _mm_set1_ps(factor); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i tempL; __m128i tempR; __m128 leftf; __m128 rightf; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i tempL; __m128i tempR; __m128 leftf; __m128 rightf; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample - 8; float factor; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; /* Wasted Bits Per Sample */ int32x4_t wbps1_4; /* Wasted Bits Per Sample */ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { __m128i lefti; __m128i righti; __m128 leftf; __m128 rightf; lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { /* Scalar fallback. */ #if 0 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { /* If we've run out of samples in this frame, go to the next. */ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; /* Couldn't read the next frame, so just break from the loop and return. */ } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { /* Generic interleaving. */ drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; } } return framesRead; } DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { if (pFlac == NULL) { return DRFLAC_FALSE; } /* Don't do anything if we're already on the seek point. */ if (pFlac->currentPCMFrame == pcmFrameIndex) { return DRFLAC_TRUE; } /* If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present when the decoder was opened. */ if (pFlac->firstFLACFramePosInBytes == 0) { return DRFLAC_FALSE; } if (pcmFrameIndex == 0) { pFlac->currentPCMFrame = 0; return drflac__seek_to_first_frame(pFlac); } else { drflac_bool32 wasSuccessful = DRFLAC_FALSE; drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; /* Clamp the sample to the end. */ if (pcmFrameIndex > pFlac->totalPCMFrameCount) { pcmFrameIndex = pFlac->totalPCMFrameCount; } /* If the target sample and the current sample are in the same frame we just move the position forward. */ if (pcmFrameIndex > pFlac->currentPCMFrame) { /* Forward. */ drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { pFlac->currentFLACFrame.pcmFramesRemaining -= offset; pFlac->currentPCMFrame = pcmFrameIndex; return DRFLAC_TRUE; } } else { /* Backward. */ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; if (currentFLACFramePCMFramesConsumed > offsetAbs) { pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; pFlac->currentPCMFrame = pcmFrameIndex; return DRFLAC_TRUE; } } /* Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so we'll instead use Ogg's natural seeking facility. */ #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); } else #endif { /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */ if (/*!wasSuccessful && */!pFlac->_noSeekTableSeek) { wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); } #if !defined(DR_FLAC_NO_CRC) /* Fall back to binary search if seek table seeking fails. This requires the length of the stream to be known. */ if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); } #endif /* Fall back to brute force if all else fails. */ if (!wasSuccessful && !pFlac->_noBruteForceSeek) { wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); } } if (wasSuccessful) { pFlac->currentPCMFrame = pcmFrameIndex; } else { /* Seek failed. Try putting the decoder back to it's original state. */ if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { /* Failed to seek back to the original PCM frame. Fall back to 0. */ drflac_seek_to_pcm_frame(pFlac, 0); } } return wasSuccessful; } } /* High Level APIs */ /* SIZE_MAX */ #if defined(SIZE_MAX) #define DRFLAC_SIZE_MAX SIZE_MAX #else #if defined(DRFLAC_64BIT) #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) #else #define DRFLAC_SIZE_MAX 0xFFFFFFFF #endif #endif /* End SIZE_MAX */ /* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ #define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ { \ type* pSampleData = NULL; \ drflac_uint64 totalPCMFrameCount; \ \ DRFLAC_ASSERT(pFlac != NULL); \ \ totalPCMFrameCount = pFlac->totalPCMFrameCount; \ \ if (totalPCMFrameCount == 0) { \ type buffer[4096]; \ drflac_uint64 pcmFramesRead; \ size_t sampleDataBufferSize = sizeof(buffer); \ \ pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ type* pNewSampleData; \ size_t newSampleDataBufferSize; \ \ newSampleDataBufferSize = sampleDataBufferSize * 2; \ pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pNewSampleData == NULL) { \ drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ goto on_error; \ } \ \ sampleDataBufferSize = newSampleDataBufferSize; \ pSampleData = pNewSampleData; \ } \ \ DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ totalPCMFrameCount += pcmFramesRead; \ } \ \ /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ protect those ears from random noise! */ \ DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ } else { \ drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ goto on_error; /* The decoded data is too big. */ \ } \ \ pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ } \ \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ \ drflac_close(pFlac); \ return pSampleData; \ \ on_error: \ drflac_close(pFlac); \ return NULL; \ } DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } #ifndef DR_FLAC_NO_STDIO DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } #endif DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { drflac__free_from_callbacks(p, pAllocationCallbacks); } else { drflac__free_default(p, NULL); } } DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; } pIter->countRemaining = commentCount; pIter->pRunningData = (const char*)pComments; } DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { drflac_int32 length; const char* pComment; /* Safety. */ if (pCommentLengthOut) { *pCommentLengthOut = 0; } if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; pIter->countRemaining -= 1; if (pCommentLengthOut) { *pCommentLengthOut = length; } return pComment; } DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; } pIter->countRemaining = trackCount; pIter->pRunningData = (const char*)pTrackData; } DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) { drflac_cuesheet_track cuesheetTrack; const char* pRunningData; drflac_uint64 offsetHi; drflac_uint64 offsetLo; if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return DRFLAC_FALSE; } pRunningData = pIter->pRunningData; offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; cuesheetTrack.offset = offsetLo | (offsetHi << 32); cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); pIter->pRunningData = pRunningData; pIter->countRemaining -= 1; if (pCuesheetTrack) { *pCuesheetTrack = cuesheetTrack; } return DRFLAC_TRUE; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif /* dr_flac_c */ #endif /* DR_FLAC_IMPLEMENTATION */ /* REVISION HISTORY ================ v0.12.42 - 2023-11-02 - Fix build for ARMv6-M. - Fix a compilation warning with GCC. v0.12.41 - 2023-06-17 - Fix an incorrect date in revision history. No functional change. v0.12.40 - 2023-05-22 - Minor code restructure. No functional change. v0.12.39 - 2022-09-17 - Fix compilation with DJGPP. - Fix compilation error with Visual Studio 2019 and the ARM build. - Fix an error with SSE 4.1 detection. - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. - Improve compatibility with compilers which lack support for explicit struct packing. - Improve compatibility with low-end and embedded hardware by reducing the amount of stack allocation when loading an Ogg encapsulated file. v0.12.38 - 2022-04-10 - Fix compilation error on older versions of GCC. v0.12.37 - 2022-02-12 - Improve ARM detection. v0.12.36 - 2022-02-07 - Fix a compilation error with the ARM build. v0.12.35 - 2022-02-06 - Fix a bug due to underestimating the amount of precision required for the prediction stage. - Fix some bugs found from fuzz testing. v0.12.34 - 2022-01-07 - Fix some misalignment bugs when reading metadata. v0.12.33 - 2021-12-22 - Fix a bug with seeking when the seek table does not start at PCM frame 0. v0.12.32 - 2021-12-11 - Fix a warning with Clang. v0.12.31 - 2021-08-16 - Silence some warnings. v0.12.30 - 2021-07-31 - Fix platform detection for ARM64. v0.12.29 - 2021-04-02 - Fix a bug where the running PCM frame index is set to an invalid value when over-seeking. - Fix a decoding error due to an incorrect validation check. v0.12.28 - 2021-02-21 - Fix a warning due to referencing _MSC_VER when it is undefined. v0.12.27 - 2021-01-31 - Fix a static analysis warning. v0.12.26 - 2021-01-17 - Fix a compilation warning due to _BSD_SOURCE being deprecated. v0.12.25 - 2020-12-26 - Update documentation. v0.12.24 - 2020-11-29 - Fix ARM64/NEON detection when compiling with MSVC. v0.12.23 - 2020-11-21 - Fix compilation with OpenWatcom. v0.12.22 - 2020-11-01 - Fix an error with the previous release. v0.12.21 - 2020-11-01 - Fix a possible deadlock when seeking. - Improve compiler support for older versions of GCC. v0.12.20 - 2020-09-08 - Fix a compilation error on older compilers. v0.12.19 - 2020-08-30 - Fix a bug due to an undefined 32-bit shift. v0.12.18 - 2020-08-14 - Fix a crash when compiling with clang-cl. v0.12.17 - 2020-08-02 - Simplify sized types. v0.12.16 - 2020-07-25 - Fix a compilation warning. v0.12.15 - 2020-07-06 - Check for negative LPC shifts and return an error. v0.12.14 - 2020-06-23 - Add include guard for the implementation section. v0.12.13 - 2020-05-16 - Add compile-time and run-time version querying. - DRFLAC_VERSION_MINOR - DRFLAC_VERSION_MAJOR - DRFLAC_VERSION_REVISION - DRFLAC_VERSION_STRING - drflac_version() - drflac_version_string() v0.12.12 - 2020-04-30 - Fix compilation errors with VC6. v0.12.11 - 2020-04-19 - Fix some pedantic warnings. - Fix some undefined behaviour warnings. v0.12.10 - 2020-04-10 - Fix some bugs when trying to seek with an invalid seek table. v0.12.9 - 2020-04-05 - Fix warnings. v0.12.8 - 2020-04-04 - Add drflac_open_file_w() and drflac_open_file_with_metadata_w(). - Fix some static analysis warnings. - Minor documentation updates. v0.12.7 - 2020-03-14 - Fix compilation errors with VC6. v0.12.6 - 2020-03-07 - Fix compilation error with Visual Studio .NET 2003. v0.12.5 - 2020-01-30 - Silence some static analysis warnings. v0.12.4 - 2020-01-29 - Silence some static analysis warnings. v0.12.3 - 2019-12-02 - Fix some warnings when compiling with GCC and the -Og flag. - Fix a crash in out-of-memory situations. - Fix potential integer overflow bug. - Fix some static analysis warnings. - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. - Fix a bug with binary search seeking where the bits per sample is not a multiple of 8. v0.12.2 - 2019-10-07 - Internal code clean up. v0.12.1 - 2019-09-29 - Fix some Clang Static Analyzer warnings. - Fix an unused variable warning. v0.12.0 - 2019-09-23 - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: - drflac_open() - drflac_open_relaxed() - drflac_open_with_metadata() - drflac_open_with_metadata_relaxed() - drflac_open_file() - drflac_open_file_with_metadata() - drflac_open_memory() - drflac_open_memory_with_metadata() - drflac_open_and_read_pcm_frames_s32() - drflac_open_and_read_pcm_frames_s16() - drflac_open_and_read_pcm_frames_f32() - drflac_open_file_and_read_pcm_frames_s32() - drflac_open_file_and_read_pcm_frames_s16() - drflac_open_file_and_read_pcm_frames_f32() - drflac_open_memory_and_read_pcm_frames_s32() - drflac_open_memory_and_read_pcm_frames_s16() - drflac_open_memory_and_read_pcm_frames_f32() Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. - Remove deprecated APIs: - drflac_read_s32() - drflac_read_s16() - drflac_read_f32() - drflac_seek_to_sample() - drflac_open_and_decode_s32() - drflac_open_and_decode_s16() - drflac_open_and_decode_f32() - drflac_open_and_decode_file_s32() - drflac_open_and_decode_file_s16() - drflac_open_and_decode_file_f32() - drflac_open_and_decode_memory_s32() - drflac_open_and_decode_memory_s16() - drflac_open_and_decode_memory_f32() - Remove drflac.totalSampleCount which is now replaced with drflac.totalPCMFrameCount. You can emulate drflac.totalSampleCount by doing pFlac->totalPCMFrameCount*pFlac->channels. - Rename drflac.currentFrame to drflac.currentFLACFrame to remove ambiguity with PCM frames. - Fix errors when seeking to the end of a stream. - Optimizations to seeking. - SSE improvements and optimizations. - ARM NEON optimizations. - Optimizations to drflac_read_pcm_frames_s16(). - Optimizations to drflac_read_pcm_frames_s32(). v0.11.10 - 2019-06-26 - Fix a compiler error. v0.11.9 - 2019-06-16 - Silence some ThreadSanitizer warnings. v0.11.8 - 2019-05-21 - Fix warnings. v0.11.7 - 2019-05-06 - C89 fixes. v0.11.6 - 2019-05-05 - Add support for C89. - Fix a compiler warning when CRC is disabled. - Change license to choice of public domain or MIT-0. v0.11.5 - 2019-04-19 - Fix a compiler error with GCC. v0.11.4 - 2019-04-17 - Fix some warnings with GCC when compiling with -std=c99. v0.11.3 - 2019-04-07 - Silence warnings with GCC. v0.11.2 - 2019-03-10 - Fix a warning. v0.11.1 - 2019-02-17 - Fix a potential bug with seeking. v0.11.0 - 2018-12-16 - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by dividing it by the channel count, and then do the same with the return value. - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as the changes to drflac_read_*() apply. - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as the changes to drflac_read_*() apply. - Optimizations. v0.10.0 - 2018-09-11 - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you need to do it yourself via the callback API. - Fix the clang build. - Fix undefined behavior. - Fix errors with CUESHEET metdata blocks. - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the Vorbis comment API. - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams. - Minor optimizations. v0.9.11 - 2018-08-29 - Fix a bug with sample reconstruction. v0.9.10 - 2018-08-07 - Improve 64-bit detection. v0.9.9 - 2018-08-05 - Fix C++ build on older versions of GCC. v0.9.8 - 2018-07-24 - Fix compilation errors. v0.9.7 - 2018-07-05 - Fix a warning. v0.9.6 - 2018-06-29 - Fix some typos. v0.9.5 - 2018-06-23 - Fix some warnings. v0.9.4 - 2018-06-14 - Optimizations to seeking. - Clean up. v0.9.3 - 2018-05-22 - Bug fix. v0.9.2 - 2018-05-12 - Fix a compilation error due to a missing break statement. v0.9.1 - 2018-04-29 - Fix compilation error with Clang. v0.9 - 2018-04-24 - Fix Clang build. - Start using major.minor.revision versioning. v0.8g - 2018-04-19 - Fix build on non-x86/x64 architectures. v0.8f - 2018-02-02 - Stop pretending to support changing rate/channels mid stream. v0.8e - 2018-02-01 - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. - Fix a crash the the Rice partition order is invalid. v0.8d - 2017-09-22 - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. v0.8c - 2017-09-07 - Fix warning on non-x86/x64 architectures. v0.8b - 2017-08-19 - Fix build on non-x86/x64 architectures. v0.8a - 2017-08-13 - A small optimization for the Clang build. v0.8 - 2017-08-12 - API CHANGE: Rename dr_* types to drflac_*. - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. - Add support for custom implementations of malloc(), realloc(), etc. - Add CRC checking to Ogg encapsulated streams. - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. - Bug fixes. v0.7 - 2017-07-23 - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). v0.6 - 2017-07-22 - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. v0.5 - 2017-07-16 - Fix typos. - Change drflac_bool* types to unsigned. - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. v0.4f - 2017-03-10 - Fix a couple of bugs with the bitstreaming code. v0.4e - 2017-02-17 - Fix some warnings. v0.4d - 2016-12-26 - Add support for 32-bit floating-point PCM decoding. - Use drflac_int* and drflac_uint* sized types to improve compiler support. - Minor improvements to documentation. v0.4c - 2016-12-26 - Add support for signed 16-bit integer PCM decoding. v0.4b - 2016-10-23 - A minor change to drflac_bool8 and drflac_bool32 types. v0.4a - 2016-10-11 - Rename drBool32 to drflac_bool32 for styling consistency. v0.4 - 2016-09-29 - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to keep it consistent with drflac_audio. v0.3f - 2016-09-21 - Fix a warning with GCC. v0.3e - 2016-09-18 - Fixed a bug where GCC 4.3+ was not getting properly identified. - Fixed a few typos. - Changed date formats to ISO 8601 (YYYY-MM-DD). v0.3d - 2016-06-11 - Minor clean up. v0.3c - 2016-05-28 - Fixed compilation error. v0.3b - 2016-05-16 - Fixed Linux/GCC build. - Updated documentation. v0.3a - 2016-05-15 - Minor fixes to documentation. v0.3 - 2016-05-11 - Optimizations. Now at about parity with the reference implementation on 32-bit builds. - Lots of clean up. v0.2b - 2016-05-10 - Bug fixes. v0.2a - 2016-05-10 - Made drflac_open_and_decode() more robust. - Removed an unused debugging variable v0.2 - 2016-05-09 - Added support for Ogg encapsulation. - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek should be relative to the start or the current position. Also changes the seeking rules such that seeking offsets will never be negative. - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count. v0.1b - 2016-05-07 - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize. - Removed a stale comment. v0.1a - 2016-05-05 - Minor formatting changes. - Fixed a warning on the GCC build. v0.1 - 2016-05-03 - Initial versioned release. */ /* This software is available as a choice of the following licenses. Choose whichever you prefer. =============================================================================== ALTERNATIVE 1 - Public Domain (www.unlicense.org) =============================================================================== This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== Copyright 2023 David Reid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ SDL2_mixer-2.8.0/src/codecs/music_fluidsynth.h0000644000076500000240000000225214551252471020231 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. James Le Cuirot chewi@aura-online.co.uk */ /* This file supports playing MIDI files with FluidSynth */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_cmd.h0000644000076500000240000000217114551252471016603 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports an external command for playing music */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_CMD; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_modplug.c0000644000076500000240000002564714551252471017517 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_MOD_MODPLUG #include "SDL_loadso.h" #include "music_modplug.h" #ifdef MODPLUG_HEADER #include MODPLUG_HEADER #else #include #endif typedef struct { int loaded; void *handle; ModPlugFile* (*ModPlug_Load)(const void* data, int size); void (*ModPlug_Unload)(ModPlugFile* file); int (*ModPlug_Read)(ModPlugFile* file, void* buffer, int size); void (*ModPlug_Seek)(ModPlugFile* file, int millisecond); void (*ModPlug_SeekOrder)(ModPlugFile* file, int order); int (*ModPlug_Tell)(ModPlugFile* file); int (*ModPlug_GetLength)(ModPlugFile* file); void (*ModPlug_GetSettings)(ModPlug_Settings* settings); void (*ModPlug_SetSettings)(const ModPlug_Settings* settings); void (*ModPlug_SetMasterVolume)(ModPlugFile* file,unsigned int cvol); const char* (*ModPlug_GetName)(ModPlugFile* file); } modplug_loader; static modplug_loader modplug; static ModPlug_Settings settings; #ifdef MODPLUG_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ modplug.FUNC = (SIG) SDL_LoadFunction(modplug.handle, #FUNC); \ if (modplug.FUNC == NULL) { SDL_UnloadObject(modplug.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ modplug.FUNC = FUNC; \ if (modplug.FUNC == NULL) { Mix_SetError("Missing libmodplug.framework"); return -1; } #endif static int MODPLUG_Load(void) #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif { if (modplug.loaded == 0) { #ifdef MODPLUG_DYNAMIC modplug.handle = SDL_LoadObject(MODPLUG_DYNAMIC); if (modplug.handle == NULL) { return -1; } #endif FUNCTION_LOADER(ModPlug_Load, ModPlugFile* (*)(const void* data, int size)) FUNCTION_LOADER(ModPlug_Unload, void (*)(ModPlugFile* file)) FUNCTION_LOADER(ModPlug_Read, int (*)(ModPlugFile* file, void* buffer, int size)) FUNCTION_LOADER(ModPlug_Seek, void (*)(ModPlugFile* file, int millisecond)) FUNCTION_LOADER(ModPlug_SeekOrder, void (*)(ModPlugFile* file, int order)) FUNCTION_LOADER(ModPlug_GetLength, int (*)(ModPlugFile* file)) FUNCTION_LOADER(ModPlug_GetSettings, void (*)(ModPlug_Settings* settings)) FUNCTION_LOADER(ModPlug_SetSettings, void (*)(const ModPlug_Settings* settings)) FUNCTION_LOADER(ModPlug_SetMasterVolume, void (*)(ModPlugFile* file,unsigned int cvol)) FUNCTION_LOADER(ModPlug_GetName, const char* (*)(ModPlugFile* file)) #ifdef MODPLUG_DYNAMIC modplug.ModPlug_Tell = (int (*)(ModPlugFile* file)) SDL_LoadFunction(modplug.handle, "ModPlug_Tell"); if (modplug.ModPlug_Tell == NULL) { SDL_ClearError(); /* ModPlug_Tell is optional. */ } #elif defined(MODPLUG_HAS_TELL) modplug.ModPlug_Tell = ModPlug_Tell; #else modplug.ModPlug_Tell = NULL; #endif } ++modplug.loaded; return 0; } static void MODPLUG_Unload(void) { if (modplug.loaded == 0) { return; } if (modplug.loaded == 1) { #ifdef MODPLUG_DYNAMIC SDL_UnloadObject(modplug.handle); #endif } --modplug.loaded; } typedef struct { int volume; int play_count; ModPlugFile *file; SDL_AudioStream *stream; void *buffer; int buffer_size; Mix_MusicMetaTags tags; } MODPLUG_Music; static int MODPLUG_Seek(void *context, double position); static void MODPLUG_Delete(void *context); static int MODPLUG_Open(const SDL_AudioSpec *spec) { /* ModPlug supports U8 or S16 audio output */ modplug.ModPlug_GetSettings(&settings); settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING; if (spec->channels == 1) { settings.mChannels = 1; } else { settings.mChannels = 2; } if (SDL_AUDIO_BITSIZE(spec->format) == 8) { settings.mBits = 8; } else { settings.mBits = 16; } if (spec->freq >= 44100) { settings.mFrequency = 44100; } else if (spec->freq >= 22050) { settings.mFrequency = 22050; } else { settings.mFrequency = 11025; } settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; settings.mReverbDepth = 0; settings.mReverbDelay = 100; settings.mBassAmount = 0; settings.mBassRange = 50; settings.mSurroundDepth = 0; settings.mSurroundDelay = 10; settings.mLoopCount = -1; modplug.ModPlug_SetSettings(&settings); return 0; } /* Load a modplug stream from an SDL_RWops object */ void *MODPLUG_CreateFromRW(SDL_RWops *src, int freesrc) { MODPLUG_Music *music; void *buffer; size_t size; music = (MODPLUG_Music *)SDL_calloc(1, sizeof(*music)); if (!music) { SDL_OutOfMemory(); return NULL; } music->volume = MIX_MAX_VOLUME; music->stream = SDL_NewAudioStream((settings.mBits == 8) ? AUDIO_U8 : AUDIO_S16SYS, (Uint8)settings.mChannels, settings.mFrequency, music_spec.format, music_spec.channels, music_spec.freq); if (!music->stream) { MODPLUG_Delete(music); return NULL; } music->buffer_size = music_spec.samples * (settings.mBits / 8) * settings.mChannels; music->buffer = SDL_malloc((size_t)music->buffer_size); if (!music->buffer) { MODPLUG_Delete(music); return NULL; } buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE); if (buffer) { music->file = modplug.ModPlug_Load(buffer, (int)size); if (!music->file) { Mix_SetError("ModPlug_Load failed"); } SDL_free(buffer); } if (!music->file) { MODPLUG_Delete(music); return NULL; } meta_tags_init(&music->tags); meta_tags_set(&music->tags, MIX_META_TITLE, modplug.ModPlug_GetName(music->file)); if (freesrc) { SDL_RWclose(src); } return music; } /* Set the volume for a modplug stream */ static void MODPLUG_SetVolume(void *context, int volume) { MODPLUG_Music *music = (MODPLUG_Music *)context; music->volume = volume; modplug.ModPlug_SetMasterVolume(music->file, (unsigned int)volume * 2); /* 0-512, reduced to 0-256 to prevent clipping */ } /* Get the volume for a modplug stream */ static int MODPLUG_GetVolume(void *context) { MODPLUG_Music *music = (MODPLUG_Music *)context; return music->volume; } /* Start playback of a given modplug stream */ static int MODPLUG_Play(void *context, int play_count) { MODPLUG_Music *music = (MODPLUG_Music *)context; music->play_count = play_count; return MODPLUG_Seek(music, 0.0); } static void MODPLUG_Stop(void *context) { MODPLUG_Music *music = (MODPLUG_Music *)context; SDL_AudioStreamClear(music->stream); } /* Play some of a stream previously started with modplug_play() */ static int MODPLUG_GetSome(void *context, void *data, int bytes, SDL_bool *done) { MODPLUG_Music *music = (MODPLUG_Music *)context; int filled, amount; filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } amount = modplug.ModPlug_Read(music->file, music->buffer, music->buffer_size); if (amount > 0) { if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else { if (music->play_count == 1) { music->play_count = 0; SDL_AudioStreamFlush(music->stream); } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (MODPLUG_Play(music, play_count) < 0) { return -1; } } } return 0; } static int MODPLUG_GetAudio(void *context, void *data, int bytes) { return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MODPLUG_GetSome); } /* Jump to a given order */ static int MODPLUG_Jump(void *context, int order) { MODPLUG_Music *music = (MODPLUG_Music *)context; modplug.ModPlug_SeekOrder(music->file, order); return 0; } /* Jump (seek) to a given position */ static int MODPLUG_Seek(void *context, double position) { MODPLUG_Music *music = (MODPLUG_Music *)context; modplug.ModPlug_Seek(music->file, (int)(position*1000)); return 0; } static double MODPLUG_Tell(void *context) { if (modplug.ModPlug_Tell) { MODPLUG_Music *music = (MODPLUG_Music *)context; return (double)(modplug.ModPlug_Tell(music->file)) / 1000.0; } else { return -1.0; } } /* Return music duration in seconds */ static double MODPLUG_Duration(void *context) { MODPLUG_Music *music = (MODPLUG_Music *)context; return modplug.ModPlug_GetLength(music->file) / 1000.0; } static const char* MODPLUG_GetMetaTag(void *context, Mix_MusicMetaTag tag_type) { MODPLUG_Music *music = (MODPLUG_Music *)context; return meta_tags_get(&music->tags, tag_type); } /* Close the given modplug stream */ static void MODPLUG_Delete(void *context) { MODPLUG_Music *music = (MODPLUG_Music *)context; meta_tags_clear(&music->tags); if (music->file) { modplug.ModPlug_Unload(music->file); } if (music->stream) { SDL_FreeAudioStream(music->stream); } if (music->buffer) { SDL_free(music->buffer); } SDL_free(music); } Mix_MusicInterface Mix_MusicInterface_MODPLUG = { "MODPLUG", MIX_MUSIC_MODPLUG, MUS_MOD, SDL_FALSE, SDL_FALSE, MODPLUG_Load, MODPLUG_Open, MODPLUG_CreateFromRW, NULL, /* CreateFromFile */ MODPLUG_SetVolume, MODPLUG_GetVolume, MODPLUG_Play, NULL, /* IsPlaying */ MODPLUG_GetAudio, MODPLUG_Jump, MODPLUG_Seek, MODPLUG_Tell, MODPLUG_Duration, NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ MODPLUG_GetMetaTag, NULL, /* GetNumTracks */ NULL, /* StartTrack */ NULL, /* Pause */ NULL, /* Resume */ MODPLUG_Stop, MODPLUG_Delete, NULL, /* Close */ MODPLUG_Unload }; #endif /* MUSIC_MOD_MODPLUG */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_nativemidi.c0000644000076500000240000000605614551252471020172 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifdef MUSIC_MID_NATIVE /* This file supports playing MIDI files with OS APIs */ #include "music_nativemidi.h" #include "native_midi/native_midi.h" static void *NATIVEMIDI_CreateFromRW(SDL_RWops *src, int freesrc) { NativeMidiSong *music = native_midi_loadsong_RW(src, freesrc); if (!music) { Mix_SetError("%s", native_midi_error()); } return music; } static int NATIVEMIDI_Play(void *context, int play_count) { NativeMidiSong *music = (NativeMidiSong *)context; int loops = play_count; if (loops > 0) { --loops; } native_midi_start(music, loops); return 0; } static void NATIVEMIDI_SetVolume(void *context, int volume) { (void)context; native_midi_setvolume(volume); } static SDL_bool NATIVEMIDI_IsPlaying(void *context) { (void)context; return native_midi_active() ? SDL_TRUE : SDL_FALSE; } static void NATIVEMIDI_Pause(void *context) { (void)context; native_midi_pause(); } static void NATIVEMIDI_Resume(void *context) { (void)context; native_midi_resume(); } static void NATIVEMIDI_Stop(void *context) { (void)context; native_midi_stop(); } static void NATIVEMIDI_Delete(void *context) { NativeMidiSong *music = (NativeMidiSong *)context; native_midi_freesong(music); } Mix_MusicInterface Mix_MusicInterface_NATIVEMIDI = { "NATIVEMIDI", MIX_MUSIC_NATIVEMIDI, MUS_MID, SDL_FALSE, SDL_FALSE, NULL, /* Load */ NULL, /* Open */ NATIVEMIDI_CreateFromRW, NULL, /* CreateFromFile */ NATIVEMIDI_SetVolume, NULL, /* GetVolume */ NATIVEMIDI_Play, NATIVEMIDI_IsPlaying, NULL, /* GetAudio */ NULL, /* Jump */ NULL, /* Seek */ NULL, /* Tell */ NULL, /* Duration */ NULL, /* LoopStart */ NULL, /* LoopEnd */ NULL, /* LoopLength */ NULL, /* GetMetaTag */ NULL, /* GetNumTracks */ NULL, /* StartTrack */ NATIVEMIDI_Pause, NATIVEMIDI_Resume, NATIVEMIDI_Stop, NATIVEMIDI_Delete, NULL, /* Close */ NULL /* Unload */ }; #endif /* MUSIC_MID_NATIVE */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/codecs/music_minimp3.h0000644000076500000240000000216614551252471017420 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file supports playing MP3 files with MiniMP3 */ #include "music.h" extern Mix_MusicInterface Mix_MusicInterface_MINIMP3; /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/effects_internal.h0000644000076500000240000000331314551252471016712 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef INCLUDE_EFFECTS_INTERNAL_H_ #define INCLUDE_EFFECTS_INTERNAL_H_ #ifndef MIX_INTERNAL_EFFECT__ #error You should not include this file or use these functions. #endif #include "SDL_mixer.h" extern int _Mix_effects_max_speed; extern void *_Eff_volume_table; void *_Eff_build_volume_table_u8(void); void *_Eff_build_volume_table_s8(void); void _Mix_InitEffects(void); void _Mix_DeinitEffects(void); void _Eff_PositionDeinit(void); int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, Mix_EffectDone_t d, void *arg); int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f); int _Mix_UnregisterAllEffects_locked(int channel); #endif /* _INCLUDE_EFFECTS_INTERNAL_H_ */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/mixer.h0000644000076500000240000000230514551252471014523 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef MIXER_H_ #define MIXER_H_ /* Locking wrapper functions */ extern void Mix_LockAudio(void); extern void Mix_UnlockAudio(void); extern void add_chunk_decoder(const char *decoder); #endif /* MIXER_H_ */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/utils.c0000644000076500000240000000765214551252471014544 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* misc helper routines */ #include "utils.h" #include #if !defined(HAVE_SDL_STRTOKR) /* * Adapted from _PDCLIB_strtok() of PDClib library at * https://github.com/DevSolar/pdclib.git * * The code was under CC0 license: * https://creativecommons.org/publicdomain/zero/1.0/legalcode : * * No Copyright * * The person who associated a work with this deed has dedicated the * work to the public domain by waiving all of his or her rights to * the work worldwide under copyright law, including all related and * neighboring rights, to the extent allowed by law. * * You can copy, modify, distribute and perform the work, even for * commercial purposes, all without asking permission. See Other * Information below. */ char *SDL_strtokr(char *s1, const char *s2, char **ptr) { const char *p = s2; if (!s2 || !ptr || (!s1 && !*ptr)) return NULL; if (s1 != NULL) { /* new string */ *ptr = s1; } else { /* old string continued */ if (*ptr == NULL) { /* No old string, no new string, nothing to do */ return NULL; } s1 = *ptr; } /* skip leading s2 characters */ while (*p && *s1) { if (*s1 == *p) { /* found separator; skip and start over */ ++s1; p = s2; continue; } ++p; } if (! *s1) { /* no more to parse */ *ptr = s1; return NULL; } /* skipping non-s2 characters */ *ptr = s1; while (**ptr) { p = s2; while (*p) { if (**ptr == *p++) { /* found separator; overwrite with '\0', position *ptr, return */ *((*ptr)++) = '\0'; return s1; } } ++(*ptr); } /* parsed to end of string */ return s1; } #endif /* HAVE_SDL_STRTOKR */ /* Is given tag a loop tag? */ SDL_bool _Mix_IsLoopTag(const char *tag) { char buf[5]; SDL_strlcpy(buf, tag, 5); return SDL_strcasecmp(buf, "LOOP") == 0; } /* Parse time string of the form HH:MM:SS.mmm and return equivalent sample * position */ Sint64 _Mix_ParseTime(char *time, long samplerate_hz) { char *num_start, *p; Sint64 result; char c; int val; /* Time is directly expressed as a sample position */ if (SDL_strchr(time, ':') == NULL) { return SDL_strtoll(time, NULL, 10); } result = 0; num_start = time; for (p = time; *p != '\0'; ++p) { if (*p == '.' || *p == ':') { c = *p; *p = '\0'; if ((val = SDL_atoi(num_start)) < 0) return -1; result = result * 60 + val; num_start = p + 1; *p = c; } if (*p == '.') { double val_f = SDL_atof(p); if (val_f < 0) return -1; return result * samplerate_hz + (Sint64) (val_f * samplerate_hz); } } if ((val = SDL_atoi(num_start)) < 0) return -1; return (result * 60 + val) * samplerate_hz; } SDL2_mixer-2.8.0/src/music.c0000644000076500000240000012245714553225265014530 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_hints.h" #include "SDL_log.h" #include "SDL_timer.h" #include "SDL_mixer.h" #include "mixer.h" #include "music.h" #include "music_cmd.h" #include "music_wav.h" #include "music_modplug.h" #include "music_xmp.h" #include "music_nativemidi.h" #include "music_fluidsynth.h" #include "music_timidity.h" #include "music_ogg.h" #include "music_opus.h" #include "music_minimp3.h" #include "music_mpg123.h" #include "music_drflac.h" #include "music_flac.h" #include "music_wavpack.h" #include "music_gme.h" #include "native_midi/native_midi.h" #include "utils.h" /* Check to make sure we are building with a new enough SDL */ #if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 7) #error You need SDL 2.0.7 or newer from http://www.libsdl.org #endif /* Set this hint to true if you want verbose logging of music interfaces */ #define SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES \ "SDL_MIXER_DEBUG_MUSIC_INTERFACES" char *music_cmd = NULL; static SDL_bool music_active = SDL_TRUE; static int music_volume = MIX_MAX_VOLUME; static Mix_Music * volatile music_playing = NULL; SDL_AudioSpec music_spec; struct _Mix_Music { Mix_MusicInterface *interface; void *context; SDL_bool playing; Mix_Fading fading; int fade_step; int fade_steps; char filename[1024]; }; /* Used to calculate fading steps */ static int ms_per_step; /* rcg06042009 report available decoders at runtime. */ static const char **music_decoders = NULL; static int num_decoders = 0; /* Semicolon-separated SoundFont paths */ static char* soundfont_paths = NULL; /* full path of timidity config file */ static char* timidity_cfg = NULL; /* Meta-Tags utility */ void meta_tags_init(Mix_MusicMetaTags *tags) { SDL_memset(tags, 0, sizeof(Mix_MusicMetaTags)); } void meta_tags_clear(Mix_MusicMetaTags *tags) { size_t i; for (i = 0; i < MIX_META_LAST; i++) { if (tags->tags[i]) { SDL_free(tags->tags[i]); tags->tags[i] = NULL; } } } void meta_tags_set(Mix_MusicMetaTags *tags, Mix_MusicMetaTag type, const char *value) { char *out; size_t len; if (!value) { return; } if (type >= MIX_META_LAST) { return; } len = SDL_strlen(value); out = (char *)SDL_malloc(sizeof(char) * len + 1); SDL_strlcpy(out, value, len +1); if (tags->tags[type]) { SDL_free(tags->tags[type]); } tags->tags[type] = out; } const char *meta_tags_get(Mix_MusicMetaTags *tags, Mix_MusicMetaTag type) { switch (type) { case MIX_META_TITLE: case MIX_META_ARTIST: case MIX_META_ALBUM: case MIX_META_COPYRIGHT: return tags->tags[type] ? tags->tags[type] : ""; case MIX_META_LAST: default: break; } return ""; } /* for music->filename */ #if defined(__WIN32__)||defined(__OS2__) static SDL_INLINE const char *get_last_dirsep (const char *p) { const char *p1 = SDL_strrchr(p, '/'); const char *p2 = SDL_strrchr(p, '\\'); if (!p1) return p2; if (!p2) return p1; return (p1 > p2)? p1 : p2; } #else /* unix */ static SDL_INLINE const char *get_last_dirsep (const char *p) { return SDL_strrchr(p, '/'); } #endif /* Interfaces for the various music interfaces, ordered by priority */ static Mix_MusicInterface *s_music_interfaces[] = { #ifdef MUSIC_CMD &Mix_MusicInterface_CMD, #endif #ifdef MUSIC_WAV &Mix_MusicInterface_WAV, #endif #ifdef MUSIC_FLAC_DRFLAC &Mix_MusicInterface_DRFLAC, #endif #ifdef MUSIC_FLAC_LIBFLAC &Mix_MusicInterface_FLAC, #endif #ifdef MUSIC_WAVPACK &Mix_MusicInterface_WAVPACK, #endif #ifdef MUSIC_OGG &Mix_MusicInterface_OGG, #endif #ifdef MUSIC_OPUS &Mix_MusicInterface_Opus, #endif #ifdef MUSIC_MP3_MINIMP3 &Mix_MusicInterface_MINIMP3, #endif #ifdef MUSIC_MP3_MPG123 &Mix_MusicInterface_MPG123, #endif #ifdef MUSIC_MOD_XMP &Mix_MusicInterface_XMP, #endif #ifdef MUSIC_MOD_MODPLUG &Mix_MusicInterface_MODPLUG, #endif #ifdef MUSIC_MID_FLUIDSYNTH &Mix_MusicInterface_FLUIDSYNTH, #endif #ifdef MUSIC_MID_TIMIDITY &Mix_MusicInterface_TIMIDITY, #endif #ifdef MUSIC_MID_NATIVE &Mix_MusicInterface_NATIVEMIDI, #endif #ifdef MUSIC_GME &Mix_MusicInterface_GME, #endif }; int get_num_music_interfaces(void) { return SDL_arraysize(s_music_interfaces); } Mix_MusicInterface *get_music_interface(int index) { return s_music_interfaces[index]; } int Mix_GetNumMusicDecoders(void) { return num_decoders; } const char *Mix_GetMusicDecoder(int index) { if ((index < 0) || (index >= num_decoders)) { return NULL; } return music_decoders[index]; } SDL_bool Mix_HasMusicDecoder(const char *name) { int index; for (index = 0; index < num_decoders; ++index) { if (SDL_strcasecmp(name, music_decoders[index]) == 0) { return SDL_TRUE; } } return SDL_FALSE; } static void add_music_decoder(const char *decoder) { void *ptr; int i; /* Check to see if we already have this decoder */ for (i = 0; i < num_decoders; ++i) { if (SDL_strcmp(music_decoders[i], decoder) == 0) { return; } } ptr = SDL_realloc((void *)music_decoders, ((size_t)num_decoders + 1) * sizeof(const char *)); if (ptr == NULL) { return; /* oh well, go on without it. */ } music_decoders = (const char **) ptr; music_decoders[num_decoders++] = decoder; } /* Local low-level functions prototypes */ static void music_internal_initialize_volume(void); static void music_internal_volume(int volume); static int music_internal_play(Mix_Music *music, int play_count, double position); static int music_internal_position(double position); static SDL_bool music_internal_playing(void); static void music_internal_halt(void); /* Support for hooking when the music has finished */ static void (SDLCALL *music_finished_hook)(void) = NULL; void Mix_HookMusicFinished(void (SDLCALL *music_finished)(void)) { Mix_LockAudio(); music_finished_hook = music_finished; Mix_UnlockAudio(); } /* Convenience function to fill audio and mix at the specified volume This is called from many music player's GetAudio callback. */ int music_pcm_getaudio(void *context, void *data, int bytes, int volume, int (*GetSome)(void *context, void *data, int bytes, SDL_bool *done)) { Uint8 *snd = (Uint8 *)data; Uint8 *dst; int len = bytes; int zero_cycles = 0; const int MAX_ZERO_CYCLES = 10; /* just try to catch infinite loops */ SDL_bool done = SDL_FALSE; if (volume == MIX_MAX_VOLUME) { dst = snd; } else { dst = SDL_stack_alloc(Uint8, (size_t)bytes); } while (len > 0 && !done) { int consumed = GetSome(context, dst, len, &done); if (consumed < 0) { break; } if (consumed == 0) { ++zero_cycles; if (zero_cycles > MAX_ZERO_CYCLES) { /* We went too many cycles with no data, we're done */ done = SDL_TRUE; } continue; } zero_cycles = 0; if (volume == MIX_MAX_VOLUME) { dst += consumed; } else { SDL_MixAudioFormat(snd, dst, music_spec.format, (Uint32)consumed, volume); snd += consumed; } len -= consumed; } if (volume != MIX_MAX_VOLUME) { SDL_stack_free(dst); } return len; } /* Mixing function */ void SDLCALL music_mixer(void *udata, Uint8 *stream, int len) { SDL_bool done = SDL_FALSE; (void)udata; while (music_playing && music_active && len > 0 && !done) { /* Handle fading */ if (music_playing->fading != MIX_NO_FADING) { if (music_playing->fade_step++ < music_playing->fade_steps) { int volume; int fade_step = music_playing->fade_step; int fade_steps = music_playing->fade_steps; if (music_playing->fading == MIX_FADING_OUT) { volume = (music_volume * (fade_steps-fade_step)) / fade_steps; } else { /* Fading in */ volume = (music_volume * fade_step) / fade_steps; } music_internal_volume(volume); } else { if (music_playing->fading == MIX_FADING_OUT) { music_internal_halt(); if (music_finished_hook) { music_finished_hook(); } return; } music_playing->fading = MIX_NO_FADING; } } if (music_playing->interface->GetAudio) { int left = music_playing->interface->GetAudio(music_playing->context, stream, len); if (left != 0) { /* Either an error or finished playing with data left */ music_playing->playing = SDL_FALSE; done = SDL_TRUE; } if (left > 0) { stream += (len - left); len = left; } else { len = 0; } } else { len = 0; } if (!music_internal_playing()) { music_internal_halt(); if (music_finished_hook) { music_finished_hook(); } } } } void pause_async_music(int pause_on) { if (!music_active || !music_playing || !music_playing->interface) { return; } if (pause_on) { if (music_playing->interface->Pause) { music_playing->interface->Pause(music_playing->context); } } else { if (music_playing->interface->Resume) { music_playing->interface->Resume(music_playing->context); } } } /* Load the music interface libraries for a given music type */ SDL_bool load_music_type(Mix_MusicType type) { size_t i; int loaded = 0; for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (interface->type != type) { continue; } if (!interface->loaded) { char hint[64]; SDL_snprintf(hint, sizeof(hint), "SDL_MIXER_DISABLE_%s", interface->tag); if (SDL_GetHintBoolean(hint, SDL_FALSE)) { continue; } if (interface->Load && interface->Load() < 0) { if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) { SDL_Log("Couldn't load %s: %s\n", interface->tag, Mix_GetError()); } continue; } interface->loaded = SDL_TRUE; } ++loaded; } return (loaded > 0) ? SDL_TRUE : SDL_FALSE; } /* Open the music interfaces for a given music type */ SDL_bool open_music_type(Mix_MusicType type) { size_t i; int opened = 0; SDL_bool use_native_midi = SDL_FALSE; if (!music_spec.format) { /* Music isn't opened yet */ return SDL_FALSE; } #ifdef MUSIC_MID_NATIVE if (type == MUS_MID && SDL_GetHintBoolean("SDL_NATIVE_MUSIC", SDL_FALSE) && native_midi_detect()) { use_native_midi = SDL_TRUE; } #endif for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (!interface->loaded) { continue; } if (type != MUS_NONE && interface->type != type) { continue; } if (interface->type == MUS_MID && use_native_midi && interface->api != MIX_MUSIC_NATIVEMIDI) { continue; } if (!interface->opened) { if (interface->Open && interface->Open(&music_spec) < 0) { if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) { SDL_Log("Couldn't open %s: %s\n", interface->tag, Mix_GetError()); } continue; } interface->opened = SDL_TRUE; add_music_decoder(interface->tag); } ++opened; } if (has_music(MUS_MOD)) { add_music_decoder("MOD"); add_chunk_decoder("MOD"); } if (has_music(MUS_MID)) { add_music_decoder("MIDI"); add_chunk_decoder("MID"); } if (has_music(MUS_OGG)) { add_music_decoder("OGG"); add_chunk_decoder("OGG"); } if (has_music(MUS_OPUS)) { add_music_decoder("OPUS"); add_chunk_decoder("OPUS"); } if (has_music(MUS_MP3)) { add_music_decoder("MP3"); add_chunk_decoder("MP3"); } if (has_music(MUS_FLAC)) { add_music_decoder("FLAC"); add_chunk_decoder("FLAC"); } if (has_music(MUS_WAVPACK)) { add_music_decoder("WAVPACK"); add_chunk_decoder("WAVPACK"); } return (opened > 0) ? SDL_TRUE : SDL_FALSE; } /* Initialize the music interfaces with a certain desired audio format */ void open_music(const SDL_AudioSpec *spec) { #ifdef MIX_INIT_SOUNDFONT_PATHS if (!soundfont_paths) { soundfont_paths = SDL_strdup(MIX_INIT_SOUNDFONT_PATHS); } #endif /* Load the music interfaces that don't have explicit initialization */ load_music_type(MUS_CMD); load_music_type(MUS_WAV); /* Open all the interfaces that are loaded */ music_spec = *spec; open_music_type(MUS_NONE); Mix_VolumeMusic(MIX_MAX_VOLUME); /* Calculate the number of ms for each callback */ ms_per_step = (int) (((float)spec->samples * 1000.0f) / spec->freq); } /* Return SDL_TRUE if the music type is available */ SDL_bool has_music(Mix_MusicType type) { size_t i; for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (interface->type != type) { continue; } if (interface->opened) { return SDL_TRUE; } } return SDL_FALSE; } Mix_MusicType detect_music_type(SDL_RWops *src) { Uint8 magic[12]; if (SDL_RWread(src, magic, 1, 12) != 12) { Mix_SetError("Couldn't read first 12 bytes of audio data"); return MUS_NONE; } SDL_RWseek(src, -12, RW_SEEK_CUR); /* WAVE files have the magic four bytes "RIFF" AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ if (((SDL_memcmp(magic, "RIFF", 4) == 0) && (SDL_memcmp((magic+8), "WAVE", 4) == 0)) || (SDL_memcmp(magic, "FORM", 4) == 0)) { return MUS_WAV; } /* Ogg Vorbis files have the magic four bytes "OggS" */ if (SDL_memcmp(magic, "OggS", 4) == 0) { SDL_RWseek(src, 28, RW_SEEK_CUR); SDL_RWread(src, magic, 1, 8); SDL_RWseek(src,-36, RW_SEEK_CUR); if (SDL_memcmp(magic, "OpusHead", 8) == 0) { return MUS_OPUS; } if (magic[0] == 0x7F && SDL_memcmp(magic + 1, "FLAC", 4) == 0) { return MUS_FLAC; } return MUS_OGG; } /* FLAC files have the magic four bytes "fLaC" */ if (SDL_memcmp(magic, "fLaC", 4) == 0) { return MUS_FLAC; } /* WavPack files have the magic four bytes "wvpk" */ if (SDL_memcmp(magic, "wvpk", 4) == 0) { return MUS_WAVPACK; } /* MIDI files have the magic four bytes "MThd" */ if (SDL_memcmp(magic, "MThd", 4) == 0) { return MUS_MID; } /* RIFF MIDI files have the magic four bytes "RIFF" and then "RMID" */ if ((SDL_memcmp(magic, "RIFF", 4) == 0) && (SDL_memcmp(magic + 8, "RMID", 4) == 0)) { return MUS_MID; } if (SDL_memcmp(magic, "ID3", 3) == 0 || /* see: https://bugzilla.libsdl.org/show_bug.cgi?id=5322 */ (magic[0] == 0xFF && (magic[1] & 0xE6) == 0xE2)) { return MUS_MP3; } /* GME Specific files */ if (SDL_memcmp(magic, "ZXAY", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "GBS\x01", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "GYMX", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "HESM", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "KSCC", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "KSSX", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "NESM", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "NSFE", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "SAP\x0D", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "SNES", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "Vgm ", 4) == 0) return MUS_GME; if (SDL_memcmp(magic, "\x1f\x8b", 2) == 0) return MUS_GME; /* Assume MOD format. * * Apparently there is no way to check if the file is really a MOD, * or there are too many formats supported by libmodplug or libxmp. * The mod library does this check by itself. */ return MUS_MOD; } /* Load a music file */ Mix_Music *Mix_LoadMUS(const char *file) { size_t i; void *context; char *ext; Mix_MusicType type; SDL_RWops *src; for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (!interface->opened || !interface->CreateFromFile) { continue; } context = interface->CreateFromFile(file); if (context) { const char *p; /* Allocate memory for the music structure */ Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music)); if (music == NULL) { Mix_OutOfMemory(); return NULL; } music->interface = interface; music->context = context; p = get_last_dirsep(file); SDL_strlcpy(music->filename, (p != NULL)? p + 1 : file, 1024); return music; } } src = SDL_RWFromFile(file, "rb"); if (src == NULL) { Mix_SetError("Couldn't open '%s'", file); return NULL; } /* Use the extension as a first guess on the file type */ type = MUS_NONE; ext = SDL_strrchr(file, '.'); if (ext) { ++ext; /* skip the dot in the extension */ if (SDL_strcasecmp(ext, "WAV") == 0) { type = MUS_WAV; } else if (SDL_strcasecmp(ext, "MID") == 0 || SDL_strcasecmp(ext, "MIDI") == 0 || SDL_strcasecmp(ext, "KAR") == 0) { type = MUS_MID; } else if (SDL_strcasecmp(ext, "OGG") == 0) { type = MUS_OGG; } else if (SDL_strcasecmp(ext, "OPUS") == 0) { type = MUS_OPUS; } else if (SDL_strcasecmp(ext, "FLAC") == 0) { type = MUS_FLAC; } else if (SDL_strcasecmp(ext, "WV") == 0) { type = MUS_WAVPACK; } else if (SDL_strcasecmp(ext, "MPG") == 0 || SDL_strcasecmp(ext, "MPEG") == 0 || SDL_strcasecmp(ext, "MP3") == 0 || SDL_strcasecmp(ext, "MAD") == 0) { type = MUS_MP3; } else if (SDL_strcasecmp(ext, "669") == 0 || SDL_strcasecmp(ext, "AMF") == 0 || SDL_strcasecmp(ext, "AMS") == 0 || SDL_strcasecmp(ext, "DBM") == 0 || SDL_strcasecmp(ext, "DSM") == 0 || SDL_strcasecmp(ext, "FAR") == 0 || SDL_strcasecmp(ext, "GDM") == 0 || SDL_strcasecmp(ext, "IT") == 0 || SDL_strcasecmp(ext, "MED") == 0 || SDL_strcasecmp(ext, "MDL") == 0 || SDL_strcasecmp(ext, "MOD") == 0 || SDL_strcasecmp(ext, "MOL") == 0 || SDL_strcasecmp(ext, "MTM") == 0 || SDL_strcasecmp(ext, "NST") == 0 || SDL_strcasecmp(ext, "OKT") == 0 || SDL_strcasecmp(ext, "PTM") == 0 || SDL_strcasecmp(ext, "S3M") == 0 || SDL_strcasecmp(ext, "STM") == 0 || SDL_strcasecmp(ext, "ULT") == 0 || SDL_strcasecmp(ext, "UMX") == 0 || SDL_strcasecmp(ext, "WOW") == 0 || SDL_strcasecmp(ext, "XM") == 0) { type = MUS_MOD; } else if (SDL_strcasecmp(ext, "GBS") == 0 || SDL_strcasecmp(ext, "M3U") == 0 || SDL_strcasecmp(ext, "NSF") == 0 || SDL_strcasecmp(ext, "SPC") == 0 || SDL_strcasecmp(ext, "VGM") == 0) { type = MUS_GME; } } return Mix_LoadMUSType_RW(src, type, SDL_TRUE); } Mix_Music *Mix_LoadMUS_RW(SDL_RWops *src, int freesrc) { return Mix_LoadMUSType_RW(src, MUS_NONE, freesrc); } Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *src, Mix_MusicType type, int freesrc) { size_t i; void *context; Sint64 start; if (!src) { Mix_SetError("RWops pointer is NULL"); return NULL; } start = SDL_RWtell(src); /* If the caller wants auto-detection, figure out what kind of file * this is. */ if (type == MUS_NONE) { if ((type = detect_music_type(src)) == MUS_NONE) { /* Don't call Mix_SetError() since detect_music_type() does that. */ if (freesrc) { SDL_RWclose(src); } return NULL; } } Mix_ClearError(); if (load_music_type(type) && open_music_type(type)) { for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (!interface->opened || type != interface->type || !interface->CreateFromRW) { continue; } context = interface->CreateFromRW(src, freesrc); if (context) { /* Allocate memory for the music structure */ Mix_Music *music = (Mix_Music *)SDL_calloc(1, sizeof(Mix_Music)); if (music == NULL) { interface->Delete(context); Mix_OutOfMemory(); return NULL; } music->interface = interface; music->context = context; if (SDL_GetHintBoolean(SDL_MIXER_HINT_DEBUG_MUSIC_INTERFACES, SDL_FALSE)) { SDL_Log("Loaded music with %s\n", interface->tag); } return music; } /* Reset the stream for the next decoder */ SDL_RWseek(src, start, RW_SEEK_SET); } } if (!*Mix_GetError()) { Mix_SetError("Unrecognized audio format"); } if (freesrc) { SDL_RWclose(src); } else { SDL_RWseek(src, start, RW_SEEK_SET); } return NULL; } /* Free a music chunk previously loaded */ void Mix_FreeMusic(Mix_Music *music) { if (music) { /* Stop the music if it's currently playing */ Mix_LockAudio(); if (music == music_playing) { /* Wait for any fade out to finish */ while (music_active && music->fading == MIX_FADING_OUT) { Mix_UnlockAudio(); SDL_Delay(100); Mix_LockAudio(); } if (music == music_playing) { music_internal_halt(); } } Mix_UnlockAudio(); music->interface->Delete(music->context); SDL_free(music); } } /* Find out the music format of a mixer music, or the currently playing music, if 'music' is NULL. */ Mix_MusicType Mix_GetMusicType(const Mix_Music *music) { Mix_MusicType type = MUS_NONE; if (music) { type = music->interface->type; } else { Mix_LockAudio(); if (music_playing) { type = music_playing->interface->type; } Mix_UnlockAudio(); } return type; } static const char * get_music_tag_internal(const Mix_Music *music, Mix_MusicMetaTag tag_type) { const char *tag = ""; Mix_LockAudio(); if (music && music->interface->GetMetaTag) { tag = music->interface->GetMetaTag(music->context, tag_type); } else if (music_playing && music_playing->interface->GetMetaTag) { tag = music_playing->interface->GetMetaTag(music_playing->context, tag_type); } else { Mix_SetError("Music isn't playing"); } Mix_UnlockAudio(); return tag; } const char *Mix_GetMusicTitleTag(const Mix_Music *music) { return get_music_tag_internal(music, MIX_META_TITLE); } /* Get music title from meta-tag if possible */ const char *Mix_GetMusicTitle(const Mix_Music *music) { const char *tag = Mix_GetMusicTitleTag(music); if (SDL_strlen(tag) > 0) { return tag; } if (music) { return music->filename; } if (music_playing) { return music_playing->filename; } return ""; } const char *Mix_GetMusicArtistTag(const Mix_Music *music) { return get_music_tag_internal(music, MIX_META_ARTIST); } const char *Mix_GetMusicAlbumTag(const Mix_Music *music) { return get_music_tag_internal(music, MIX_META_ALBUM); } const char *Mix_GetMusicCopyrightTag(const Mix_Music *music) { return get_music_tag_internal(music, MIX_META_COPYRIGHT); } /* Play a music chunk. Returns 0, or -1 if there was an error. */ static int music_internal_play(Mix_Music *music, int play_count, double position) { int retval = 0; /* Note the music we're playing */ if (music_playing) { music_internal_halt(); } music_playing = music; music_playing->playing = SDL_TRUE; /* Set the initial volume */ music_internal_initialize_volume(); /* Set up for playback */ retval = music->interface->Play(music->context, play_count); /* Set the playback position, note any errors if an offset is used */ if (retval == 0) { if (position > 0.0) { if (music_internal_position(position) < 0) { Mix_SetError("Position not implemented for music type"); retval = -1; } } else { music_internal_position(0.0); } } /* If the setup failed, we're not playing any music anymore */ if (retval < 0) { music->playing = SDL_FALSE; music_playing = NULL; } return retval; } int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) { int retval; if (ms_per_step == 0) { return Mix_SetError("Audio device hasn't been opened"); } /* Don't play null pointers :-) */ if (music == NULL) { return Mix_SetError("music parameter was NULL"); } /* Setup the data */ if (ms) { music->fading = MIX_FADING_IN; } else { music->fading = MIX_NO_FADING; } music->fade_step = 0; music->fade_steps = (ms + ms_per_step - 1) / ms_per_step; /* Play the puppy */ Mix_LockAudio(); /* If the current music is fading out, wait for the fade to complete */ while (music_playing && (music_playing->fading == MIX_FADING_OUT)) { Mix_UnlockAudio(); SDL_Delay(100); Mix_LockAudio(); } if (loops == 0) { /* Loop is the number of times to play the audio */ loops = 1; } retval = music_internal_play(music, loops, position); /* Set music as active */ music_active = (retval == 0); Mix_UnlockAudio(); return retval; } int Mix_FadeInMusic(Mix_Music *music, int loops, int ms) { return Mix_FadeInMusicPos(music, loops, ms, 0.0); } int Mix_PlayMusic(Mix_Music *music, int loops) { return Mix_FadeInMusicPos(music, loops, 0, 0.0); } /* Jump to a given order in mod music. */ int Mix_ModMusicJumpToOrder(int order) { int retval = -1; Mix_LockAudio(); if (music_playing) { if (music_playing->interface->Jump) { retval = music_playing->interface->Jump(music_playing->context, order); } else { Mix_SetError("Jump not implemented for music type"); } } else { Mix_SetError("Music isn't playing"); } Mix_UnlockAudio(); return retval; } /* Set the playing music position */ int music_internal_position(double position) { if (music_playing->interface->Seek) { return music_playing->interface->Seek(music_playing->context, position); } return -1; } int Mix_SetMusicPosition(double position) { int retval; Mix_LockAudio(); if (music_playing) { retval = music_internal_position(position); if (retval < 0) { Mix_SetError("Position not implemented for music type"); } } else { Mix_SetError("Music isn't playing"); retval = -1; } Mix_UnlockAudio(); return retval; } /* Set the playing music position */ static double music_internal_position_get(Mix_Music *music) { if (music->interface->Tell) { return music->interface->Tell(music->context); } return -1; } double Mix_GetMusicPosition(Mix_Music *music) { double retval; Mix_LockAudio(); if (music) { retval = music_internal_position_get(music); } else if (music_playing) { retval = music_internal_position_get(music_playing); } else { Mix_SetError("Music isn't playing"); retval = -1.0; } Mix_UnlockAudio(); return retval; } static double music_internal_duration(Mix_Music *music) { if (music->interface->Duration) { return music->interface->Duration(music->context); } else { Mix_SetError("Duration not implemented for music type"); return -1; } } double Mix_MusicDuration(Mix_Music *music) { double retval; Mix_LockAudio(); if (music) { retval = music_internal_duration(music); } else if (music_playing) { retval = music_internal_duration(music_playing); } else { Mix_SetError("music is NULL and no playing music"); retval = -1.0; } Mix_UnlockAudio(); return retval; } /* Get Loop start position */ static double music_internal_loop_start(Mix_Music *music) { if (music->interface->LoopStart) { return music->interface->LoopStart(music->context); } return -1; } double Mix_GetMusicLoopStartTime(Mix_Music *music) { double retval; Mix_LockAudio(); if (music) { retval = music_internal_loop_start(music); } else if (music_playing) { retval = music_internal_loop_start(music_playing); } else { Mix_SetError("Music isn't playing"); retval = -1.0; } Mix_UnlockAudio(); return retval; } /* Get Loop end position */ static double music_internal_loop_end(Mix_Music *music) { if (music->interface->LoopEnd) { return music->interface->LoopEnd(music->context); } return -1; } double Mix_GetMusicLoopEndTime(Mix_Music *music) { double retval; Mix_LockAudio(); if (music) { retval = music_internal_loop_end(music); } else if (music_playing) { retval = music_internal_loop_end(music_playing); } else { Mix_SetError("Music isn't playing"); retval = -1.0; } Mix_UnlockAudio(); return retval; } /* Get Loop end position */ static double music_internal_loop_length(Mix_Music *music) { if (music->interface->LoopLength) { return music->interface->LoopLength(music->context); } return -1; } double Mix_GetMusicLoopLengthTime(Mix_Music *music) { double retval; Mix_LockAudio(); if (music) { retval = music_internal_loop_length(music); } else if (music_playing) { retval = music_internal_loop_length(music_playing); } else { Mix_SetError("Music isn't playing"); retval = -1.0; } Mix_UnlockAudio(); return retval; } /* Set the music's initial volume */ static void music_internal_initialize_volume(void) { if (music_playing->fading == MIX_FADING_IN) { music_internal_volume(0); } else { music_internal_volume(music_volume); } } /* Set the music volume */ static void music_internal_volume(int volume) { if (music_playing->interface->SetVolume) { music_playing->interface->SetVolume(music_playing->context, volume); } } int Mix_VolumeMusic(int volume) { int prev_volume; prev_volume = music_volume; if (volume < 0) { return prev_volume; } if (volume > SDL_MIX_MAXVOLUME) { volume = SDL_MIX_MAXVOLUME; } music_volume = volume; Mix_LockAudio(); if (music_playing) { music_internal_volume(music_volume); } Mix_UnlockAudio(); return prev_volume; } int Mix_GetMusicVolume(Mix_Music *music) { int prev_volume; if (music && music->interface->GetVolume) prev_volume = music->interface->GetVolume(music->context); else if (music_playing && music_playing->interface->GetVolume) { prev_volume = music_playing->interface->GetVolume(music_playing->context); } else { prev_volume = music_volume; } return prev_volume; } /* Halt playing of music */ static void music_internal_halt(void) { if (music_playing->interface->Stop) { music_playing->interface->Stop(music_playing->context); } music_playing->playing = SDL_FALSE; music_playing->fading = MIX_NO_FADING; music_playing = NULL; } int Mix_HaltMusic(void) { Mix_LockAudio(); if (music_playing) { music_internal_halt(); if (music_finished_hook) { music_finished_hook(); } } Mix_UnlockAudio(); return 0; } /* Progressively stop the music */ int Mix_FadeOutMusic(int ms) { int retval = 0; if (ms_per_step == 0) { Mix_SetError("Audio device hasn't been opened"); return 0; } if (ms <= 0) { /* just halt immediately. */ Mix_HaltMusic(); return 1; } Mix_LockAudio(); if (music_playing) { int fade_steps = (ms + ms_per_step - 1) / ms_per_step; if (music_playing->fading == MIX_NO_FADING) { music_playing->fade_step = 0; } else { int step; int old_fade_steps = music_playing->fade_steps; if (music_playing->fading == MIX_FADING_OUT) { step = music_playing->fade_step; } else { step = old_fade_steps - music_playing->fade_step + 1; } music_playing->fade_step = (step * fade_steps) / old_fade_steps; } music_playing->fading = MIX_FADING_OUT; music_playing->fade_steps = fade_steps; retval = 1; } Mix_UnlockAudio(); return retval; } Mix_Fading Mix_FadingMusic(void) { Mix_Fading fading = MIX_NO_FADING; Mix_LockAudio(); if (music_playing) { fading = music_playing->fading; } Mix_UnlockAudio(); return fading; } /* Pause/Resume the music stream */ void Mix_PauseMusic(void) { Mix_LockAudio(); if (music_playing) { if (music_playing->interface->Pause) { music_playing->interface->Pause(music_playing->context); } } music_active = SDL_FALSE; Mix_UnlockAudio(); } void Mix_ResumeMusic(void) { Mix_LockAudio(); if (music_playing) { if (music_playing->interface->Resume) { music_playing->interface->Resume(music_playing->context); } } music_active = SDL_TRUE; Mix_UnlockAudio(); } void Mix_RewindMusic(void) { Mix_SetMusicPosition(0.0); } int Mix_PausedMusic(void) { return (music_active == SDL_FALSE); } int Mix_StartTrack(Mix_Music *music, int track) { int result; Mix_LockAudio(); if (music && music->interface->StartTrack) { if (music->interface->Pause) { music->interface->Pause(music->context); } result = music->interface->StartTrack(music->context, track); } else { result = Mix_SetError("That operation is not supported"); } Mix_UnlockAudio(); return result; } int Mix_GetNumTracks(Mix_Music *music) { int result; Mix_LockAudio(); if (music && music->interface->GetNumTracks) { result = music->interface->GetNumTracks(music->context); } else { result = Mix_SetError("That operation is not supported"); } Mix_UnlockAudio(); return result; } /* Check the status of the music */ static SDL_bool music_internal_playing(void) { if (!music_playing) { return SDL_FALSE; } if (music_playing->interface->IsPlaying) { music_playing->playing = music_playing->interface->IsPlaying(music_playing->context); } return music_playing->playing; } int Mix_PlayingMusic(void) { SDL_bool playing; Mix_LockAudio(); playing = music_internal_playing(); Mix_UnlockAudio(); return playing ? 1 : 0; } /* Set the external music playback command */ int Mix_SetMusicCMD(const char *command) { Mix_HaltMusic(); if (music_cmd) { SDL_free(music_cmd); music_cmd = NULL; } if (command) { size_t length = SDL_strlen(command) + 1; music_cmd = (char *)SDL_malloc(length); if (music_cmd == NULL) { return SDL_OutOfMemory(); } SDL_memcpy(music_cmd, command, length); } return 0; } int Mix_SetSynchroValue(int i) { /* Not supported by any players at this time */ (void) i; return -1; } int Mix_GetSynchroValue(void) { /* Not supported by any players at this time */ return -1; } /* Uninitialize the music interfaces */ void close_music(void) { size_t i; Mix_HaltMusic(); for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (!interface || !interface->opened) { continue; } if (interface->Close) { interface->Close(); } interface->opened = SDL_FALSE; } if (soundfont_paths) { SDL_free(soundfont_paths); soundfont_paths = NULL; } /* rcg06042009 report available decoders at runtime. */ if (music_decoders) { SDL_free((void *)music_decoders); music_decoders = NULL; } num_decoders = 0; ms_per_step = 0; } /* Unload the music interface libraries */ void unload_music(void) { size_t i; for (i = 0; i < SDL_arraysize(s_music_interfaces); ++i) { Mix_MusicInterface *interface = s_music_interfaces[i]; if (!interface || !interface->loaded) { continue; } if (interface->Unload) { interface->Unload(); } interface->loaded = SDL_FALSE; } } int Mix_SetTimidityCfg(const char *path) { if (timidity_cfg) { SDL_free(timidity_cfg); timidity_cfg = NULL; } if (path && *path) { if (!(timidity_cfg = SDL_strdup(path))) { Mix_SetError("Insufficient memory to set Timidity cfg file"); return 0; } } return 1; } const char* Mix_GetTimidityCfg(void) { return timidity_cfg; } int Mix_SetSoundFonts(const char *paths) { if (soundfont_paths) { SDL_free(soundfont_paths); soundfont_paths = NULL; } if (paths) { if (!(soundfont_paths = SDL_strdup(paths))) { Mix_SetError("Insufficient memory to set SoundFonts"); return 0; } } return 1; } const char* Mix_GetSoundFonts(void) { const char *env_paths = SDL_getenv("SDL_SOUNDFONTS"); SDL_bool force_env_paths = SDL_GetHintBoolean("SDL_FORCE_SOUNDFONTS", SDL_FALSE); if (force_env_paths && (!env_paths || !*env_paths)) { force_env_paths = SDL_FALSE; } if (soundfont_paths && *soundfont_paths && !force_env_paths) { return soundfont_paths; } if (env_paths) { return env_paths; } /* We don't have any sound fonts set programmatically or in the environment Time to start guessing where they might be... */ { static char *s_soundfont_paths[] = { "/usr/share/sounds/sf2/FluidR3_GM.sf2" /* Remember to add ',' here */ }; unsigned i; for (i = 0; i < SDL_arraysize(s_soundfont_paths); ++i) { SDL_RWops *rwops = SDL_RWFromFile(s_soundfont_paths[i], "rb"); if (rwops) { SDL_RWclose(rwops); return s_soundfont_paths[i]; } } } return NULL; } int Mix_EachSoundFont(int (SDLCALL *function)(const char*, void*), void *data) { char *context, *path, *paths; const char* cpaths = Mix_GetSoundFonts(); int soundfonts_found = 0; if (!cpaths) { Mix_SetError("No SoundFonts have been requested"); return 0; } if (!(paths = SDL_strdup(cpaths))) { Mix_SetError("Insufficient memory to iterate over SoundFonts"); return 0; } #if defined(_WIN32) || defined(__OS2__) #define PATHSEP ";" #else #define PATHSEP ":;" #endif for (path = SDL_strtokr(paths, PATHSEP, &context); path; path = SDL_strtokr(NULL, PATHSEP, &context)) { if (!function(path, data)) { continue; } soundfonts_found++; } SDL_free(paths); return (soundfonts_found > 0); } /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/effect_position.c0000644000076500000240000023632414551252471016564 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file by Ryan C. Gordon (icculus@icculus.org) These are some internally supported special effects that use SDL_mixer's effect callback API. They are meant for speed over quality. :) */ #include "SDL_endian.h" #include "SDL_mixer.h" #include "mixer.h" #define MIX_INTERNAL_EFFECT__ #include "effects_internal.h" /* profile code: #include #include struct timeval tv1; struct timeval tv2; gettimeofday(&tv1, NULL); ... do your thing here ... gettimeofday(&tv2, NULL); printf("%ld\n", tv2.tv_usec - tv1.tv_usec); */ /* * Positional effects...panning, distance attenuation, etc. */ typedef struct _Eff_positionargs { volatile float left_f; volatile float right_f; volatile Uint8 left_u8; volatile Uint8 right_u8; volatile float left_rear_f; volatile float right_rear_f; volatile float center_f; volatile float lfe_f; volatile Uint8 left_rear_u8; volatile Uint8 right_rear_u8; volatile Uint8 center_u8; volatile Uint8 lfe_u8; volatile float distance_f; volatile Uint8 distance_u8; volatile Sint16 room_angle; volatile int in_use; volatile int channels; } position_args; static position_args **pos_args_array = NULL; static position_args *pos_args_global = NULL; static int position_channels = 0; void _Eff_PositionDeinit(void) { int i; for (i = 0; i < position_channels; i++) { SDL_free(pos_args_array[i]); } position_channels = 0; SDL_free(pos_args_global); pos_args_global = NULL; SDL_free(pos_args_array); pos_args_array = NULL; } /* This just frees up the callback-specific data. */ static void SDLCALL _Eff_PositionDone(int channel, void *udata) { (void)udata; if (channel < 0) { if (pos_args_global != NULL) { SDL_free(pos_args_global); pos_args_global = NULL; } } else if (pos_args_array[channel] != NULL) { SDL_free(pos_args_array[channel]); pos_args_array[channel] = NULL; } } static void SDLCALL _Eff_position_u8(int chan, void *stream, int len, void *udata) { Uint8 *ptr = (Uint8 *) stream; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Uint16) != 0) { *ptr = (Uint8) (((float) *ptr) * dist_f); ptr++; len--; } if (((position_args *)udata)->room_angle == 180) { for (i = 0; i < len; i += sizeof(Uint8) * 2) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * right_f) * dist_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * left_f) * dist_f) + 128); ptr++; } } else { for (i = 0; i < len; i += sizeof(Uint8) * 2) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * left_f) * dist_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * right_f) * dist_f) + 128); ptr++; } } } static void SDLCALL _Eff_position_u8_c4(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Uint8 *ptr = (Uint8 *) stream; int i; (void)chan; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Uint16) != 0) { *ptr = (Uint8) (((float) *ptr) * args->distance_f); ptr++; len--; } if (args->room_angle == 0) { for (i = 0; i < len; i += sizeof(Uint8) * 4) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 90) { for (i = 0; i < len; i += sizeof(Uint8) * 4) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 180) { for (i = 0; i < len; i += sizeof(Uint8) * 4) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 270) { for (i = 0; i < len; i += sizeof(Uint8) * 4) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; } } } static void SDLCALL _Eff_position_u8_c6(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Uint8 *ptr = (Uint8 *) stream; int i; (void)chan; (void)len; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Uint16) != 0) { *ptr = (Uint8) (((float) *ptr) * args->distance_f); ptr++; len--; } if (args->room_angle == 0) { for (i = 0; i < len; i += sizeof(Uint8) * 6) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->center_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->lfe_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 90) { for (i = 0; i < len; i += sizeof(Uint8) * 6) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f/2) + 128) + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f/2) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->lfe_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 180) { for (i = 0; i < len; i += sizeof(Uint8) * 6) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f/2) + 128) + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f/2) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->lfe_f) * args->distance_f) + 128); ptr++; } } else if (args->room_angle == 270) { for (i = 0; i < len; i += sizeof(Uint8) * 6) { /* must adjust the sample so that 0 is the center */ *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_rear_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->right_f) * args->distance_f) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_f) * args->distance_f/2) + 128) + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->left_rear_f) * args->distance_f/2) + 128); ptr++; *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) * args->lfe_f) * args->distance_f) + 128); ptr++; } } } /* * This one runs about 10.1 times faster than the non-table version, with * no loss in quality. It does, however, require 64k of memory for the * lookup table. Also, this will only update position information once per * call; the non-table version always checks the arguments for each sample, * in case the user has called Mix_SetPanning() or whatnot again while this * callback is running. */ static void SDLCALL _Eff_position_table_u8(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Uint8 *ptr = (Uint8 *) stream; Uint32 *p; int i; Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8); Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8); Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8); (void)chan; if (args->room_angle == 180) { Uint8 *temp = l; l = r; r = temp; } /* * if there's only a mono channnel, then l[] and r[] are always * volume 255, and are therefore throwaways. Still, we have to * be sure not to overrun the audio buffer... */ while (len % (int)sizeof(Uint32) != 0) { *ptr = d[l[*ptr]]; ptr++; if (args->channels > 1) { *ptr = d[r[*ptr]]; ptr++; } len -= args->channels; } p = (Uint32 *) ptr; for (i = 0; i < len; i += sizeof(Uint32)) { #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) *p = (d[l[(*p & 0xFF000000) >> 24]] << 24) | (d[r[(*p & 0x00FF0000) >> 16]] << 16) | (d[l[(*p & 0x0000FF00) >> 8]] << 8) | (d[r[(*p & 0x000000FF) ]] ) ; #else *p = (d[r[(*p & 0xFF000000) >> 24]] << 24) | (d[l[(*p & 0x00FF0000) >> 16]] << 16) | (d[r[(*p & 0x0000FF00) >> 8]] << 8) | (d[l[(*p & 0x000000FF) ]] ) ; #endif ++p; } } static void SDLCALL _Eff_position_s8(int chan, void *stream, int len, void *udata) { Sint8 *ptr = (Sint8 *) stream; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Sint16) != 0) { *ptr = (Sint8) (((float) *ptr) * dist_f); ptr++; len--; } if (((position_args *)udata)->room_angle == 180) { for (i = 0; i < len; i += sizeof(Sint8) * 2) { *ptr = (Sint8)((((float) *ptr) * right_f) * dist_f); ptr++; *ptr = (Sint8)((((float) *ptr) * left_f) * dist_f); ptr++; } } else { for (i = 0; i < len; i += sizeof(Sint8) * 2) { *ptr = (Sint8)((((float) *ptr) * left_f) * dist_f); ptr++; *ptr = (Sint8)((((float) *ptr) * right_f) * dist_f); ptr++; } } } static void SDLCALL _Eff_position_s8_c4(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Sint8 *ptr = (Sint8 *) stream; int i; (void)chan; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Sint16) != 0) { *ptr = (Sint8) (((float) *ptr) * args->distance_f); ptr++; len--; } for (i = 0; i < len; i += sizeof(Sint8) * 4) { switch (args->room_angle) { case 0: *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; break; case 90: *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; break; case 180: *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; break; case 270: *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; break; } } } static void SDLCALL _Eff_position_s8_c6(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Sint8 *ptr = (Sint8 *) stream; int i; (void)chan; /* * if there's only a mono channnel (the only way we wouldn't have * a len divisible by 2 here), then left_f and right_f are always * 1.0, and are therefore throwaways. */ if (len % (int)sizeof(Sint16) != 0) { *ptr = (Sint8) (((float) *ptr) * args->distance_f); ptr++; len--; } for (i = 0; i < len; i += sizeof(Sint8) * 6) { switch (args->room_angle) { case 0: *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->center_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; break; case 90: *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + (Sint8)((((float) *ptr) * args->right_f) * args->distance_f / 2); ptr++; *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; break; case 180: *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; break; case 270: *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f / 2) + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; break; } } } /* * This one runs about 10.1 times faster than the non-table version, with * no loss in quality. It does, however, require 64k of memory for the * lookup table. Also, this will only update position information once per * call; the non-table version always checks the arguments for each sample, * in case the user has called Mix_SetPanning() or whatnot again while this * callback is running. */ static void SDLCALL _Eff_position_table_s8(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Sint8 *ptr = (Sint8 *) stream; Uint32 *p; int i; Sint8 *l = ((Sint8 *) _Eff_volume_table) + (256 * args->left_u8); Sint8 *r = ((Sint8 *) _Eff_volume_table) + (256 * args->right_u8); Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8); (void)chan; if (args->room_angle == 180) { Sint8 *temp = l; l = r; r = temp; } while (len % (int)sizeof(Uint32) != 0) { *ptr = d[l[*ptr]]; ptr++; if (args->channels > 1) { *ptr = d[r[*ptr]]; ptr++; } len -= args->channels; } p = (Uint32 *) ptr; for (i = 0; i < len; i += sizeof(Uint32)) { #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) *p = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | (d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | (d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | (d[r[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; #else *p = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | (d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | (d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | (d[l[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; #endif ++p; } } /* !!! FIXME : Optimize the code for 16-bit samples? */ static void SDLCALL _Eff_position_u16lsb(int chan, void *stream, int len, void *udata) { Uint16 *ptr = (Uint16 *) stream; const SDL_bool opp = ((position_args *)udata)->room_angle == 180 ? SDL_TRUE : SDL_FALSE; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; for (i = 0; i < len; i += sizeof(Uint16) * 2) { Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * left_f) * dist_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * right_f) * dist_f) + 32768); if (opp) { *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); } else { *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); } } } static void SDLCALL _Eff_position_u16lsb_c4(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Uint16 *ptr = (Uint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Uint16) * 4) { Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) * args->distance_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) * args->distance_f) + 32768); Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) * args->distance_f) + 32768); Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) * args->distance_f) + 32768); switch (args->room_angle) { case 0: *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); break; case 90: *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); break; case 180: *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); break; case 270: *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); break; } } } static void SDLCALL _Eff_position_u16lsb_c6(int chan, void *stream, int len, void *udata) { volatile position_args *args = (volatile position_args *) udata; Uint16 *ptr = (Uint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Uint16) * 6) { Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); Sint16 sampce = (Sint16) (SDL_SwapLE16(*(ptr+4)) - 32768); Sint16 sampwf = (Sint16) (SDL_SwapLE16(*(ptr+5)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) * args->distance_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) * args->distance_f) + 32768); Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) * args->distance_f) + 32768); Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) * args->distance_f) + 32768); Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) * args->distance_f) + 32768); Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) * args->distance_f) + 32768); switch (args->room_angle) { case 0: *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swapce); *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); break; case 90: *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swapr)/2 + (Uint16) SDL_SwapLE16(swaprr)/2; *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); break; case 180: *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); break; case 270: *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl); *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); *(ptr++) = (Uint16) SDL_SwapLE16(swapr); *(ptr++) = (Uint16) SDL_SwapLE16(swapl)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); break; } } } static void SDLCALL _Eff_position_s16lsb(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 2 channels. */ Sint16 *ptr = (Sint16 *) stream; const SDL_bool opp = ((position_args *)udata)->room_angle == 180 ? SDL_TRUE : SDL_FALSE; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; #if 0 if (len % (int)(sizeof(Sint16) * 2)) { fprintf(stderr,"Not an even number of frames! len=%d\n", len); return; } #endif for (i = 0; i < len; i += sizeof(Sint16) * 2) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * left_f) * dist_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * right_f) * dist_f); if (opp) { *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); } else { *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); } } } static void SDLCALL _Eff_position_s16lsb_c4(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint16 *ptr = (Sint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 4) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * args->left_f) * args->distance_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * args->right_f) * args->distance_f); Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * args->left_rear_f) * args->distance_f); Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * args->right_rear_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); break; case 90: *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); break; case 180: *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); break; case 270: *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); break; } } } static void SDLCALL _Eff_position_s16lsb_c6(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint16 *ptr = (Sint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 6) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * args->left_f) * args->distance_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * args->right_f) * args->distance_f); Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+3))) * args->right_rear_f) * args->distance_f); Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+4))) * args->center_f) * args->distance_f); Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+5))) * args->lfe_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swapce); *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); break; case 90: *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swapr)/2 + (Sint16) SDL_SwapLE16(swaprr)/2; *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); break; case 180: *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); break; case 270: *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl); *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); *(ptr++) = (Sint16) SDL_SwapLE16(swapr); *(ptr++) = (Sint16) SDL_SwapLE16(swapl)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); break; } } } static void SDLCALL _Eff_position_u16msb(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 2 channels. */ Uint16 *ptr = (Uint16 *) stream; const SDL_bool opp = ((position_args *)udata)->room_angle == 180 ? SDL_TRUE : SDL_FALSE; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 2) { Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * left_f) * dist_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * right_f) * dist_f) + 32768); if (opp) { *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); } else { *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); } } } static void SDLCALL _Eff_position_u16msb_c4(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; Uint16 *ptr = (Uint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 4) { Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) * args->distance_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) * args->distance_f) + 32768); Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) * args->distance_f) + 32768); Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) * args->distance_f) + 32768); switch (args->room_angle) { case 0: *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); break; case 90: *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); break; case 180: *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); break; case 270: *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); break; } } } static void SDLCALL _Eff_position_u16msb_c6(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; Uint16 *ptr = (Uint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 6) { Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); Sint16 sampce = (Sint16) (SDL_SwapBE16(*(ptr+4)) - 32768); Sint16 sampwf = (Sint16) (SDL_SwapBE16(*(ptr+5)) - 32768); Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) * args->distance_f) + 32768); Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) * args->distance_f) + 32768); Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) * args->distance_f) + 32768); Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) * args->distance_f) + 32768); Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) * args->distance_f) + 32768); Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) * args->distance_f) + 32768); switch (args->room_angle) { case 0: *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swapce); *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); break; case 90: *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swapr)/2 + (Uint16) SDL_SwapBE16(swaprr)/2; *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); break; case 180: *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); break; case 270: *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl); *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); *(ptr++) = (Uint16) SDL_SwapBE16(swapr); *(ptr++) = (Uint16) SDL_SwapBE16(swapl)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); break; } } } static void SDLCALL _Eff_position_s16msb(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 2 channels. */ Sint16 *ptr = (Sint16 *) stream; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 2) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * left_f) * dist_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * right_f) * dist_f); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); } } static void SDLCALL _Eff_position_s16msb_c4(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint16 *ptr = (Sint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 4) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * args->left_f) * args->distance_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * args->right_f) * args->distance_f); Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * args->right_rear_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); break; case 90: *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); break; case 180: *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); break; case 270: *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); break; } } } static void SDLCALL _Eff_position_s16msb_c6(int chan, void *stream, int len, void *udata) { /* 16 signed bits (lsb) * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint16 *ptr = (Sint16 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint16) * 6) { Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * args->left_f) * args->distance_f); Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * args->right_f) * args->distance_f); Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * args->right_rear_f) * args->distance_f); Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+4))) * args->center_f) * args->distance_f); Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+5))) * args->lfe_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swapce); *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); break; case 90: *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swapr)/2 + (Sint16) SDL_SwapBE16(swaprr)/2; *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); break; case 180: *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); break; case 270: *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl); *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); *(ptr++) = (Sint16) SDL_SwapBE16(swapr); *(ptr++) = (Sint16) SDL_SwapBE16(swapl)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); break; } } } static void SDLCALL _Eff_position_s32lsb(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 2 channels. */ Sint32 *ptr = (Sint32 *) stream; const SDL_bool opp = ((position_args *)udata)->room_angle == 180 ? SDL_TRUE : SDL_FALSE; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; #if 0 if (len % (int)(sizeof(Sint32) * 2)) { fprintf(stderr,"Not an even number of frames! len=%d\n", len); return; } #endif for (i = 0; i < len; i += sizeof(Sint32) * 2) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+0))) * left_f) * dist_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+1))) * right_f) * dist_f); if (opp) { *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); } else { *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); } } } static void SDLCALL _Eff_position_s32lsb_c4(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint32 *ptr = (Sint32 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint32) * 4) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+0))) * args->left_f) * args->distance_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+1))) * args->right_f) * args->distance_f); Sint32 swaplr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+1))) * args->left_rear_f) * args->distance_f); Sint32 swaprr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+2))) * args->right_rear_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); break; case 90: *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); break; case 180: *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); break; case 270: *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); break; } } } static void SDLCALL _Eff_position_s32lsb_c6(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint32 *ptr = (Sint32 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint32) * 6) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+0))) * args->left_f) * args->distance_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+1))) * args->right_f) * args->distance_f); Sint32 swaplr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint32 swaprr = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+3))) * args->right_rear_f) * args->distance_f); Sint32 swapce = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+4))) * args->center_f) * args->distance_f); Sint32 swapwf = (Sint32) ((((float) (Sint32) SDL_SwapLE32(*(ptr+5))) * args->lfe_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swapce); *(ptr++) = (Sint32) SDL_SwapLE32(swapwf); break; case 90: *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swapr)/2 + (Sint32) SDL_SwapLE32(swaprr)/2; *(ptr++) = (Sint32) SDL_SwapLE32(swapwf); break; case 180: *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr)/2 + (Sint32) SDL_SwapLE32(swaplr)/2; *(ptr++) = (Sint32) SDL_SwapLE32(swapwf); break; case 270: *(ptr++) = (Sint32) SDL_SwapLE32(swaplr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl); *(ptr++) = (Sint32) SDL_SwapLE32(swaprr); *(ptr++) = (Sint32) SDL_SwapLE32(swapr); *(ptr++) = (Sint32) SDL_SwapLE32(swapl)/2 + (Sint32) SDL_SwapLE32(swaplr)/2; *(ptr++) = (Sint32) SDL_SwapLE32(swapwf); break; } } } static void SDLCALL _Eff_position_s32msb(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 2 channels. */ Sint32 *ptr = (Sint32 *) stream; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint32) * 2) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+0))) * left_f) * dist_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+1))) * right_f) * dist_f); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); } } static void SDLCALL _Eff_position_s32msb_c4(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint32 *ptr = (Sint32 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint32) * 4) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+0))) * args->left_f) * args->distance_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+1))) * args->right_f) * args->distance_f); Sint32 swaplr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint32 swaprr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+3))) * args->right_rear_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); break; case 90: *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); break; case 180: *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); break; case 270: *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); break; } } } static void SDLCALL _Eff_position_s32msb_c6(int chan, void *stream, int len, void *udata) { /* 32 signed bits (lsb) * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; Sint32 *ptr = (Sint32 *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(Sint32) * 6) { Sint32 swapl = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+0))) * args->left_f) * args->distance_f); Sint32 swapr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+1))) * args->right_f) * args->distance_f); Sint32 swaplr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+2))) * args->left_rear_f) * args->distance_f); Sint32 swaprr = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+3))) * args->right_rear_f) * args->distance_f); Sint32 swapce = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+4))) * args->center_f) * args->distance_f); Sint32 swapwf = (Sint32) ((((float) (Sint32) SDL_SwapBE32(*(ptr+5))) * args->lfe_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swapce); *(ptr++) = (Sint32) SDL_SwapBE32(swapwf); break; case 90: *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swapr)/2 + (Sint32) SDL_SwapBE32(swaprr)/2; *(ptr++) = (Sint32) SDL_SwapBE32(swapwf); break; case 180: *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr)/2 + (Sint32) SDL_SwapBE32(swaplr)/2; *(ptr++) = (Sint32) SDL_SwapBE32(swapwf); break; case 270: *(ptr++) = (Sint32) SDL_SwapBE32(swaplr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl); *(ptr++) = (Sint32) SDL_SwapBE32(swaprr); *(ptr++) = (Sint32) SDL_SwapBE32(swapr); *(ptr++) = (Sint32) SDL_SwapBE32(swapl)/2 + (Sint32) SDL_SwapBE32(swaplr)/2; *(ptr++) = (Sint32) SDL_SwapBE32(swapwf); break; } } } static void SDLCALL _Eff_position_f32sys(int chan, void *stream, int len, void *udata) { /* float * 2 channels. */ float *ptr = (float *) stream; const float dist_f = ((position_args *)udata)->distance_f; const float left_f = ((position_args *)udata)->left_f; const float right_f = ((position_args *)udata)->right_f; int i; (void)chan; for (i = 0; i < len; i += sizeof(float) * 2) { float swapl = ((*(ptr+0) * left_f) * dist_f); float swapr = ((*(ptr+1) * right_f) * dist_f); *(ptr++) = swapl; *(ptr++) = swapr; } } static void SDLCALL _Eff_position_f32sys_c4(int chan, void *stream, int len, void *udata) { /* float * 4 channels. */ volatile position_args *args = (volatile position_args *) udata; float *ptr = (float *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(float) * 4) { float swapl = ((*(ptr+0) * args->left_f) * args->distance_f); float swapr = ((*(ptr+1) * args->right_f) * args->distance_f); float swaplr = ((*(ptr+2) * args->left_rear_f) * args->distance_f); float swaprr = ((*(ptr+3) * args->right_rear_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = swapl; *(ptr++) = swapr; *(ptr++) = swaplr; *(ptr++) = swaprr; break; case 90: *(ptr++) = swapr; *(ptr++) = swaprr; *(ptr++) = swapl; *(ptr++) = swaplr; break; case 180: *(ptr++) = swaprr; *(ptr++) = swaplr; *(ptr++) = swapr; *(ptr++) = swapl; break; case 270: *(ptr++) = swaplr; *(ptr++) = swapl; *(ptr++) = swaprr; *(ptr++) = swapr; break; } } } static void SDLCALL _Eff_position_f32sys_c6(int chan, void *stream, int len, void *udata) { /* float * 6 channels. */ volatile position_args *args = (volatile position_args *) udata; float *ptr = (float *) stream; int i; (void)chan; for (i = 0; i < len; i += sizeof(float) * 6) { float swapl = ((*(ptr+0) * args->left_f) * args->distance_f); float swapr = ((*(ptr+1) * args->right_f) * args->distance_f); float swaplr = ((*(ptr+2) * args->left_rear_f) * args->distance_f); float swaprr = ((*(ptr+3) * args->right_rear_f) * args->distance_f); float swapce = ((*(ptr+4) * args->center_f) * args->distance_f); float swapwf = ((*(ptr+5) * args->lfe_f) * args->distance_f); switch (args->room_angle) { case 0: *(ptr++) = swapl; *(ptr++) = swapr; *(ptr++) = swaplr; *(ptr++) = swaprr; *(ptr++) = swapce; *(ptr++) = swapwf; break; case 90: *(ptr++) = swapr; *(ptr++) = swaprr; *(ptr++) = swapl; *(ptr++) = swaplr; *(ptr++) = swapr/2.0f + swaprr/2.0f; *(ptr++) = swapwf; break; case 180: *(ptr++) = swaprr; *(ptr++) = swaplr; *(ptr++) = swapr; *(ptr++) = swapl; *(ptr++) = swaprr/2.0f + swaplr/2.0f; *(ptr++) = swapwf; break; case 270: *(ptr++) = swaplr; *(ptr++) = swapl; *(ptr++) = swaprr; *(ptr++) = swapr; *(ptr++) = swapl/2.0f + swaplr/2.0f; *(ptr++) = swapwf; break; } } } static void init_position_args(position_args *args) { SDL_memset(args, '\0', sizeof(position_args)); args->in_use = 0; args->room_angle = 0; args->left_u8 = args->right_u8 = args->distance_u8 = 255; args->left_f = args->right_f = args->distance_f = 1.0f; args->left_rear_u8 = args->right_rear_u8 = args->center_u8 = args->lfe_u8 = 255; args->left_rear_f = args->right_rear_f = args->center_f = args->lfe_f = 1.0f; Mix_QuerySpec(NULL, NULL, (int *) &args->channels); } static position_args *get_position_arg(int channel) { void *rc; int i; if (channel < 0) { if (pos_args_global == NULL) { pos_args_global = SDL_malloc(sizeof(position_args)); if (pos_args_global == NULL) { Mix_OutOfMemory(); return NULL; } init_position_args(pos_args_global); } return pos_args_global; } if (channel >= position_channels) { rc = SDL_realloc(pos_args_array, (size_t)(channel + 1) * sizeof(position_args *)); if (rc == NULL) { Mix_OutOfMemory(); return NULL; } pos_args_array = (position_args **) rc; for (i = position_channels; i <= channel; i++) { pos_args_array[i] = NULL; } position_channels = channel + 1; } if (pos_args_array[channel] == NULL) { pos_args_array[channel] = (position_args *)SDL_malloc(sizeof(position_args)); if (pos_args_array[channel] == NULL) { Mix_OutOfMemory(); return NULL; } init_position_args(pos_args_array[channel]); } return pos_args_array[channel]; } static Mix_EffectFunc_t get_position_effect_func(Uint16 format, int channels) { Mix_EffectFunc_t f = NULL; switch (format) { case AUDIO_U8: switch (channels) { case 1: case 2: f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 : _Eff_position_u8; break; case 4: f = _Eff_position_u8_c4; break; case 6: f = _Eff_position_u8_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_S8: switch (channels) { case 1: case 2: f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 : _Eff_position_s8; break; case 4: f = _Eff_position_s8_c4; break; case 6: f = _Eff_position_s8_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_U16LSB: switch (channels) { case 1: case 2: f = _Eff_position_u16lsb; break; case 4: f = _Eff_position_u16lsb_c4; break; case 6: f = _Eff_position_u16lsb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_S16LSB: switch (channels) { case 1: case 2: f = _Eff_position_s16lsb; break; case 4: f = _Eff_position_s16lsb_c4; break; case 6: f = _Eff_position_s16lsb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_U16MSB: switch (channels) { case 1: case 2: f = _Eff_position_u16msb; break; case 4: f = _Eff_position_u16msb_c4; break; case 6: f = _Eff_position_u16msb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_S16MSB: switch (channels) { case 1: case 2: f = _Eff_position_s16msb; break; case 4: f = _Eff_position_s16msb_c4; break; case 6: f = _Eff_position_s16msb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_S32MSB: switch (channels) { case 1: case 2: f = _Eff_position_s32msb; break; case 4: f = _Eff_position_s32msb_c4; break; case 6: f = _Eff_position_s32msb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_S32LSB: switch (channels) { case 1: case 2: f = _Eff_position_s32lsb; break; case 4: f = _Eff_position_s32lsb_c4; break; case 6: f = _Eff_position_s32lsb_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; case AUDIO_F32SYS: switch (channels) { case 1: case 2: f = _Eff_position_f32sys; break; case 4: f = _Eff_position_f32sys_c4; break; case 6: f = _Eff_position_f32sys_c6; break; default: Mix_SetError("Unsupported audio channels"); break; } break; default: Mix_SetError("Unsupported audio format"); break; } return f; } static Uint8 speaker_amplitude[6]; static void set_amplitudes(int channels, int angle, int room_angle) { int left = 255, right = 255; int left_rear = 255, right_rear = 255, center = 255; /* our only caller Mix_SetPosition() already makes angle between 0 and 359. */ if (channels == 2) { /* * We only attenuate by position if the angle falls on the far side * of center; That is, an angle that's due north would not attenuate * either channel. Due west attenuates the right channel to 0.0, and * due east attenuates the left channel to 0.0. Slightly east of * center attenuates the left channel a little, and the right channel * not at all. I think of this as occlusion by one's own head. :) * * ...so, we split our angle circle into four quadrants... */ if (angle < 90) { left = 255 - ((int) (255.0f * (((float) angle) / 89.0f))); } else if (angle < 180) { left = (int) (255.0f * (((float) (angle - 90)) / 89.0f)); } else if (angle < 270) { right = 255 - ((int) (255.0f * (((float) (angle - 180)) / 89.0f))); } else { right = (int) (255.0f * (((float) (angle - 270)) / 89.0f)); } } if (channels == 4 || channels == 6) { /* * An angle that's due north does not attenuate the center channel. * An angle in the first quadrant, 0-90, does not attenuate the RF. * * ...so, we split our angle circle into 8 ... * * CE * 0 * LF | RF * | * 270<-------|----------->90 * | * LR | RR * 180 * */ if (angle < 45) { left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); left_rear = 255 - ((int) (255.0f * (((float) (angle + 45)) / 89.0f))); right_rear = 255 - ((int) (255.0f * (((float) (90 - angle)) / 179.0f))); } else if (angle < 90) { center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); left_rear = 255 - ((int) (255.0f * (((float) (135 - angle)) / 89.0f))); right_rear = ((int) (255.0f * (((float) (90 + angle)) / 179.0f))); } else if (angle < 135) { center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); left = 255 - ((int) (255.0f * (((float) (angle - 45)) / 89.0f))); right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); } else if (angle < 180) { center = 255 - ((int) (255.0f * (((float) (angle - 90)) / 89.0f))); left = 255 - ((int) (255.0f * (((float) (225 - angle)) / 89.0f))); right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); } else if (angle < 225) { center = 255 - ((int) (255.0f * (((float) (270 - angle)) / 89.0f))); left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); right = 255 - ((int) (255.0f * (((float) (angle - 135)) / 89.0f))); right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); } else if (angle < 270) { center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); right = 255 - ((int) (255.0f * (((float) (315 - angle)) / 89.0f))); right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); } else if (angle < 315) { center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); right_rear = 255 - ((int) (255.0f * (((float) (angle - 225)) / 89.0f))); } else { right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); right_rear = 255 - ((int) (255.0f * (((float) (405 - angle)) / 89.0f))); } } if (left < 0) left = 0; if (left > 255) left = 255; if (right < 0) right = 0; if (right > 255) right = 255; if (left_rear < 0) left_rear = 0; if (left_rear > 255) left_rear = 255; if (right_rear < 0) right_rear = 0; if (right_rear > 255) right_rear = 255; if (center < 0) center = 0; if (center > 255) center = 255; if (room_angle == 90) { speaker_amplitude[0] = (Uint8)left_rear; speaker_amplitude[1] = (Uint8)left; speaker_amplitude[2] = (Uint8)right_rear; speaker_amplitude[3] = (Uint8)right; } else if (room_angle == 180) { if (channels == 2) { speaker_amplitude[0] = (Uint8)right; speaker_amplitude[1] = (Uint8)left; } else { speaker_amplitude[0] = (Uint8)right_rear; speaker_amplitude[1] = (Uint8)left_rear; speaker_amplitude[2] = (Uint8)right; speaker_amplitude[3] = (Uint8)left; } } else if (room_angle == 270) { speaker_amplitude[0] = (Uint8)right; speaker_amplitude[1] = (Uint8)right_rear; speaker_amplitude[2] = (Uint8)left; speaker_amplitude[3] = (Uint8)left_rear; } else { speaker_amplitude[0] = (Uint8)left; speaker_amplitude[1] = (Uint8)right; speaker_amplitude[2] = (Uint8)left_rear; speaker_amplitude[3] = (Uint8)right_rear; } speaker_amplitude[4] = (Uint8)center; speaker_amplitude[5] = 255; } int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance); int Mix_SetPanning(int channel, Uint8 left, Uint8 right) { Mix_EffectFunc_t f = NULL; int channels; Uint16 format; position_args *args = NULL; int retval = 1; Mix_QuerySpec(NULL, &format, &channels); if (channels != 2 && channels != 4 && channels != 6) /* it's a no-op; we call that successful. */ return 1; if (channels > 2) { /* left = right = 255 => angle = 0, to unregister effect as when channels = 2 */ /* left = 255 => angle = -90; left = 0 => angle = +89 */ int angle = 0; if ((left != 255) || (right != 255)) { angle = (int)left; angle = 127 - angle; angle = -angle; angle = angle * 90 / 128; /* Make it larger for more effect? */ } return Mix_SetPosition(channel, angle, 0); } f = get_position_effect_func(format, channels); if (f == NULL) return 0; Mix_LockAudio(); args = get_position_arg(channel); if (!args) { Mix_UnlockAudio(); return 0; } /* it's a no-op; unregister the effect, if it's registered. */ if ((args->distance_u8 == 255) && (left == 255) && (right == 255)) { if (args->in_use) { retval = _Mix_UnregisterEffect_locked(channel, f); Mix_UnlockAudio(); return retval; } else { Mix_UnlockAudio(); return 1; } } args->left_u8 = left; args->left_f = ((float) left) / 255.0f; args->right_u8 = right; args->right_f = ((float) right) / 255.0f; args->room_angle = 0; if (!args->in_use) { args->in_use = 1; retval=_Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void*)args); } Mix_UnlockAudio(); return retval; } int Mix_SetDistance(int channel, Uint8 distance) { Mix_EffectFunc_t f = NULL; Uint16 format; position_args *args = NULL; int channels; int retval = 1; Mix_QuerySpec(NULL, &format, &channels); f = get_position_effect_func(format, channels); if (f == NULL) return 0; Mix_LockAudio(); args = get_position_arg(channel); if (!args) { Mix_UnlockAudio(); return 0; } distance = 255 - distance; /* flip it to our scale. */ /* it's a no-op; unregister the effect, if it's registered. */ if ((distance == 255) && (args->left_u8 == 255) && (args->right_u8 == 255)) { if (args->in_use) { retval = _Mix_UnregisterEffect_locked(channel, f); Mix_UnlockAudio(); return retval; } else { Mix_UnlockAudio(); return 1; } } args->distance_u8 = distance; args->distance_f = ((float) distance) / 255.0f; if (!args->in_use) { args->in_use = 1; retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); } Mix_UnlockAudio(); return retval; } int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance) { Mix_EffectFunc_t f = NULL; Uint16 format; int channels; position_args *args = NULL; Sint16 room_angle = 0; int retval = 1; Mix_QuerySpec(NULL, &format, &channels); f = get_position_effect_func(format, channels); if (f == NULL) return 0; /* make angle between 0 and 359. */ angle %= 360; if (angle < 0) angle += 360; Mix_LockAudio(); args = get_position_arg(channel); if (!args) { Mix_UnlockAudio(); return 0; } /* it's a no-op; unregister the effect, if it's registered. */ if ((!distance) && (!angle)) { if (args->in_use) { retval = _Mix_UnregisterEffect_locked(channel, f); Mix_UnlockAudio(); return retval; } else { Mix_UnlockAudio(); return 1; } } if (channels == 2) { if (angle > 180) room_angle = 180; /* exchange left and right channels */ else room_angle = 0; } if (channels == 4 || channels == 6) { if (angle > 315) room_angle = 0; else if (angle > 225) room_angle = 270; else if (angle > 135) room_angle = 180; else if (angle > 45) room_angle = 90; else room_angle = 0; } distance = 255 - distance; /* flip it to scale Mix_SetDistance() uses. */ set_amplitudes(channels, angle, room_angle); args->left_u8 = speaker_amplitude[0]; args->left_f = ((float) speaker_amplitude[0]) / 255.0f; args->right_u8 = speaker_amplitude[1]; args->right_f = ((float) speaker_amplitude[1]) / 255.0f; args->left_rear_u8 = speaker_amplitude[2]; args->left_rear_f = ((float) speaker_amplitude[2]) / 255.0f; args->right_rear_u8 = speaker_amplitude[3]; args->right_rear_f = ((float) speaker_amplitude[3]) / 255.0f; args->center_u8 = speaker_amplitude[4]; args->center_f = ((float) speaker_amplitude[4]) / 255.0f; args->lfe_u8 = speaker_amplitude[5]; args->lfe_f = ((float) speaker_amplitude[5]) / 255.0f; args->distance_u8 = distance; args->distance_f = ((float) distance) / 255.0f; args->room_angle = room_angle; if (!args->in_use) { args->in_use = 1; retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); } Mix_UnlockAudio(); return retval; } /* end of effects_position.c ... */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/effects_internal.c0000644000076500000240000000647714551252471016723 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file by Ryan C. Gordon (icculus@icculus.org) These are some helper functions for the internal mixer special effects. */ /* ------ These are used internally only. Don't touch. ------ */ #include "SDL_mixer.h" #define MIX_INTERNAL_EFFECT__ #include "effects_internal.h" /* Should we favor speed over memory usage and/or quality of output? */ int _Mix_effects_max_speed = 0; void _Mix_InitEffects(void) { _Mix_effects_max_speed = (SDL_getenv(MIX_EFFECTSMAXSPEED) != NULL); } void _Mix_DeinitEffects(void) { _Eff_PositionDeinit(); } void *_Eff_volume_table = NULL; /* Build the volume table for Uint8-format samples. * * Each column of the table is a possible sample, while each row of the * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full * volume. So _Eff_volume_table[128][mysample] would be the value of * mysample, at half volume. */ void *_Eff_build_volume_table_u8(void) { int volume; int sample; Uint8 *rc; if (!_Mix_effects_max_speed) { return NULL; } if (!_Eff_volume_table) { rc = SDL_malloc(256 * 256); if (rc) { _Eff_volume_table = (void *) rc; for (volume = 0; volume < 256; volume++) { for (sample = -128; sample < 128; sample ++) { *rc = (Uint8)(((float) sample) * ((float) volume / 255.0f)) + 128; rc++; } } } } return _Eff_volume_table; } /* Build the volume table for Sint8-format samples. * * Each column of the table is a possible sample, while each row of the * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full * volume. So _Eff_volume_table[128][mysample+128] would be the value of * mysample, at half volume. */ void *_Eff_build_volume_table_s8(void) { int volume; int sample; Sint8 *rc; if (!_Eff_volume_table) { rc = SDL_malloc(256 * 256); if (rc) { _Eff_volume_table = (void *) rc; for (volume = 0; volume < 256; volume++) { for (sample = -128; sample < 128; sample ++) { *rc = (Sint8)(((float) sample) * ((float) volume / 255.0f)); rc++; } } } } return _Eff_volume_table; } /* end of effects.c ... */ /* vi: set ts=4 sw=4 expandtab: */ SDL2_mixer-2.8.0/src/effect_stereoreverse.c0000644000076500000240000000744614551252471017616 0ustar valvestaff/* SDL_mixer: An audio mixer library based on the SDL library Copyright (C) 1997-2024 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file by Ryan C. Gordon (icculus@icculus.org) These are some internally supported special effects that use SDL_mixer's effect callback API. They are meant for speed over quality. :) */ #include "SDL_mixer.h" #define MIX_INTERNAL_EFFECT__ #include "effects_internal.h" /* profile code: #include #include struct timeval tv1; struct timeval tv2; gettimeofday(&tv1, NULL); ... do your thing here ... gettimeofday(&tv2, NULL); printf("%ld\n", tv2.tv_usec - tv1.tv_usec); */ /* * Stereo reversal effect...this one's pretty straightforward... */ static void SDLCALL _Eff_reversestereo32(int chan, void *stream, int len, void *udata) { /* 16 bits * 2 channels. */ Uint32 *ptr = (Uint32 *) stream; Uint32 tmp; int i; (void)chan; (void)udata; for (i = 0; i < len; i += 2 * sizeof(Uint32), ptr += 2) { tmp = ptr[0]; ptr[0] = ptr[1]; ptr[1] = tmp; } } static void SDLCALL _Eff_reversestereo16(int chan, void *stream, int len, void *udata) { /* 16 bits * 2 channels. */ Uint32 *ptr = (Uint32 *) stream; int i; (void)chan; (void)udata; for (i = 0; i < len; i += sizeof(Uint32), ptr++) { *ptr = (((*ptr) & 0xFFFF0000) >> 16) | (((*ptr) & 0x0000FFFF) << 16); } } static void SDLCALL _Eff_reversestereo8(int chan, void *stream, int len, void *udata) { /* 8 bits * 2 channels. */ Uint32 *ptr = (Uint32 *) stream; int i; (void)chan; (void)udata; /* get the last two bytes if len is not divisible by four... */ if (len % (int)sizeof(Uint32) != 0) { Uint16 *p = (Uint16 *) (((Uint8 *) stream) + (len - 2)); *p = (Uint16)((((*p) & 0xFF00) >> 8) | (((*ptr) & 0x00FF) << 8)); len -= 2; } for (i = 0; i < len; i += sizeof(Uint32), ptr++) { *ptr = (((*ptr) & 0x0000FF00) >> 8) | (((*ptr) & 0x000000FF) << 8) | (((*ptr) & 0xFF000000) >> 8) | (((*ptr) & 0x00FF0000) << 8); } } int Mix_SetReverseStereo(int channel, int flip) { Mix_EffectFunc_t f = NULL; int channels; Uint16 format; Mix_QuerySpec(NULL, &format, &channels); if (channels == 2) { int bits = (format & 0xFF); switch (bits) { case 8: f = _Eff_reversestereo8; break; case 16: f = _Eff_reversestereo16; break; case 32: f = _Eff_reversestereo32; break; default: Mix_SetError("Unsupported audio format"); return 0; } if (!flip) { return Mix_UnregisterEffect(channel, f); } return Mix_RegisterEffect(channel, f, NULL, NULL); } Mix_SetError("Trying to reverse stereo on a non-stereo stream"); return 0; } /* end of effect_stereoreverse.c ... */ /* vi: set ts=4 sw=4 expandtab: */