diff -r 6ed75d34edc9 Makefile.in --- a/Makefile.in Sat Jun 26 08:01:15 2010 -0700 +++ b/Makefile.in Tue Oct 05 23:03:02 2010 +0100 @@ -38,7 +38,7 @@ PLAYWAVE_OBJECTS = @PLAYWAVE_OBJECTS@ PLAYMUS_OBJECTS = @PLAYMUS_OBJECTS@ -DIST = 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 = 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 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 6ed75d34edc9 SDL_mixer.h --- a/SDL_mixer.h Sat Jun 26 08:01:15 2010 -0700 +++ b/SDL_mixer.h Tue Oct 05 23:03:02 2010 +0100 @@ -605,6 +605,10 @@ extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value); extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void); +/* Set/Get SoundFonts paths to use by supported MIDI backends */ +extern DECLSPEC int SDLCALL Mix_SetSoundFonts(const char *paths); +extern DECLSPEC const char* SDLCALL Mix_GetSoundFonts(); + /* 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 6ed75d34edc9 configure.in --- a/configure.in Sat Jun 26 08:01:15 2010 -0700 +++ b/configure.in Tue Oct 05 23:03:02 2010 +0100 @@ -379,6 +379,25 @@ 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]) + if test x$enable_music_fluidsynth_midi = xyes; then + AC_CHECK_HEADER([fluidsynth.h], [have_fluidsynth_hdr=yes]) + AC_CHECK_LIB([fluidsynth], [new_fluid_synth], [have_fluidsynth_lib=yes]) + if test x$have_fluidsynth_hdr = xyes -a x$have_fluidsynth_lib = xyes; then + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lfluidsynth" + EXTRA_CFLAGS="$EXTRA_CFLAGS -DUSE_FLUIDSYNTH_MIDI" + SOURCES="$SOURCES $srcdir/fluidsynth.c" + if test x$use_music_native_midi != xyes; then + EXTRA_CFLAGS="$EXTRA_CFLAGS -I\$(srcdir)/native_midi" + SOURCES="$SOURCES $srcdir/native_midi/native_midi_common.c" + 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 6ed75d34edc9 fluidsynth.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fluidsynth.c Tue Oct 05 23:03:02 2010 +0100 @@ -0,0 +1,228 @@ +#include "fluidsynth.h" +#include "native_midi_common.h" +#include "SDL_mixer.h" +#include + +#ifndef strtok_r +#define strtok_r strtok_s +#endif + +unsigned int fluid_event_get_time(fluid_event_t* evt); +void fluid_event_set_time(fluid_event_t* evt, unsigned int time); + +static int freq; + +int fluidsynth_init(SDL_AudioSpec *mixer) +{ + if (!Mix_GetSoundFonts()) + return -1; + + freq = mixer->freq; + return 0; +} + +static void fluidsynth_finish(unsigned int time, fluid_event_t* event, fluid_sequencer_t* seq, void* data) +{ + FluidSynthMidiSong *song = (FluidSynthMidiSong*) data; + song->active = 0; +} + +static int MIDI_to_FluidSynth(FluidSynthMidiSong *song, MIDIEvent *events) +{ + MIDIEvent *event, *last; + fluid_event_t *fse; + unsigned int chan, i = 1; + + song->count = 1; + for (event = events; event; event = event->next) song->count++; + song->events = malloc(song->count * sizeof(fluid_event_t*)); + if (!song->events) return -1; + + /* Allocate the finishing event now but set up later. */ + if (!(song->events[0] = new_fluid_event())) { + free(song->events); + return -1; + } + + for (event = events; event; event = event->next) + { + last = event; + chan = event->status & 15; + + if (!(fse = new_fluid_event())) { + /* Oh dear. Let's just play what we have. */ + song->count = i; + break; + } + + switch (event->status >> 4) + { + case MIDI_STATUS_NOTE_OFF: + fluid_event_noteoff(fse, chan, event->data[0]); + break; + case MIDI_STATUS_NOTE_ON: + fluid_event_noteon(fse, chan, event->data[0], event->data[1]); + break; + case MIDI_STATUS_AFTERTOUCH: + fluid_event_channel_pressure(fse, chan, event->data[0]); + break; + case MIDI_STATUS_CONTROLLER: + fluid_event_control_change(fse, chan, event->data[0], event->data[1]); + break; + case MIDI_STATUS_PROG_CHANGE: + fluid_event_program_change(fse, chan, event->data[0]); + break; + case MIDI_STATUS_PRESSURE: + fluid_event_channel_pressure(fse, chan, event->data[0]); + break; + case MIDI_STATUS_PITCH_WHEEL: + fluid_event_pitch_bend(fse, chan, event->data[0]); + break; + case MIDI_STATUS_SYSEX: + default: + delete_fluid_event(fse); + song->events[i++] = NULL; + continue; + } + + fluid_event_set_source(fse, -1); + fluid_event_set_dest(fse, song->synth_id); + fluid_event_set_time(fse, event->time); + song->events[i++] = fse; + } + + /* Set up the finishing event now. */ + fse = song->events[0]; + fluid_event_timer(fse, NULL); + fluid_event_set_source(fse, -1); + fluid_event_set_dest(fse, song->finish_id); + fluid_event_set_time(fse, last ? last->time : 1000); + + return 0; +} + +FluidSynthMidiSong *fluidsynth_loadsong(const char *midifile) +{ + FluidSynthMidiSong *song; + SDL_RWops *rw; + + if ((rw = SDL_RWFromFile(midifile, "rb"))) { + song = fluidsynth_loadsong_RW(rw); + SDL_RWclose(rw); + return song; + } else { + return NULL; + } +} + +FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw) +{ + Uint16 ppqn; + FluidSynthMidiSong *song; + char *context, *path, *paths; + + MIDIEvent *events = NULL; + fluid_settings_t* settings = NULL; + + if ((song = malloc(sizeof(FluidSynthMidiSong)))) { + memset(song, 0, sizeof(FluidSynthMidiSong)); + + if ((paths = strdup(Mix_GetSoundFonts()))) { + if ((settings = new_fluid_settings())) { + fluid_settings_setnum(settings, "synth.sample-rate", (double) freq); + + if ((song->synth = new_fluid_synth(settings))) { + for (path = strtok_r(paths, ";", &context); path; path = strtok_r(NULL, ";", &context)) { + /* If this fails, it's too late to try Timidity so pray that at least one works. */ + fluid_synth_sfload(song->synth, path, 1); + } + + if ((events = CreateMIDIEventList(rw, &ppqn))) { + if ((song->seq = new_fluid_sequencer2(1))) { + fluid_sequencer_set_time_scale(song->seq, ppqn); + song->synth_id = fluid_sequencer_register_fluidsynth(song->seq, song->synth); + song->finish_id = fluid_sequencer_register_client(song->seq, "SDL_mixer", fluidsynth_finish, song); + + if (MIDI_to_FluidSynth(song, events) == 0) { + free(paths); + FreeMIDIEventList(events); + return song; + } + + delete_fluid_sequencer(song->seq); + } + FreeMIDIEventList(events); + } + delete_fluid_synth(song->synth); + } + free(settings); + } + free(paths); + } + free(song); + } + return NULL; +} + +void fluidsynth_freesong(FluidSynthMidiSong *song) +{ + unsigned int i; + + if (song) { + delete_fluid_sequencer(song->seq); + delete_fluid_settings(fluid_synth_get_settings(song->synth)); + delete_fluid_synth(song->synth); + + for (i = 0; i < song->count; i++) { + if (song->events[i]) { + delete_fluid_event(song->events[i]); + } + } + + free(song->events); + free(song); + } +} + +void fluidsynth_start(FluidSynthMidiSong *song) +{ + unsigned int i, time; + unsigned int now = fluid_sequencer_get_tick(song->seq); + song->active = 1; + + for (i = 0; i < song->count; i++) { + if (song->events[i]) { + time = fluid_event_get_time(song->events[i]); + fluid_sequencer_send_at(song->seq, song->events[i], now + time, 1); + + /* fluid_sequencer_send_at currently modifies the event's + * time property. We need to revert it back to the old + * value in case we need to play the song again. I will + * ask FluidSynth's devs if this can be changed. */ + fluid_event_set_time(song->events[i], time); + } + } +} + +void fluidsynth_stop(FluidSynthMidiSong *song) +{ + fluid_sequencer_remove_events(song->seq, -1, -1, -1); + fluid_synth_system_reset(song->synth); + song->active = 0; +} + +int fluidsynth_active(FluidSynthMidiSong *song) +{ + return song->active; +} + +void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume) +{ + /* FluidSynth's default is 0.2. Make 0.8 the maximum. */ + fluid_synth_set_gain(song->synth, volume * 0.00625); +} + +int fluidsynth_playsome(FluidSynthMidiSong *song, void *stream, int len) +{ + return fluid_synth_write_s16(song->synth, len / 4, stream, 0, 2, stream, 1, 2); +} diff -r 6ed75d34edc9 fluidsynth.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fluidsynth.h Tue Oct 05 23:03:02 2010 +0100 @@ -0,0 +1,28 @@ +#ifndef _FLUIDSYNTH_H_ +#define _FLUIDSYNTH_H_ + +#include +#include +#include + +typedef struct { + short synth_id; + short finish_id; + fluid_synth_t* synth; + fluid_sequencer_t* seq; + fluid_event_t** events; + unsigned int count; + int active; +} 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 6ed75d34edc9 mixer.c --- a/mixer.c Sat Jun 26 08:01:15 2010 -0700 +++ b/mixer.c Tue Oct 05 23:03:02 2010 +0100 @@ -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); @@ -207,6 +212,11 @@ Mix_QuitOgg(); } #endif +#ifdef MID_MUSIC + if (soundfont_paths) { + free(soundfont_paths); + } +#endif initialized = 0; } diff -r 6ed75d34edc9 music.c --- a/music.c Sat Jun 26 08:01:15 2010 -0700 +++ b/music.c Tue Oct 05 23:03:02 2010 +0100 @@ -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 ) { @@ -924,11 +984,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; @@ -1007,11 +1075,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; @@ -1040,6 +1116,8 @@ /* Unknown music type?? */ return; } + +skip: music_playing->fading = MIX_NO_FADING; music_playing = NULL; } @@ -1163,12 +1241,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; @@ -1204,6 +1291,8 @@ playing = 0; break; } + +skip: return(playing); } int Mix_PlayingMusic(void) @@ -1381,7 +1470,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 ) { @@ -1424,9 +1523,41 @@ 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) { + soundfont_paths = strdup(paths); + return soundfont_paths == NULL ? -1 : 0; + } +#endif + return 0; +} + +const char* Mix_GetSoundFonts() +{ +#ifdef MID_MUSIC + const char* force = getenv("SDL_FORCE_SOUNDFONTS"); + + if (!soundfont_paths || (force && force[0] == '1')) { + return getenv("SDL_SOUNDFONTS"); + } else { + return soundfont_paths; + } +#endif + return NULL; +}