Good news, everyone!

Following Matt Giuca's great work on playing files from memory, I've
more or less finished my SDL_mixer patch. The attached patch requires
the latest FluidSynth from Subversion and was created against the latest
SDL_mixer from Mercurial, though it can be applied against 1.2.11. You
can safely ignore the conflict in Makefile.in but remember to run
./autogen.sh in either case. As before, set SDL_SOUNDFONTS to some
path in order to try this out. Note that I've only tried it on Linux
so far.

There's not much to say on the FluidSynth side of things. Matt's
additions made it incredibly simple. :) My only thought was that it
might be nice to allow SoundFonts to be loaded from memory but that can
always be added later.

Having seen how constrained some environments like the PSP can be, I
decided to use the regular old fluid_player_add for files on disk,
otherwise they would have to be copied in memory at least twice after
the initial read. This was more feasible than it was before because now
I'm using the player interface for in-memory files as well. It also
gave me an opportunity to use function pointers for the first time.
That was fun. ;)

It took me some time to decide on the best way to handle SoundFont
selection and when to fall back to Timidity and there are several other
SDL_mixer aspects to discuss but I'll save all that for the SDL mailing
list. I'll post there tomorrow to get some feedback.

Cheers,
James
diff -r 7a95f36a8bbf Makefile.in
--- a/Makefile.in	Thu Feb 17 13:35:14 2011 -0800
+++ b/Makefile.in	Fri Mar 18 23:16:44 2011 +0000
@@ -39,7 +39,7 @@
 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 -r 7a95f36a8bbf SDL_mixer.h
--- a/SDL_mixer.h	Thu Feb 17 13:35:14 2011 -0800
+++ b/SDL_mixer.h	Fri Mar 18 23:16:44 2011 +0000
@@ -67,10 +67,11 @@
 
 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_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 -r 7a95f36a8bbf configure.in
--- a/configure.in	Thu Feb 17 13:35:14 2011 -0800
+++ b/configure.in	Fri Mar 18 23:16:44 2011 +0000
@@ -387,6 +387,49 @@
             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 -r 7a95f36a8bbf dynamic_fluidsynth.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamic_fluidsynth.c	Fri Mar 18 23:16:44 2011 +0000
@@ -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
+    ch...@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 -r 7a95f36a8bbf dynamic_fluidsynth.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamic_fluidsynth.h	Fri Mar 18 23:16:44 2011 +0000
@@ -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
+    ch...@aura-online.co.uk
+*/
+
+#ifdef USE_FLUIDSYNTH_MIDI
+
+#include <fluidsynth.h>
+
+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 -r 7a95f36a8bbf fluidsynth.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fluidsynth.c	Fri Mar 18 23:16:44 2011 +0000
@@ -0,0 +1,221 @@
+/*
+    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
+    ch...@aura-online.co.uk
+*/
+
+#include "fluidsynth.h"
+#include "SDL_mixer.h"
+#include <stdio.h>
+
+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, 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 -r 7a95f36a8bbf fluidsynth.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fluidsynth.h	Fri Mar 18 23:16:44 2011 +0000
@@ -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
+    ch...@aura-online.co.uk
+*/
+
+#ifndef _FLUIDSYNTH_H_
+#define _FLUIDSYNTH_H_
+
+#include "dynamic_fluidsynth.h"
+#include <SDL_rwops.h>
+#include <SDL_audio.h>
+
+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 -r 7a95f36a8bbf mixer.c
--- a/mixer.c	Thu Feb 17 13:35:14 2011 -0800
+++ b/mixer.c	Fri Mar 18 23:16:44 2011 +0000
@@ -107,6 +107,11 @@
 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 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 @@
 
 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();
@@ -207,6 +226,11 @@
 		Mix_QuitOgg();
 	}
 #endif
+#ifdef MID_MUSIC
+	if (soundfont_paths) {
+		free(soundfont_paths);
+	}
+#endif
 	initialized = 0;
 }
 
diff -r 7a95f36a8bbf music.c
--- a/music.c	Thu Feb 17 13:35:14 2011 -0800
+++ b/music.c	Fri Mar 18 23:16:44 2011 +0000
@@ -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 @@
 #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 @@
 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 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,15 +279,22 @@
 				break;
 #endif
 #ifdef MID_MUSIC
+			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
-			case MUS_MID:
 				if ( timidity_ok ) {
 					int samples = len / samplesize;
   					Timidity_PlaySome(stream, samples);
+					goto skip;
 				}
+#endif
 				break;
 #endif
-#endif
 #ifdef OGG_MUSIC
 			case MUS_OGG:
 				
@@ -306,6 +322,7 @@
 		}
 	}
 
+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 @@
 		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_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_SetError("Unrecognized music format");
 		music->error = 1;
 	}
+
+skip:
 	if ( music->error ) {
 		free(music);
 		music = NULL;
@@ -616,11 +657,19 @@
 #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 @@
 				/* Unknown music type?? */
 				break;
 		}
+
+    skip:
 		free(music);
 	}
 }
@@ -720,11 +771,19 @@
 #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 @@
 		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 @@
 #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 @@
 #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 @@
 		/* Unknown music type?? */
 		return;
 	}
+
+skip:
 	music_playing->fading = MIX_NO_FADING;
 	music_playing = NULL;
 }
@@ -1173,12 +1251,21 @@
 		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 @@
 		playing = 0;
 		break;
 	}
+
+skip:
 	return(playing);
 }
 int Mix_PlayingMusic(void)
@@ -1393,7 +1482,17 @@
 		  		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_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
_______________________________________________
fluid-dev mailing list
fluid-dev@nongnu.org
http://lists.nongnu.org/mailman/listinfo/fluid-dev

Reply via email to