/* 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" /** monophonic playing *******************************************************/ extern void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key); /*----------------------------------------------------------------------------- Monophonic list methods ------------------------------------------------------------------------------*/ /** * Add a note to the monophonic list. * @param chan fluid_channel_t * @param key MIDI note number (0-127) * @param vel MIDI velocity (0-127, 0=noteoff) * return prev index. */ static short fluid_channel_add_monolist(fluid_channel_t* chan, unsigned char key, unsigned char vel) { short iPrev = chan->iLast; short iNext = chan->iNext; chan->monolist[iNext].note = key; chan->monolist[iNext].vel = vel; chan->iLast = iNext; /* most recent adding */ /* iNext will be used for the next adding */ chan->iNext = chan->monolist[iNext].next; if(chan->nNotes < maxNotes) chan->nNotes++; /* update nNotes */ else { /* overflow situation. So circular motion for iFirst */ chan->iFirst = chan->iNext; /* warning */ FLUID_LOG(FLUID_INFO, "Overflow on monophonic list channel %d ", chan->channum); } return iPrev; } /** * Search a note in the monophonic list. * @param chan fluid_channel_t * @param key MIDI note number (0-127) to search * @return index of the note if find, -1 otherwise. */ static short fluid_channel_search_monolist(fluid_channel_t* chan, unsigned char key) { short n = chan->nNotes; /* number of notes in monophonic list */ short i= chan->iFirst; /* search starts at iFirst included */ while(n) { if(chan->monolist[i].note == key) break; /* found */ i = chan->monolist[i].next; /* next element */ n--; } if (n) return i;/* found i */ else return -1; /* not found */ } /** * remove a note from the monophonic list. * @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. * return prev index (>= 0) if the note is the last note in the list -1 otherwise. */ static short fluid_channel_rem_monolist(fluid_channel_t* chan, short i) { short iPrev = -1; if(i < 0 || i >= maxNotes || !chan->nNotes) return 0; /* index is valid */ /* The element is about to be removed and inserted between iLast and iNext */ /* Note: when i is egal to iLast or egal to iFirst, Removing/Inserting isn't necessary */ if (i == chan->iLast) { /* Removing/Inserting isn't necessary */ /* update iLast to the previous if it exists */ iPrev = chan->monolist[i].prev; if(chan->iLast != chan->iFirst) chan->iLast = iPrev; chan->iNext = i; /* i becames iNext for the next adding */ } else { /* i is before iLast */ if(i == chan->iFirst) chan->iFirst = chan->monolist[i].next; else { /* i is between iFist ans iLast */ /* Removing element i and inserting between iLast and iNext */ unsigned char next,prev; /* removing by chainning prev and next */ next = chan->monolist[i].next; prev = chan->monolist[i].prev; chan->monolist[next].prev = prev; chan->monolist[prev].next = next; /* inserting between iLast and iNext */ chan->monolist[i].next = chan->iNext; chan->monolist[chan->iNext].prev = i; chan->monolist[i].prev = chan->iLast; chan->monolist[chan->iLast].next = i; chan->iNext = i; /* i becames iNext for the next adding */ } } chan->nNotes--; return iPrev; } /** * remove all notees from the monophonic list. * @param chan fluid_channel_t */ void fluid_channel_clear_monolist(fluid_channel_t* chan) { chan->iFirst = chan->iLast = chan->iNext = chan->nNotes = 0; } /** * The monophonic list is flushed keeping last note only. * @param chan fluid_channel_t */ void fluid_channel_keep_lastnote_monolist(fluid_channel_t* chan) { chan->iFirst = chan->iLast; chan->nNotes = 1; } /** * The function store the note in the first position of monophonic list * @param chan fluid_channel_t */ void fluid_channel_set_onenote_monolist(fluid_channel_t* chan, unsigned char key, unsigned char vel) { fluid_channel_clear_monolist(chan); fluid_channel_add_monolist(chan, key, vel); } /*----------------------------------------------------------------------------- noteon - noteoff on a channel in "monophonic playing". A channel needs to be played monophonic if this channel has been set monophonic by basic channel API.(see fluid_synth_polymono.c). A channel needs also to be played monophonic if it has been set polyphonic and legato pedal is On. When in "monophonic playing" only one note at a time can be played in a staccato or legato manner. ------------------------------------------------------------------------------*/ int fluid_synth_noteon_mono(fluid_synth_t* synth, int chan, int key, int vel); int fluid_synth_noteoff_monopoly(fluid_synth_t* synth, int chan, int key, char Mono); int fluid_synth_noteon_mono_legato(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); /** * Send a note-on event to a FluidSynth object in "monophonic playing". * Please see the description above about "monophonic playing". * @param synth FluidSynth 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) { int status; char iPrev; fluid_channel_t* channel = synth->channel[chan];; /* Add note to the monophonic list */ iPrev = fluid_channel_add_monolist(channel,(unsigned char)key, (unsigned char)vel); /* legato/staccato playing detection */ if(channel->nNotes >= 2) { /* legato playing */ /* legato from iPrev to key */ /* the voices from iPrev key number are to be used to play key number */ status = fluid_synth_noteon_mono_legato(synth, chan, channel->monolist[iPrev].note, key, vel); } /* staccato playing */ else status = fluid_synth_noteon_mono(synth, chan, key, vel); return status; } /** * Send a note-off event to a FluidSynth object in "monophonic playing". * Please see the description above about "monophonic playing". * @param synth FluidSynth 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; short i,iPrev; fluid_channel_t* channel = synth->channel[chan];; /* search the note in monophonic list */ i=fluid_channel_search_monolist(channel, (unsigned char)key); if (i >= 0) { /* the note is in monophonic list */ /* Remove note from the monophonic list */ iPrev = fluid_channel_rem_monolist(channel,i); /* legato playing detection */ if(channel->nNotes) { /* the list contains others notes */ if(iPrev >=0) { /* legato playing detection */ /* legato from key to iPrev key */ /* the voices from key number are to be used to play iPrev key number. */ status = fluid_synth_noteon_mono_legato(synth, chan, key, channel->monolist[iPrev].note, channel->monolist[iPrev].vel); } /* else the note doesn't need to be played off */ else status = FLUID_OK; } else { /* the monophonic list is empty */ /* play the monophonic note noteoff and eventually held by sustain only (R2) */ status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); } } else { /* the note is not found in the list so the note will be played On when we was in polyphonic playing */ /* play the noteoff as for polyphonic */ status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); } return status; } /*---------------------------------------------------------------------------- staccato playing -----------------------------------------------------------------------------*/ /** * noteon for monophonic note. * Please see the description above about "monophonic playing". * @param synth FluidSynth 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(fluid_synth_t* synth, int chan, int key, int vel) { fluid_channel_t* channel = synth->channel[chan]; /* Before playing a new note, il a previous monophonic note is currently sustained it needs to be released */ fluid_synth_release_voice_on_same_note_LOCAL(synth,chan, channel->key_sustained); /* The note needs to be played by voices allocation */ return fluid_preset_noteon(channel->preset, synth, chan, key, vel); } /** * noteoff for a polyphonic or monophonic note * Please see the description above about "monophonic playing". * @param synth FluidSynth 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, sustained note needs to be remembered * in key_sustained. * On noteon for a monophonic note if a previous monophonic note is sustained * it will be released. Remembering 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, IsSustained; fluid_channel_t* channel = synth->channel[chan]; /* Key_sustained is prepared to return no note sustained (-1) */ if (Mono) channel->key_sustained = -1; /* 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 (_ON(voice) && (voice->chan == chan) && (voice->key == 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", voice->chan, voice->key, 0, voice->id, (fluid_curtime() - synth->start) / 1000.0f, used_voices); } /* if verbose */ IsSustained = fluid_voice_noteoff(voice); /* noteoff on monophonic note */ /* Key remembering if the note is sustained */ if(Mono && IsSustained) channel->key_sustained = key; status = FLUID_OK; } /* if voice on */ } /* for all voices */ return status; } /*---------------------------------------------------------------------------- legato playing -----------------------------------------------------------------------------*/ int fluid_synth_noteon_mono_legato_retrigger(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); int fluid_synth_noteon_mono_legato_multi_retrigger(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); int fluid_synth_noteon_mono_legato_single_trigger0(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); fluid_synth_noteon_mono_legato_single_trigger1(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel); /** * noteon for monophonic note played legato. * Please see the description above about "monophonic playing". * @param synth FluidSynth 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'. */ int fluid_synth_noteon_mono_legato(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel) { int status; unsigned char legatomode = GetChanLegatoMode(synth->channel[chan]); switch (legatomode) { case RETRIGGER: /* mode 0 */ status= fluid_synth_noteon_mono_legato_retrigger(synth,chan,fromkey, tokey,vel); break; case MULTI_RETRIGGER: /* mode 1 */ status = fluid_synth_noteon_mono_legato_multi_retrigger(synth,chan,fromkey, tokey,vel); break; case SINGLE_TRIGGER_0: /* mode 2 */ status= fluid_synth_noteon_mono_legato_single_trigger0(synth,chan,fromkey, tokey,vel); break; case SINGLE_TRIGGER_1: /* mode 3 */ status= fluid_synth_noteon_mono_legato_single_trigger1(synth,chan,fromkey, tokey,vel); break; default: FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d",legatomode); status = FLUID_FAILED; } return status; } /*----------------------------------------------------------------------------- /* Mode 0: retrigger */ int fluid_synth_noteon_mono_legato_retrigger(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel) { fluid_channel_t* channel = synth->channel[chan]; fluid_voice_t* voice; int i, status ; /* Mode 0: retrigger (with crossfading between release and attack) */ /* Release the fromkey note */ for (i = 0; i < synth->polyphony; i++) { /* search fromkey voice: only those who don't have 'note off' */ voice = synth->voice[i]; if (_ON(voice) && (voice->chan == chan) && (voice->key == fromkey)){ fluid_update_release(voice); } } /* tokey note needs to be played by voices allocation */ synth->fromkey = fromkey; /* portamento fromkey */ status = fluid_preset_noteon(channel->preset, synth, chan, tokey, vel); /* When fromkey is set to -1 , portamento is disabled even if portamento pedal is pressed. Once portamento is triggered by fluid_preset_noteon(), fromkey is set to -1 to disable portamento for the next fluid_preset_noteon. This is very useful has the first note (n1) of a legato passage (n1,n2,n3,..) will be always without portamento but not the following notes (n2,n3,..) */ synth->fromkey = -1; /* disable portamento */ return status; } /*----------------------------------------------------------------------------- /* Mode 1: multi_retrigger */ int fluid_synth_noteon_mono_legato_multi_retrigger(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel) { fluid_channel_t* channel = synth->channel[chan]; unsigned char found = 0; fluid_voice_t* voice; int i, status = FLUID_OK; /* Mode 0: retrigger (with crossfading between release and attack) */ for (i = 0; i < synth->polyphony; i++) { /* search fromkey voice: only those who don't have 'note off' */ voice = synth->voice[i]; if (_ON(voice) && (voice->chan == chan) && (voice->key == fromkey)){ found = 1; /* Skip in attack section */ fluid_update_multi_retrigger_attack(voice,tokey,vel); /* Start portamento if enabled */ if( fluid_channel_portamento(channel)) /* Send portamento parameters to the voice dsp */ fluid_voice_update_portamento(voice,fromkey,tokey); } } if(!found) /* fromkey note have finished */ { /* The note needs to be played by voices allocation */ int i, status ; synth->fromkey = fromkey; /* portamento fromkey */ status = fluid_preset_noteon(channel->preset,synth,chan,tokey,vel); synth->fromkey = -1; /* disable portamento */ } return status; } /*----------------------------------------------------------------------------- /* Mode 2: single_trigger_0 */ int fluid_synth_noteon_mono_legato_single_trigger0(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel) { fluid_channel_t* channel = synth->channel[chan]; unsigned char found = 0; fluid_voice_t* voice; int i, status = FLUID_OK; /* Mode 0: retrigger (with crossfading between release and attack) */ for (i = 0; i < synth->polyphony; i++) { /* search fromkey voice: only those who don't have 'note off' */ voice = synth->voice[i]; if (_ON(voice) && (voice->chan == chan) && (voice->key == fromkey)){ found = 1; fluid_update_single_trigger0(voice,fromkey,tokey,vel); /* Start portamento if enabled */ if( fluid_channel_portamento(channel)) /* Send portamento parameters to the voice dsp */ fluid_voice_update_portamento(voice,fromkey,tokey); } } if(!found) /* fromkey note have finished */ { /* The note needs to be played by voices allocation */ synth->fromkey = fromkey; /* portamento fromkey */ status = fluid_preset_noteon(channel->preset,synth,chan,tokey,vel); synth->fromkey = -1; /* disable portamento */ } return status; } /*----------------------------------------------------------------------------- /* Mode 3: single_trigger_1 */ int fluid_synth_noteon_mono_legato_single_trigger1(fluid_synth_t* synth, int chan, int fromkey, int tokey, int vel) { fluid_channel_t* channel = synth->channel[chan]; unsigned char found = 0; fluid_voice_t* voice; int i, status = FLUID_OK; /* Mode 0: retrigger (with crossfading between release and attack) */ for (i = 0; i < synth->polyphony; i++) { /* search fromkey voice: only those who don't have 'note off' */ voice = synth->voice[i]; if (_ON(voice) && (voice->chan == chan) && (voice->key == fromkey)){ found = 1; fluid_update_single_trigger1(voice,fromkey,tokey,vel); /* Start portamento if enabled */ if( fluid_channel_portamento(channel)) /* Send portamento parameters to the voice dsp */ fluid_voice_update_portamento(voice,fromkey,tokey); } } if(!found) /* fromkey note have finished */ { /* The note needs to be played by voices allocation */ synth->fromkey = fromkey; /* portamento fromkey */ status = fluid_preset_noteon(channel->preset,synth,chan,tokey,vel); synth->fromkey = -1; /* disable portamento */ } return status; }