diff -r 6ed75d34edc9 Makefile.in --- a/Makefile.in Sat Jun 26 08:01:15 2010 -0700 +++ b/Makefile.in Sun Oct 17 16:41:40 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 Sun Oct 17 16:41:40 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 Sun Oct 17 16:41:40 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 Sun Oct 17 16:41:40 2010 +0100 @@ -0,0 +1,293 @@ +#include "fluidsynth.h" +#include "native_midi_common.h" +#include "SDL_mixer.h" +#include + +static Uint16 format; +static Uint8 channels; +static int freq; + +static char* fluidsynth_error_ptr = ""; + +int fluidsynth_init(SDL_AudioSpec *mixer) +{ + if (!Mix_GetSoundFonts()) + return -1; + + format = mixer->format; + channels = mixer->channels; + freq = mixer->freq; + + return 0; +} + +static void fluidsynth_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + FluidSynthMidiSong *song; + double* time_scale = (double*) fluid_event_get_data(event); + + if (time_scale) { + fluid_sequencer_set_time_scale(seq, *time_scale); + } else { + song = (FluidSynthMidiSong*) data; + song->active = 0; + } +} + +static int MIDI_to_FluidSynth(FluidSynthMidiSong *song, MIDIEvent *events, Uint16 ppqn, short synth_id, short callback_id) +{ + double* time_scale; + MIDIEvent *event, *last; + fluid_event_t *fse; + unsigned int chan, i = 1; + + song->count = 1; + for (event = events; event; event = event->next) song->count++; + + if (!(song->events = malloc(song->count * sizeof(fluid_event_t*)))) { + return -1; + } + + if (!(song->times = malloc(song->count * sizeof(unsigned int)))) { + free(song->events); + return -1; + } + + /* Allocate the finishing event now but set up later. */ + if (!(song->events[0] = new_fluid_event())) { + free(song->times); + free(song->events); + return -1; + } + + for (event = events; event; event = event->next) + { + last = event; + chan = event->status & 15; + + if (!(fse = new_fluid_event())) { + song->count = i; + goto incomplete; + } + + 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: + if (event->status == 0xFF && event->data[0] == 0x51) { + if (!(time_scale = (double*) malloc(sizeof(double)))) { + delete_fluid_event(fse); + song->count = i; + goto incomplete; + } + + *time_scale = (double) ((event->extraData[0] << 16) | (event->extraData[1] << 8) | event->extraData[2]); + *time_scale = (double) ppqn / *time_scale * 1000000; + + fluid_event_timer(fse, time_scale); + fluid_event_set_dest(fse, callback_id); + goto skip_dest; + } + default: + delete_fluid_event(fse); + song->events[i++] = NULL; + continue; + } + + fluid_event_set_dest(fse, synth_id); + + skip_dest: + fluid_event_set_source(fse, -1); + song->times[i] = event->time; + song->events[i] = fse; + i++; + } + +incomplete: + /* Set up the finishing event now. */ + fse = song->events[0]; + fluid_event_timer(fse, NULL); + fluid_event_set_dest(fse, callback_id); + fluid_event_set_source(fse, -1); + song->times[0] = 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; + short synth_id, callback_id; + + MIDIEvent *events = NULL; + 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 ((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))) { +#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 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(0))) { + synth_id = fluid_sequencer_register_fluidsynth(song->seq, song->synth); + callback_id = fluid_sequencer_register_client(song->seq, "SDL_mixer", fluidsynth_callback, song); + + if (MIDI_to_FluidSynth(song, events, ppqn, synth_id, callback_id) == 0) { + free(paths); + FreeMIDIEventList(events); + return song; + } + + delete_fluid_sequencer(song->seq); + } + FreeMIDIEventList(events); + } + delete_fluid_synth(song->synth); + } + delete_fluid_settings(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]) { + free(fluid_event_get_data(song->events[i])); + delete_fluid_event(song->events[i]); + } + } + + free(song->times); + 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]) fluid_sequencer_send_at(song->seq, song->events[i], now + song->times[i], 1); + } +} + +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 *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))) + return result; + } + + if (fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) + goto finish; + + song->convert.buf = src; + song->convert.len = src_len; + + if (SDL_ConvertAudio(&song->convert) < 0) + goto finish; + + if (src != dest) + memcpy(dest, src, dest_len); + + result = 0; + +finish: + if (src != dest) + free(src); + + return result; +} + +const char* fluidsynth_error(void) +{ + return fluidsynth_error_ptr; +} diff -r 6ed75d34edc9 fluidsynth.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fluidsynth.h Sun Oct 17 16:41:40 2010 +0100 @@ -0,0 +1,29 @@ +#ifndef _FLUIDSYNTH_H_ +#define _FLUIDSYNTH_H_ + +#include +#include +#include + +typedef struct { + SDL_AudioCVT convert; + fluid_synth_t *synth; + fluid_sequencer_t *seq; + fluid_event_t **events; + unsigned int *times; + 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); +const char *fluidsynth_error(void); + +#endif /* _FLUIDSYNTH_H_ */ diff -r 6ed75d34edc9 mixer.c --- a/mixer.c Sat Jun 26 08:01:15 2010 -0700 +++ b/mixer.c Sun Oct 17 16:41:40 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 Sun Oct 17 16:41:40 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,18 @@ 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 ) { + Mix_SetError("%s", fluidsynth_error()); + music->error = 1; + } + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { @@ -565,6 +605,8 @@ Mix_SetError("Unrecognized music format"); music->error = 1; } + +skip: if ( music->error ) { free(music); music = NULL; @@ -616,11 +658,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 +699,8 @@ /* Unknown music type?? */ break; } + + skip: free(music); } } @@ -720,11 +772,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 +817,7 @@ break; } +skip: /* Set the playback position, note any errors if an offset is used */ if ( retval == 0 ) { if ( position > 0.0 ) { @@ -924,11 +985,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 +1076,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 +1117,8 @@ /* Unknown music type?? */ return; } + +skip: music_playing->fading = MIX_NO_FADING; music_playing = NULL; } @@ -1163,12 +1242,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 +1292,8 @@ playing = 0; break; } + +skip: return(playing); } int Mix_PlayingMusic(void) @@ -1381,7 +1471,18 @@ 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 ) { + Mix_SetError("%s", fluidsynth_error()); + music->error = 1; + } + goto skip; + } #endif #ifdef USE_TIMIDITY_MIDI if ( timidity_ok ) { @@ -1424,9 +1525,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; +}