From a2951fc37da37d6f56725667bf440aec6326ecfa Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Thu, 3 Aug 2017 16:50:08 +0200 Subject: [PATCH] Implement polyphonic key pressure (aftertouch) handling Signed-off-by: Marcus Weseloh --- fluidsynth/AUTHORS | 1 + fluidsynth/include/fluidsynth/event.h | 2 ++ fluidsynth/include/fluidsynth/synth.h | 1 + fluidsynth/src/midi/fluid_seqbind.c | 13 +++++++ fluidsynth/src/synth/fluid_chan.c | 6 +++- fluidsynth/src/synth/fluid_chan.h | 10 +++--- fluidsynth/src/synth/fluid_event.c | 21 +++++++++++ fluidsynth/src/synth/fluid_mod.c | 4 +-- fluidsynth/src/synth/fluid_synth.c | 52 +++++++++++++++++++++++++++ 9 files changed, 102 insertions(+), 8 deletions(-) diff --git a/fluidsynth/AUTHORS b/fluidsynth/AUTHORS index 910d7401..bed8bf14 100644 --- a/fluidsynth/AUTHORS +++ b/fluidsynth/AUTHORS @@ -138,3 +138,4 @@ Nick Daly David Hilvert Bernat Arlandis i Mañó Sven Meier +Marcus Weseloh diff --git a/fluidsynth/include/fluidsynth/event.h b/fluidsynth/include/fluidsynth/event.h index 41fdfbb4..872a7f57 100644 --- a/fluidsynth/include/fluidsynth/event.h +++ b/fluidsynth/include/fluidsynth/event.h @@ -56,6 +56,7 @@ enum fluid_seq_event_type { FLUID_SEQ_TIMER, /**< Timer event (DOCME) */ FLUID_SEQ_ANYCONTROLCHANGE, /**< DOCME (used for remove_events only) */ FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ + FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 1.1.7 */ FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ FLUID_SEQ_LASTEVENT /**< Defines the count of event enums */ @@ -103,6 +104,7 @@ FLUIDSYNTH_API void fluid_event_volume(fluid_event_t* evt, int channel, short va FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t* evt, int channel, short val); +FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t* evt, int channel, short key, short val); FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val); FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t* evt); diff --git a/fluidsynth/include/fluidsynth/synth.h b/fluidsynth/include/fluidsynth/synth.h index d54f0d5c..a6475921 100644 --- a/fluidsynth/include/fluidsynth/synth.h +++ b/fluidsynth/include/fluidsynth/synth.h @@ -81,6 +81,7 @@ FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t* synth, int chan, FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t* synth, int chan, int program); FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t* synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val); FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank); FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id); FLUIDSYNTH_API diff --git a/fluidsynth/src/midi/fluid_seqbind.c b/fluidsynth/src/midi/fluid_seqbind.c index 4cead706..f456d7a7 100644 --- a/fluidsynth/src/midi/fluid_seqbind.c +++ b/fluidsynth/src/midi/fluid_seqbind.c @@ -237,6 +237,14 @@ fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t* evt, fluid_seque } break; + case FLUID_SEQ_KEYPRESSURE: + { + fluid_synth_key_pressure(synth, fluid_event_get_channel(evt), + fluid_event_get_key(evt), + fluid_event_get_value(evt)); + } + break; + case FLUID_SEQ_SYSTEMRESET: { fluid_synth_system_reset(synth); @@ -314,6 +322,11 @@ fluid_sequencer_add_midi_event_to_buffer(void* data, fluid_midi_event_t* event) case CHANNEL_PRESSURE: fluid_event_channel_pressure(&evt, chan, fluid_midi_event_get_program(event)); break; + case KEY_PRESSURE: + fluid_event_key_pressure(&evt, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_value(event)); + break; case MIDI_SYSTEM_RESET: fluid_event_system_reset(&evt); break; diff --git a/fluidsynth/src/synth/fluid_chan.c b/fluidsynth/src/synth/fluid_chan.c index ede33474..cc031112 100644 --- a/fluidsynth/src/synth/fluid_chan.c +++ b/fluidsynth/src/synth/fluid_chan.c @@ -101,7 +101,6 @@ fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) { int i; - chan->key_pressure = 0; chan->channel_pressure = 0; chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ @@ -132,6 +131,11 @@ fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off) } } + /* Reset polyphonic key pressure on all voices */ + for (i = 0; i < 128; i++) { + fluid_channel_set_key_pressure(chan, i, 0); + } + /* Set RPN controllers to NULL state */ fluid_channel_set_cc (chan, RPN_LSB, 127); fluid_channel_set_cc (chan, RPN_MSB, 127); diff --git a/fluidsynth/src/synth/fluid_chan.h b/fluidsynth/src/synth/fluid_chan.h index 06d6e674..a0c3124b 100644 --- a/fluidsynth/src/synth/fluid_chan.h +++ b/fluidsynth/src/synth/fluid_chan.h @@ -41,7 +41,7 @@ struct _fluid_channel_t int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ fluid_preset_t* preset; /**< Selected preset */ - int key_pressure; /**< MIDI key pressure */ + int key_pressure[128]; /**< MIDI polyphonic key pressure */ int channel_pressure; /**< MIDI channel pressure */ int pitch_bend; /**< Current pitch bend value */ int pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ @@ -106,10 +106,10 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan); ((chan)->cc[num] = (val)) #define fluid_channel_get_cc(chan, num) \ ((chan)->cc[num]) -#define fluid_channel_get_key_pressure(chan) \ - ((chan)->key_pressure) -#define fluid_channel_set_key_pressure(chan, val) \ - ((chan)->key_pressure = (val)) +#define fluid_channel_get_key_pressure(chan, key) \ + ((chan)->key_pressure[key]) +#define fluid_channel_set_key_pressure(chan, key, val) \ + ((chan)->key_pressure[key] = (val)) #define fluid_channel_get_channel_pressure(chan) \ ((chan)->channel_pressure) #define fluid_channel_set_channel_pressure(chan, val) \ diff --git a/fluidsynth/src/synth/fluid_event.c b/fluidsynth/src/synth/fluid_event.c index 3b27cb7b..e32ed614 100644 --- a/fluidsynth/src/synth/fluid_event.c +++ b/fluidsynth/src/synth/fluid_event.c @@ -432,6 +432,27 @@ fluid_event_channel_pressure(fluid_event_t* evt, int channel, short val) evt->value = val; } +/** + * Set a sequencer event to be a polyphonic aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param val Aftertouch amount (0-127) + * @since 1.1.7 + */ +void +fluid_event_key_pressure(fluid_event_t* evt, int channel, short key, short val) +{ + evt->type = FLUID_SEQ_KEYPRESSURE; + evt->channel = channel; + if (key < 0) key = 0; + if (key > 127) key = 127; + if (val < 0) val = 0; + if (val > 127) val = 127; + evt->key = key; + evt->value = val; +} + /** * Set a sequencer event to be a midi system reset event. * @param evt Sequencer event structure diff --git a/fluidsynth/src/synth/fluid_mod.c b/fluidsynth/src/synth/fluid_mod.c index 1f3a56fa..d53c7e32 100644 --- a/fluidsynth/src/synth/fluid_mod.c +++ b/fluidsynth/src/synth/fluid_mod.c @@ -224,7 +224,7 @@ fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voic v1 = voice->key; break; case FLUID_MOD_KEYPRESSURE: - v1 = fluid_channel_get_key_pressure (chan); + v1 = fluid_channel_get_key_pressure(chan, voice->key); break; case FLUID_MOD_CHANNELPRESSURE: v1 = fluid_channel_get_channel_pressure (chan); @@ -317,7 +317,7 @@ fluid_mod_get_value(fluid_mod_t* mod, fluid_channel_t* chan, fluid_voice_t* voic v2 = voice->key; break; case FLUID_MOD_KEYPRESSURE: - v2 = fluid_channel_get_key_pressure (chan); + v2 = fluid_channel_get_key_pressure(chan, voice->key); break; case FLUID_MOD_CHANNELPRESSURE: v2 = fluid_channel_get_channel_pressure (chan); diff --git a/fluidsynth/src/synth/fluid_synth.c b/fluidsynth/src/synth/fluid_synth.c index b52015f5..cd0f9518 100644 --- a/fluidsynth/src/synth/fluid_synth.c +++ b/fluidsynth/src/synth/fluid_synth.c @@ -58,6 +58,7 @@ static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, int is_cc, int ctrl); static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int channum); +static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t* synth, int chan, int key); static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_set_preset (fluid_synth_t *synth, int chan, @@ -1717,6 +1718,52 @@ fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t* synth, int chan) return fluid_synth_modulate_voices_LOCAL (synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); } +/** + * Set the MIDI polyphonic key pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI key number (0-127) + * @param val MIDI key pressure value (0-127) + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int +fluid_synth_key_pressure(fluid_synth_t* synth, int chan, int key, int val) +{ + int result; + fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); + + fluid_channel_set_key_pressure (synth->channel[chan], key, val); + + result = fluid_synth_update_key_pressure_LOCAL (synth, chan, key); + FLUID_API_RETURN(result); +} + +/* Updates key pressure from within synthesis thread */ +static int +fluid_synth_update_key_pressure_LOCAL(fluid_synth_t* synth, int chan, int key) +{ + fluid_voice_t* voice; + int i; + int result = FLUID_OK; + + for (i = 0; i < synth->polyphony; i++) { + voice = synth->voice[i]; + + if (voice->chan == chan && voice->key == key) { + result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); + if (result != FLUID_OK) + return result; + } + } + return result; +} + /** * Set the MIDI pitch bend controller value on a MIDI channel. * @param synth FluidSynth instance @@ -4957,6 +5004,11 @@ fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event) case CHANNEL_PRESSURE: return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); + case KEY_PRESSURE: + return fluid_synth_key_pressure(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_value(event)); + case PITCH_BEND: return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));