summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_synth_monopoly.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_synth_monopoly.c')
-rw-r--r--libs/fluidsynth/src/fluid_synth_monopoly.c727
1 files changed, 727 insertions, 0 deletions
diff --git a/libs/fluidsynth/src/fluid_synth_monopoly.c b/libs/fluidsynth/src/fluid_synth_monopoly.c
new file mode 100644
index 0000000000..b7828af5b7
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_synth_monopoly.c
@@ -0,0 +1,727 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * 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
+ * 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.
+ *
+ * You should have received a copy of the GNU Library 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
+ */
+
+#include "fluid_synth.h"
+#include "fluid_chan.h"
+#include "fluid_defsfont.h"
+
+
+/******************************************************************************
+ The legato detector is composed as this,
+ variables:
+ - monolist: monophonic list variable.
+ - prev_note: to store the most recent note before adding on noteon or before
+ removing on noteoff.
+ - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on
+ legato or staccato playing.
+ functions:
+ - fluid_channel_add_monolist(), for inserting a new note.
+ - fluid_channel_search_monolist(), for searching the position of a note
+ into the list.
+ - fluid_channel_remove_monolist(), for removing a note from the list.
+
+ The monophonic list
+ +------------------------------------------------+
+ | +----+ +----+ +----+ +----+ |
+ | |note| |note| |note| |note| |
+ +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+
+ +----+ +----+ +----+ +----+
+ /|\ /|\
+ | |
+ i_first i_last
+
+ The list allows an easy automatic detection of a legato passage when it is
+ played on a MIDI keyboard input device.
+ It is useful also when the input device is an ewi (electronic wind instrument)
+ or evi (electronic valve instrument) and these instruments are unable to send
+ MIDI CC legato on/off.
+
+ The list memorizes the notes in playing order.
+ - (a) On noteOn n2, if a previous note n1 exists, there is a legato
+ detection with n1 (with or without portamento from n1 to n2 See note below).
+ - (b) On noteOff of the running note n2, if a previous note n1 exists,
+ there is a legato detection from n2 to n1, allowing fast trills playing
+ (with or without portamento from n2 to n1. See note below).
+
+ Notes in the list are inserted to the end of the list that works like a
+ circular buffer.The features are:
+
+ 1) It is always possible to play an infinite legato passage in
+ direct order (n1_On,n2_On,n3_On,....).
+
+ 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in
+ fast trills playing as the list memorizes 10 most recent notes.
+
+ 3) Playing an infinite lagato passage in ascendant or descendant order,
+ without playing trills is always possible using the usual way like this:
+ First we begin with an ascendant passage,
+ n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then
+ we continue with a descendant passage
+ (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on
+
+ Each MIDI channel have a legato detector.
+
+ Note:
+ Portamento is a feature independant of the legato detector. So
+ portamento isn't part of the lagato detector. However portamento
+ (when enabled) is triggered at noteOn (like legato). Like in legato
+ situation it is usual to have a portamento from a note 'fromkey' to another
+ note 'tokey'. Portamento fromkey note choice is determined at noteOn by
+ fluid_synth_get_fromkey_portamento_legato() (see below).
+
+ More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+******************************************************************************/
+
+
+/*****************************************************************************
+ Portamento related functions in Poly or Mono mode
+******************************************************************************/
+
+/**
+ * fluid_synth_get_fromkey_portamento_legato returns two informations:
+ * - fromkey note for portamento.
+ * - fromkey note for legato.
+ * +-----> fromkey_portamento
+ * ______|________
+ * portamento modes >------->| |
+ * | get_fromkey |
+ * Porta.on/off >------------------------->|_______________|
+ * (PTC) |
+ * +-----> fromkey_legato
+ *
+ * The functions is intended to be call on noteOn mono
+ * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato()
+ * -------
+ * 1)The function determines if a portamento must occur on next noteOn.
+ * The value returned is 'fromkey portamento' which is the pitchstart key
+ * of a portamento, as function of PTC or (default_fromkey, prev_note) both
+ * if Portamento On. By order of precedence the result is:
+ * 1.1) PTC have precedence over Portamento On.
+ * If CC PTC has been received, its value supersedes and any
+ * portamento pedal On, default_fromkey,prev_note or portamento mode.
+ * 1.2) Otherwise ,when Portamento On the function takes the following value:
+ * - default_fromkey if valid
+ * - otherwise prev_note(prev_note is the note prior the most recent
+ * note played).
+ * Then portamento mode is applied to validate the value choosen.
+ * Where portamento mode is:
+ * - each note, a portamento occurs on each note.
+ * - legato only, portamento only on notes played legato.
+ * - staccato only, portamento only on notes played staccato.
+ * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled).
+ * ------
+ * 2)The function determines if a legato playing must occur on next noteOn.
+ * 'fromkey legato note' is returned as a function of default_fromkey, PTC,
+ * current mono/poly mode,actual 'staccato/legato' playing state and prev_note.
+ * By order of precedence the result is:
+ * 2.1) If valid, default_fromkey have precedence over any others values.
+ * 2.2) Otherwise if CC PTC has been received its value is returned.
+ * 2.3) Otherwise fromkey legato is determined from the mono/poly mode,
+ * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note
+ * as this:
+ * - in (poly/Mono) staccato , INVALID_NOTE is returned.
+ * - in poly legato , actually we don't want playing legato. So
+ * INVALID_NOTE is returned.
+ * - in mono legato , prev_note is returned.
+ *
+ * On input
+ * @param chan fluid_channel_t.
+ * @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato'
+ * note (see description above).
+ *
+ * @return
+ * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento.
+ * If valid,it means that portamento is enabled .
+ *
+ * 2)The 'fromkey legato' note is returned.
+ *
+ * Notes about usage:
+ * The function is intended to be called when the following event occurs:
+ * - On noteOn (Poly or Mono) after insertion in the monophonic list.
+ * - On noteOff(mono legato playing). In this case, default_fromkey must be valid.
+ *
+ * Typical calling usage:
+ * - In poly, default_fromkey must be INVALID_NOTE.
+ * - In mono staccato playing,default_fromkey must be INVALID_NOTE.
+ * - In mono legato playing,default_fromkey must be valid.
+ */
+static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan,
+ int default_fromkey)
+{
+ unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL);
+
+ if(fluid_channel_is_valid_note(ptc))
+ {
+ /* CC PTC has been received */
+ fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */
+ chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */
+
+ /* returns fromkey legato */
+ if(!fluid_channel_is_valid_note(default_fromkey))
+ {
+ default_fromkey = ptc;
+ }
+ }
+ else
+ {
+ /* determines and returns fromkey portamento */
+ unsigned char fromkey_portamento = INVALID_NOTE;
+
+ if(fluid_channel_portamento(chan))
+ {
+ /* Portamento when Portamento pedal is On */
+ /* 'fromkey portamento'is determined from the portamento mode
+ and the most recent note played (prev_note)*/
+ enum fluid_channel_portamento_mode portamentomode = chan->portamentomode;
+
+ if(fluid_channel_is_valid_note(default_fromkey))
+ {
+ fromkey_portamento = default_fromkey; /* on each note */
+ }
+ else
+ {
+ fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */
+ }
+
+ if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY)
+ {
+ /* Mode portamento:legato only */
+ if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING))
+ {
+ fromkey_portamento = INVALID_NOTE;
+ }
+ }
+ else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY)
+ {
+ /* Mode portamento:staccato only */
+ if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)
+ {
+ fromkey_portamento = INVALID_NOTE;
+ }
+ }
+
+ /* else Mode portamento: on each note (staccato/legato) */
+ }
+
+ /* Returns fromkey portamento */
+ chan->synth->fromkey_portamento = fromkey_portamento;
+
+ /* Determines and returns fromkey legato */
+ if(!fluid_channel_is_valid_note(default_fromkey))
+ {
+ /* in staccato (poly/Mono) returns INVALID_NOTE */
+ /* In mono mode legato playing returns the note prior most
+ recent note played */
+ if(fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING))
+ {
+ default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */
+ }
+
+ /* In poly mode legato playing, actually we don't want playing legato.
+ So returns INVALID_NOTE */
+ }
+ }
+
+ return default_fromkey; /* Returns legato fromkey */
+}
+
+/*****************************************************************************
+ noteon - noteoff functions in Mono mode
+******************************************************************************/
+/*
+ * noteon - noteoff on a channel in "monophonic playing".
+ *
+ * A channel needs to be played monophonic if this channel has been set in
+ * monophonic mode by basic channel API.(see fluid_synth_polymono.c).
+ * A channel needs also to be played monophonic if it has been set in
+ * polyphonic mode and legato pedal is On during the playing.
+ * When a channel is in "monophonic playing" state, only one note at a time can be
+ * played in a staccato or legato manner (with or without portamento).
+ * More informations in FluidPolyMono-0004.pdf chapter 4 (Appendices).
+ * _______________
+ * ________________ | noteon |
+ * | legato detector| O-->| mono_staccato |--*-> preset_noteon
+ * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without)
+ * LOCAL |________________| O /|\ | (portamento)
+ * /|\ set_onenote | | fromkey |
+ * | | | portamento|
+ * noteOn poly >---*------------------* | |
+ * | | |
+ * | _____ |________ |
+ * portamento modes >--- | ->| | |
+ * | | get_fromkey | |
+ * Porta.on/off >--------------------- | ->|_______________| |
+ * (PTC) | | |
+ * | fromkey | fromkey |
+ * | legato | portamento|
+ * | _____\|/_______ |
+ * *-->| noteon |--/
+ * | | monopoly |
+ * | | legato |----> voices
+ * legato modes >------- | ->|_______________| triggering
+ * | (with or without)
+ * | (portamento)
+ * |
+ * |
+ * noteOff poly >---*----------------- | ---------+
+ * | clear | |
+ * _\|/_____________ | |
+ * | legato detector | O |
+ * noteoff_mono->|(search_monolist)|-O-- _____\|/_______
+ * LOCAL |(remove_monolist)| O-->| noteoff |
+ * |_________________| | monopoly |----> noteoff
+ * Sust.on/off >------------------------->|_______________|
+ * Sost.on/off
+------------------------------------------------------------------------------*/
+int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key,
+ char Mono);
+
+int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan,
+ int fromkey, int tokey, int vel);
+
+/**
+ * Plays a noteon event for a Synth instance in "monophonic playing" state.
+ * Please see the description above about "monophonic playing".
+ * _______________
+ * ________________ | noteon |
+ * | legato detector| O-->| mono_staccato |--->preset_noteon
+ * noteon_mono ->| (add_monolist) |--O-- |_______________|
+ * LOCAL |________________| O
+ * |
+ * |
+ * |
+ * |
+ * |
+ * |
+ * |
+ * |
+ * |
+ * | _______________
+ * | | noteon |
+ * +-->| monopoly |
+ * | legato |---> voices
+ * |_______________| triggering
+ *
+ * The function uses the legato detector (see above) to determine if the note must
+ * be played staccato or legato.
+ *
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param key MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ */
+int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan,
+ int key, int vel)
+{
+ fluid_channel_t *channel = synth->channel[chan];
+
+ /* Adds the note into the monophonic list */
+ fluid_channel_add_monolist(channel, key, vel, 0);
+
+ /* in Breath Sync mode, the noteon triggering is postponed
+ until the musician starts blowing in the breath controller */
+ if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) ||
+ fluid_channel_breath_msb(channel))
+ {
+ /* legato/staccato playing detection */
+ if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING)
+ {
+ /* legato playing */
+ /* legato from prev_note to key */
+ /* the voices from prev_note key number are to be used to play key number */
+ /* fromkey must be valid */
+ return fluid_synth_noteon_monopoly_legato(synth, chan,
+ fluid_channel_prev_note(channel), key, vel);
+ }
+ else
+ {
+ /* staccato playing */
+ return fluid_synth_noteon_mono_staccato(synth, chan, key, vel);
+ }
+ }
+ else
+ {
+ return FLUID_OK;
+ }
+}
+
+/**
+ * Plays a noteoff event for a Synth instance in "monophonic playing" state.
+ * Please see the description above about "monophonic playing"
+ *
+ * _______________
+ * | noteon |
+ * +-->| monopoly |
+ * | | legato |----> voices
+ * | |_______________| triggering
+ * | (with or without)
+ * | (portamento)
+ * |
+ * |
+ * |
+ * |
+ * |
+ * |
+ * _________________ |
+ * | legato detector | O
+ * noteoff_mono->|(search_monolist)|-O-- _______________
+ * LOCAL |(remove_monolist)| O-->| noteoff |
+ * |_________________| | monopoly |----> noteoff
+ * |_______________|
+ *
+ * The function uses the legato detector (see above) to determine if the noteoff must
+ * be played staccato or legato.
+ *
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param key MIDI note number (0-127).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ */
+int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key)
+{
+ int status;
+ int i, i_prev;
+ fluid_channel_t *channel = synth->channel[chan];
+ /* searching the note in the monophonic list */
+ i = fluid_channel_search_monolist(channel, key, &i_prev);
+
+ if(i >= 0)
+ {
+ /* the note is in the monophonic list */
+ /* Removes the note from the monophonic list */
+ fluid_channel_remove_monolist(channel, i, &i_prev);
+
+ /* in Breath Sync mode, the noteoff triggering is done
+ if the musician is blowing in the breath controller */
+ if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) ||
+ fluid_channel_breath_msb(channel))
+ {
+ /* legato playing detection */
+ if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING)
+ {
+ /* the list contains others notes */
+ if(i_prev >= 0)
+ {
+ /* legato playing detection on noteoff */
+ /* legato from key to i_prev key */
+ /* the voices from key number are to be used to
+ play i_prev key number. */
+ status = fluid_synth_noteon_monopoly_legato(synth, chan,
+ key, channel->monolist[i_prev].note,
+ channel->monolist[i_prev].vel);
+ }
+ /* else the note doesn't need to be played off */
+ else
+ {
+ status = FLUID_OK;
+ }
+ }
+ else
+ {
+ /* the monophonic list is empty */
+ /* plays the monophonic note noteoff and eventually held
+ by sustain/sostenuto */
+ status = fluid_synth_noteoff_monopoly(synth, chan, key, 1);
+ }
+ }
+ else
+ {
+ status = FLUID_OK;
+ }
+ }
+ else
+ {
+ /* the note is not found in the list so the note was
+ played On when the channel was in polyphonic playing */
+ /* plays the noteoff as for polyphonic */
+ status = fluid_synth_noteoff_monopoly(synth, chan, key, 0);
+ }
+
+ return status;
+}
+
+/*----------------------------------------------------------------------------
+ staccato playing
+-----------------------------------------------------------------------------*/
+/**
+ * Plays noteon for a monophonic note in staccato manner.
+ * Please see the description above about "monophonic playing".
+ * _______________
+ * | noteon |
+ * noteon_mono >------------------------>| mono_staccato |----> preset_noteon
+ * |_______________| (with or without)
+ * LOCAL /|\ (portamento)
+ * | fromkey
+ * | portamento
+ * |
+ * |
+ * ______|________
+ * portamento modes >----->| |
+ * | get_fromkey |
+ * Porta.on/off >----------------------->|_______________|
+ * Portamento
+ * (PTC)
+ *
+ * We are in staccato situation (where no previous note have been depressed).
+ * Before the note been passed to fluid_preset_noteon(), the function must determine
+ * the from_key_portamento parameter used by fluid_preset_noteon().
+ *
+ * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function.
+ * fromkey_portamento is set to valid/invalid key value depending of the portamento
+ * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control
+ * (PTC).
+ *
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param key MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ */
+int
+fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel)
+{
+ fluid_channel_t *channel = synth->channel[chan];
+
+ /* Before playing a new note, if a previous monophonic note is currently
+ sustained it needs to be released */
+ fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained);
+ /* Get possible 'fromkey portamento' */
+ fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE);
+ /* The note needs to be played by voices allocation */
+ return fluid_preset_noteon(channel->preset, synth, chan, key, vel);
+}
+
+/**
+ * Plays noteoff for a polyphonic or monophonic note
+ * Please see the description above about "monophonic playing".
+ *
+ *
+ * noteOff poly >---------------------------------+
+ * |
+ * |
+ * |
+ * noteoff_mono _____\|/_______
+ * LOCAL >------------------------->| noteoff |
+ * | monopoly |----> noteoff
+ * Sust.on/off >------------------------->|_______________|
+ * Sost.on/off
+ *
+ * The function has the same behaviour when the noteoff is poly of mono, except
+ * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the
+ * key is memorized. This is neccessary when the next mono note will be played
+ * staccato, as any current mono note currently sustained will need to be released
+ * (see fluid_synth_noteon_mono_staccato()).
+ * Note also that for a monophonic legato passage, the function is called only when
+ * the last noteoff of the passage occurs. That means that if sustain or sostenuto
+ * is depressed, only the last note of a legato passage will be sustained.
+ *
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param key MIDI note number (0-127).
+ * @param Mono, 1 noteoff on monophonic note.
+ * 0 noteoff on polyphonic note.
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ *
+ * Note: On return, on monophonic, possible sustained note is memorized in
+ * key_mono_sustained. Memorization is done here on noteOff.
+ */
+int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key,
+ char Mono)
+{
+ int status = FLUID_FAILED;
+ fluid_voice_t *voice;
+ int i;
+ fluid_channel_t *channel = synth->channel[chan];
+
+ /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */
+ if(Mono)
+ {
+ channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */
+ }
+
+ /* noteoff for all voices with same chan and same key */
+ for(i = 0; i < synth->polyphony; i++)
+ {
+ voice = synth->voice[i];
+
+ if(fluid_voice_is_on(voice) &&
+ fluid_voice_get_channel(voice) == chan &&
+ fluid_voice_get_key(voice) == key)
+ {
+ if(synth->verbose)
+ {
+ int used_voices = 0;
+ int k;
+
+ for(k = 0; k < synth->polyphony; k++)
+ {
+ if(!_AVAILABLE(synth->voice[k]))
+ {
+ used_voices++;
+ }
+ }
+
+ FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d",
+ fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0,
+ fluid_voice_get_id(voice),
+ (fluid_curtime() - synth->start) / 1000.0f,
+ used_voices);
+ } /* if verbose */
+
+ fluid_voice_noteoff(voice);
+
+ /* noteoff on monophonic note */
+ /* Key memorization if the note is sustained */
+ if(Mono &&
+ (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)))
+ {
+ channel->key_mono_sustained = key;
+ }
+
+ status = FLUID_OK;
+ } /* if voice on */
+ } /* for all voices */
+
+ return status;
+}
+
+/*----------------------------------------------------------------------------
+ legato playing
+-----------------------------------------------------------------------------*/
+/**
+ * Plays noteon for a monophonic note played legato.
+ * Please see the description above about "monophonic playing".
+ *
+ *
+ * _______________
+ * portamento modes >----->| |
+ * | get_fromkey |
+ * Porta.on/off >----------------------->|_______________|
+ * Portamento |
+ * (PTC) | +-->preset_noteon
+ * fromkey | fromkey | (with or without)
+ * legato | portamento| (portamento)
+ * _____\|/_______ |
+ * | noteon |--+
+ * noteon_mono >------------------------>| monopoly |
+ * LOCAL | legato |----->voices
+ * |_______________| triggering
+ * /|\ (with or without)
+ * | (portamento)
+ * legato modes >-----------------+
+ *
+ * We are in legato situation (where a previous note has been depressed).
+ * The function must determine the from_key_portamento and from_key_legato parameters
+ * used respectively by fluid_preset_noteon() function and voices triggering functions.
+ *
+ * from_key_portamento and from_key_legato are returned by
+ * fluid_synth_get_fromkey_portamento_legato() function.
+ * fromkey_portamento is set to valid/invalid key value depending of the portamento
+ * modes (see portamento mode API), CC portamento On/Off, and CC portamento control
+ * (PTC).
+ * Then, depending of the legato modes (see legato mode API), the function will call
+ * the appropriate triggering functions.
+ * @param synth instance.
+ * @param chan MIDI channel number (0 to MIDI channel count - 1).
+ * @param fromkey MIDI note number (0-127).
+ * @param tokey MIDI note number (0-127).
+ * @param vel MIDI velocity (0-127).
+ * @return FLUID_OK on success, FLUID_FAILED otherwise.
+ *
+ * Note: The voices with key 'fromkey' are to be used to play key 'tokey'.
+ * The function is able to play legato through Preset Zone(s) (PZ) and
+ * Instrument Zone(s) (IZ) as far as possible.
+ * When key tokey is outside the current Instrument Zone, Preset Zone,
+ * current 'fromkey' voices are released. If necessary new voices
+ * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s).
+ * More informations in FluidPolyMono-0004.pdf chapter 4.7 (Appendices).
+ */
+int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan,
+ int fromkey, int tokey, int vel)
+{
+ fluid_channel_t *channel = synth->channel[chan];
+ enum fluid_channel_legato_mode legatomode = channel->legatomode;
+ fluid_voice_t *voice;
+ int i ;
+ /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */
+ fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey);
+
+ if(fluid_channel_is_valid_note(fromkey))
+ {
+ for(i = 0; i < synth->polyphony; i++)
+ {
+ /* searching fromkey voices: only those who don't have 'note off' */
+ voice = synth->voice[i];
+
+ if(fluid_voice_is_on(voice) &&
+ fluid_voice_get_channel(voice) == chan &&
+ fluid_voice_get_key(voice) == fromkey)
+ {
+ fluid_zone_range_t *zone_range = voice->zone_range;
+
+ /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise
+ checks if tokey is inside the range of the running voice */
+ if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel))
+ {
+ switch(legatomode)
+ {
+ case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */
+ fluid_voice_release(voice); /* normal release */
+ break;
+
+ case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */
+ /* Skip in attack section */
+ fluid_voice_update_multi_retrigger_attack(voice, tokey, vel);
+
+ /* Starts portamento if enabled */
+ if(fluid_channel_is_valid_note(synth->fromkey_portamento))
+ {
+ /* Sends portamento parameters to the voice dsp */
+ fluid_voice_update_portamento(voice,
+ synth->fromkey_portamento,
+ tokey);
+ }
+
+ /* The voice is now used to play tokey in legato manner */
+ /* Marks this Instrument Zone to be ignored during next
+ fluid_preset_noteon() */
+ zone_range->ignore = TRUE;
+ break;
+
+ default: /* Invalid mode: this should never happen */
+ FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d",
+ legatomode);
+ return FLUID_FAILED;
+ }
+ }
+ else
+ {
+ /* tokey note is outside the voice range, so the voice is released */
+ fluid_voice_release(voice);
+ }
+ }
+ }
+ }
+
+ /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in
+ this case it needs to be played by voices allocation */
+ return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel);
+}