Add sostenuto pedal functionality (ticket #47, #136)

Apart from adding sostenuto pedal functionality, this also changes
the behaviour of dampening currently sounding notes on the same key
to ignore pedals.

Signed-off-by: David Henningsson <diwic@ubuntu.com>
This commit is contained in:
Jean-Jacques Ceresa 2015-04-07 13:48:04 +02:00 committed by David Henningsson
parent f11592a8a4
commit 048c51c4ab
5 changed files with 94 additions and 17 deletions

View file

@ -67,6 +67,8 @@ fluid_channel_init(fluid_channel_t* chan)
fluid_preset_t *newpreset;
int prognum, banknum;
chan->sostenuto_orderid = 0;
chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC;
prognum = 0;
banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0;

View file

@ -48,6 +48,11 @@ struct _fluid_channel_t
int cc[128]; /**< MIDI controller values */
/* Sostenuto order id gives the order of SostenutoOn event.
This value is useful to known when the sostenuto pedal is depressed
(before or after a key note). We need to compare SostenutoOrderId with voice id.
*/
unsigned int sostenuto_orderid;
int interp_method; /**< Interpolation method (enum fluid_interp) */
fluid_tuning_t* tuning; /**< Micro tuning */
int tuning_bank; /**< Current tuning bank number */
@ -134,6 +139,7 @@ int fluid_channel_get_interp_method(fluid_channel_t* chan);
#define fluid_channel_set_tuning_prog(chan, prog) \
((chan)->tuning_prog = (prog))
#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64)
#define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64)
#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; }
#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n])
#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n])

View file

@ -42,7 +42,6 @@ static void fluid_synth_init(void);
static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key,
int vel);
static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key);
static int fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan);
static int fluid_synth_cc_LOCAL(fluid_synth_t* synth, int channum, int num);
static int fluid_synth_update_device_id (fluid_synth_t *synth, char *name,
int value);
@ -950,7 +949,6 @@ fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel)
advance it to the release phase. */
fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key);
synth->storeid = synth->noteid++;
return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
}
@ -1007,9 +1005,10 @@ fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key)
return status;
}
/* Damp all voices on a channel (turn notes off) */
/* Damp voices on a channel (turn notes off), if they're sustained by
sustain pedal */
static int
fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan)
fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan)
{
fluid_voice_t* voice;
int i;
@ -1018,12 +1017,31 @@ fluid_synth_damp_voices_LOCAL(fluid_synth_t* synth, int chan)
voice = synth->voice[i];
if ((voice->chan == chan) && _SUSTAINED(voice))
fluid_voice_noteoff(voice);
fluid_voice_release(voice);
}
return FLUID_OK;
}
/* Damp voices on a channel (turn notes off), if they're sustained by
sostenuto pedal */
static int
fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan)
{
fluid_voice_t* voice;
int i;
for (i = 0; i < synth->polyphony; i++) {
voice = synth->voice[i];
if ((voice->chan == chan) && _HELD_BY_SOSTENUTO(voice))
fluid_voice_release(voice);
}
return FLUID_OK;
}
/**
* Send a MIDI controller event on a MIDI channel.
* @param synth FluidSynth instance
@ -1060,8 +1078,20 @@ fluid_synth_cc_LOCAL (fluid_synth_t* synth, int channum, int num)
switch (num) {
case SUSTAIN_SWITCH:
if (value < 64) fluid_synth_damp_voices_LOCAL (synth, channum);
/* Release voices if Sustain switch is released */
if (value < 64) /* Sustain is released */
fluid_synth_damp_voices_by_sustain_LOCAL (synth, channum);
break;
case SOSTENUTO_SWITCH:
/* Release voices if Sostetuno switch is released */
if (value < 64) /* Sostenuto is released */
fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum);
else /* Sostenuto is depressed */
/* Update sostenuto order id when pedaling on Sostenuto */
chan->sostenuto_orderid = synth->noteid; /* future voice id value */
break;
case BANK_SELECT_MSB:
fluid_channel_set_bank_msb (chan, value & 0x7F);
break;
@ -3933,13 +3963,19 @@ fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan,
int i;
fluid_voice_t* voice;
synth->storeid = synth->noteid++;
for (i = 0; i < synth->polyphony; i++) {
voice = synth->voice[i];
if (_PLAYING(voice)
&& (voice->chan == chan)
&& (voice->key == key)
&& (fluid_voice_get_id(voice) != synth->noteid)) {
fluid_voice_noteoff(voice);
/* Id of voices that was sustained by sostenuto */
if(_HELD_BY_SOSTENUTO(voice))
synth->storeid = voice->id;
/* Force the voice into release stage (pedaling is ignored) */
fluid_voice_release(voice);
}
}
}

View file

@ -1182,24 +1182,46 @@ int fluid_voice_modulate_all(fluid_voice_t* voice)
return FLUID_OK;
}
/*
Force the voice into release stage. Useful anywhere a voice
needs to be damped even if pedals (sustain sostenuto) are depressed.
See fluid_synth_damp_voices_by_sustain_LOCAL(),
fluid_synth_damp_voices_by_sostenuto_LOCAL,
fluid_voice_noteoff().
*/
void
fluid_voice_release(fluid_voice_t* voice)
{
unsigned int at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
voice->has_noteoff = 1; // voice is marked as noteoff occured
}
/*
* fluid_voice_noteoff
*/
int
fluid_voice_noteoff(fluid_voice_t* voice)
{
unsigned int at_tick;
fluid_channel_t* channel;
fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref);
channel = voice->channel;
if (voice->channel && fluid_channel_sustained(voice->channel)) {
voice->status = FLUID_VOICE_SUSTAINED;
} else {
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
voice->has_noteoff = 1;
/* Sustain a note under Sostenuto pedal */
if (fluid_channel_sostenuto(channel) &&
channel->sostenuto_orderid > voice->id)
{ // Sostenuto depressed after note
voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO;
}
/* Or sustain a note under Sustain pedal */
else if (fluid_channel_sustained(channel)) {
voice->status = FLUID_VOICE_SUSTAINED;
}
/* Or force the voice to release stage */
else
fluid_voice_release(voice);
return FLUID_OK;
}
@ -1569,7 +1591,7 @@ fluid_voice_get_overflow_prio(fluid_voice_t* voice,
else if (voice->has_noteoff) {
/* Noteoff has */
this_voice_prio += score->released;
} else if (_SUSTAINED(voice)){
} else if (_SUSTAINED(voice) || _HELD_BY_SOSTENUTO(voice)) {
/* This voice is still active, since the sustain pedal is held down.
* Consider it less important than non-sustained channels.
* This decision is somehow subjective. But usually the sustain pedal

View file

@ -48,7 +48,8 @@ enum fluid_voice_status
{
FLUID_VOICE_CLEAN,
FLUID_VOICE_ON,
FLUID_VOICE_SUSTAINED,
FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */
FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */
FLUID_VOICE_OFF
};
@ -142,6 +143,13 @@ int fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value);
function.*/
void fluid_voice_update_param(fluid_voice_t* voice, int gen);
/** fluid_voice_release
Force the voice into release stage. Usefuf anywhere a voice
needs to be damped even if pedals (sustain sostenuto) are depressed.
See fluid_synth_damp_voices_LOCAL(), fluid_synth_damp_voices_by_sostenuto_LOCAL,
fluid_voice_noteoff(), fluid_synth_stop_LOCAL().
*/
void fluid_voice_release(fluid_voice_t* voice);
int fluid_voice_noteoff(fluid_voice_t* voice);
int fluid_voice_off(fluid_voice_t* voice);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t* voice);
@ -183,13 +191,16 @@ fluid_voice_unlock_rvoice(fluid_voice_t* voice)
#define fluid_voice_get_chan(_voice) (_voice)->chan
#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED))
#define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || \
_SUSTAINED(voice) || \
_HELD_BY_SOSTENUTO(voice) )
/* A voice is 'ON', if it has not yet received a noteoff
* event. Sending a noteoff event will advance the envelopes to
* section 5 (release). */
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _HELD_BY_SOSTENUTO(voice) ((voice)->status == FLUID_VOICE_HELD_BY_SOSTENUTO)
#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \
(((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)))
//#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)