diff --git a/fluidsynth/src/fluid_event_queue.h b/fluidsynth/src/fluid_event_queue.h index a8efa3f3..6a68409e 100644 --- a/fluidsynth/src/fluid_event_queue.h +++ b/fluidsynth/src/fluid_event_queue.h @@ -41,6 +41,7 @@ enum fluid_event_queue_elem FLUID_EVENT_QUEUE_ELEM_SET_TUNING, /**< Set tuning event. Uses set_tuning field of event value */ FLUID_EVENT_QUEUE_ELEM_REPL_TUNING, /**< Replace tuning event. Uses repl_tuning field of event value */ FLUID_EVENT_QUEUE_ELEM_UNREF_TUNING, /**< Unref tuning return event. Uses unref_tuning field of event value */ + FLUID_EVENT_QUEUE_ELEM_MIDI_MODE /**< Set MIDI mode event. Uses ival field of event value */ }; /** diff --git a/fluidsynth/src/fluid_midi.h b/fluidsynth/src/fluid_midi.h index 5fe1a04d..30cc1b37 100644 --- a/fluidsynth/src/fluid_midi.h +++ b/fluidsynth/src/fluid_midi.h @@ -171,15 +171,20 @@ enum midi_meta_event { /* MIDI SYSEX useful manufacturer values */ enum midi_sysex_manuf { + MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ }; #define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ -/* Sysex sub-ID #1 (following the device ID) for MIDI tuning messages */ -#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 +/* SYSEX sub-ID #1 which follows device ID */ +#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ +#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ +/** + * SYSEX tuning message IDs. + */ enum midi_sysex_tuning_msg_id { MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ @@ -193,6 +198,10 @@ enum midi_sysex_tuning_msg_id { MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ }; +/* General MIDI sub-ID #2 */ +#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ +#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ + enum fluid_driver_status { FLUID_MIDI_READY, diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index f9650146..44b18288 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -61,6 +61,16 @@ static int fluid_synth_cc_real(fluid_synth_t* synth, int channum, int num, int value, int noqueue); static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value); +static int fluid_synth_update_midi_mode (fluid_synth_t *synth, char *name, + char *value); +static int fluid_synth_update_midi_mode_lock (fluid_synth_t *synth, char *name, + char *value); +static void fluid_synth_set_midi_mode_LOCAL (fluid_synth_t *synth, + fluid_synth_midi_mode_t mode); +static int fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); @@ -239,6 +249,10 @@ void fluid_synth_settings(fluid_settings_t* settings) fluid_settings_add_option(settings, "synth.midi-mode", "normal"); fluid_settings_add_option(settings, "synth.midi-mode", "gm"); fluid_settings_add_option(settings, "synth.midi-mode", "gs"); + + fluid_settings_register_str(settings, "synth.midi-mode-lock", "no", 0, NULL, NULL); + fluid_settings_add_option(settings, "synth.midi-mode-lock", "no"); + fluid_settings_add_option(settings, "synth.midi-mode-lock", "yes"); } /* @@ -550,6 +564,7 @@ new_fluid_synth(fluid_settings_t *settings) synth->with_chorus = fluid_settings_str_equal(settings, "synth.chorus.active", "yes"); synth->verbose = fluid_settings_str_equal(settings, "synth.verbose", "yes"); synth->dump = fluid_settings_str_equal(settings, "synth.dump", "yes"); + synth->midi_mode_lock = fluid_settings_str_equal(settings, "synth.midi-mode-lock", "yes"); fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); @@ -571,7 +586,11 @@ new_fluid_synth(fluid_settings_t *settings) synth); fluid_settings_register_int(settings, "synth.device-id", synth->device_id, 126, 0, 0, - (fluid_int_update_t) fluid_synth_update_device_id, NULL); + (fluid_int_update_t) fluid_synth_update_device_id, synth); + fluid_settings_register_str(settings, "synth.midi-mode", "normal", 0, + (fluid_str_update_t) fluid_synth_update_midi_mode, synth); + fluid_settings_register_str(settings, "synth.midi-mode-lock", "no", 0, + (fluid_str_update_t) fluid_synth_update_midi_mode_lock, synth); /* do some basic sanity checking on the settings */ @@ -1351,9 +1370,21 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) { - int i; fluid_voice_t* voice; int status = FLUID_FAILED; + int midi_mode; + int i; + + /* We ignore percussion note offs in GM/GS for everything except + * Long Guiro and Long Whistle, as per GM revision 2 spec - JG */ + if (chan == 9) + { + midi_mode = fluid_atomic_int_get (&synth->midi_mode); + + if ((midi_mode == FLUID_SYNTH_MIDI_MODE_GM || midi_mode == FLUID_SYNTH_MIDI_MODE_GS) + && key != 72 && key != 74) + return FLUID_OK; + } for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; @@ -1588,6 +1619,60 @@ fluid_synth_update_device_id (fluid_synth_t *synth, char *name, int value) return 0; } +/* Handler for synth.midi-mode setting. */ +static int +fluid_synth_update_midi_mode (fluid_synth_t *synth, char *name, char *value) +{ + int mode = FLUID_SYNTH_MIDI_MODE_NORMAL; + + if (FLUID_STRCMP (value, "gm") == 0) + mode = FLUID_SYNTH_MIDI_MODE_GM; + else if (FLUID_STRCMP (value, "gs") == 0) + mode = FLUID_SYNTH_MIDI_MODE_GS; + + if (fluid_synth_should_queue (synth)) + fluid_synth_queue_int_event (synth, FLUID_EVENT_QUEUE_ELEM_MIDI_MODE, mode); + else fluid_synth_set_midi_mode_LOCAL (synth, mode); + + return 0; +} + +/* Local synthesis context version of set MIDI mode */ +static void +fluid_synth_set_midi_mode_LOCAL (fluid_synth_t *synth, fluid_synth_midi_mode_t mode) +{ + char *modestr; + + if (synth->verbose) + { + switch (mode) + { + case FLUID_SYNTH_MIDI_MODE_GM: + modestr = "gm"; + break; + case FLUID_SYNTH_MIDI_MODE_GS: + modestr = "gs"; + break; + default: + modestr = "default"; + break; + } + + FLUID_LOG (FLUID_INFO, "midimode\t%s", modestr); + } + + fluid_atomic_int_set (&synth->midi_mode, mode); +} + +/* Handler for synth.midi-mode-lock setting. */ +static int +fluid_synth_update_midi_mode_lock (fluid_synth_t *synth, char *name, char *value) +{ + fluid_atomic_int_set (&synth->midi_mode_lock, + value && FLUID_STRCMP (value, "yes") == 0); + return 0; +} + /** * Process a MIDI SYSEX (system exclusive) message. * @param synth FluidSynth instance @@ -1614,15 +1699,6 @@ fluid_synth_sysex(fluid_synth_t *synth, char *data, int len, char *response, int *response_len, int *handled, int dryrun) { int avail_response = 0; - int realtime, msgid; - int bank = 0, prog, channels; - double tunedata[128]; - int keys[128]; - char name[17]; - int note, frac, frac2; - uint8 chksum; - int i, count, index; - char *dataptr; if (handled) *handled = FALSE; @@ -1637,21 +1713,68 @@ fluid_synth_sysex(fluid_synth_t *synth, char *data, int len, fluid_return_val_if_fail (len > 0, FLUID_FAILED); fluid_return_val_if_fail (!response || response_len, FLUID_FAILED); - /* Not enough data or not a universal realtime or non-realtime message? */ - if (len < 6 || (data[0] != MIDI_SYSEX_UNIV_NON_REALTIME - && data[0] != MIDI_SYSEX_UNIV_REALTIME)) - return FLUID_OK; + if (len < 6) return FLUID_OK; - /* Device ID isn't wildcard and doesn't match our device ID? */ - if (data[1] != synth->device_id && data[1] != MIDI_SYSEX_DEVICE_ID_ALL) - return FLUID_OK; + /* MIDI tuning SYSEX message? */ + if ((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) + return fluid_synth_sysex_midi_tuning (synth, data, len, response, response_len, + avail_response, handled, dryrun); + + /* General MIDI message? */ + if (data[0] == MIDI_SYSEX_UNIV_NON_REALTIME + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GM_ID) + { + switch (data[3]) + { + case MIDI_SYSEX_GM_ON: + if (!fluid_atomic_int_get (&synth->midi_mode_lock)) + fluid_synth_setstr (synth, "synth.midi-mode", "gm"); + + if (handled) *handled = FALSE; + break; + case MIDI_SYSEX_GM_OFF: + if (!fluid_atomic_int_get (&synth->midi_mode_lock)) + fluid_synth_setstr (synth, "synth.midi-mode", "normal"); + + if (handled) *handled = FALSE; + break; + } + } + + /* GS reset message? */ + if (data[0] == MIDI_SYSEX_MANUF_ROLAND + && data[1] == 0x10 && data[2] == 0x42 && data[3] == 0x12 && data[4] == 0x40 + && data[5] == 0x00 && data[6] == 0x7F && data[7] == 0x00 && data[8] == 0x41) + { + if (!fluid_atomic_int_get (&synth->midi_mode_lock)) + fluid_synth_setstr (synth, "synth.midi-mode", "gs"); + + if (handled) *handled = FALSE; + } + + return FLUID_OK; +} + +/* Handler for MIDI tuning SYSEX messages */ +static int +fluid_synth_sysex_midi_tuning (fluid_synth_t *synth, char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int realtime, msgid; + int bank = 0, prog, channels; + double tunedata[128]; + int keys[128]; + char name[17]; + int note, frac, frac2; + uint8 chksum; + int i, count, index; + char *dataptr; realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; - - /* Not a MIDI Tuning Standard message? */ - if (data[2] != MIDI_SYSEX_MIDI_TUNING_ID) - return FLUID_OK; - msgid = data[3]; switch (msgid) @@ -3405,6 +3528,9 @@ fluid_synth_process_event_queue_LOCAL (fluid_synth_t *synth, event->repl_tuning.new_tuning, event->repl_tuning.apply, TRUE); break; + case FLUID_EVENT_QUEUE_ELEM_MIDI_MODE: + fluid_synth_set_midi_mode_LOCAL (synth, event->ival); + break; } fluid_event_queue_next_outptr (queue); diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index d191521f..ae2850a5 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -77,6 +77,16 @@ enum fluid_synth_status #define SYNTH_REVERB_CHANNEL 0 #define SYNTH_CHORUS_CHANNEL 1 +/** + * Defines the current MIDI mode. + */ +typedef enum +{ + FLUID_SYNTH_MIDI_MODE_NORMAL, /**< Normal MIDI mode (percussion channel not special) */ + FLUID_SYNTH_MIDI_MODE_GM, /**< GM MIDI mode, no note-offs on percussion channel (except for long guiro and whistle) */ + FLUID_SYNTH_MIDI_MODE_GS /**< GS MIDI mode, identical to #FLUID_SYNTH_MIDI_MODE_GM at the moment */ +} fluid_synth_midi_mode_t; + /** * Structure used for sfont_info field in #fluid_synth_t for each loaded * SoundFont with the SoundFont instance and additional fields. @@ -131,6 +141,8 @@ typedef struct _fluid_sfont_info_t { * with_reverb * with_chorus * state + * midi_mode + * midi_mode_lock * cpu_load * noteid * storeid @@ -175,6 +187,8 @@ struct _fluid_synth_t int state; /**< the synthesizer state */ unsigned int ticks; /**< the number of audio samples since the start */ unsigned int start; /**< the start in msec, as returned by system clock */ + int midi_mode; /**< Current MIDI mode (#fluid_synth_midi_mode_t) */ + int midi_mode_lock; /**< TRUE if MIDI mode cannot be changed by SYSEX messages */ fluid_list_t *loaders; /**< the SoundFont loaders */ fluid_list_t *sfont_info; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */