From 4fd1e33acdf6c44520fdf6ad1272d7d3d9747376 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Sun, 20 Mar 2011 14:34:18 +0000 Subject: [PATCH] Add FluidSynth backend and generic SoundFont functions. No FluidSynth DLL or headers for MSVC in this commit. --- Makefile.in | 2 +- SDL_mixer.h | 14 ++- VisualC/SDL_mixer.dsp | 20 +++- VisualC/SDL_mixer.vcproj | 24 ++++- configure.in | 43 ++++++++ dynamic_fluidsynth.c | 85 +++++++++++++++ dynamic_fluidsynth.h | 55 ++++++++++ fluidsynth.c | 222 +++++++++++++++++++++++++++++++++++++++ fluidsynth.h | 46 ++++++++ mixer.c | 24 +++++ music.c | 192 ++++++++++++++++++++++++++++++--- 11 files changed, 701 insertions(+), 26 deletions(-) create mode 100644 dynamic_fluidsynth.c create mode 100644 dynamic_fluidsynth.h create mode 100644 fluidsynth.c create mode 100644 fluidsynth.h diff --git a/Makefile.in b/Makefile.in index a004973d..c4d9e5f7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -39,7 +39,7 @@ VERSION_OBJECTS = @VERSION_OBJECTS@ PLAYWAVE_OBJECTS = @PLAYWAVE_OBJECTS@ PLAYMUS_OBJECTS = @PLAYMUS_OBJECTS@ -DIST = Android.mk CHANGES COPYING CWProjects.sea.bin MPWmake.sea.bin Makefile.in SDL_mixer.pc.in README SDL_mixer.h SDL_mixer.qpg.in SDL_mixer.spec SDL_mixer.spec.in VisualC.zip Watcom-OS2.zip Xcode.tar.gz acinclude autogen.sh build-scripts configure configure.in dynamic_flac.c dynamic_flac.h dynamic_mod.c dynamic_mod.h dynamic_mp3.c dynamic_mp3.h dynamic_ogg.c dynamic_ogg.h effect_position.c effect_stereoreverse.c effects_internal.c effects_internal.h gcc-fat.sh libmikmod-3.1.12.zip load_aiff.c load_aiff.h load_flac.c load_flac.h load_ogg.c load_ogg.h load_voc.c load_voc.h mixer.c music.c music_cmd.c music_cmd.h music_flac.c music_flac.h music_mad.c music_mad.h music_mod.c music_mod.h music_modplug.c music_modplug.h music_ogg.c music_ogg.h native_midi native_midi_gpl playmus.c playwave.c timidity wavestream.c wavestream.h version.rc +DIST = Android.mk CHANGES COPYING CWProjects.sea.bin MPWmake.sea.bin Makefile.in SDL_mixer.pc.in README SDL_mixer.h SDL_mixer.qpg.in SDL_mixer.spec SDL_mixer.spec.in VisualC.zip Watcom-OS2.zip Xcode.tar.gz acinclude autogen.sh build-scripts configure configure.in dynamic_flac.c dynamic_flac.h dynamic_fluidsynth.c dynamic_fluidsynth.h dynamic_mod.c dynamic_mod.h dynamic_mp3.c dynamic_mp3.h dynamic_ogg.c dynamic_ogg.h effect_position.c effect_stereoreverse.c effects_internal.c effects_internal.h fluidsynth.c fluidsynth.h gcc-fat.sh libmikmod-3.1.12.zip load_aiff.c load_aiff.h load_flac.c load_flac.h load_ogg.c load_ogg.h load_voc.c load_voc.h mixer.c music.c music_cmd.c music_cmd.h music_flac.c music_flac.h music_mad.c music_mad.h music_mod.c music_mod.h music_modplug.c music_modplug.h music_ogg.c music_ogg.h native_midi native_midi_gpl playmus.c playwave.c timidity wavestream.c wavestream.h version.rc LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ diff --git a/SDL_mixer.h b/SDL_mixer.h index a8ec0045..9447acf3 100644 --- a/SDL_mixer.h +++ b/SDL_mixer.h @@ -67,10 +67,11 @@ extern DECLSPEC const SDL_version * SDLCALL Mix_Linked_Version(void); typedef enum { - MIX_INIT_FLAC = 0x00000001, - MIX_INIT_MOD = 0x00000002, - MIX_INIT_MP3 = 0x00000004, - MIX_INIT_OGG = 0x00000008 + MIX_INIT_FLAC = 0x00000001, + MIX_INIT_MOD = 0x00000002, + MIX_INIT_MP3 = 0x00000004, + MIX_INIT_OGG = 0x00000008, + MIX_INIT_FLUIDSYNTH = 0x00000016 } MIX_InitFlags; /* Loads dynamic libraries and prepares them for use. Flags should be @@ -605,6 +606,11 @@ extern DECLSPEC int SDLCALL Mix_SetMusicCMD(const char *command); extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value); extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void); +/* Set/Get/Iterate SoundFonts paths to use by supported MIDI backends */ +extern DECLSPEC int SDLCALL Mix_SetSoundFonts(const char *paths); +extern DECLSPEC const char* SDLCALL Mix_GetSoundFonts(); +extern DECLSPEC int SDLCALL Mix_EachSoundFont(int (*function)(const char*, void*), void *data); + /* Get the Mix_Chunk currently associated with a mixer channel Returns NULL if it's an invalid channel, or there's no chunk associated. */ diff --git a/VisualC/SDL_mixer.dsp b/VisualC/SDL_mixer.dsp index 636443e1..f0ce1ae1 100755 --- a/VisualC/SDL_mixer.dsp +++ b/VisualC/SDL_mixer.dsp @@ -44,7 +44,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\mikmod" /I "..\timidity" /I "..\native_midi" /I "vorbis\include" /I "smpeg\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WAV_MUSIC" /D "MOD_MUSIC" /D MOD_DYNAMIC=\"mikmod.dll\" /D "MID_MUSIC" /D "USE_TIMIDITY_MIDI" /D "USE_NATIVE_MIDI" /D "OGG_MUSIC" /D OGG_DYNAMIC=\"libvorbisfile-3.dll\" /D "MP3_MUSIC" /D MP3_DYNAMIC=\"smpeg.dll\" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\mikmod" /I "..\timidity" /I "..\native_midi" /I "fluidsynth\include" /I "vorbis\include" /I "smpeg\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WAV_MUSIC" /D "MOD_MUSIC" /D MOD_DYNAMIC=\"mikmod.dll\" /D "MID_MUSIC" /D "USE_TIMIDITY_MIDI" /D "USE_NATIVE_MIDI" /D "USE_FLUIDSYNTH_MIDI" /D FLUIDSYNTH_DYNAMIC=\"libfluidsynth.dll\" /D "OGG_MUSIC" /D OGG_DYNAMIC=\"libvorbisfile-3.dll\" /D "MP3_MUSIC" /D MP3_DYNAMIC=\"smpeg.dll\" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" @@ -70,7 +70,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\mikmod" /I "..\timidity" /I "..\native_midi" /I "vorbis\include" /I "smpeg\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WAV_MUSIC" /D "MOD_MUSIC" /D MOD_DYNAMIC=\"mikmod.dll\" /D MOD_DYNAMIC=\"mikmod.dll\" /D "MID_MUSIC" /D "USE_TIMIDITY_MIDI" /D "USE_NATIVE_MIDI" /D "OGG_MUSIC" /D OGG_DYNAMIC=\"libvorbisfile-3.dll\" /D "MP3_MUSIC" /D MP3_DYNAMIC=\"smpeg.dll\" /YX /FD /c +# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\mikmod" /I "..\timidity" /I "..\native_midi" /I "fluidsynth\include" /I "vorbis\include" /I "smpeg\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WAV_MUSIC" /D "MOD_MUSIC" /D MOD_DYNAMIC=\"mikmod.dll\" /D MOD_DYNAMIC=\"mikmod.dll\" /D "MID_MUSIC" /D "USE_TIMIDITY_MIDI" /D "USE_NATIVE_MIDI" /D "USE_FLUIDSYNTH_MIDI" /D FLUIDSYNTH_DYNAMIC=\"libfluidsynth.dll\" /D "OGG_MUSIC" /D OGG_DYNAMIC=\"libvorbisfile-3.dll\" /D "MP3_MUSIC" /D MP3_DYNAMIC=\"smpeg.dll\" /YX /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" @@ -98,6 +98,14 @@ SOURCE=..\dynamic_flac.h # End Source File # Begin Source File +SOURCE=..\dynamic_fluidsynth.c +# End Source File +# Begin Source File + +SOURCE=..\dynamic_fluidsynth.h +# End Source File +# Begin Source File + SOURCE=..\dynamic_mod.c # End Source File # Begin Source File @@ -138,6 +146,14 @@ SOURCE=..\effects_internal.h # End Source File # Begin Source File +SOURCE=..\fluidsynth.c +# End Source File +# Begin Source File + +SOURCE=..\fluidsynth.h +# End Source File +# Begin Source File + SOURCE=..\load_aiff.c # End Source File # Begin Source File diff --git a/VisualC/SDL_mixer.vcproj b/VisualC/SDL_mixer.vcproj index 597ce746..93e69dcb 100755 --- a/VisualC/SDL_mixer.vcproj +++ b/VisualC/SDL_mixer.vcproj @@ -48,8 +48,8 @@ Name="VCCLCompilerTool" AdditionalOptions="/D OGG_DYNAMIC=\"libvorbisfile-3.dll\"" Optimization="0" - AdditionalIncludeDirectories="..\timidity;..\native_midi;mikmod\include;smpeg\include;vorbis\include" - PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;WAV_MUSIC;MOD_MUSIC;MOD_DYNAMIC=\"mikmod.dll\";MID_MUSIC;USE_TIMIDITY_MIDI;USE_NATIVE_MIDI;OGG_MUSIC;OGG_DYNAMIC=\"libvorbisfile-3.dll\";MP3_MUSIC;MP3_DYNAMIC=\"smpeg.dll\"" + AdditionalIncludeDirectories="..\timidity;..\native_midi;fluidsynth\include;mikmod\include;smpeg\include;vorbis\include" + PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;WAV_MUSIC;MOD_MUSIC;MOD_DYNAMIC=\"mikmod.dll\";MID_MUSIC;USE_TIMIDITY_MIDI;USE_NATIVE_MIDI;USE_FLUIDSYNTH_MIDI;FLUIDSYNTH_DYNAMIC=\"libfluidsynth.dll\";OGG_MUSIC;OGG_DYNAMIC=\"libvorbisfile-3.dll\";MP3_MUSIC;MP3_DYNAMIC=\"smpeg.dll\"" MinimalRebuild="true" RuntimeLibrary="2" PrecompiledHeaderFile=".\Debug/SDL_mixer.pch" @@ -145,8 +145,8 @@ AdditionalOptions="/D OGG_DYNAMIC=\"libvorbisfile-3.dll\"" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="..\timidity;..\native_midi;mikmod\include;smpeg\include;vorbis\include" - PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;WAV_MUSIC;MOD_MUSIC;MID_MUSIC;MOD_DYNAMIC=\"mikmod.dll\";USE_TIMIDITY_MIDI;USE_NATIVE_MIDI;OGG_MUSIC;OGG_DYNAMIC=\"libvorbisfile-3.dll\";MP3_MUSIC;MP3_DYNAMIC=\"smpeg.dll\"" + AdditionalIncludeDirectories="..\timidity;..\native_midi;fluidsynth/include;mikmod\include;smpeg\include;vorbis\include" + PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;WAV_MUSIC;MOD_MUSIC;MID_MUSIC;MOD_DYNAMIC=\"mikmod.dll\";USE_TIMIDITY_MIDI;USE_NATIVE_MIDI;USE_FLUIDSYNTH_MIDI;FLUIDSYNTH_DYNAMIC=\"libfluidsynth.dll\";OGG_MUSIC;OGG_DYNAMIC=\"libvorbisfile-3.dll\";MP3_MUSIC;MP3_DYNAMIC=\"smpeg.dll\"" StringPooling="true" RuntimeLibrary="2" EnableFunctionLevelLinking="true" @@ -218,6 +218,14 @@ RelativePath="..\dynamic_flac.h" > + + + + @@ -348,6 +356,14 @@ RelativePath="..\effects_internal.h" > + + + + diff --git a/configure.in b/configure.in index c1cdde0e..5f04deff 100644 --- a/configure.in +++ b/configure.in @@ -387,6 +387,49 @@ AC_HELP_STRING([--enable-music-native-midi-gpl], [enable native MIDI on UNIX usi SOURCES="$SOURCES $srcdir/native_midi_gpl/*.c" fi fi + AC_ARG_ENABLE([music-fluidsynth-midi], +AC_HELP_STRING([--enable-music-fluidsynth-midi], [enable FluidSynth MIDI output [[default=yes]]]), + [], [enable_music_fluidsynth_midi=yes]) + AC_ARG_ENABLE([music-fluidsynth-shared], +AC_HELP_STRING([--enable-music-fluidsynth-shared], [dynamically load FluidSynth support [[default=yes]]]), + [], [enable_music_fluidsynth_shared=yes]) + if test x$enable_music_fluidsynth_midi = xyes; then + AC_CHECK_HEADER([fluidsynth.h], [have_fluidsynth_hdr=yes]) + AC_CHECK_LIB([fluidsynth], [fluid_player_add_mem], [have_fluidsynth_lib=yes]) + if test x$have_fluidsynth_hdr = xyes -a x$have_fluidsynth_lib = xyes; then + case "$host" in + *-*-darwin*) + fluidsynth_lib=[`find_lib libfluidsynth.dylib`] + if test x$fluidsynth_lib = x; then + fluidsynth_lib=[`find_lib libfluidsynth.[0-9]`] + fi + if test x$fluidsynth_lib = x; then + fluidsynth_lib=[`find_lib libfluidsynth.[0-9]*`] + fi + ;; + *-*-cygwin* | *-*-mingw32*) + fluidsynth_lib=[`find_lib "fluidsynth*.dll"`] + ;; + *) + fluidsynth_lib=[`find_lib "libfluidsynth.so.[0-9]"`] + if test x$fluidsynth_lib = x; then + fluidsynth_lib=[`find_lib "libfluidsynth.so.[0-9]*"`] + fi + ;; + esac + SOURCES="$SOURCES $srcdir/dynamic_fluidsynth.c $srcdir/fluidsynth.c" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DUSE_FLUIDSYNTH_MIDI" + if test x$enable_music_fluidsynth_shared = xyes && test x$fluidsynth_lib != x; then + echo "-- dynamic libfluidsyth -> $fluidsynth_lib" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DFLUIDSYNTH_DYNAMIC=\\\"$fluidsynth_lib\\\"" + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lfluidsynth" + fi + else + AC_MSG_WARN([*** Unable to find FluidSynth library (http://www.fluidsynth.org/)]) + AC_MSG_WARN([FluidSynth support disabled]) + fi + fi fi AC_ARG_ENABLE([music-ogg], diff --git a/dynamic_fluidsynth.c b/dynamic_fluidsynth.c new file mode 100644 index 00000000..31823c1e --- /dev/null +++ b/dynamic_fluidsynth.c @@ -0,0 +1,85 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2011 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include "SDL_loadso.h" +#include "dynamic_fluidsynth.h" + +fluidsynth_loader fluidsynth = { + 0, NULL +}; + +#ifdef FLUIDSYNTH_DYNAMIC +#define FLUIDSYNTH_LOADER(FUNC, SIG) \ + fluidsynth.FUNC = (SIG) SDL_LoadFunction(fluidsynth.handle, #FUNC); \ + if (fluidsynth.FUNC == NULL) { SDL_UnloadObject(fluidsynth.handle); return -1; } +#else +#define FLUIDSYNTH_LOADER(FUNC, SIG) \ + fluidsynth.FUNC = FUNC; +#endif + +int Mix_InitFluidSynth() +{ + if ( fluidsynth.loaded == 0 ) { +#ifdef FLUIDSYNTH_DYNAMIC + fluidsynth.handle = SDL_LoadObject(FLUIDSYNTH_DYNAMIC); + if ( fluidsynth.handle == NULL ) return -1; +#endif + + FLUIDSYNTH_LOADER(delete_fluid_player, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*)); + FLUIDSYNTH_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*)); + FLUIDSYNTH_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t)); + FLUIDSYNTH_LOADER(fluid_player_get_status, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_player_play, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int)); + FLUIDSYNTH_LOADER(fluid_player_stop, int (*)(fluid_player_t*)); + FLUIDSYNTH_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double)); + FLUIDSYNTH_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float)); + FLUIDSYNTH_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int)); + FLUIDSYNTH_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int)); + FLUIDSYNTH_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*)); + FLUIDSYNTH_LOADER(new_fluid_settings, fluid_settings_t* (*)(void)); + FLUIDSYNTH_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*)); + } + ++fluidsynth.loaded; + + return 0; +} + +void Mix_QuitFluidSynth() +{ + if ( fluidsynth.loaded == 0 ) { + return; + } + if ( fluidsynth.loaded == 1 ) { +#ifdef FLUIDSYNTH_DYNAMIC + SDL_UnloadObject(fluidsynth.handle); +#endif + } + --fluidsynth.loaded; +} + +#endif /* USE_FLUIDSYNTH_MIDI */ diff --git a/dynamic_fluidsynth.h b/dynamic_fluidsynth.h new file mode 100644 index 00000000..219e8838 --- /dev/null +++ b/dynamic_fluidsynth.h @@ -0,0 +1,55 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2011 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifdef USE_FLUIDSYNTH_MIDI + +#include + +typedef struct { + int loaded; + void *handle; + + int (*delete_fluid_player)(fluid_player_t*); + void (*delete_fluid_settings)(fluid_settings_t*); + int (*delete_fluid_synth)(fluid_synth_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); + 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); + 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; + +extern fluidsynth_loader fluidsynth; + +#endif /* USE_FLUIDSYNTH_MIDI */ + +extern int Mix_InitFluidSynth(); +extern void Mix_QuitFluidSynth(); diff --git a/fluidsynth.c b/fluidsynth.c new file mode 100644 index 00000000..a9be72f7 --- /dev/null +++ b/fluidsynth.c @@ -0,0 +1,222 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2011 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#include "fluidsynth.h" +#include "SDL_mixer.h" +#include +#include + +static Uint16 format; +static Uint8 channels; +static int freq; + +int fluidsynth_check_soundfont(const char *path, void *data) +{ + FILE *file = fopen(path, "r"); + + if (file) { + fclose(file); + return 1; + } else { + Mix_SetError("Failed to access the SoundFont %s", path); + return 0; + } +} + +int 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; +} + +int fluidsynth_init(SDL_AudioSpec *mixer) +{ + if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) + return -1; + + format = mixer->format; + channels = mixer->channels; + freq = mixer->freq; + + return 0; +} + +FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data) +{ + FluidSynthMidiSong *song; + fluid_settings_t *settings = NULL; + + if ((song = malloc(sizeof(FluidSynthMidiSong)))) { + memset(song, 0, sizeof(FluidSynthMidiSong)); + + if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) { + if ((settings = fluidsynth.new_fluid_settings())) { + fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq); + + if ((song->synth = fluidsynth.new_fluid_synth(settings))) { + if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) { + if ((song->player = fluidsynth.new_fluid_player(song->synth))) { + if (function(song, data)) return song; + fluidsynth.delete_fluid_player(song->player); + } else { + Mix_SetError("Failed to create FluidSynth player"); + } + } + fluidsynth.delete_fluid_synth(song->synth); + } else { + Mix_SetError("Failed to create FluidSynth synthesizer"); + } + fluidsynth.delete_fluid_settings(settings); + } else { + Mix_SetError("Failed to create FluidSynth settings"); + } + } else { + Mix_SetError("Failed to set up audio conversion"); + } + free(song); + } else { + Mix_SetError("Insufficient memory for song"); + } + return NULL; +} + +int fluidsynth_loadsong_internal(FluidSynthMidiSong *song, void *data) +{ + const char* path = (const char*) data; + + if (fluidsynth.fluid_player_add(song->player, path) == FLUID_OK) { + return 1; + } else { + Mix_SetError("FluidSynth failed to load %s", path); + return 0; + } +} + +int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data) +{ + off_t offset; + size_t size; + char *buffer; + SDL_RWops *rw = (SDL_RWops*) data; + + offset = SDL_RWtell(rw); + SDL_RWseek(rw, 0, RW_SEEK_END); + size = SDL_RWtell(rw) - offset; + SDL_RWseek(rw, offset, RW_SEEK_SET); + + if ((buffer = (char*) malloc(size))) { + if(SDL_RWread(rw, buffer, size, 1) == 1) { + if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) { + return 1; + } else { + Mix_SetError("FluidSynth failed to load in-memory song"); + } + } else { + Mix_SetError("Failed to read in-memory song"); + } + free(buffer); + } else { + Mix_SetError("Insufficient memory for song"); + } + return 0; +} + +FluidSynthMidiSong *fluidsynth_loadsong(const char *midifile) +{ + return fluidsynth_loadsong_common(fluidsynth_loadsong_internal, (void*) midifile); +} + +FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw) +{ + return fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) rw); +} + +void fluidsynth_freesong(FluidSynthMidiSong *song) +{ + if (!song) return; + fluidsynth.delete_fluid_player(song->player); + fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth)); + fluidsynth.delete_fluid_synth(song->synth); + free(song); +} + +void fluidsynth_start(FluidSynthMidiSong *song) +{ + fluidsynth.fluid_player_set_loop(song->player, 1); + fluidsynth.fluid_player_play(song->player); +} + +void fluidsynth_stop(FluidSynthMidiSong *song) +{ + fluidsynth.fluid_player_stop(song->player); +} + +int fluidsynth_active(FluidSynthMidiSong *song) +{ + return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0; +} + +void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume) +{ + /* FluidSynth's default is 0.2. Make 0.8 the maximum. */ + fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 0.00625)); +} + +int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len) +{ + int result = -1; + int frames = dest_len / channels / ((format & 0xFF) / 8); + int src_len = frames * 4; /* 16-bit stereo */ + void *src = dest; + + if (dest_len < src_len) { + if (!(src = malloc(src_len))) { + Mix_SetError("Insufficient memory for audio conversion"); + return result; + } + } + + if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) { + Mix_SetError("Error generating FluidSynth audio"); + goto finish; + } + + song->convert.buf = src; + song->convert.len = src_len; + + if (SDL_ConvertAudio(&song->convert) < 0) { + Mix_SetError("Error during audio conversion"); + goto finish; + } + + if (src != dest) + memcpy(dest, src, dest_len); + + result = 0; + +finish: + if (src != dest) + free(src); + + return result; +} diff --git a/fluidsynth.h b/fluidsynth.h new file mode 100644 index 00000000..447f47f1 --- /dev/null +++ b/fluidsynth.h @@ -0,0 +1,46 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2011 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + James Le Cuirot + chewi@aura-online.co.uk +*/ + +#ifndef _FLUIDSYNTH_H_ +#define _FLUIDSYNTH_H_ + +#include "dynamic_fluidsynth.h" +#include +#include + +typedef struct { + SDL_AudioCVT convert; + fluid_synth_t *synth; + fluid_player_t* player; +} FluidSynthMidiSong; + +int fluidsynth_init(SDL_AudioSpec *mixer); +FluidSynthMidiSong *fluidsynth_loadsong(const char *midifile); +FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw); +void fluidsynth_freesong(FluidSynthMidiSong *song); +void fluidsynth_start(FluidSynthMidiSong *song); +void fluidsynth_stop(FluidSynthMidiSong *song); +int fluidsynth_active(FluidSynthMidiSong *song); +void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume); +int fluidsynth_playsome(FluidSynthMidiSong *song, void *stream, int len); + +#endif /* _FLUIDSYNTH_H_ */ diff --git a/mixer.c b/mixer.c index 3f144269..e7e27c0c 100644 --- a/mixer.c +++ b/mixer.c @@ -107,6 +107,11 @@ static void *music_data = NULL; static const char **chunk_decoders = NULL; static int num_decoders = 0; +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +extern char* soundfont_paths; +#endif + int Mix_GetNumChunkDecoders(void) { return(num_decoders); @@ -144,6 +149,15 @@ int Mix_Init(int flags) { int result = 0; + if (flags & MIX_INIT_FLUIDSYNTH) { +#ifdef USE_FLUIDSYNTH_MIDI + if ((initialized & MIX_INIT_FLUIDSYNTH) || Mix_InitFluidSynth() == 0) { + result |= MIX_INIT_FLUIDSYNTH; + } +#else + Mix_SetError("Mixer not built with FluidSynth support"); +#endif + } if (flags & MIX_INIT_FLAC) { #ifdef FLAC_MUSIC if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) { @@ -187,6 +201,11 @@ int Mix_Init(int flags) void Mix_Quit() { +#ifdef USE_FLUIDSYNTH_MIDI + if (initialized & MIX_INIT_FLUIDSYNTH) { + Mix_QuitFluidSynth(); + } +#endif #ifdef FLAC_MUSIC if (initialized & MIX_INIT_FLAC) { Mix_QuitFLAC(); @@ -206,6 +225,11 @@ void Mix_Quit() if (initialized & MIX_INIT_OGG) { Mix_QuitOgg(); } +#endif +#ifdef MID_MUSIC + if (soundfont_paths) { + free(soundfont_paths); + } #endif initialized = 0; } diff --git a/music.c b/music.c index a03b061c..f7991b98 100644 --- a/music.c +++ b/music.c @@ -48,14 +48,12 @@ # ifdef USE_TIMIDITY_MIDI # include "timidity.h" # endif +# ifdef USE_FLUIDSYNTH_MIDI +# include "fluidsynth.h" +# endif # ifdef USE_NATIVE_MIDI # include "native_midi.h" # endif -# if defined(USE_TIMIDITY_MIDI) && defined(USE_NATIVE_MIDI) -# define MIDI_ELSE else -# else -# define MIDI_ELSE -# endif #endif #ifdef OGG_MUSIC #include "music_ogg.h" @@ -101,6 +99,9 @@ struct _Mix_Music { #ifdef USE_TIMIDITY_MIDI MidiSong *midi; #endif +#ifdef USE_FLUIDSYNTH_MIDI + FluidSynthMidiSong *fluidsynthmidi; +#endif #ifdef USE_NATIVE_MIDI NativeMidiSong *nativemidi; #endif @@ -128,6 +129,9 @@ struct _Mix_Music { static int timidity_ok; static int samplesize; #endif +#ifdef USE_FLUIDSYNTH_MIDI +static int fluidsynth_ok; +#endif #ifdef USE_NATIVE_MIDI static int native_midi_ok; #endif @@ -140,6 +144,11 @@ static int ms_per_step; static const char **music_decoders = NULL; static int num_decoders = 0; +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +char* soundfont_paths = NULL; +#endif + int Mix_GetNumMusicDecoders(void) { return(num_decoders); @@ -270,14 +279,21 @@ void music_mixer(void *udata, Uint8 *stream, int len) break; #endif #ifdef MID_MUSIC -#ifdef USE_TIMIDITY_MIDI case MUS_MID: +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_playsome(music_playing->data.fluidsynthmidi, stream, len); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { int samples = len / samplesize; Timidity_PlaySome(stream, samples); + goto skip; } - break; #endif + break; #endif #ifdef OGG_MUSIC case MUS_OGG: @@ -306,6 +322,7 @@ void music_mixer(void *udata, Uint8 *stream, int len) } } +skip: /* Handle seamless music looping */ if (left > 0 && left < len && music_halt_or_loop()) { music_mixer(udata, stream+(len-left), left); @@ -341,9 +358,21 @@ int open_music(SDL_AudioSpec *mixer) timidity_ok = 0; } #endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_init(mixer) == 0 ) { + fluidsynth_ok = 1; + add_music_decoder("FLUIDSYNTH"); + } else { + fluidsynth_ok = 0; + } +#endif #ifdef USE_NATIVE_MIDI +#ifdef USE_FLUIDSYNTH_MIDI + native_midi_ok = !fluidsynth_ok; + if ( native_midi_ok ) +#endif #ifdef USE_TIMIDITY_MIDI - native_midi_ok = !timidity_ok; + native_midi_ok = !timidity_ok; if ( !native_midi_ok ) { native_midi_ok = (getenv("SDL_NATIVE_MUSIC") != NULL); } @@ -469,7 +498,17 @@ Mix_Music *Mix_LoadMUS(const char *file) Mix_SetError("%s", native_midi_error()); music->error = 1; } - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + music->data.fluidsynthmidi = fluidsynth_loadsong(file); + if ( music->data.fluidsynthmidi == NULL ) { + music->error = 1; + } + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { @@ -565,6 +604,8 @@ Mix_Music *Mix_LoadMUS(const char *file) Mix_SetError("Unrecognized music format"); music->error = 1; } + +skip: if ( music->error ) { free(music); music = NULL; @@ -616,11 +657,19 @@ void Mix_FreeMusic(Mix_Music *music) #ifdef USE_NATIVE_MIDI if ( native_midi_ok ) { native_midi_freesong(music->data.nativemidi); - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_freesong(music->data.fluidsynthmidi); + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { Timidity_FreeSong(music->data.midi); + goto skip; } #endif break; @@ -649,6 +698,8 @@ void Mix_FreeMusic(Mix_Music *music) /* Unknown music type?? */ break; } + + skip: free(music); } } @@ -720,11 +771,19 @@ static int music_internal_play(Mix_Music *music, double position) #ifdef USE_NATIVE_MIDI if ( native_midi_ok ) { native_midi_start(music->data.nativemidi); - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if (fluidsynth_ok ) { + fluidsynth_start(music->data.fluidsynthmidi); + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { Timidity_Start(music->data.midi); + goto skip; } #endif break; @@ -757,6 +816,7 @@ static int music_internal_play(Mix_Music *music, double position) break; } +skip: /* Set the playback position, note any errors if an offset is used */ if ( retval == 0 ) { if ( position > 0.0 ) { @@ -929,11 +989,19 @@ static void music_internal_volume(int volume) #ifdef USE_NATIVE_MIDI if ( native_midi_ok ) { native_midi_setvolume(volume); - } MIDI_ELSE + return; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_setvolume(music_playing->data.fluidsynthmidi, volume); + return; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { Timidity_SetVolume(volume); + return; } #endif break; @@ -1012,11 +1080,19 @@ static void music_internal_halt(void) #ifdef USE_NATIVE_MIDI if ( native_midi_ok ) { native_midi_stop(); - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_stop(music_playing->data.fluidsynthmidi); + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { Timidity_Stop(); + goto skip; } #endif break; @@ -1045,6 +1121,8 @@ static void music_internal_halt(void) /* Unknown music type?? */ return; } + +skip: music_playing->fading = MIX_NO_FADING; music_playing = NULL; } @@ -1173,12 +1251,21 @@ static int music_internal_playing() if ( native_midi_ok ) { if ( ! native_midi_active() ) playing = 0; - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + if ( ! fluidsynth_active(music_playing->data.fluidsynthmidi) ) + playing = 0; + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { if ( ! Timidity_Active() ) playing = 0; + goto skip; } #endif break; @@ -1214,6 +1301,8 @@ static int music_internal_playing() playing = 0; break; } + +skip: return(playing); } int Mix_PlayingMusic(void) @@ -1393,7 +1482,17 @@ Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) Mix_SetError("%s", native_midi_error()); music->error = 1; } - } MIDI_ELSE + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw); + if ( music->data.fluidsynthmidi == NULL ) { + music->error = 1; + } + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { @@ -1436,9 +1535,72 @@ Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) Mix_SetError("Unrecognized music format"); music->error=1; } + +skip: if (music->error) { free(music); music=NULL; } return(music); } + +int Mix_SetSoundFonts(const char *paths) +{ +#ifdef MID_MUSIC + if (soundfont_paths) { + free(soundfont_paths); + soundfont_paths = NULL; + } + + if (paths) { + if (!(soundfont_paths = strdup(paths))) { + Mix_SetError("Insufficient memory to set SoundFonts"); + return 0; + } + } +#endif + return 1; +} + +#ifdef MID_MUSIC +const char* Mix_GetSoundFonts() +{ + const char* force = getenv("SDL_FORCE_SOUNDFONTS"); + + if (!soundfont_paths || (force && force[0] == '1')) { + return getenv("SDL_SOUNDFONTS"); + } else { + return soundfont_paths; + } +} + +int Mix_EachSoundFont(int (*function)(const char*, void*), void *data) +{ + char *context, *path, *paths; + const char* cpaths = Mix_GetSoundFonts(); + + if (!cpaths) { + Mix_SetError("No SoundFonts have been requested"); + return 0; + } + + if (!(paths = strdup(cpaths))) { + Mix_SetError("Insufficient memory to iterate over SoundFonts"); + return 0; + } + +#ifdef _WIN32 + for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) { +#else + for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) { +#endif + if (!function(path, data)) { + free(paths); + return 0; + } + } + + free(paths); + return 1; +} +#endif