summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_chan.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_chan.c')
-rw-r--r--libs/fluidsynth/src/fluid_chan.c805
1 files changed, 613 insertions, 192 deletions
diff --git a/libs/fluidsynth/src/fluid_chan.c b/libs/fluidsynth/src/fluid_chan.c
index c6eb723146..49ef99ecbe 100644
--- a/libs/fluidsynth/src/fluid_chan.c
+++ b/libs/fluidsynth/src/fluid_chan.c
@@ -3,16 +3,16 @@
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public License
- * as published by the Free Software Foundation; either version 2 of
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
@@ -36,256 +36,677 @@
#define SFONT_MASKVAL 0xFFC00000
-static void fluid_channel_init(fluid_channel_t* chan);
+static void fluid_channel_init(fluid_channel_t *chan);
-fluid_channel_t*
-new_fluid_channel(fluid_synth_t* synth, int num)
+fluid_channel_t *
+new_fluid_channel(fluid_synth_t *synth, int num)
{
- fluid_channel_t* chan;
+ fluid_channel_t *chan;
- chan = FLUID_NEW(fluid_channel_t);
- if (chan == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return NULL;
- }
+ chan = FLUID_NEW(fluid_channel_t);
- chan->synth = synth;
- chan->channum = num;
- chan->preset = NULL;
- chan->tuning = NULL;
+ if(chan == NULL)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ chan->synth = synth;
+ chan->channum = num;
+ chan->preset = NULL;
+ chan->tuning = NULL;
- fluid_channel_init(chan);
- fluid_channel_init_ctrl(chan, 0);
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan, 0);
- return chan;
+ return chan;
}
static void
-fluid_channel_init(fluid_channel_t* chan)
+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;
-
- chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL
- | prognum << PROG_SHIFTVAL;
-
- newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum);
- fluid_channel_set_preset(chan, newpreset);
-
- chan->interp_method = FLUID_INTERP_DEFAULT;
- chan->tuning_bank = 0;
- chan->tuning_prog = 0;
- chan->nrpn_select = 0;
- chan->nrpn_active = 0;
+ fluid_preset_t *newpreset;
+ int i, prognum, banknum;
+
+ chan->sostenuto_orderid = 0;
+ /*--- Init poly/mono modes variables --------------------------------------*/
+ chan->mode = 0;
+ chan->mode_val = 0;
+
+ /* monophonic list initialization */
+ for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++)
+ {
+ chan->monolist[i].next = i + 1;
+ }
- if (chan->tuning)
- {
- fluid_tuning_unref (chan->tuning, 1);
- chan->tuning = NULL;
- }
+ chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */
+ chan->i_last = chan->n_notes = 0; /* clears the list */
+ chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */
+ fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */
+ /*---*/
+ chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */
+ chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */
+ chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */
+ /*--- End of poly/mono initialization --------------------------------------*/
+
+ 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;
+
+ chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL
+ | prognum << PROG_SHIFTVAL;
+
+ newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum);
+ fluid_channel_set_preset(chan, newpreset);
+
+ chan->interp_method = FLUID_INTERP_DEFAULT;
+ chan->tuning_bank = 0;
+ chan->tuning_prog = 0;
+ chan->nrpn_select = 0;
+ chan->nrpn_active = 0;
+
+ if(chan->tuning)
+ {
+ fluid_tuning_unref(chan->tuning, 1);
+ chan->tuning = NULL;
+ }
}
/*
- @param is_all_ctrl_off if nonzero, only resets some controllers, according to
- http://www.midi.org/techspecs/rp15.php
+ @param is_all_ctrl_off if nonzero, only resets some controllers, according to
+ http://www.midi.org/techspecs/rp15.php
*/
void
-fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off)
+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 */
-
- for (i = 0; i < GEN_LAST; i++) {
- chan->gen[i] = 0.0f;
- chan->gen_abs[i] = 0;
- }
-
- if (is_all_ctrl_off) {
- for (i = 0; i < ALL_SOUND_OFF; i++) {
- if (i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) {
- continue;
- }
- if (i >= SOUND_CTRL1 && i <= SOUND_CTRL10) {
- continue;
- }
- if (i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB ||
- i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB) {
- continue;
- }
-
- fluid_channel_set_cc (chan, i, 0);
- }
- }
- else {
- for (i = 0; i < 128; i++) {
- fluid_channel_set_cc (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);
-
- /* Set NRPN controllers to NULL state */
- fluid_channel_set_cc (chan, NRPN_LSB, 127);
- fluid_channel_set_cc (chan, NRPN_MSB, 127);
-
- /* Expression (MSB & LSB) */
- fluid_channel_set_cc (chan, EXPRESSION_MSB, 127);
- fluid_channel_set_cc (chan, EXPRESSION_LSB, 127);
-
- if (!is_all_ctrl_off) {
-
- chan->pitch_wheel_sensitivity = 2; /* two semi-tones */
-
- /* Just like panning, a value of 64 indicates no change for sound ctrls */
- for (i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) {
- fluid_channel_set_cc (chan, i, 64);
- }
-
- /* Volume / initial attenuation (MSB & LSB) */
- fluid_channel_set_cc (chan, VOLUME_MSB, 100);
- fluid_channel_set_cc (chan, VOLUME_LSB, 0);
-
- /* Pan (MSB & LSB) */
- fluid_channel_set_cc (chan, PAN_MSB, 64);
- fluid_channel_set_cc (chan, PAN_LSB, 0);
-
- /* Reverb */
- /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */
- /* Note: although XG standard specifies the default amount of reverb to
- be 40, most people preferred having it at zero.
- See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */
- }
+ int i;
+
+ chan->channel_pressure = 0;
+ chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */
+
+ for(i = 0; i < GEN_LAST; i++)
+ {
+ chan->gen[i] = 0.0f;
+ chan->gen_abs[i] = 0;
+ }
+
+ if(is_all_ctrl_off)
+ {
+ for(i = 0; i < ALL_SOUND_OFF; i++)
+ {
+ if(i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5)
+ {
+ continue;
+ }
+
+ if(i >= SOUND_CTRL1 && i <= SOUND_CTRL10)
+ {
+ continue;
+ }
+
+ if(i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB ||
+ i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB ||
+ i == BALANCE_MSB || i == BALANCE_LSB
+ )
+ {
+ continue;
+ }
+
+ fluid_channel_set_cc(chan, i, 0);
+ }
+ }
+ else
+ {
+ for(i = 0; i < 128; i++)
+ {
+ fluid_channel_set_cc(chan, i, 0);
+ }
+
+ fluid_channel_clear_portamento(chan); /* Clear PTC receive */
+ chan->previous_cc_breath = 0;/* Reset previous breath */
+ }
+
+ /* 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);
+
+ /* Set NRPN controllers to NULL state */
+ fluid_channel_set_cc(chan, NRPN_LSB, 127);
+ fluid_channel_set_cc(chan, NRPN_MSB, 127);
+
+ /* Expression (MSB & LSB) */
+ fluid_channel_set_cc(chan, EXPRESSION_MSB, 127);
+ fluid_channel_set_cc(chan, EXPRESSION_LSB, 127);
+
+ if(!is_all_ctrl_off)
+ {
+
+ chan->pitch_wheel_sensitivity = 2; /* two semi-tones */
+
+ /* Just like panning, a value of 64 indicates no change for sound ctrls */
+ for(i = SOUND_CTRL1; i <= SOUND_CTRL10; i++)
+ {
+ fluid_channel_set_cc(chan, i, 64);
+ }
+
+ /* Volume / initial attenuation (MSB & LSB) */
+ fluid_channel_set_cc(chan, VOLUME_MSB, 100);
+ fluid_channel_set_cc(chan, VOLUME_LSB, 0);
+
+ /* Pan (MSB & LSB) */
+ fluid_channel_set_cc(chan, PAN_MSB, 64);
+ fluid_channel_set_cc(chan, PAN_LSB, 0);
+
+ /* Balance (MSB & LSB) */
+ fluid_channel_set_cc(chan, BALANCE_MSB, 64);
+ fluid_channel_set_cc(chan, BALANCE_LSB, 0);
+
+ /* Reverb */
+ /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */
+ /* Note: although XG standard specifies the default amount of reverb to
+ be 40, most people preferred having it at zero.
+ See http://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */
+ }
}
/* Only called by delete_fluid_synth(), so no need to queue a preset free event */
-int
-delete_fluid_channel(fluid_channel_t* chan)
+void
+delete_fluid_channel(fluid_channel_t *chan)
{
- if (chan->preset) delete_fluid_preset (chan->preset);
- FLUID_FREE(chan);
- return FLUID_OK;
+ fluid_return_if_fail(chan != NULL);
+
+ FLUID_FREE(chan);
}
/* FIXME - Calls fluid_channel_init() potentially in synthesis context */
void
-fluid_channel_reset(fluid_channel_t* chan)
+fluid_channel_reset(fluid_channel_t *chan)
{
- fluid_channel_init(chan);
- fluid_channel_init_ctrl(chan, 0);
+ fluid_channel_init(chan);
+ fluid_channel_init_ctrl(chan, 0);
}
/* Should only be called from synthesis context */
int
-fluid_channel_set_preset(fluid_channel_t* chan, fluid_preset_t* preset)
+fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset)
{
+ fluid_sfont_t *sfont;
- fluid_preset_notify (chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
+ if(chan->preset == preset)
+ {
+ return FLUID_OK;
+ }
- if (chan->preset) {
- fluid_sfont_t *sfont;
- sfont = chan->preset->sfont;
- delete_fluid_preset (chan->preset);
- fluid_synth_sfont_unref (chan->synth, sfont); /* -- unref preset's SoundFont */
- }
-
- chan->preset = preset;
+ if(chan->preset)
+ {
+ sfont = chan->preset->sfont;
+ sfont->refcount--;
+ }
+
+ fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum);
- fluid_preset_notify (preset, FLUID_PRESET_SELECTED, chan->channum);
+ chan->preset = preset;
+
+ if(preset)
+ {
+ sfont = preset->sfont;
+ sfont->refcount++;
+ }
- return FLUID_OK;
+ fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum);
+
+ return FLUID_OK;
}
/* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */
void
-fluid_channel_set_sfont_bank_prog(fluid_channel_t* chan, int sfontnum,
+fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfontnum,
int banknum, int prognum)
{
- int oldval, newval, oldmask;
+ int oldval, newval, oldmask;
- newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0)
- | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0)
- | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0);
+ newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0)
+ | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0)
+ | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0);
- oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL)
- | ((banknum != -1) ? 0 : BANK_MASKVAL)
- | ((prognum != -1) ? 0 : PROG_MASKVAL);
+ oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL)
+ | ((banknum != -1) ? 0 : BANK_MASKVAL)
+ | ((prognum != -1) ? 0 : PROG_MASKVAL);
- oldval = chan->sfont_bank_prog;
- newval = (newval & ~oldmask) | (oldval & oldmask);
- chan->sfont_bank_prog = newval;
+ oldval = chan->sfont_bank_prog;
+ newval = (newval & ~oldmask) | (oldval & oldmask);
+ chan->sfont_bank_prog = newval;
}
/* Set bank LSB 7 bits */
void
-fluid_channel_set_bank_lsb(fluid_channel_t* chan, int banklsb)
+fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb)
{
- int oldval, newval, style;
-
- style = chan->synth->bank_select;
- if (style == FLUID_BANK_STYLE_GM ||
- style == FLUID_BANK_STYLE_GS)
- return; /* ignored */
-
- oldval = chan->sfont_bank_prog;
- if (style == FLUID_BANK_STYLE_XG)
- newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL);
- else /* style == FLUID_BANK_STYLE_MMA */
- newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL);
- chan->sfont_bank_prog = newval;
+ int oldval, newval, style;
+
+ style = chan->synth->bank_select;
+
+ if(style == FLUID_BANK_STYLE_GM ||
+ style == FLUID_BANK_STYLE_GS)
+ {
+ return; /* ignored */
+ }
+
+ oldval = chan->sfont_bank_prog;
+
+ if(style == FLUID_BANK_STYLE_XG)
+ {
+ newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL);
+ }
+ else /* style == FLUID_BANK_STYLE_MMA */
+ {
+ newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL);
+ }
+
+ chan->sfont_bank_prog = newval;
}
/* Set bank MSB 7 bits */
void
-fluid_channel_set_bank_msb(fluid_channel_t* chan, int bankmsb)
+fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb)
{
- int oldval, newval, style;
-
- style = chan->synth->bank_select;
-
- if (style == FLUID_BANK_STYLE_XG)
- {
- /* XG bank, do drum-channel auto-switch */
- /* The number "120" was based on several keyboards having drums at 120 - 127,
- reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */
- chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC;
- return;
- }
-
- if (style == FLUID_BANK_STYLE_GM ||
- chan->channel_type == CHANNEL_TYPE_DRUM)
- return; /* ignored */
-
- oldval = chan->sfont_bank_prog;
- if (style == FLUID_BANK_STYLE_GS)
- newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL);
- else /* style == FLUID_BANK_STYLE_MMA */
- newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7));
- chan->sfont_bank_prog = newval;
+ int oldval, newval, style;
+
+ style = chan->synth->bank_select;
+
+ if(style == FLUID_BANK_STYLE_XG)
+ {
+ /* XG bank, do drum-channel auto-switch */
+ /* The number "120" was based on several keyboards having drums at 120 - 127,
+ reference: http://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */
+ chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC;
+ return;
+ }
+
+ if(style == FLUID_BANK_STYLE_GM ||
+ chan->channel_type == CHANNEL_TYPE_DRUM)
+ {
+ return; /* ignored */
+ }
+
+ oldval = chan->sfont_bank_prog;
+
+ if(style == FLUID_BANK_STYLE_GS)
+ {
+ newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL);
+ }
+ else /* style == FLUID_BANK_STYLE_MMA */
+ {
+ newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7));
+ }
+
+ chan->sfont_bank_prog = newval;
}
/* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */
void
-fluid_channel_get_sfont_bank_prog(fluid_channel_t* chan, int *sfont,
+fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont,
int *bank, int *prog)
{
- int sfont_bank_prog;
+ int sfont_bank_prog;
- sfont_bank_prog = chan->sfont_bank_prog;
+ sfont_bank_prog = chan->sfont_bank_prog;
+
+ if(sfont)
+ {
+ *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL;
+ }
+
+ if(bank)
+ {
+ *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL;
+ }
+
+ if(prog)
+ {
+ *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL;
+ }
+}
+
+/**
+ * Updates legato/ staccato playing state
+ * The function is called:
+ * - on noteon before adding a note into the monolist.
+ * - on noteoff after removing a note out of the monolist.
+ * @param chan fluid_channel_t.
+*/
+static void
+fluid_channel_update_legato_staccato_state(fluid_channel_t *chan)
+{
+ /* Updates legato/ staccato playing state */
+ if(chan->n_notes)
+ {
+ chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */
+ }
+ else
+ {
+ chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */
+ }
+}
+
+/**
+ * Adds a note into the monophonic list. The function is part of the legato
+ * detector. fluid_channel_add_monolist() is intended to be called by
+ * fluid_synth_noteon_mono_LOCAL().
+ *
+ * When a note is added at noteOn each element is use in the forward direction
+ * and indexed by i_last variable.
+ *
+ * @param chan fluid_channel_t.
+ * @param key MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127, 0=noteoff).
+ * @param onenote. When 1 the function adds the note but the monophonic list
+ * keeps only one note (used on noteOn poly).
+ * Note: i_last index keeps a trace of the most recent note added.
+ * prev_note keeps a trace of the note prior i_last note.
+ * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state.
+ *
+ * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+*/
+void
+fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key,
+ unsigned char vel, unsigned char onenote)
+{
+ unsigned char i_last = chan->i_last;
+ /* Updates legato/ staccato playing state */
+ fluid_channel_update_legato_staccato_state(chan);
+
+ if(chan->n_notes)
+ {
+ /* keeps trace of the note prior last note */
+ chan->prev_note = chan->monolist[i_last].note;
+ }
+
+ /* moves i_last forward before writing new note */
+ i_last = chan->monolist[i_last].next;
+ chan->i_last = i_last; /* now ilast indexes the last note */
+ chan->monolist[i_last].note = key; /* we save note and velocity */
+ chan->monolist[i_last].vel = vel;
+
+ if(onenote)
+ {
+ /* clears monolist before one note addition */
+ chan->i_first = i_last;
+ chan->n_notes = 0;
+ }
+
+ if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST)
+ {
+ chan->n_notes++; /* updates n_notes */
+ }
+ else
+ {
+ /* The end of buffer is reach. So circular motion for i_first */
+ /* i_first index is moved forward */
+ chan->i_first = chan->monolist[i_last].next;
+ }
+}
+
+/**
+ * Searching a note in the monophonic list. The function is part of the legato
+ * detector. fluid_channel_search_monolist() is intended to be called by
+ * fluid_synth_noteoff_mono_LOCAL().
+ *
+ * The search starts from the first note in the list indexed by i_first
+
+ * @param chan fluid_channel_t.
+ * @param key MIDI note number (0-127) to search.
+ * @param i_prev pointer on returned index of the note prior the note to search.
+ * @return index of the note if find, FLUID_FAILED otherwise.
+ *
+ */
+int
+fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev)
+{
+ short n = chan->n_notes; /* number of notes in monophonic list */
+ short j, i = chan->i_first; /* searching starts from i_first included */
+
+ for(j = 0 ; j < n ; j++)
+ {
+ if(chan->monolist[i].note == key)
+ {
+ if(i == chan->i_first)
+ {
+ /* tracking index of the previous note (i_prev) */
+ for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++)
+ {
+ j = chan->monolist[j].next;
+ }
+
+ * i_prev = j; /* returns index of the previous note */
+ }
+
+ return i; /* returns index of the note to search */
+ }
+
+ * i_prev = i; /* tracking index of the previous note (i_prev) */
+ i = chan->monolist[i].next; /* next element */
+ }
+
+ return FLUID_FAILED; /* not found */
+}
+
+/**
+ * removes a note from the monophonic list. The function is part of
+ * the legato detector.
+ * fluid_channel_remove_monolist() is intended to be called by
+ * fluid_synth_noteoff_mono_LOCAL().
+ *
+ * When a note is removed at noteOff the element concerned is fast unlinked
+ * and relinked after the i_last element.
+ *
+ * @param chan fluid_channel_t.
+ * @param
+ * i, index of the note to remove. If i is invalid or the list is
+ * empty, the function do nothing and returns FLUID_FAILED.
+ * @param
+ * On input, i_prev is a pointer on index of the note previous i.
+ * On output i_prev is a pointer on index of the note previous i if i is the last note
+ * in the list,FLUID_FAILED otherwise. When the returned index is valid it means
+ * a legato detection on noteoff.
+ *
+ * Note: the following variables in Channel keeps trace of the situation.
+ * - i_last index keeps a trace of the most recent note played even if
+ * the list is empty.
+ * - prev_note keeps a trace of the note removed if it is i_last.
+ * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state.
+ *
+ * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+ */
+void
+fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev)
+{
+ unsigned char i_last = chan->i_last;
+
+ /* checks if index is valid */
+ if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes)
+ {
+ * i_prev = FLUID_FAILED;
+ }
+
+ /* The element is about to be removed and inserted between i_last and next */
+ /* Note: when i is egal to i_last or egal to i_first, removing/inserting
+ isn't necessary */
+ if(i == i_last)
+ {
+ /* Removing/Inserting isn't necessary */
+ /* keeps trace of the note prior last note */
+ chan->prev_note = chan->monolist[i_last].note;
+ /* moves i_last backward to the previous */
+ chan->i_last = *i_prev; /* i_last index is moved backward */
+ }
+ else
+ {
+ /* i is before i_last */
+ if(i == chan->i_first)
+ {
+ /* Removing/inserting isn't necessary */
+ /* i_first index is moved forward to the next element*/
+ chan->i_first = chan->monolist[i].next;
+ }
+ else
+ {
+ /* i is between i_first and i_last */
+ /* Unlinks element i and inserts after i_last */
+ chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */
+ /*inserts i after i_last */
+ chan->monolist[i].next = chan->monolist[i_last].next;
+ chan->monolist[i_last].next = i;
+ }
+
+ * i_prev = FLUID_FAILED;
+ }
+
+ chan->n_notes--; /* updates the number of note in the list */
+ /* Updates legato/ staccato playing state */
+ fluid_channel_update_legato_staccato_state(chan);
+}
+
+/**
+ * On noteOff on a polyphonic channel,the monophonic list is fully flushed.
+ *
+ * @param chan fluid_channel_t.
+ * Note: i_last index keeps a trace of the most recent note played even if
+ * the list is empty.
+ * prev_note keeps a trace of the note i_last .
+ * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing.
+ */
+void fluid_channel_clear_monolist(fluid_channel_t *chan)
+{
+ /* keeps trace off the most recent note played */
+ chan->prev_note = chan->monolist[chan->i_last].note;
+
+ /* flushes the monolist */
+ chan->i_first = chan->monolist[chan->i_last].next;
+ chan->n_notes = 0;
+ /* Update legato/ sataccato playing state */
+ chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */
+}
+
+/**
+ * On noteOn on a polyphonic channel,adds the note into the monophonic list
+ * keeping only this note.
+ * @param
+ * chan fluid_channel_t.
+ * key, vel, note and velocity added in the monolist
+ * Note: i_last index keeps a trace of the most recent note inserted.
+ * prev_note keeps a trace of the note prior i_last note.
+ * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing.
+ */
+void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key,
+ unsigned char vel)
+{
+ fluid_channel_add_monolist(chan, key, vel, 1);
+}
+
+/**
+ * The function changes the state (Valid/Invalid) of the previous note played in
+ * a staccato manner (fluid_channel_prev_note()).
+ * When potamento mode 'each note' or 'staccato only' is selected, on next
+ * noteOn a portamento will be started from the most recent note played
+ * staccato.
+ * It will be possible that it isn't appropriate. To give the musician the
+ * possibility to choose a portamento from this note , prev_note will be forced
+ * to invalid state on noteOff if portamento pedal is Off.
+ *
+ * The function is intended to be called when the following event occurs:
+ * - On noteOff (in poly or mono mode), to mark prev_note invalid.
+ * - On Portamento Off(in poly or mono mode), to mark prev_note invalid.
+ * @param chan fluid_channel_t.
+ */
+void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan)
+{
+ /* checks if the playing is staccato */
+ if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING))
+ {
+
+ /* checks if portamento pedal is off */
+ if(! fluid_channel_portamento(chan))
+ {
+ /* forces prev_note invalid */
+ fluid_channel_clear_prev_note(chan);
+ }
+ }
+
+ /* else prev_note still remains valid for next fromkey portamento */
+}
+
+/**
+ * The function handles poly/mono commutation on legato pedal On/Off.
+ * @param chan fluid_channel_t.
+ * @param value, value of the CC legato.
+ */
+void fluid_channel_cc_legato(fluid_channel_t *chan, int value)
+{
+ /* Special handling of the monophonic list */
+ if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */
+ {
+ if(value < 64) /* legato is released */
+ {
+ /* returns from monophonic to polyphonic with notes in the monophonic list */
+
+ /* The monophonic list is flushed keeping last note only
+ Note: i_last index keeps a trace of the most recent note played.
+ prev_note keeps a trace of the note i_last.
+ FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing.
+ */
+ chan->i_first = chan->i_last;
+ chan->n_notes = 1;
+ }
+ else /* legato is depressed */
+ {
+ /* Inters in monophonic from polyphonic with note in monophonic list */
+ /* Stops the running note to remain coherent with Breath Sync mode */
+ if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan))
+ {
+ fluid_synth_noteoff_monopoly(chan->synth, chan->channum,
+ fluid_channel_last_note(chan), 1);
+ }
+ }
+ }
+}
+
+/**
+ * The function handles CC Breath On/Off detection. When a channel is in
+ * Breath Sync mode and in monophonic playing, the breath controller allows
+ * to trigger noteon/noteoff note when the musician starts to breath (noteon) and
+ * stops to breath (noteoff).
+ * @param chan fluid_channel_t.
+ * @param value, value of the CC Breath..
+ */
+void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value)
+{
+ if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) &&
+ (chan->n_notes))
+ {
+ /* The monophonic list isn't empty */
+ if((value > 0) && (chan->previous_cc_breath == 0))
+ {
+ /* CC Breath On detection */
+ fluid_synth_noteon_mono_staccato(chan->synth, chan->channum,
+ fluid_channel_last_note(chan),
+ fluid_channel_last_vel(chan));
+ }
+ else if((value == 0) && (chan->previous_cc_breath > 0))
+ {
+ /* CC Breath Off detection */
+ fluid_synth_noteoff_monopoly(chan->synth, chan->channum,
+ fluid_channel_last_note(chan), 1);
+ }
+ }
- if (sfont) *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL;
- if (bank) *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL;
- if (prog) *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL;
+ chan->previous_cc_breath = value;
}