diff -Naur ./fluidsynth-1.1.6/include/fluidsynth/synth.h ./fluid-polymono-0003/include/fluidsynth/synth.h --- ./fluidsynth-1.1.6/include/fluidsynth/synth.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/include/fluidsynth/synth.h Sun Jul 31 15:49:44 2016 @@ -45,6 +45,130 @@ * fluid_synth_noteon(), fluid_synth_noteoff(), ... */ + +/******************************************************************* + API: Poly mono mode + */ + +/* Macros interface to poly/mono mode variables */ +enum PolyMonoMode +{ + OMNION_POLY, /* MIDI mode 0 */ + OMNION_MONO, /* MIDI mode 1 */ + OMNIOFF_POLY, /* MIDI mode 2 */ + OMNIOFF_MONO, /* MIDI mode 3 */ + MODE_NBR +}; + +/* bits basic channel infos */ +#define MONO 0x01 /* b0, 0: poly on , 1: mono on */ +#define OMNI 0x02 /* b1, 0: omni on, 1:omni off */ +#define MASKMODE (OMNI|MONO) +#define BASIC_CHANNEL 0x04 /* b2, 1: channel is basic channel */ +#define ENABLED 0x08 /* b3, 1: channel is listened */ + +/* access to mode */ +#define GetModeMode(mode) (mode & MASKMODE) +#define IsModeMono(mode) (mode & MONO) +#define IsModeBasicChan(mode) (mode & BASIC_CHANNEL) +#define SetModeBasicChan(mode) (mode |= BASIC_CHANNEL) +#define ResetModeBasicChan(mode) (mode &= ~ BASIC_CHANNEL) +#define IsModeChanEn(mode) (mode & ENABLED) +#define SetModeChanEn(mode) (mode |= ENABLED) +#define ResetModeChanEn(mode) (mode &= ~ENABLED) + +#define FLUID_POLYMONO_WARNING (-2) /* returned by fluid_synth_reset_basic_channels() */ +struct _fluid_basic_channel_infos_t +{ + int basicchan; /* MIDI channel numer to set as basic channel */ + int mode; /* 0:OmniOn_Poly 1:OmniOn_Mono 2:OmniOff_Poly 3 OmniOff_Mono */ + int val; /* Number of monophonic channel (Mode 3) */ +}; +typedef struct _fluid_basic_channel_infos_t fluid_basic_channel_infos_t; + +FLUIDSYNTH_API int fluid_synth_get_basic_channels( fluid_synth_t* synth, + fluid_basic_channel_infos_t **basicChannelInfos); +FLUIDSYNTH_API int fluid_synth_reset_basic_channels(fluid_synth_t* synth, int n, + fluid_basic_channel_infos_t *basicChannelInfos); +FLUIDSYNTH_API int fluid_synth_get_channel_mode(fluid_synth_t* synth, int chan, + fluid_basic_channel_infos_t *modeInfos); +FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t* synth, + int basicchan, int mode, int val); +/* Interface to mono legato mode */ +/* n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played + legato with previous note. n2,n3,..make use of previous voices if any. +*/ +enum LegatoMode +{ + /* Release previous note (fast release), start a new note */ + RETRIGGER_0, /* mode 0 */ + + /* Release previous note (normal release), start a new note */ + RETRIGGER_1, /* mode 1 */ + /* On n2,n3,.. retrigger in attack section using current value and + shape attack using current dynamic */ + MULTI_RETRIGGER, /* mode 2 */ + + /* On n2,n3,.stay in current value section and shape current section + using current dynamic */ + SINGLE_TRIGGER_0, /* mode 3 */ + + /* On n2,n3,.stay in current value section using current dynamic (don't shape adsr) */ + SINGLE_TRIGGER_1, /* mode 4 */ + LEGATOMODE_NBR +}; + +FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t* synth, + int chan, int legatomode); +FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t* synth, + int chan, int *legatomode); +/* End of API: Poly mono mode */ + +/* Interface to portamento mode */ +/* Macros interface to portamento mode variable */ +enum PortamentoMode +{ + /* Portamento on each note (staccato or legato) */ + EACH_NOTE, /* mode 0 */ + /* Portamento only on legato note */ + LEGATO_ONLY, /* mode 1 */ + /* Portamento only on staccato note */ + STACCATO_ONLY, /* mode 2 */ + PORTAMENTOMODE_NBR +}; + +FLUIDSYNTH_API int fluid_synth_set_portamento_model(fluid_synth_t* synth, + int chan, int portamentomode); +FLUIDSYNTH_API int fluid_synth_get_portamento_model(fluid_synth_t* synth, + int chan, int * portamentomode); + +/* End of API: portamento mode */ + +/* Interface to breath mode */ +/* breath mode bits infos */ +#define BREATH_POLY 0x10 /* b4, 1: default breath poly On */ +#define BREATH_MONO 0x20 /* b5, 1: default breath mono On */ +#define BREATH_SYNC 0x40 /* b6, 1: BreathSyn On */ + +/* access to breath mode bits */ +#define IsPolyDefaultBreath(breath) (breath & BREATH_POLY) +#define SetPolyDefaultBreath(breath) (breath |= BREATH_POLY) +#define ResetPolyDefaultBreath(breath) (breath &= ~ BREATH_POLY) +#define IsMonoDefaultBreath(breath) (breath & BREATH_MONO) +#define SetMonoDefaultBreath(breath) (breath |= BREATH_MONO) +#define ResetMonoDefaultBreath(breath) (breath &= ~ BREATH_MONO) + +#define IsBreathSync(breath) (breath & BREATH_SYNC) +#define SetBreathSync(breath) (breath |= BREATH_SYNC) +#define ResetBreathSync(breath) (breath &= ~ BREATH_SYNC) + +FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t* synth, + int chan, int breathmode); +FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t* synth, + int chan, int *breathmode); + +/* End of API: breath mode */ + #define FLUID_SYNTH_CHANNEL_INFO_NAME_SIZE 32 /**< Length of channel info name field (including zero terminator) */ /** @@ -299,7 +423,8 @@ /* Synthesizer's interface to handle SoundFont loaders */ FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t* synth, fluid_sfloader_t* loader); -FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, +FLUIDSYNTH_API fluid_voice_t* fluid_synth_alloc_voice(fluid_synth_t* synth, + fluid_inst_zone_t* inst_zone, int channum, int key, int vel); FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice); FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth, diff -Naur ./fluidsynth-1.1.6/include/fluidsynth/types.h ./fluid-polymono-0003/include/fluidsynth/types.h --- ./fluidsynth-1.1.6/include/fluidsynth/types.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/include/fluidsynth/types.h Tue Jul 12 18:55:22 2016 @@ -40,6 +40,7 @@ typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */ typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */ typedef struct _fluid_preset_t fluid_preset_t; /**< SoundFont preset */ +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /**< Soundfont Instrument Zone */ typedef struct _fluid_sample_t fluid_sample_t; /**< SoundFont sample */ typedef struct _fluid_mod_t fluid_mod_t; /**< SoundFont modulator */ typedef struct _fluid_audio_driver_t fluid_audio_driver_t; /**< Audio driver instance */ diff -Naur ./fluidsynth-1.1.6/src/bindings/fluid_cmd.c ./fluid-polymono-0003/src/bindings/fluid_cmd.c --- ./fluidsynth-1.1.6/src/bindings/fluid_cmd.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/bindings/fluid_cmd.c Sun Jul 31 15:44:26 2016 @@ -149,7 +149,10 @@ "settings Print out all settings" }, { "echo", "general", (fluid_cmd_func_t) fluid_handle_echo, NULL, "echo arg Print arg" }, - /* LADSPA-related commands */ + /* Sleep command, useful to insert a delay between commands */ + { "sleep", "general", (fluid_cmd_func_t) fluid_handle_sleep, NULL, + "sleep duration sleep duration(in ms)" }, + /* LADSPA-related commands */ #ifdef LADSPA { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL, "ladspa_clear Resets LADSPA effect unit to bypass state"}, @@ -176,7 +179,28 @@ "router_par2 min max mul add filters and maps parameter 2 (vel/cc val)"}, { "router_end", "router", (fluid_cmd_func_t) fluid_midi_router_handle_end, NULL, "router_end closes and commits the current routing rule"}, - { NULL, NULL, NULL, NULL, NULL } + /* Poly Mono mode commands */ + { "basicchannels", "polymono", (fluid_cmd_func_t) fluid_handle_basicchannels, NULL, + "basicchannels Display the list of basic channels"}, + { "resetbasicchannels", "polymono", (fluid_cmd_func_t) fluid_handle_resetbasicchannels, NULL, + "resetbasicchannels [chan mode val..] Reset the list of basic channels"}, + { "setbasicchannels", "polymono", (fluid_cmd_func_t) fluid_handle_setbasicchannels, NULL, + "setbasicchannels chan mode val [chan mode val..] Change or add basic channels"}, + { "channelsmode", "polymono", (fluid_cmd_func_t) fluid_handle_channelsmode, NULL, + "channelsmode [chan1 chan2..] Print channels mode"}, + { "legatomode", "polymono", (fluid_cmd_func_t) fluid_handle_legatomode, NULL, + "legatomode [chan1 chan2..] Print channels legato mode"}, + { "setlegatomode", "polymono", (fluid_cmd_func_t) fluid_handle_setlegatomode, NULL, + "setlegatomode chan mode [chan mode..] Change legato mode"}, + { "portamentomode", "polymono", (fluid_cmd_func_t) fluid_handle_portamentomode, NULL, + "portamentomode [chan1 chan2..] Print channels portamento mode"}, + { "setportamentomode", "polymono", (fluid_cmd_func_t) fluid_handle_setportamentomode, NULL, + "setportamentomode chan mode [chan mode..] Change portamento mode"}, + { "breathmode", "polymono", (fluid_cmd_func_t) fluid_handle_breathmode, NULL, + "breathmode [chan1 chan2..] Print channels breath mode"}, + { "setbreathmode", "polymono", (fluid_cmd_func_t) fluid_handle_setbreathmode, NULL, + "setbreathmode chan poly(1/0) mono(1/0) breath_sync(1/0) [..] Set breath mode"}, + { NULL, NULL, NULL, NULL, NULL }, }; /** @@ -946,6 +970,30 @@ } fluid_ostream_printf(out, "%s\n",av[0]); + + return 0; +} + +/* Purpose: + * Sleep during a time in ms + * The command itself is useful to insert a delay between commands. + * It can help for exemple to build a small song using noteon/noteoff commands + * in a command file. + */ +int +fluid_handle_sleep(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) +{ +// int delay; + if (ac < 1) { + fluid_ostream_printf(out, "sleep: too few arguments.\n"); + return -1; + } + if (!fluid_is_number(av[0])) { + fluid_ostream_printf(out, "sleep: argument should be a number in ms.\n"); + return -1; + } +// delay = atoi(av[0] * 1000); /* delay in micro second.*/ + g_usleep(atoi(av[0]) * 1000); /* delay in micro second.*/ return 0; } diff -Naur ./fluidsynth-1.1.6/src/bindings/fluid_cmd.h ./fluid-polymono-0003/src/bindings/fluid_cmd.h --- ./fluidsynth-1.1.6/src/bindings/fluid_cmd.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/bindings/fluid_cmd.h Sun Jul 31 15:38:36 2016 @@ -71,12 +71,24 @@ int fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); int fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); +int fluid_handle_sleep(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); int fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); int fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); - +/* Poly Mono mode commands */ +int fluid_handle_basicchannels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_resetbasicchannels (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_setbasicchannels (fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_channelsmode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_handle_legatomode(fluid_synth_t* synth, int ac, char** av,fluid_ostream_t out); +int fluid_handle_setlegatomode(fluid_synth_t* synth, int ac, char** av,fluid_ostream_t out); +int fluid_handle_portamentomode(fluid_synth_t* synth, int ac, char** av,fluid_ostream_t out); +int fluid_handle_setportamentomode(fluid_synth_t* synth, int ac, char** av,fluid_ostream_t out); +int fluid_handle_breathmode(fluid_synth_t* synth, int ac, char** av,fluid_ostream_t out); +int fluid_handle_setbreathmode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out); fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd); void delete_fluid_cmd(fluid_cmd_t* cmd); diff -Naur ./fluidsynth-1.1.6/src/CMakeLists.txt ./fluid-polymono-0003/src/CMakeLists.txt --- ./fluidsynth-1.1.6/src/CMakeLists.txt Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/CMakeLists.txt Mon Jul 04 15:23:27 2016 @@ -162,6 +162,8 @@ synth/fluid_mod.h synth/fluid_synth.c synth/fluid_synth.h + synth/fluid_synth_mono.c + synth/fluid_synth_polymono.c synth/fluid_tuning.c synth/fluid_tuning.h synth/fluid_voice.c diff -Naur ./fluidsynth-1.1.6/src/Makefile.am ./fluid-polymono-0003/src/Makefile.am --- ./fluidsynth-1.1.6/src/Makefile.am Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/Makefile.am Mon Jul 04 15:23:27 2016 @@ -143,6 +143,8 @@ synth/fluid_mod.h \ synth/fluid_synth.c \ synth/fluid_synth.h \ + synth/fluid_synth_mono.c \ + synth/fluid_synth_polymono.c \ synth/fluid_tuning.c \ synth/fluid_tuning.h \ synth/fluid_voice.c \ diff -Naur ./fluidsynth-1.1.6/src/midi/fluid_midi.h ./fluid-polymono-0003/src/midi/fluid_midi.h --- ./fluidsynth-1.1.6/src/midi/fluid_midi.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/midi/fluid_midi.h Mon Jun 06 19:24:28 2016 @@ -106,7 +106,7 @@ PORTAMENTO_SWITCH = 0x41, SOSTENUTO_SWITCH = 0x42, SOFT_PEDAL_SWITCH = 0x43, - LEGATO_SWITCH = 0x45, + LEGATO_SWITCH = 0x44, HOLD2_SWITCH = 0x45, SOUND_CTRL1 = 0x46, SOUND_CTRL2 = 0x47, diff -Naur ./fluidsynth-1.1.6/src/rvoice/fluid_adsr_env.h ./fluid-polymono-0003/src/rvoice/fluid_adsr_env.h --- ./fluidsynth-1.1.6/src/rvoice/fluid_adsr_env.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/rvoice/fluid_adsr_env.h Wed Jun 22 13:37:59 2016 @@ -95,9 +95,11 @@ env->section++; env->count = 0; } + else env->count++; env->val = x; - env->count++; + + } /* This one cannot be inlined since it is referenced in diff -Naur ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice.c ./fluid-polymono-0003/src/rvoice/fluid_rvoice.c --- ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/rvoice/fluid_rvoice.c Thu Jul 28 17:44:12 2016 @@ -319,11 +319,32 @@ * buffer. It is the ratio between the frequencies of original * waveform and output waveform.*/ voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + voice->dsp.pitchoffset + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) / voice->dsp.root_pitch_hz; + /******************* Update portamento **************/ + /* pitchoffset is updated if enabled. + Pitchoffset will be added to dsp pitch at + phase calculation time */ + if (voice->dsp.pitchinc > 0.0f) + { /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if (voice->dsp.pitchoffset > 0.0f) + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + else if (voice->dsp.pitchinc < 0.0f) + { /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if (voice->dsp.pitchoffset < 0.0f) + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + /*--*/ + fluid_check_fpe ("voice_write phase calculation"); /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ @@ -473,7 +494,13 @@ calculate the volume increment during processing */ - /* mod env initialization*/ + /* legato initialization */ + /* dsp.prev_sav_attenuation: used by fluid_rvoice_single_trigger() */ + voice->dsp.prev_sav_attenuation = -1.0f; + voice->dsp.pitchoffset = 0.0; + voice->dsp.pitchinc = 0.0; + + /* mod env initialization*/ fluid_adsr_env_reset(&voice->envlfo.modenv); /* vol env initialization */ @@ -524,6 +551,217 @@ fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); } +/*----------------------------------------------------------------------------*/ +/* skip to Attack section + Update vol and attack data + Correction on volume val to achieve equivalent amplitude at noteOn legato +*/ +void fluid_retrigger_attack (fluid_rvoice_t* voice) +{ + /* skip to Attack section */ + /* Once in Attack section, current count must be reset, to be sure + that the section will be not be prematurely finished. */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + { + /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ + fluid_env_data_t* env_data; + fluid_real_t peak = fluid_atten2amp (voice->dsp.attenuation); + fluid_real_t prev_peak = fluid_atten2amp (voice->dsp.prev_attenuation); + voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; + /* Correction on slope direction for Attack section */ + env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; + if(voice->envlfo.volenv.val <=1.0f) + { /* slope attack for legato note needs to be positive from val up to 1 */ + env_data->increment = 1.0f / env_data->count; + env_data->min = -1.0f; env_data->max = 1.0f; + } + else + { /* slope attack for legato note needs to be negative: from val down to 1 */ + env_data->increment = -voice->envlfo.volenv.val / env_data->count; + env_data->min = 1.0f; env_data->max = voice->envlfo.volenv.val; + } + } +} + +/*----------------------------------------------------------------------------*/ +/* Used by legato Mode 1: multi_retrigger - + see fluid_synth_noteon_mono_legato_multi_retrigger() */ +void +fluid_rvoice_multi_retrigger_attack (fluid_rvoice_t* voice) +{ + int section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + if (section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculate new + volenv_val to achieve equivalent amplitude during the attack phase + for seamless volume transition. */ + fluid_real_t amp_cb, env_value; + amp_cb = 960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); +// env_value = pow (10.0, amp_cb / -200); + env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ + fluid_clip (env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + /* next, skip to Attack section */ + } + /* skip to Attack section from any section */ + /* Update vol and attack data */ + fluid_retrigger_attack(voice); + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + /* Skip from any section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); + /* Actually (v 1.1.6) all sections are linear, so there is no need to + correct val value. However soundfont 2.01/2.4 spec. says that Attack should + be convex (see ticket 155 from Christian Collins). In the case Attack + section would be changed to a non linear shape it will be necessary to do + a correction for seamless val transition. Here is the place */ +} + +/*----------------------------------------------------------------------------*/ +/* Used by legato Mode 2: single_trigger - */ +/* dholdcount: difference hold data count with previous note. + decaycount: decay data count +*/ +void +fluid_rvoice_single_trigger(fluid_rvoice_t* voice, int dholdcount, int decaycount) +{ + int section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + + fluid_env_data_t* env_data_d; /* Decay data section */ + /* update Decay count section */ + env_data_d = &voice->envlfo.volenv.data[FLUID_VOICE_ENVDECAY]; + env_data_d->count = decaycount; + + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + if (section == FLUID_VOICE_ENVDELAY) + /* Skip from DELAY section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + else if (section <= FLUID_VOICE_ENVHOLD) + { /* ATTACK or HOLD section */ + fluid_env_data_t* env_data_hold; int hc; + env_data_hold = &voice->envlfo.volenv.data[FLUID_VOICE_ENVHOLD]; + /* update count of Hold section */ + hc = env_data_hold->count + dholdcount ; + if (section == FLUID_VOICE_ENVHOLD) + { /* memorize that adsr is in HOLD section */ + env_data_hold->max = 3.0f; /* 3.0 means legato occurs during HOLD */ + } + /* When legato occurs during HOLD, HOLD count is reduced */ + if(env_data_hold->max == 3.0f) { /* Count of HOLD section is reduced */ + hc -= voice->envlfo.volenv.count; + } + if(hc <0) hc = 0; /* hold section is finished */ + env_data_hold->count = hc; + /* Skip from HOLD section to ATTACK section */ + fluid_retrigger_attack(voice); + } + else /* Here adsr is in DECAY or SUSTAIN section */ + { /* dAtt is the attenuation variation (in cB) */ + fluid_real_t volval,finalval, dAtt, dAttFinal; + if(voice->dsp.prev_sav_attenuation < 0.0f) /* n1,n2 */ + dAtt = voice->dsp.attenuation - voice->dsp.prev_attenuation; + else /* n2,n3 or n3,n4,... */ + dAtt = voice->dsp.attenuation - voice->dsp.prev_sav_attenuation; + /* save effective attenuation */ + voice->dsp.prev_sav_attenuation = voice->dsp.attenuation; + + if (dAtt == 0.0f) + { + return; /* no attenuation variation */ + } + /* Skip from SUSTAIN section (eventually) to DECAY section */ + /* Once in DECAY section, current count must be reset, to be sure + that the section will be not be prematurely finished */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVDECAY); + volval = fluid_adsr_env_get_val(&voice->envlfo.volenv); + finalval = (env_data_d->increment <= 0.0f) ? env_data_d->min: env_data_d->max; + /* Variation is positive or negative */ + if (dAtt > 0.0f) { /* Attenuation increase */ + fluid_real_t maxAtt; /* rest of limitation agaisnt Max attenuation */ + fluid_real_t maxAttVol = 960.0f * (1.0f - volval); + /* Attenuation must stay in range [0..1440] cB */ + /* limit dAtt against the maximum possible (1440 cB] for dsp.attenuation */ + maxAtt = voice->dsp.prev_attenuation + dAtt - + (fluid_real_t)(FLUID_ATTEN_AMP_SIZE-1); + if (maxAtt > 0.0f) dAtt -= maxAtt; /* limit dAtt */ + else maxAtt = 0; /* no rest */ + /* limit dAtt to the maximum possible from vol to 1.0 */ + dAttFinal = dAtt - maxAttVol; /* rest of limitation */ + if(dAttFinal > 0.0f) dAtt = maxAttVol; /* limit dAtt */ + else dAttFinal = 0.0f; /* no rest */ + /* accumulate rest dAttFinal and maxAtt */ + dAttFinal += maxAtt; + } + else { /* Attenuation decrease */ + fluid_real_t minAtt; + fluid_real_t minAttVol = -volval * 960.0f; + /* limit dAtt against the minimum possible for dsp.attenuation */ + minAtt = voice->dsp.prev_attenuation + dAtt; + if(minAtt < 0.0f) dAtt = - voice->dsp.prev_attenuation; /* limit */ + else minAtt = 0.0f; /* no rest */ + /*limit dAtt against the minimum possible from vol to 0.0*/ + dAttFinal = dAtt - minAttVol; /* rest of limitation */ + if(dAttFinal < 0.0f) dAtt = minAttVol; /* limit dAtt */ + else dAttFinal = 0.0f; /* no rest */ + /* accumulate dAttFinal and minAtt */ + dAttFinal += minAtt; + } + + /* now dAtt is applied to attenuation and volume value */ + voice->dsp.attenuation = voice->dsp.prev_attenuation + dAtt; + volval += dAtt / 960.0f; + fluid_clip (volval, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, volval); + /* And dAttFinal is applied to finalval */ + finalval -= dAttFinal / 960.0f; + fluid_clip (finalval, 0.0, 1.0); + + /* Volval and finalval have been changed , so the slope is changed */ + /* minimum acceptable decay time */ + if (env_data_d->count == 0) env_data_d->count = 1; + if (volval >= finalval) { /* Decay needs negative slope */ + env_data_d->increment = -1.0f / env_data_d->count; + env_data_d->min = finalval; env_data_d->max = 1.0f; + } + else { /* Decay needs positive slope */ + env_data_d->increment = +1.0f / env_data_d->count; + env_data_d->min = 0.0f; env_data_d->max = finalval; + } + } + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + /* In legato mode 2: single-trigger: the goal is to stay in current section. + So , no skip */ +} + +/*---------------------------------------------------------------------------- + set the portamento parameter. + voice, rvoice to set portamento. + countinc, increment count number. + pitchoffset, pitch offset to apply to voice dsp.pitch. + + Notes + 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), + pitchoffet is accumulated in current dsp pitchoffset. + 2) And to get constant portamento duration, dsp pitch increment is updated. +*/ +void fluid_rvoice_set_portamento(fluid_rvoice_t * voice, unsigned int countinc, + fluid_real_t pitchoffset) +{ + if (countinc) + { + voice->dsp.pitchoffset += pitchoffset; + voice->dsp.pitchinc = - voice->dsp.pitchoffset/ countinc; + } +} + void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value) @@ -553,6 +791,7 @@ void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value) { + voice->dsp.prev_attenuation = voice->dsp.attenuation; voice->dsp.attenuation = value; } diff -Naur ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice.h ./fluid-polymono-0003/src/rvoice/fluid_rvoice.h --- ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/rvoice/fluid_rvoice.h Thu Jul 28 17:44:12 2016 @@ -95,6 +95,10 @@ int loopend; /* Note: first point following the loop (superimposed on loopstart) */ enum fluid_loop samplemode; + /* Stuff needed for portamento calculations */ + fluid_real_t pitchoffset; /* the portamento range in midicents */ + fluid_real_t pitchinc; /* the portamento increment in midicents */ + /* Stuff needed for phase calculations */ fluid_real_t pitch; /* the pitch in midicents */ @@ -105,6 +109,10 @@ int has_looped; /* Flag that is set as soon as the first loop is completed. */ fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t prev_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_multi_retrigger_attack() */ + fluid_real_t prev_sav_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_single_trigger() */ fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation * during the lifetime of the voice */ fluid_real_t amplitude_that_reaches_noise_floor_nonloop; @@ -165,7 +173,10 @@ unsigned int bufnum, int mapping); /* Dynamic update functions */ - +void fluid_rvoice_set_portamento(fluid_rvoice_t * voice, unsigned int countinc, + fluid_real_t pitchoffset); +void fluid_rvoice_multi_retrigger_attack(fluid_rvoice_t* voice); +void fluid_rvoice_single_trigger(fluid_rvoice_t* voice, int holdcount, int decaycount); void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks); void fluid_rvoice_voiceoff(fluid_rvoice_t* voice); void fluid_rvoice_reset(fluid_rvoice_t* voice); diff -Naur ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice_event.c ./fluid-polymono-0003/src/rvoice/fluid_rvoice_event.c --- ./fluidsynth-1.1.6/src/rvoice/fluid_rvoice_event.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/rvoice/fluid_rvoice_event.c Sat Jun 18 22:10:46 2016 @@ -72,6 +72,10 @@ EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*); EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*); + + EVENTFUNC_0(fluid_rvoice_multi_retrigger_attack, fluid_rvoice_t*); + EVENTFUNC_IR(fluid_rvoice_single_trigger, fluid_rvoice_t*); + EVENTFUNC_IR(fluid_rvoice_set_portamento, fluid_rvoice_t*); EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*); diff -Naur ./fluidsynth-1.1.6/src/sfloader/fluid_defsfont.c ./fluid-polymono-0003/src/sfloader/fluid_defsfont.c --- ./fluidsynth-1.1.6/src/sfloader/fluid_defsfont.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/sfloader/fluid_defsfont.c Tue Jul 19 11:07:25 2016 @@ -822,7 +822,9 @@ /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { - + /* ignoreIZ is set in mono legato playing */ + unsigned char ignoreIZ = IsIgnoreInstZone(inst_zone); + ResetIgnoreInstZone(inst_zone); /* Reset the 'ignore' request */ /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); if ((sample == NULL) || fluid_sample_in_rom(sample)) { @@ -830,15 +832,17 @@ continue; } - /* check if the note falls into the key and velocity range of this - instrument */ - - if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { + /* check if the instrument zone doesn't be ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_mono_legato()) */ + if (! ignoreIZ && + fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { /* this is a good zone. allocate a new synthesis process and initialize it */ - voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + voice = fluid_synth_alloc_voice(synth, inst_zone, chan, key, vel); if (voice == NULL) { return FLUID_FAILED; } @@ -1210,7 +1214,8 @@ FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } - if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { + if (fluid_inst_import_sfont(zone, zone->inst, + (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) { return FLUID_FAILED; } } @@ -1431,7 +1436,8 @@ * fluid_inst_import_sfont */ int -fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont) +fluid_inst_import_sfont(fluid_preset_zone_t* zonePZ, fluid_inst_t* inst, + SFInst *sfinst, fluid_defsfont_t* sfont) { fluid_list_t *p; SFZone* sfzone; @@ -1457,7 +1463,7 @@ return FLUID_FAILED; } - if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) { + if (fluid_inst_zone_import_sfont(zonePZ,zone, sfzone, sfont) != FLUID_OK) { delete_fluid_inst_zone(zone); return FLUID_FAILED; } @@ -1541,7 +1547,7 @@ zone->keyhi = 128; zone->vello = 0; zone->velhi = 128; - + zone->flags = 0; /* 0: This IZ must not ignored, 1:ZI is ignored */ /* Flag the generators as unused. * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ fluid_gen_set_default_values(&zone->gen[0]); @@ -1583,7 +1589,8 @@ * fluid_inst_zone_import_sfont */ int -fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont) +fluid_inst_zone_import_sfont(fluid_preset_zone_t* zonePZ, fluid_inst_zone_t* zone, + SFZone *sfzone, fluid_defsfont_t* sfont) { fluid_list_t *r; SFGen* sfgen; @@ -1609,6 +1616,13 @@ } r = fluid_list_next(r); } + + /* adjust IZ keyrange to integrate PZ keyrange */ + if (zonePZ->keylo > zone->keylo) zone->keylo = zonePZ->keylo; + if (zonePZ->keyhi < zone->keyhi) zone->keyhi = zonePZ->keyhi; + /* adjust IZ velrange to integrate PZ velrange */ + if (zonePZ->vello > zone->vello) zone->vello = zonePZ->vello; + if (zonePZ->velhi < zone->velhi) zone->velhi = zonePZ->velhi; /* FIXME */ /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ diff -Naur ./fluidsynth-1.1.6/src/sfloader/fluid_defsfont.h ./fluid-polymono-0003/src/sfloader/fluid_defsfont.h --- ./fluidsynth-1.1.6/src/sfloader/fluid_defsfont.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/sfloader/fluid_defsfont.h Tue Jul 19 10:50:47 2016 @@ -367,7 +367,6 @@ typedef struct _fluid_defpreset_t fluid_defpreset_t; typedef struct _fluid_preset_zone_t fluid_preset_zone_t; typedef struct _fluid_inst_t fluid_inst_t; -typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /* @@ -490,7 +489,8 @@ fluid_inst_t* new_fluid_inst(void); int delete_fluid_inst(fluid_inst_t* inst); -int fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont); +int fluid_inst_import_sfont(fluid_preset_zone_t* zonePZ, fluid_inst_t* inst, + SFInst *sfinst, fluid_defsfont_t* sfont); int fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); int fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone); fluid_inst_zone_t* fluid_inst_get_zone(fluid_inst_t* inst); @@ -508,14 +508,25 @@ int keyhi; int vello; int velhi; + unsigned char flags; /* for legato playing */ fluid_gen_t gen[GEN_LAST]; fluid_mod_t * mod; /* List of modulators */ }; +/* Flag IGNORE_IZ is set on legato playing to ignore this IZ */ +#define IGNORE_IZ 0x01 +/* IsIgnoreInstZone return True when an IZ must be ignored */ +#define IsIgnoreInstZone(iz) (iz->flags & IGNORE_IZ) +/* SetIgnoreInstZone request an IZ to be ignored */ +#define SetIgnoreInstZone(iz) (iz->flags |= IGNORE_IZ) +/* ResetgnoreInstZone reset the request to ignore an IZ */ +#define ResetIgnoreInstZone(iz) (iz->flags &= ~IGNORE_IZ) + fluid_inst_zone_t* new_fluid_inst_zone(char* name); int delete_fluid_inst_zone(fluid_inst_zone_t* zone); fluid_inst_zone_t* fluid_inst_zone_next(fluid_inst_zone_t* zone); -int fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); +int fluid_inst_zone_import_sfont(fluid_preset_zone_t* zonePZ, + fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont); int fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel); fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone); diff -Naur ./fluidsynth-1.1.6/src/sfloader/fluid_ramsfont.c ./fluid-polymono-0003/src/sfloader/fluid_ramsfont.c --- ./fluidsynth-1.1.6/src/sfloader/fluid_ramsfont.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/sfloader/fluid_ramsfont.c Tue Jul 19 11:07:59 2016 @@ -913,6 +913,9 @@ /* run thru all the zones of this instrument */ inst_zone = fluid_inst_get_zone(inst); while (inst_zone != NULL) { + /* ignoreIZ is set in mono legato playing */ + unsigned char ignoreIZ = IsIgnoreInstZone(inst_zone); + IsIgnoreInstZone(inst_zone); /* Reset the 'ignore' request */ /* make sure this instrument zone has a valid sample */ sample = fluid_inst_zone_get_sample(inst_zone); @@ -921,15 +924,17 @@ continue; } - /* check if the note falls into the key and velocity range of this - instrument */ - - if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { + /* check if the instrument zone doesn't be ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage */ + if (! ignoreIZ && + fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) { /* this is a good zone. allocate a new synthesis process and initialize it */ - voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel); + voice = fluid_synth_alloc_voice(synth, inst_zone, chan, key, vel); if (voice == NULL) { return FLUID_FAILED; } diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_chan.c ./fluid-polymono-0003/src/synth/fluid_chan.c --- ./fluidsynth-1.1.6/src/synth/fluid_chan.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_chan.c Sat Jul 30 21:26:26 2016 @@ -65,10 +65,28 @@ fluid_channel_init(fluid_channel_t* chan) { fluid_preset_t *newpreset; - int prognum, banknum; + 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 < maxNotes; i++) { + chan->monolist[i].next = i+1; + chan->monolist[i].prev = i-1; + } + chan->monolist[maxNotes -1].next = 0; /* ending element chained to the 1st */ + chan->monolist[0].prev = maxNotes -1; /* first element chained to the ending */ + chan->iLast = chan->nNotes = 0; /* list is clear */ + chan->iFirst = 1; + ChanClearPrevNote(chan); /* Mark previous note invalid */ + /*---*/ + chan->key_sustained = -1; /* No previous mono note sustained */ + chan->legatomode = RETRIGGER_0; /* Default mode */ + chan->portamentomode = EACH_NOTE; /* 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; @@ -130,6 +148,8 @@ for (i = 0; i < 128; i++) { fluid_channel_set_cc (chan, i, 0); } + clearPortamentoCtrl(chan); /* Clear PTC receive */ + ChanClearPreviousBreath(chan);/* Reset previous breath */ } /* Set RPN controllers to NULL state */ diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_chan.h ./fluid-polymono-0003/src/synth/fluid_chan.h --- ./fluidsynth-1.1.6/src/synth/fluid_chan.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_chan.h Sun Jul 31 16:30:36 2016 @@ -25,6 +25,17 @@ #include "fluid_midi.h" #include "fluid_tuning.h" + +/* mononophonic list for monophonic mode */ +#define maxNotes 10 /* Size of the monophonic list */ +struct mononote +{ + unsigned char prev; /* previous note */ + unsigned char next; /* next note */ + unsigned char note; /* note */ + unsigned char vel; /* velocity */ +}; + /* * fluid_channel_t * @@ -37,7 +48,21 @@ fluid_synth_t* synth; /**< Parent synthesizer instance */ int channum; /**< MIDI channel number */ - + /* Poly Mono variables see macro access description */ + int mode; /**< Poly Mono mode */ + int mode_val; /**< number of monophonic channel (for mode 3) */ + /* monophonic list */ + struct mononote monolist[maxNotes]; /**< monophonic list */ + unsigned char iFirst; /**< First note index */ + unsigned char iLast; /**< most recent note index since the most recent add */ + unsigned char PrevNote; /**< previous note of the most recent add */ + unsigned char nNotes; /**< actual number of notes in the list */ + /*--*/ + int key_sustained; /**< previous sustained monophonic note */ + unsigned char legatomode; /**< legato mode */ + unsigned char portamentomode; /**< portamento mode */ + int previous_cc_breath; /**< Previous Breath */ + /*- End of Poly/mono variables description */ int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ fluid_preset_t* preset; /**< Selected preset */ @@ -85,6 +110,93 @@ }; +/* Macros interface to monophonic list variables */ +/* ChanLastNote() return the note in iLast entry of the monophonic list */ +#define InvalidNote 255 +#define IsInvalidNote(n) (n == InvalidNote) +#define IsValidNote(n) (n != InvalidNote) +#define ChanLastNote(chan) (chan->monolist[chan->iLast].note) +#define ChanLastVel(chan) (chan->monolist[chan->iLast].vel) +#define ChanPrevNote(chan) (chan->PrevNote) +#define ChanClearPrevNote(chan) (chan->PrevNote = InvalidNote) +/* End of interface to monophonic list variables */ + +/* Macros interface to poly/mono mode variables */ +#define MASK_BASICCHANINFOS (MASKMODE|BASIC_CHANNEL|ENABLED) +/* access to channel mode */ +/* SetBasicChanInfos set the basic channel infos for a MIDI basic channel */ +#define SetBasicChanInfos(chan,Infos) \ +(chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) +/* ResetBasicChanInfos restset the basic channel infos for a MIDI basic channel */ +#define ResetBasicChanInfos(chan) (chan->mode &= ~MASK_BASICCHANINFOS) + +/* GetChanMode get the mode for a MIDI basic channel */ +#define GetChanMode(chan) GetModeMode(chan->mode) +/* GetChanModeVal get the mode_val for a MIDI basic channel */ +#define GetChanModeVal(chan) (chan->mode_val) + +/* IsChanMono(chan) return true when channnel is Mono */ +#define IsChanMono(chan) (IsModeMono(chan->mode)) +/* IsChanPoly(chan) return true when channnel is Poly */ +#define IsChanPoly(chan) (!IsChanMono(chan)) +/* IsChanOmniOff(chan) return true when channnel is Omni off */ +#define IsChanOmniOff(chan) (chan->mode & OMNI) +/* IsChanOmniOn(chan) return true when channnel is Omni on */ +#define IsChanOmniOn(chan) (!IsChanOmniOff(chan)) + +/* IsChanBasicChannel(chan) return true when channnel is Basic channel */ +#define IsChanBasicChannel(chan) IsModeBasicChan(chan->mode) +/* IsChanEnabled(chan) return true when channnel is listened */ +#define IsChanEnabled(chan) IsModeChanEn(chan->mode) +/* IsChanPlayingMono return true when channel is Mono or legato is on */ +#define IsChanPlayingMono(chan) (IsChanMono(chan) || fluid_channel_legato(chan)) + +/* b7, 1: means legato playing , 0: means staccato playing */ +#define LEGATO_PLAYING 0x80 +#define IsChanLegato(chan) (chan->mode & LEGATO_PLAYING) +#define IsChanStaccato(chan) (!IsChanLegato(chan)) +#define SetChanLegato(chan) (chan->mode |= LEGATO_PLAYING) +#define ResetChanLegato(chan) (chan->mode &= ~ LEGATO_PLAYING) + +/* End of macros interface to poly/mono mode variables */ + +/* Macros interface to breath variables */ +#define MASK_BREATH_MODE (BREATH_POLY|BREATH_MONO|BREATH_SYNC) +/* access to default breath infos */ +/* SetBreathInfos set the breath infos for a MIDI channel */ +#define SetBreathInfos(chan,BreathInfos) \ +(chan->mode = (chan->mode & ~MASK_BREATH_MODE) | (BreathInfos & MASK_BREATH_MODE)) +#define GetBreathInfos(chan) (chan->mode & MASK_BREATH_MODE) + + +/* IsChanPolyDefaultBreath(chan) return true when default breath is set for a channel */ +#define IsChanPolyDefaultBreath(chan) IsPolyDefaultBreath(chan->mode) +#define SetChanPolyDefaultBreath(chan) SetPolyDefaultBreath(chan->mode) +#define ResetChanPolyDefaultBreath(chan) ResetPolyDefaultBreath(chan->mode) +#define IsChanMonoDefaultBreath(chan) IsMonoDefaultBreath(chan->mode) +#define SetChanMonoDefaultBreath(chan) SetMonoDefaultBreath(chan->mode) +#define ResetChanMonoDefaultBreath(chan) ResetMonoDefaultBreath(chan->mode) +#define IsChanBreathSync(chan) IsBreathSync(chan->mode) +#define ChanClearPreviousBreath(chan) (chan->previous_cc_breath = 0) +/* End of interface to breath variables */ + + +/* acces to channel legato mode */ +/* SetChanLegatoMode set the legato mode for a MIDI channel */ +#define SetChanLegatoMode(chan,mode) (chan->legatomode = mode) + +/* GetChanLegatoMode get the legato mode for a MIDI channel */ +#define GetChanLegatoMode(chan) (chan->legatomode) +/* End of macros interface to legato mode variables */ + +/* acces to channel portamento mode */ +/* SetChanPortamentoMode set the portamento mode for a MIDI channel */ +#define SetChanPortamentoMode(chan,mode) (chan->portamentomode = mode) + +/* GetChanPortamentoMode get the portamento mode for a MIDI channel */ +#define GetChanPortamentoMode(chan) (chan->portamentomode) +/* End of macros interface to portamento mode variables */ + fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); void fluid_channel_init_ctrl(fluid_channel_t* chan, int is_all_ctrl_off); int delete_fluid_channel(fluid_channel_t* chan); @@ -138,6 +250,13 @@ ((chan)->tuning_prog) #define fluid_channel_set_tuning_prog(chan, prog) \ ((chan)->tuning_prog = (prog)) +#define fluid_channel_portamentotime(_c) \ + ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) +#define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) +#define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) +#define clearPortamentoCtrl(_c) ((_c)->cc[PORTAMENTO_CTRL] = InvalidNote) +#define portamentoCtrl(_c) ((unsigned char)(_c)->cc[PORTAMENTO_CTRL]) +#define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) #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; } @@ -145,5 +264,4 @@ #define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n]) #define fluid_channel_get_min_note_length_ticks(chan) \ ((chan)->synth->min_note_length_ticks) - #endif /* _FLUID_CHAN_H */ diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_synth.c ./fluid-polymono-0003/src/synth/fluid_synth.c --- ./fluidsynth-1.1.6/src/synth/fluid_synth.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_synth.c Mon Aug 01 17:05:34 2016 @@ -37,6 +37,28 @@ extern int feenableexcept (int excepts); #endif +/* extern declared fluid_synth_polymono.c */ +extern int fluid_synth_set_basic_channel_LOCAL(fluid_synth_t* synth, + int basicchan,int mode, int val); +/* extern declared in fluid_synth_mono.c */ +extern int fluid_synth_noteon_mono_LOCAL(fluid_synth_t* synth, int chan, + int key, int vel); +extern int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t* synth, int chan, + int key); +extern int fluid_synth_noteon_mono_legato(fluid_synth_t* synth, int chan, + int fromkey, int tokey, int vel); +extern int fluid_synth_noteoff_monopoly(fluid_synth_t* synth, int chan, int key, + char Mono); + +extern void fluid_channel_set_onenote_monolist(fluid_channel_t* chan, + unsigned char key, + unsigned char vel); +extern void fluid_channel_clear_monolist(fluid_channel_t* chan); +extern void ValidInvalidPrevNoteStaccato(fluid_channel_t* chan); +extern void LegatoOnOff(fluid_channel_t* chan, int value); +extern void BreathOnOff(fluid_channel_t* chan, int value); + +/* End of extern declared in fluid_synth_mono.c , fluid_synth_polymono.c */ static void fluid_synth_init(void); static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, @@ -51,7 +73,7 @@ int len, char *response, int *response_len, int avail_response, int *handled, int dryrun); -static int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t* synth, int chan); static int fluid_synth_system_reset_LOCAL(fluid_synth_t* synth); static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t* synth, int chan, @@ -90,7 +112,7 @@ static fluid_sfont_info_t *new_fluid_sfont_info (fluid_synth_t *synth, fluid_sfont_t *sfont); static int fluid_synth_sfunload_callback(void* data, unsigned int msec); -static void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key); static fluid_tuning_t* fluid_synth_get_tuning(fluid_synth_t* synth, int bank, int prog); @@ -128,6 +150,13 @@ * explicitly overridden by the sound font in order to turn them off. */ +/* default_breath2att_modulator is not a default modulator specified in SF +it is intended to replace default_vel2att_mod on demand using +API fluid_set_breath_mode() or command shell setbreathmode. +*/ +fluid_mod_t default_breath2att_mod; + + fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ @@ -195,6 +224,17 @@ 256, 1, 65535, 0, NULL, NULL); fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0, NULL, NULL); + /* basic channel settings for poly/mono mode */ + /* synth.basic-channel: default: 0, min:0, max:255, no hint,no callback function */ + fluid_settings_register_int(settings, "synth.basic-channel", + 0, 0, 255, 0, NULL, NULL); + /* synth.basic-channel.mode: default: 0, min:0, max:3, no hint,no callback */ + fluid_settings_register_int(settings, "synth.basic-channel-mode", + 0, 0, 3, 0, NULL, NULL); + /* synth.basic-channel.modeval: default: 0, min:0, max:255, no hint,no callback*/ + fluid_settings_register_int(settings, "synth.basic-channel-modeval", + 0, 0, 255, 0, NULL, NULL); + /* End of settings for poly/mono mode */ fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0, NULL, NULL); @@ -288,6 +328,20 @@ init_dither(); + /* default_breath2att_modulator is not a default modulator specified in SF2.01. + it is intended to replace default_vel2att_mod on demand using + API fluid_set_breath_mode() or command shell setbreathmode. + */ + fluid_mod_set_source1(&default_breath2att_mod, /* The modulator we are programming here */ + 2, /* Source. breath MSB corresponds to 2. */ + FLUID_MOD_CC /* MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&default_breath2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&default_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_breath2att_mod, 960.0); /* Modulation amount: 960 */ /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ @@ -539,7 +593,6 @@ intparam, realparam); } - /** * Create new FluidSynth instance. * @param settings Configuration parameters to use (used directly). @@ -659,6 +712,7 @@ synth->sfont_info = NULL; synth->sfont_hash = new_fluid_hashtable (NULL, NULL); synth->noteid = 0; + synth->fromkey_portamento = InvalidNote; /* disable portamento */ synth->ticks_since_start = 0; synth->tuning = NULL; fluid_private_init(synth->tuning_iter); @@ -713,6 +767,24 @@ } } + /* set basic channel */ + { + int basicchan, mode,val; + + fluid_settings_getint(settings, "synth.basic-channel", &basicchan); + fluid_settings_getint(settings, "synth.basic-channel-mode", &mode); + fluid_settings_getint(settings, "synth.basic-channel-modeval", &val); + if (basicchan >= synth->midi_channels || + basicchan + val > synth->midi_channels) + { + /* Set basic channel 0, mode 0 (Poly Omni On) */ + /* (i.e all channels are polyphonic */ + basicchan = 0; mode = OMNION_POLY; val = 0; + FLUID_LOG(FLUID_WARN, "Requested basic channel, mode, modeval is incorrect.\n" + "basic channel:0 , poly omni on (mode 0) has been set"); + } + fluid_synth_set_basic_channel(synth, basicchan, mode, val); + } fluid_synth_set_sample_rate(synth, synth->sample_rate); fluid_synth_update_overflow(synth, "", 0.0f); @@ -917,8 +989,8 @@ fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); fluid_return_val_if_fail (vel >= 0 && vel <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - result = fluid_synth_noteon_LOCAL (synth, chan, key, vel); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else result = fluid_synth_noteon_LOCAL (synth, chan, key, vel); FLUID_API_RETURN(result); } @@ -926,13 +998,12 @@ static int fluid_synth_noteon_LOCAL(fluid_synth_t* synth, int chan, int key, int vel) { - fluid_channel_t* channel; - + fluid_channel_t* channel ; /* notes with velocity zero go to noteoff */ if (vel == 0) return fluid_synth_noteoff_LOCAL(synth, chan, key); channel = synth->channel[chan]; - + /* make sure this channel has a preset */ if (channel->preset == NULL) { if (synth->verbose) { @@ -944,13 +1015,24 @@ } return FLUID_FAILED; } + + if(IsChanPlayingMono(channel)) /* channel is mono or legato On) */ + { /* play the noteOn in monophonic */ + return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); + } + else { /* channel is poly and legato Off) */ + + /* play the noteOn in polyphonic */ + /* Set the note at first position in monophonic list */ + fluid_channel_set_onenote_monolist(channel, (unsigned char) key, + (unsigned char) vel); + + /* If there is another voice process on the same channel and key, + advance it to the release phase. */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); - /* If there is another voice process on the same channel and key, - advance it to the release phase. */ - fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); - - - return fluid_preset_noteon(channel->preset, synth, chan, key, vel); + return fluid_synth_noteon_mono_legato(synth, chan, InvalidNote, key, vel); + } } /** @@ -968,7 +1050,8 @@ fluid_return_val_if_fail (key >= 0 && key <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - result = fluid_synth_noteoff_LOCAL (synth, chan, key); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else result = fluid_synth_noteoff_LOCAL (synth, chan, key); FLUID_API_RETURN(result); } @@ -977,31 +1060,20 @@ static int fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key) { - fluid_voice_t* voice; - int status = FLUID_FAILED; - int i; - - 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 */ - - fluid_voice_noteoff(voice); - status = FLUID_OK; - } /* if voice on */ - } /* for all voices */ + int status; + fluid_channel_t* channel = synth->channel[chan]; + if(IsChanPlayingMono(channel)) /* channel is mono or legato On) */ + { /* play the noteOff in monophonic */ + status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); + } + else { /* channel is poly and legato Off) */ + /* remove the note from the monophonic list */ + if(key == ChanLastNote(channel)) fluid_channel_clear_monolist(channel); + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + /* Change the state (Valid/Invalid) of the most recent note played in a + staccato manner */ + ValidInvalidPrevNoteStaccato(channel); return status; } @@ -1010,6 +1082,7 @@ static int fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t* synth, int chan) { + fluid_channel_t* channel = synth->channel[chan]; fluid_voice_t* voice; int i; @@ -1017,7 +1090,11 @@ voice = synth->voice[i]; if ((voice->chan == chan) && _SUSTAINED(voice)) - fluid_voice_release(voice); + { /* key_sustained must be marked released (-1) only if key_sustain + is sustained by sustain pedal */ + if(voice->key == channel->key_sustained) channel->key_sustained = -1; + fluid_voice_release(voice); + } } return FLUID_OK; @@ -1028,6 +1105,7 @@ static int fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t* synth, int chan) { + fluid_channel_t* channel = synth->channel[chan]; fluid_voice_t* voice; int i; @@ -1035,7 +1113,11 @@ voice = synth->voice[i]; if ((voice->chan == chan) && _HELD_BY_SOSTENUTO(voice)) - fluid_voice_release(voice); + { /* key_sustained must be marked released (-1) only if key_sustain + is sustained by sostenuto pedal */ + if(voice->key == channel->key_sustained) channel->key_sustained = -1; + fluid_voice_release(voice); + } } return FLUID_OK; @@ -1049,20 +1131,50 @@ * @param num MIDI controller number (0-127) * @param val MIDI controller value (0-127) * @return FLUID_OK on success, FLUID_FAILED otherwise + * Note: The function support MIDI CC global which will be send to + * all channels when the basic channel is in mode (OmniOn,Mono). + * The CC needs to be send on MIDI channel one below the 'basic Channel' + * of the receiver. */ int fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) { int result; + fluid_channel_t* channel; fluid_return_val_if_fail (num >= 0 && num <= 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, "cc\t%d\t%d\t%d", chan, num, val); - - fluid_channel_set_cc (synth->channel[chan], num, val); - result = fluid_synth_cc_LOCAL (synth, chan, num); + channel = synth->channel[chan]; + if( IsChanEnabled(channel)) + { /* chan is enabled */ + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + fluid_channel_set_cc (channel, num, val); + result = fluid_synth_cc_LOCAL (synth, chan, num); + } + else /* chan is disabled so it is a candidate for global channel */ + { /* look for next basic channel */ + int nChan = synth->midi_channels; /* MIDI Channels number */ + int basicchan ; + if (chan < nChan-1) basicchan = chan + 1; /* next channel */ + else basicchan = 0; /* wrap to 0 */ + channel = synth->channel[basicchan]; + /* Channel must be a basicchan in mode OMNIOFF_MONO */ + if (IsChanBasicChannel(channel) && (GetChanMode(channel) == OMNIOFF_MONO)) + { /* send cc to all channels in this basic channel */ + int i,val = GetChanModeVal(channel); + for (i = basicchan; i < basicchan+val; i++) + { + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + fluid_channel_set_cc (synth->channel[i], num, val); + result = fluid_synth_cc_LOCAL (synth, i, num); + } + } + /* The channel chan is not a valid 'global channel' */ + else result = FLUID_FAILED; + } FLUID_API_RETURN(result); } @@ -1077,6 +1189,83 @@ value = fluid_channel_get_cc (chan, num); switch (num) { + + /* Poly Mono mode */ + case POLY_OFF: /* Mono On */ + /* allowed only if channum is a basic channel */ + if (IsChanBasicChannel(chan)) + { + int new_mode, new_val; + if(IsChanOmniOn(chan)) /* channel is actually Omni On */ + {new_mode = OMNION_MONO; new_val=0;} /* set channel in mode 1 */ + else /* channel is actually Omni Off */ + {new_mode = OMNIOFF_MONO; new_val= value;} /* set channel in mode 3 */ + return fluid_synth_set_basic_channel_LOCAL(synth,channum, + new_mode, new_val); + } + else return FLUID_FAILED; + break; + + case POLY_ON: /* Mono Off */ + /* allowed only if channum is a basic channel */ + if (IsChanBasicChannel(chan)) + { + int new_mode, new_val; + if(IsChanOmniOn(chan)) /* channel is actually Omni On */ + {new_mode = OMNION_POLY; new_val=0;} /* set channel in mode 0 */ + else /* channel is actually Omni Off */ + {new_mode = OMNIOFF_POLY; new_val= 0;} /* set channel in mode 2 */ + return fluid_synth_set_basic_channel_LOCAL(synth,channum, + new_mode, new_val); + } + else return FLUID_FAILED; + break; + + case OMNI_ON: /* Omni On */ + /* allowed only if channum is a basic channel */ + if (IsChanBasicChannel(chan)) + { + int new_mode, new_val; + if(IsChanPoly(chan)) /* channel is actually Poly On */ + {new_mode = OMNION_POLY; new_val=0;} /* set channel in mode 0 */ + else /* channel is actually Mono On */ + {new_mode = OMNION_MONO; new_val=0;} /* set channel in mode 1 */ + return fluid_synth_set_basic_channel_LOCAL(synth,channum, + new_mode, new_val); + } + else return FLUID_FAILED; + break; + + case OMNI_OFF: /* Omni Off */ + /* allowed only if channum is a basic channel */ + if (IsChanBasicChannel(chan)) + { + int new_mode, new_val; + if(IsChanPoly(chan)) /* channel is actually Poly On */ + {new_mode = OMNIOFF_POLY; new_val= 0;} /* set channel in mode 2 */ + else /* channel is actually Mono On */ + /* Channel will bet set in mode 3 with only one channel enabled(ie the + basic channel). After sending cc OMNI OFF, the sending MIDI transmitter + have to send cc MONO ON next to OMNI_OFF to change the number of + monophonic channel that need to be enabled */ + {new_mode = OMNIOFF_MONO; new_val= 1;} /* set channel in mode 3 */ + return fluid_synth_set_basic_channel_LOCAL(synth,channum, + new_mode, new_val); + } + else return FLUID_FAILED; + break; + + case LEGATO_SWITCH: + /* handle Poly/mono commutation on Legato pedal On/Off.*/ + LegatoOnOff(chan,value); + break; + + case PORTAMENTO_SWITCH: + /* Special handling of the monophonic list */ + /* Invalid) the most recent note played in a staccato manner */ + ValidInvalidPrevNoteStaccato(chan); + break; + case SUSTAIN_SWITCH: /* Release voices if Sustain switch is released */ if (value < 64) /* Sustain is released */ @@ -1186,6 +1375,9 @@ chan->nrpn_active = 0; break; default: + /* handle CC Breath On/Off noteOn/noteOff mode */ + if (num == BREATH_MSB) BreathOnOff(chan, value); + return fluid_synth_modulate_voices_LOCAL (synth, channum, 1, num); } @@ -1203,13 +1395,17 @@ int fluid_synth_get_cc(fluid_synth_t* synth, int chan, int num, int* pval) { + int result; fluid_return_val_if_fail (num >= 0 && num < 128, FLUID_FAILED); fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - *pval = fluid_channel_get_cc (synth->channel[chan], num); - FLUID_API_RETURN(FLUID_OK); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else{ + *pval = fluid_channel_get_cc (synth->channel[chan], num); + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /* @@ -1517,12 +1713,14 @@ if (chan >= synth->midi_channels) result = FLUID_FAILED; else - result = fluid_synth_all_notes_off_LOCAL (synth, chan); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else result = fluid_synth_all_notes_off_LOCAL (synth, chan); FLUID_API_RETURN(result); } /* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ -static int +//static int +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan) { fluid_voice_t* voice; @@ -1555,7 +1753,8 @@ if (chan >= synth->midi_channels) result = FLUID_FAILED; else - result = fluid_synth_all_sounds_off_LOCAL (synth, chan); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else result = fluid_synth_all_sounds_off_LOCAL (synth, chan); FLUID_API_RETURN(result); } @@ -1626,7 +1825,6 @@ { fluid_voice_t* voice; int i; - for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; @@ -1637,6 +1835,10 @@ for (i = 0; i < synth->midi_channels; i++) fluid_channel_reset(synth->channel[i]); + /* Basic channel 0, Mode Omni On Poly */ + fluid_synth_set_basic_channel(synth, 0, OMNION_POLY, 0); + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_fx, 0, 0.0f); return FLUID_OK; @@ -1700,13 +1902,15 @@ fluid_return_val_if_fail (val >= 0 && val <= 127, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); - fluid_channel_set_channel_pressure (synth->channel[chan], val); + fluid_channel_set_channel_pressure (synth->channel[chan], val); - result = fluid_synth_update_channel_pressure_LOCAL (synth, chan); + result = fluid_synth_update_channel_pressure_LOCAL (synth, chan); + } FLUID_API_RETURN(result); } @@ -1731,12 +1935,15 @@ fluid_return_val_if_fail (val >= 0 && val <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); - fluid_channel_set_pitch_bend (synth->channel[chan], val); + fluid_channel_set_pitch_bend (synth->channel[chan], val); - result = fluid_synth_update_pitch_bend_LOCAL (synth, chan); + result = fluid_synth_update_pitch_bend_LOCAL (synth, chan); + } FLUID_API_RETURN(result); } @@ -1758,11 +1965,16 @@ int fluid_synth_get_pitch_bend(fluid_synth_t* synth, int chan, int* ppitch_bend) { + int result; fluid_return_val_if_fail (ppitch_bend != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + *ppitch_bend = fluid_channel_get_pitch_bend (synth->channel[chan]); + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /** @@ -1779,12 +1991,15 @@ fluid_return_val_if_fail (val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ FLUID_API_ENTRY_CHAN(FLUID_FAILED); - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); - fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val); + fluid_channel_set_pitch_wheel_sensitivity (synth->channel[chan], val); - result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan); + result = fluid_synth_update_pitch_wheel_sens_LOCAL (synth, chan); + } FLUID_API_RETURN(result); } @@ -1806,11 +2021,16 @@ int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval) { + int result; fluid_return_val_if_fail (pval != NULL, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); - FLUID_API_RETURN(FLUID_OK); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + *pval = fluid_channel_get_pitch_wheel_sensitivity (synth->channel[chan]); + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /** @@ -1945,61 +2165,64 @@ FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; - if (channel->channel_type == CHANNEL_TYPE_DRUM) - banknum = DRUM_INST_BANK; - else - fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); - - if (synth->verbose) - FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); - - /* I think this is a hack for MIDI files that do bank changes in GM mode. - * Proper way to handle this would probably be to ignore bank changes when in - * GM mode. - JG - * This is now possible by setting synth.midi-bank-select=gm, but let the hack - * stay for the time being. - DH - */ - if (prognum != FLUID_UNSET_PROGRAM) - { - subst_bank = banknum; - subst_prog = prognum; + if(! IsChanEnabled(channel)) result = FLUID_FAILED; + else { + if (channel->channel_type == CHANNEL_TYPE_DRUM) + banknum = DRUM_INST_BANK; + else + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); + + if (synth->verbose) + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + + /* I think this is a hack for MIDI files that do bank changes in GM mode. + * Proper way to handle this would probably be to ignore bank changes when + * in GM mode. - JG + * This is now possible by setting synth.midi-bank-select=gm, but let the + * hack stay for the time being. - DH + */ + if (prognum != FLUID_UNSET_PROGRAM) + { + subst_bank = banknum; + subst_prog = prognum; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - /* Fallback to another preset if not found */ - if (!preset) { - /* Percussion: Fallback to preset 0 in percussion bank */ - if (subst_bank == DRUM_INST_BANK) { - subst_prog = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - } - /* Melodic instrument */ - else { - /* Fallback first to bank 0:prognum */ - subst_bank = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - - /* Fallback to first preset in bank 0 (usually piano...) */ - if (!preset) - { - subst_prog = 0; - preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); - } - } + /* Fallback to another preset if not found */ + if (!preset) { + /* Percussion: Fallback to preset 0 in percussion bank */ + if (subst_bank == DRUM_INST_BANK) { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + /* Melodic instrument */ + else { + /* Fallback first to bank 0:prognum */ + subst_bank = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to first preset in bank 0 (usually piano...) */ + if (!preset) + { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + } + + if (preset) + FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", + chan, banknum, prognum, subst_bank, subst_prog); + else + FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", + chan, banknum, prognum); + } + } - if (preset) - FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", - chan, banknum, prognum, subst_bank, subst_prog); - else - FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", - chan, banknum, prognum); - } + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, + -1, prognum); + result = fluid_synth_set_preset (synth, chan, preset); } - - /* Assign the SoundFont ID and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, preset ? fluid_sfont_get_id (preset->sfont) : 0, - -1, prognum); - result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } @@ -2013,11 +2236,15 @@ int fluid_synth_bank_select(fluid_synth_t* synth, int chan, unsigned int bank) { + int result; fluid_return_val_if_fail (bank <= 16383, FLUID_FAILED); FLUID_API_ENTRY_CHAN(FLUID_FAILED); - - fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); - FLUID_API_RETURN(FLUID_OK); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + fluid_channel_set_sfont_bank_prog (synth->channel[chan], -1, bank, -1); + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /** @@ -2030,11 +2257,15 @@ int fluid_synth_sfont_select(fluid_synth_t* synth, int chan, unsigned int sfont_id) { + int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); - fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); - - FLUID_API_RETURN(FLUID_OK); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /** @@ -2054,7 +2285,10 @@ int result; FLUID_API_ENTRY_CHAN(FLUID_FAILED); - result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM); + if(! IsChanEnabled(synth->channel[chan])) result = FLUID_FAILED; + else { + result = fluid_synth_program_change (synth, chan, FLUID_UNSET_PROGRAM); + } FLUID_API_RETURN(result); } @@ -2071,6 +2305,7 @@ fluid_synth_get_program(fluid_synth_t* synth, int chan, unsigned int* sfont_id, unsigned int* bank_num, unsigned int* preset_num) { + int result; fluid_channel_t* channel; fluid_return_val_if_fail (sfont_id != NULL, FLUID_FAILED); @@ -2079,13 +2314,16 @@ FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; - fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, - (int *)preset_num); - - /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ - if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; + if(! IsChanEnabled(channel)) result = FLUID_FAILED; + else { + fluid_channel_get_sfont_bank_prog(channel, (int *)sfont_id, (int *)bank_num, + (int *)preset_num); - FLUID_API_RETURN(FLUID_OK); + /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ + if (*preset_num == FLUID_UNSET_PROGRAM) *preset_num = 0; + result = FLUID_OK; + } + FLUID_API_RETURN(result); } /** @@ -2107,21 +2345,22 @@ FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; + if(! IsChanEnabled(channel)) result = FLUID_FAILED; + else { + /* ++ Allocate preset */ + preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); - /* ++ Allocate preset */ - preset = fluid_synth_get_preset (synth, sfont_id, bank_num, preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %d", - bank_num, preset_num, sfont_id); - FLUID_API_RETURN(FLUID_FAILED); + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); + result = fluid_synth_set_preset (synth, chan, preset); } - - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, sfont_id, bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); - FLUID_API_RETURN(result); } @@ -2147,21 +2386,23 @@ FLUID_API_ENTRY_CHAN(FLUID_FAILED); channel = synth->channel[chan]; + if(! IsChanEnabled(channel)) result = FLUID_FAILED; + else { + /* ++ Allocate preset */ + preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, + preset_num); + if (preset == NULL) { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %s", + bank_num, preset_num, sfont_name); + FLUID_API_RETURN(FLUID_FAILED); + } - /* ++ Allocate preset */ - preset = fluid_synth_get_preset_by_sfont_name (synth, sfont_name, bank_num, - preset_num); - if (preset == NULL) { - FLUID_LOG(FLUID_ERR, - "There is no preset with bank number %d and preset number %d in SoundFont %s", - bank_num, preset_num, sfont_name); - FLUID_API_RETURN(FLUID_FAILED); + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), + bank_num, preset_num); + result = fluid_synth_set_preset (synth, chan, preset); } - - /* Assign the new SoundFont ID, bank and program number to the channel */ - fluid_channel_set_sfont_bank_prog (channel, fluid_sfont_get_id (preset->sfont), - bank_num, preset_num); - result = fluid_synth_set_preset (synth, chan, preset); FLUID_API_RETURN(result); } @@ -2936,7 +3177,8 @@ /** * Allocate a synthesis voice. * @param synth FluidSynth instance - * @param sample Sample to assign to the voice +// * @param sample Sample to assign to the voice + * @param inst_zone Instrument Zone to assign to the voice * @param chan MIDI channel number (0 to MIDI channel count - 1) * @param key MIDI note number for the voice (0-127) * @param vel MIDI velocity for the voice (0-127) @@ -2950,14 +3192,16 @@ * SoundFont loader preset noteon method. */ fluid_voice_t* -fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int key, int vel) +fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_inst_zone_t *inst_zone, + int chan, int key, int vel) { int i, k; fluid_voice_t* voice = NULL; fluid_channel_t* channel = NULL; unsigned int ticks; - fluid_return_val_if_fail (sample != NULL, NULL); +// fluid_return_val_if_fail (sample != NULL, NULL); + fluid_return_val_if_fail (fluid_inst_zone_get_sample != NULL, NULL); FLUID_API_ENTRY_CHAN(NULL); /* check if there's an available synthesis process */ @@ -3000,14 +3244,28 @@ channel = synth->channel[chan]; } - if (fluid_voice_init (voice, sample, channel, key, vel, + if (fluid_voice_init (voice, inst_zone, channel, key, vel, synth->storeid, ticks, synth->gain) != FLUID_OK) { FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); FLUID_API_RETURN(NULL); } /* add the default modulators to the synthesis process. */ - fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */ + + /* default_breath2att_modulator is not a default modulator specified in SF + it is intended to replace default_vel2att_mod on demand using + API fluid_set_setbreath_mode() or command shell setbreathmode. + */ + { + unsigned char mono = IsChanPlayingMono(channel); + if((!mono && IsChanPolyDefaultBreath(channel)) || + (mono && IsChanMonoDefaultBreath(channel))) + /* add breathToAttenuation modulator */ + fluid_voice_add_mod(voice, &default_breath2att_mod, FLUID_VOICE_DEFAULT); + else /* add default velocityToAttenuation modulator */ + fluid_voice_add_mod(voice, &default_vel2att_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.1 */ + } + fluid_voice_add_mod(voice, &default_vel2filter_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.2 */ fluid_voice_add_mod(voice, &default_at2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.3 */ fluid_voice_add_mod(voice, &default_mod2viblfo_mod, FLUID_VOICE_DEFAULT); /* SF2.01 $8.4.4 */ @@ -3956,15 +4214,20 @@ * several voice processes, for example a stereo sample. Don't * release those... */ -static void +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, int chan, int key) { int i; fluid_voice_t* voice; - synth->storeid = synth->noteid++; + /* storeid is a parameter for fluid_voice_init() */ + synth->storeid = synth->noteid++; + /* for "monophonic playing" key is the previous sustained note + if it exists (0 to 127) or -1 otherwise */ + if(key < 0) return; + for (i = 0; i < synth->polyphony; i++) { voice = synth->voice[i]; if (_PLAYING(voice) diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_synth.h ./fluid-polymono-0003/src/synth/fluid_synth.h --- ./fluidsynth-1.1.6/src/synth/fluid_synth.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_synth.h Tue Jul 19 10:14:24 2016 @@ -154,6 +154,7 @@ int active_voice_count; /**< count of active voices */ unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ unsigned int storeid; + int fromkey_portamento; /**< fromkey portamento */ fluid_rvoice_eventhandler_t* eventhandler; float reverb_roomsize; /**< Shadow of reverb roomsize */ diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_synth_mono.c ./fluid-polymono-0003/src/synth/fluid_synth_mono.c --- ./fluidsynth-1.1.6/src/synth/fluid_synth_mono.c Thu Jan 01 01:00:00 1970 +++ ./fluid-polymono-0003/src/synth/fluid_synth_mono.c Mon Aug 01 17:25:07 2016 @@ -0,0 +1,627 @@ +/* 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" + +extern void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t* synth, + int chan, int key); +/** monophonic playing *******************************************************/ + +/****************************************************************************** + 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). + * @param onenote. When 1 the function add the note but the monophonic + * keeps only one note + * Note: iLast index keeps a trace of the most recent note inserted. + * PrevNote keeps a trace of the note prior iLast note. + * ChanLegato bit keeps trace of legato/staccato playing + */ +static void +fluid_channel_add_monolist(fluid_channel_t* chan, unsigned char key, + unsigned char vel, unsigned char onenote) +{ + unsigned char iLast = chan->iLast; + if (chan->nNotes) SetChanLegato(chan); /* update Legato playing bit */ + else ResetChanLegato(chan); /* update Staccato playing bit */ + /* keep trace of the note prior last note */ + if(chan->nNotes) chan->PrevNote = chan->monolist[iLast].note; + /* update iLast before writing new note */ + iLast = chan->monolist[iLast].next; + chan->iLast = iLast; /* now ilast is the last note */ + chan->monolist[iLast].note = key; /* save note and velocity */ + chan->monolist[iLast].vel = vel; + if (onenote) { /* clear monolist to one note addition */ + chan->iFirst = iLast; chan->nNotes = 0; + } + if(chan->nNotes < maxNotes) chan->nNotes++; /* update nNotes */ + else { /* overflow situation. So circular motion for iFirst */ + chan->iFirst = chan->monolist[iLast].next; + /* warning */ + FLUID_LOG(FLUID_INFO, "Overflow on monophonic list channel %d ", + chan->channum); + } +} + +/** + * 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, InvalidNote otherwise. + */ +static unsigned 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 InvalidNote; /* 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 and return + * InvalideNote. + * return prev index if the note is the last note in the list, + * InvalidNote otherwise. + * Note: iLast index keeps a trace of the most recent note played even when + * the list is empty. + * PrevNote keeps a trace of the note removed. + * ChanLegato bit keeps trace of legato/staccato playing + */ +static unsigned char +fluid_channel_remove_monolist(fluid_channel_t* chan, short i) +{ + unsigned char iPrev = InvalidNote; + unsigned char iLast = chan->iLast; + /* check if index is valid */ + if(IsInvalidNote(i) || i >= maxNotes || !chan->nNotes) return InvalidNote; + /* 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 == iLast) { /* Removing/Inserting isn't necessary */ + /* keep trace of the note prior last note */ + chan->PrevNote= chan->monolist[iLast].note; + /* update iLast to the previous */ + iPrev = chan->monolist[i].prev; + chan->iLast = iPrev; + } + else { /* i is before iLast */ + if(i == chan->iFirst) chan->iFirst = chan->monolist[i].next; + else { /* i is between iFirst ans iLast */ + /* Removing element i and inserting between iLast and iNext */ + unsigned char next,prev,nextend; + /* removing by chaining prev and next */ + next = chan->monolist[i].next; prev = chan->monolist[i].prev; + chan->monolist[next].prev = prev; chan->monolist[prev].next = next; + /* inserting after iLast */ + nextend = chan->monolist[iLast].next; + chan->monolist[i].next = nextend; + chan->monolist[nextend].prev = i; + chan->monolist[i].prev = iLast; + chan->monolist[iLast].next = i; + } + } + chan->nNotes--; + if (chan->nNotes) SetChanLegato(chan); /* update Legato playing bit */ + else ResetChanLegato(chan); /* update Staccato playing bit */ + return iPrev; +} + +/** + * remove all notees from the monophonic list. + * @param chan fluid_channel_t. + * Note: iLast index keeps a trace of the most recent note played even when + * the list is empty. + * PrevNote keeps a trace of the note . + * ChanLegato bit keeps trace of legato/staccato playing +*/ +void fluid_channel_clear_monolist(fluid_channel_t* chan) +{ + chan->iFirst = chan->monolist[chan->iLast].next; + chan->PrevNote= chan->monolist[chan->iLast].note; + chan->nNotes = 0; + ResetChanLegato(chan); +} + +/** + * The function is called on legato off + * The monophonic list is flushed keeping last note only. + * @param chan fluid_channel_t. + * Note: iLast index keeps a trace of the most recent note played. + * PrevNote keeps a trace of the note . + * ChanLegato bit keeps trace of legato/staccato playing +*/ +static void fluid_channel_keep_lastnote_monolist(fluid_channel_t* chan) +{ + chan->iFirst = chan->iLast; + chan->nNotes = 1; +} + + +/** + * The function add the note in monophonic list , but keep only this note + * @param chan fluid_channel_t. + * Note: iLast index keeps a trace of the most recent note inserted. + * PrevNote keeps a trace of the note prior iLast note. + * ChanLegato 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); +} + + + +/***************************************************************************** + Portamemto related functions in Poly or Mono mode +******************************************************************************/ + +/*----------------------------------------------------------------------------- + The function return two informations: + 1)The function determines if a portamento must occurs on next noteOn + (PTC, or Portamento On).On portamento On, the function takes into account the + 'portamento mode' + 'fromkey portamento' which is the pitchstart key of a portamento is returned + in fluid_synth_t.fromkey_portamento to enable the portamento. + -When CC PTC has been received its value supersedes the defaultFromkey and any + Portamento pedal and portamento mode. + -When CC PTC haven't received and Portamento is On ,'fromkey portamento' is + the 'defaultFromkey' note if this parameter is valid, otherwise + 'fromkey portamento' is determined from the portamento mode and the note prior + the most recent note played. + Where portamento mode is: + - each note, the note prior the most recent note is any note played staccato + or legato. + - legato only, the note prior the most recent note is a note played legato + with the next.Any staccato note and the first note of a legato passage will + be played without portamento. + + 2)The function determines if a legato playing must occurs on next noteOn. + 'fromkey legato note' is returned in the case of legato playing. + - When en CC PTC has been received its value supersede the defaultFromkey + only if defaultFromkey is invalid. + - When CC PTC haven't received, 'fromkey legato' is 'defaultFromkey' + if this parameter is valid, otherwise 'fromkey legato' is determined from + the mono/poly mode and the actual 'staccato/legato' playing state as this: + - in staccato (poly/Mono), fromkey legato is InvalidNote. + - in mono mode legato playing, fromkey legato is the note prior the most + recent note played. + - in poly mode legato playing, actually we don't want playing legato. So + fromkey legato is InvalidNote. + + On input + @param chan fluid_channel_t. + @param defaultFromkey, the defaut 'fromkey portamento' note or 'fromkey legato' + note (see description above). + + On return + 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. + If valid,it means that portamento is enabled (by PTC receive or Portamento On). + otherwise it means that portamento is disabled. + During next voices staccato/legato starting process, if fromkey is valid the + portamento will be started. + + 2) The 'fromkey legato' note is returned in the case of legato playing. + + 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, defaultFromkey must be valid. + + In poly, defaultFromkey must be InvalidNote. + In mono staccato playing,defaultFromkey must be InvalidNote + In mono when legato playing,defaultFromkey must be valid + +------------------------------------------------------------------------------*/ +static unsigned char GetFromKeyPortamentoLegato(fluid_channel_t* chan, + unsigned char defaultFromkey) +{ + unsigned char ptc = portamentoCtrl(chan); + if(IsValidNote(ptc)) + { /* CC PTC has been received */ + clearPortamentoCtrl(chan); /* clear the CC PTC receive */ + chan->synth->fromkey_portamento = ptc;/* return fromkey portamento */ + /* return fromkey legato */ + if(IsInvalidNote(defaultFromkey)) defaultFromkey= ptc; + } + else + { /* determine and return fromkey portamento */ + unsigned char fromkey_portamento = InvalidNote; + 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 */ + unsigned char portamentomode = GetChanPortamentoMode(chan); + if(IsValidNote(defaultFromkey)) + fromkey_portamento = defaultFromkey; /* on each note */ + else fromkey_portamento = ChanPrevNote(chan); /* on each note */ + if(portamentomode == LEGATO_ONLY) + { /* Mode portamento:legato only */ + if(IsChanStaccato(chan)) fromkey_portamento = InvalidNote; + } + else if(portamentomode == STACCATO_ONLY) + { /* Mode portamento:staccato only */ + if(IsChanLegato(chan)) fromkey_portamento = InvalidNote; + } + /* else Mode portamento: on each note (staccato/legato) */ + } + /* Return fromkey portamento */ + chan->synth->fromkey_portamento = fromkey_portamento; + /* Determine and return fromkey legato */ + if(IsInvalidNote(defaultFromkey)) + { + /* in staccato (poly/Mono) return InvalidNote */ + /* In mono mode legato playing return the note prior most + recent note played */ + if (IsChanPlayingMono(chan) && IsChanLegato(chan)) + defaultFromkey = ChanPrevNote(chan); /* note prior last note */ + /* In poly mode legato playing, actually we don't want playing legato. + So return InvalidNote */ + } + } + return defaultFromkey; /* Return legato fromkey */ +} + +/*----------------------------------------------------------------------------- + The function change the state (Valid/Invalid) of the previous note played in + a staccato manner (ChanPrevNote()). + 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 ChanLastNote. + It will be possible that isn't appropriate. To give the musician the possibility + to choose this note , the note will be marked valid on noteOff if portamento pedal + is On, otherwise the note will be marked invalid. + + The function is intended to be called when the following event occurs: + - On noteOff (in poly or mono mode), to mark previous note valid/invalid. + - On Portamento Off(in poly or mono mode), to mark the previous note invalid. +------------------------------------------------------------------------------*/ +void ValidInvalidPrevNoteStaccato(fluid_channel_t* chan) +{ + if(IsChanStaccato(chan)) /* the monophonic list is empty */ + if(! fluid_channel_portamento(chan)) ChanClearPrevNote(chan); + /* else PrevNote still remains valid for next fromkey portamento */ +} + +/***************************************************************************** + 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 + 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" state only one note at a time can be played in + a staccato or legato manner. + +------------------------------------------------------------------------------*/ +static int fluid_synth_noteon_mono_staccato(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 fromkey MIDI note number (0-127). + * previous note if legato playing. + * @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]; + + /* Add note to the monophonic list */ + fluid_channel_add_monolist(channel,(unsigned char)key,(unsigned char)vel,0); + if (!IsChanBreathSync(channel) || fluid_channel_breath_msb(channel) ) + { + /* legato/staccato playing detection */ + if(IsChanLegato(channel)) { /* legato playing */ + /* legato from iPrev to key */ + /* the voices from iPrev key number are to be used to play key number */ + /* fromkey must be valid */ + return fluid_synth_noteon_mono_legato(synth, chan, + ChanPrevNote(channel), key, vel); + } + /* staccato playing */ + else return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); + } + else return FLUID_OK; +} + +/** + * 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; + unsigned char 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 (IsValidNote(i)) { /* the note is in monophonic list */ + /* Remove note out the monophonic list */ + iPrev = fluid_channel_remove_monolist(channel,i); + + if (!IsChanBreathSync(channel) || fluid_channel_breath_msb(channel) ) + { + /* legato playing detection */ + if(IsChanLegato(channel)) { /* the list contains others notes */ + if(IsValidNote(iPrev)) { /* 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/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 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. + */ +static 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, 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); + /* Get possible 'fromkey portamento' */ + GetFromKeyPortamentoLegato( channel, InvalidNote); + /* 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 +-----------------------------------------------------------------------------*/ +/** + * 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'. + * The fonction is able to play legato over Preset Zone(s) and Intrument Zone(s) + * as far as possible. When key tokey is outside the current IZ,PZ current + * fromkey voices are released.If necessary new voices are restarted when tokey + * enters in new IZ,PZ. + */ +int fluid_synth_noteon_mono_legato(fluid_synth_t* synth, int chan, + int fromkey, int tokey, int vel) +{ + fluid_channel_t* channel = synth->channel[chan]; + unsigned char legatomode = GetChanLegatoMode(channel); + fluid_voice_t* voice; + int i ; + /* Get possible 'fromkey portamento' and possible 'fromkey legato' note */ + fromkey = GetFromKeyPortamentoLegato( channel, (unsigned char)fromkey); + + if (IsValidNote(fromkey)) 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)){ + /* Check if tokey is inside the range of the running voice */ + if (fluid_inst_zone_inside_range(voice->inst_zone, tokey, vel)) { + switch (legatomode) + { + case RETRIGGER_0: /* mode 0 */ + fluid_update_release(voice,0); /* fast release */ + break; + case RETRIGGER_1: /* mode 1 */ + fluid_update_release(voice,1); /* normal release */ + break; + case MULTI_RETRIGGER: /* mode 2 */ + /* Skip in attack section */ + fluid_update_multi_retrigger_attack(voice,tokey,vel); + break; + case SINGLE_TRIGGER_0: /* mode 3 */ + fluid_update_single_trigger0(voice,fromkey,tokey,vel); + break; + case SINGLE_TRIGGER_1: /* mode 4 */ + fluid_update_single_trigger1(voice,fromkey,tokey,vel); + break; + default: /* Invalid mode */ + FLUID_LOG(FLUID_WARN, + "Failed to execute legato mode: %d",legatomode); + return FLUID_FAILED; + } + if (legatomode >= MULTI_RETRIGGER) { + /* Start portamento if enabled */ + if( IsValidNote(synth->fromkey_portamento)) + /* Send 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 */ + /* mark this IZ to be ignored during next fluid_preset_noteon() */ + SetIgnoreInstZone (voice->inst_zone); + } + } + else + { /* tokey note is outside the voice range, so the voice is released */ + fluid_update_release(voice,legatomode); + } + } + } + /* May be,tokey will enter in others IZ,PZ , in this case it needs to be + played by others voices by voice allocation */ + return fluid_preset_noteon(channel->preset,synth,chan,tokey,vel); +} + + +/** + * The function handle Poly/mono commutation on Legato pedal On/Off. + * @param chan fluid_channel_t. + */ +void LegatoOnOff(fluid_channel_t* chan, int value) +{ + /* Special handling of the monophonic list */ + if (IsChanPoly(chan) && chan->nNotes) /* The monophonic list have notes */ + { + if (value < 64 ) /* legato is released */ + { /* return from monophonic to polyphonic with note in monophonic list */ + fluid_channel_keep_lastnote_monolist(chan); + } + else /* legato is depressed */ + { /* inter in monophonic from polyphonic with note in monophonic list */ + /* Stop the running note to remain coherent with Breath Sync mode */ + if (IsChanBreathSync(chan) && !fluid_channel_breath_msb(chan)) + fluid_synth_noteoff_monopoly(chan->synth,chan->channum, + ChanLastNote(chan),1); + } + } +} + +/** + * The function handle CC Breath On/Off detection. + * @param chan fluid_channel_t. + */ +void BreathOnOff(fluid_channel_t* chan, int value) +{ + if (IsChanBreathSync(chan) && IsChanPlayingMono(chan) && (chan->nNotes)) + { + /* 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, + ChanLastNote(chan),ChanLastVel(chan)); + } + else if( (value == 0) && (chan->previous_cc_breath > 0)) + { /* CC Breath Off detection */ + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + ChanLastNote(chan), 1); + } + } + chan->previous_cc_breath = value; +} \ No newline at end of file diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_synth_polymono.c ./fluid-polymono-0003/src/synth/fluid_synth_polymono.c --- ./fluidsynth-1.1.6/src/synth/fluid_synth_polymono.c Thu Jan 01 01:00:00 1970 +++ ./fluid-polymono-0003/src/synth/fluid_synth_polymono.c Sun Jul 31 16:06:28 2016 @@ -0,0 +1,1149 @@ +/* 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" + +extern int fluid_synth_all_notes_off_LOCAL(fluid_synth_t* synth, int chan); +extern int fluid_is_number(char* a); + + +/** API Poly/mono mode ******************************************************/ +/** + The API function get the list of basic channel informations in the synthesizer. + + * @param synth, FluidSynth instance + * @param basicChannelInfos, + * @ If non NULL the function returns a pointer to allocated table of + * @ fluid_basic_channels_infos or NULL on allocation error. + * @ The caller must free this table when finished with it. + * @ + * @ If NULL the function return only the count of basic channel. + * @ + * @ Each entry in the table is a fluid_basic_channels_infos_t + * @ -basicchan is the Basic Channel number (0 to MIDI channel count-1) + * @ -mode is MIDI mode infos for basicchan (0 to 3) + * @ -val is the number of channels (0 to MIDI channel count) + + * @return, Count of basic channel informations in the returned table or + * @ FLUID_FAILED if synth is NULL or allocation error. + + * @ Remark: By default a FluidSynth instance have only one basic channel + * @ on MIDI channel 0 in Poly Omni On (i.e all MIDI channels are polyphonic). + + * @ Note: The default shell have equivalent command "basicchannels" to display + * @ basics channels + +*/ +int fluid_synth_get_basic_channels( fluid_synth_t* synth, + fluid_basic_channel_infos_t **basicChannelInfos) +{ + int i,nChan; /* MIDI channel index and number */ + int nBasicChan; /* Basic Channel number to return */ + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + nChan = synth->midi_channels; /* MIDI Channels number */ + + /* count basic channels */ + for(i = 0, nBasicChan = 0; i < nChan; i++) + { + if (IsChanBasicChannel(synth->channel[i])) nBasicChan++; + } + + if (basicChannelInfos && nBasicChan) + { + /* allocate table for Basic Channel only */ + fluid_basic_channel_infos_t * bci; /* basics channels information table */ + int b; /* index in bci */ + bci = FLUID_ARRAY(fluid_basic_channel_infos_t, nBasicChan ); + /* fill table */ + if (bci) for(i = 0, b=0; i < nChan; i++) + { + fluid_channel_t* chan = synth->channel[i]; + if (IsChanBasicChannel(chan)) + { /* This channel is a basic channel */ + bci[b].basicchan = i; /* channel number */ + bci[b].mode = GetChanMode(chan); /* MIDI mode:0,1,2,3 */ + bci[b].val = chan->mode_val; /* value (for mode 3 only) */ + b++; + } + } + else + { + nBasicChan = FLUID_FAILED; /* allocation error */ + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + *basicChannelInfos = bci; /* return table */ + } + fluid_synth_api_exit(synth); + return nBasicChan; +} + +int fluid_synth_set_basic_channel_LOCAL(fluid_synth_t* synth, + int basicchan,int mode, int val); +/** + The API function set a new list of basic channel informations in the synthesizer. + This list replace the previous list. + + * @param synth, FluidSynth instance. + * @param n, number of entry in basicChannelInfos. + * @param basicChannelInfos, the list of basic channel infos to set. + * @ + * @ If n is 0 or basicChannelInfos is NULL, the function set one channel basic at + * @ basicchan 0 in Omni On Poly (i.e all the MIDI channel are polyphonic). + * @ + * @ Each entry in the table is a fluid_basic_channels_infos_t + * @ -basicchan is the Basic Channel number (0 to MIDI channel count-1) + * @ -mode is MIDI mode infos for basicchan (0 to 3) + * @ -val is the value (for mode 3 only) (0 to MIDI channel count) + * @ + * @return + * @ FLUID_OK if success + * @ FLUID_POLYMONO_WARNING prevent about entries coherence in the table. + * @ -Different entries have the same basic channel.An entry supersedes + * @ a previous entry with the same basic channel. + * @ -Val have a number of channels that overlaps the next basic channel. + * @ Anyway, the function does the job and restricts val to the right value. + * @ FLUID_FAILED + * @ synth is NULL. + * @ n, basicchan or val is outside MIDI channel count. + * @ mode is invalid. + * @ + * @ Note:This API is the only one to replace all the basics channels in one + * @ synth instance. + * @ The default shell have equivalent command "resetbasicchannels" to set one + * @ or more basic channels. +*/ +int fluid_synth_reset_basic_channels(fluid_synth_t* synth, + int n, + fluid_basic_channel_infos_t *basicChannelInfos) +{ + int i,nChan; + int result; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + if( n < 0 || n > nChan) return FLUID_FAILED; + /* Check if information are valid */ + if(n && basicChannelInfos ) for (i = 0; i < n; i++) + { + if (basicChannelInfos[i].basicchan < 0 || + basicChannelInfos[i].basicchan >= nChan || + basicChannelInfos[i].mode < 0 || + basicChannelInfos[i].mode >= MODE_NBR || + basicChannelInfos[i].val < 0 || + basicChannelInfos[i].basicchan + basicChannelInfos[i].val > nChan) + return FLUID_FAILED; + } + + fluid_synth_api_enter(synth); + /* Clear previous list of basic channel */ + for(i = 0; i < nChan; i++) { + ResetBasicChanInfos(synth->channel[i]); + synth->channel[i]->mode_val = 0; + } + if(n && basicChannelInfos) + { + result = FLUID_OK; + + /* Set the new list of basic channel */ + for (i = 0; i < n; i++) + { + + int bchan = basicChannelInfos[i].basicchan; + if (IsChanBasicChannel(synth->channel[bchan])) + /* Different entries have the same basic channel + An entry supersedes a previous entry with the same + basic channel.*/ + result = FLUID_POLYMONO_WARNING; + /* Set Basic channel first */ + else SetModeBasicChan(synth->channel[bchan]->mode); + } + + for (i = 0; i < n; i++) + { + int r =fluid_synth_set_basic_channel_LOCAL( synth, + basicChannelInfos[i].basicchan, + basicChannelInfos[i].mode, + basicChannelInfos[i].val); + if (result == FLUID_OK) result = r; + } + } + else result = fluid_synth_set_basic_channel_LOCAL( synth, 0, OMNION_POLY,0); + fluid_synth_api_exit(synth); + return result; +} + +/** + The API function change the mode of a an existing basic channel or insert a new + basic channel part. + -if basicchan is already a basic channel, his mode is changed. + -If basicchan is not a basic channel, a new basic channel part is inserted + between the previous basic channel and the next basic channel. + val value of the previous basic channel will be narrowed if necessary. + + * @param synth, FluidSynth instance. + * @param basicchan is the Basic Channel number (0 to MIDI channel count-1). + * @param mode is MIDI mode infos for basichan (0 to 3). + * @param val Number of monophonic channels (for mode 3 only) (0 to MIDI channel count). + + * @return + * @ FLUID_OK if success + * @ FLUID_POLYMONO_WARNING + * @ 1)val of the previous basic channel has been narrowed or + * @ 2)val have a number of channels that overlaps the next basic channel part. + * @ Anyway, the function does the job and restricts val to the right value. + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan or val is outside MIDI channel count. + * @ mode is invalid. +*/ +int fluid_synth_set_basic_channel(fluid_synth_t* synth, int basicchan, int mode, int val) +{ + int nChan; + int result; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + + if (basicchan < 0 || basicchan >= nChan || + mode < 0 ||mode >= MODE_NBR || + val < 0 || basicchan + val > nChan) + return FLUID_FAILED; + + fluid_synth_api_enter(synth); + /**/ + result = fluid_synth_set_basic_channel_LOCAL(synth, basicchan,mode,val); + /**/ + fluid_synth_api_exit(synth); + return result; +} + +/** + The internal function change the mode of a an existing basic channel or insert + a new basic channel part. + -if basicchan is already a basic channel, his mode is changed. + -If basicchan is not a basic channel, a new basic channel part is inserted + between the previous basic channel and the next basic channel. + val value of the previous basic channel will be narrowed if necessary. + + The function is used internally by API fluid_synth_reset_basic_channels(), + fluid_synth_set_basic_channel() + + * @param synth, FluidSynth instance. + * @param basicchan is the Basic Channel number (0 to MIDI channel count-1). + * @param mode is MIDI mode infos for basichan (0 to 3). + * @param val is the value (for mode 3 only) (0 to MIDI channel count). + + * @return + * @ FLUID_OK if success + * @ FLUID_POLYMONO_WARNING + * @ 1)val of the previous basic channel has been narrowed or + * @ 2)val have a number of channels that overlaps the next basic + * @ channel part or val is outside MIDI channel count. + * @ Anyway, the function does the job and restricts val to the right value. + * @ FLUID_FAILED basicchan is outside MIDI channel count. + * @ + * @ Note: The default shell have equivalent command "setbasicchannels". +*/ +int fluid_synth_set_basic_channel_LOCAL(fluid_synth_t* synth, + int basicchan,int mode, int val) +{ + int nChan = synth->midi_channels; /* MIDI Channels number */ + int result = FLUID_FAILED; /* default return */ + if (basicchan < nChan) + { + int LastBeginRange; /* Last channel num inside the beginning range + 1. */ + int LastEndRange; /* Last channel num inside the ending range + 1. */ + int i; + result = FLUID_OK; + if ( !IsChanBasicChannel(synth->channel[basicchan])) + { /* a new basic channel is inserted between previous basic channel + and the next basic channel. */ + if ( IsChanEnabled(synth->channel[basicchan])) + { /* val value of the previous basic channel need to be narrowed */ + for (i = basicchan - 1; i >=0; i--) + { /* search previous basic channel */ + if (IsChanBasicChannel(synth->channel[i])) + { /* i is the previous basic channel */ + /* val of previous is narrowed */ + synth->channel[i]->mode_val = basicchan - i; + result = FLUID_POLYMONO_WARNING; + break; + } + } + } + } + + /* LastEndRange: next basic channel or midi_channels count */ + for (LastEndRange = basicchan +1; LastEndRange < nChan; LastEndRange++) + { + if (IsChanBasicChannel(synth->channel[LastEndRange])) break; + } + /* Now LastBeginRange is set */ + switch (mode = GetModeMode(mode)) + { + case OMNION_POLY: /* Mode 0 and 1 */ + case OMNION_MONO: + LastBeginRange = LastEndRange; + break; + case OMNIOFF_POLY: /* Mode 2 */ + LastBeginRange = basicchan + 1; + break; + case OMNIOFF_MONO: /* Mode 3 */ + if (val) LastBeginRange = basicchan + val; + else LastBeginRange = LastEndRange; + } + /* LastBeginRange limited up to LastEndRange */ + if (LastBeginRange > LastEndRange) + { + LastBeginRange = LastEndRange; + /* val have number of channel that overlaps the next basic channel */ + result = FLUID_POLYMONO_WARNING; + } + + /* val is limited up to LastBeginRange */ + val = LastBeginRange - basicchan; + /* Set the Mode to the range zone: Beginning range + Ending range */ + for (i = basicchan; i < LastEndRange; i++) + { + int newmode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ + /* MIDI specs: when mode is changed, channel must receive + ALL_NOTES_OFF */ + fluid_synth_all_notes_off_LOCAL (synth, i); + /* basicchan only is marked Basic Channel */ + if (i == basicchan) SetModeBasicChan(newmode); + else val =0; /* val is 0 for other channel than basic channel */ + /* Channel in beginning zone are enabled */ + if (i < LastBeginRange) SetModeChanEn(newmode); + /* Channel in ending zone are disabled */ + else newmode = 0; + /* Now mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not + ENABLED or not */ + SetBasicChanInfos(synth->channel[i],newmode); + synth->channel[i]->mode_val = val; + } + } + return result; +} + +/** + The API function returns poly mono mode informations about any + MIDI channel. + + * @param synth, FluidSynth instance. + * @param chan, any MIDI channel number to get mode (0 to MIDI channel count - 1). + * @param modeInfos, pointer to a fluid_basic_channels_infos_t. + * @ -basicchan , chan. + * @ -mode is MIDI mode infos of chan: + * @ bit 0: MONO: 0,Polyphonique; 1,Monophonique. + * @ bit 1: OMNI: 0,Omni On; 1,Omni Off. + * @ bit 2: BASIC_CHANNEL: 1, this channel is a Basic Channel. + * @ bit 3: ENABLED: 1,chan is listened; + * @ 0, voices messages (MIDI note on/of, cc) are ignored on chan. + * @ -val, number of channels in the group from basic channel (if bit 2 is set), + * @ or 0 if bit 2 is 0. + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth s NULL. + * @ chan is outside MIDI channel count or modeInfos is NULL. + + * @ Note: The default shell have equivalent command "channelsmode". + +*/ +int fluid_synth_get_channel_mode(fluid_synth_t* synth, int chan, + fluid_basic_channel_infos_t *modeInfos) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (modeInfos!= NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + if(chan < 0 || chan >= nChan) return FLUID_FAILED; + fluid_synth_api_enter(synth); + /**/ + modeInfos->basicchan= chan; + modeInfos->mode = synth->channel[chan]->mode; + modeInfos->val = synth->channel[chan]->mode_val; + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} +/** API legato mode *********************************************************/ +/** + The API function set the legato mode for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param legatomode . + * 0: RETRIGGER_0 (fast release) 1: RETRIGGER_1 (normal release) + * 2: MULTI_RETRIGGER 3: SINGLE_TRIGGER_0 4 : SINGLE_TRIGGER_1 + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. + * @ legatomode is invalid. +*/ +int fluid_synth_set_legato_mode(fluid_synth_t* synth, int chan, int legatomode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + + if (chan < 0 || chan >= nChan || + legatomode < 0 ||legatomode >= LEGATOMODE_NBR ) + return FLUID_FAILED; + + fluid_synth_api_enter(synth); + /**/ + SetChanLegatoMode(synth->channel[chan],legatomode); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** + The API function get the legato mode for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param legatomode, pointer to returned mode . + * 0: RETRIGGER_0 (fast release) 1: RETRIGGER_1 (normal release) + * 2: MULTI_RETRIGGER 3: SINGLE_TRIGGER_0 4 : SINGLE_TRIGGER_1 + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. + * @ legatomode is NULL. +*/ +int fluid_synth_get_legato_mode(fluid_synth_t* synth, int chan, int *legatomode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (legatomode!= NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + if(chan < 0 || chan >= nChan) return FLUID_FAILED; + fluid_synth_api_enter(synth); + /**/ + * legatomode = GetChanLegatoMode(synth->channel[chan]); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** API portamento mode *********************************************************/ +/** + The API function set the portamento mode for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param portamentomode . + * 0: EACH_NOTE 1: LEGATO_ONLY + * 2: STACCATO_ONLY + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. + * @ portamentomode is invalid. +*/ +int fluid_synth_set_portamento_mode(fluid_synth_t* synth, int chan, + int portamentomode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + + if (chan < 0 || chan >= nChan || + portamentomode < 0 ||portamentomode >= PORTAMENTOMODE_NBR ) + return FLUID_FAILED; + + fluid_synth_api_enter(synth); + /**/ + SetChanPortamentoMode(synth->channel[chan],portamentomode); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** + The API function get the portamento mode for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param portamentomode pointer to returned mode. + * 0: EACH_NOTE 1: LEGATO_ONLY + * 2: STACCATO_ONLY + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. + * @ portamentomode is NULL. +*/ +int fluid_synth_get_portamento_mode(fluid_synth_t* synth, int chan, + int *portamentomode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (portamentomode!= NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + if(chan < 0 || chan >= nChan) return FLUID_FAILED; + fluid_synth_api_enter(synth); + /**/ + * portamentomode = GetChanPortamentoMode(synth->channel[chan]); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** API breath mode *********************************************************/ +/** + The API function set the breath mode for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param breathmode bits + * BREATH_POLY default breath poly On/Off + * BREATH_MONO default breath mono On/Off + * BREATH_SYNC breath noteOn/noteOff triggering On/Off + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. +*/ +int fluid_synth_set_breath_mode(fluid_synth_t* synth, int chan, int breathmode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + + if (chan < 0 || chan >= nChan ) + return FLUID_FAILED; + + fluid_synth_api_enter(synth); + /**/ + SetBreathInfos(synth->channel[chan],breathmode); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** + The API function get the breath mode option for a channel. + + * @param synth, FluidSynth instance. + * @param chan, MIDI Channel number (0 to MIDI channel count-1). + * @param breathmode, pointer to returned breath infos . + * BREATH_POLY default breath poly On/Off + * BREATH_MONO default breath mono On/Off + * BREATH_SYNC breath noteOn/noteOff triggering On/Off + + * @return + * @ FLUID_OK if success + * @ FLUID_FAILED + * @ synth is NULL. + * @ chan outside MIDI channel count. + * @ breathmode is NULL. +*/ +int fluid_synth_get_breath_mode(fluid_synth_t* synth, int chan, int *breathmode) +{ + int nChan; + /* check parameters first */ + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail (breathmode!= NULL, FLUID_FAILED); + nChan = synth->midi_channels; /* MIDI Channels number */ + if(chan < 0 || chan >= nChan) return FLUID_FAILED; + fluid_synth_api_enter(synth); + /**/ + * breathmode = GetBreathInfos(synth->channel[chan]); + /**/ + fluid_synth_api_exit(synth); + return FLUID_OK; +} + +/** commands Poly/mono mode *************************************************/ + +/*----------------------------------------------------------------------------- + basicchannels + Print the list of all MIDI basic channels informations + example: + + "Basic channel: 0, poly omni on (0), nbr: 3" + "Basic channel: 3, poly omni off(2), nbr: 1" + "Basic channel: 8, mono omni off(3), nbr: 2" + "Basic channel: 13, mono omni on (1), nbr: 3" +*/ +char * modename[]={ "poly omni on (0)","mono omni on (1)", + "poly omni off(2)","mono omni off(3)"}; +int fluid_handle_basicchannels (fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + fluid_basic_channel_infos_t *bci; /* basic channels table */ + /* get list of basic channels */ + int n = fluid_synth_get_basic_channels(synth, &bci); + if (n > 0) + { int i; + /* display all basic channels */ + for (i =0; i< n; i++) + { + fluid_ostream_printf(out, + "Basic channel:%3d, %s, nbr:%3d\n", bci[i].basicchan, + modename[bci[i].mode], + bci[i].val); + } + /* bci has been allocated by fluid_synth_get_basic_channels() */ + free(bci); + } + /* n is 1 or more basic channel number (never 0)*/ + else if(n < 0) return -1; /* error */ + if (n == 0) fluid_ostream_printf(out,"no basic channels\n"); + return 0; +} + +char * WarningMsg ="resetbasicchannels: warning:\n\ +-(1) Different entries have the same basic channel.An entry supersedes\n\ + a previous entry with the same basic channel.\n\ +-(2) Number of channels in one entry overlaps the next basic channel.\n\ +Anyway, the command succeeds and restricts nbr to the right value.\n"; + +char *InvalidArg =" invalid argument"; +char *TooFewArg = " too few argument, chan mode val [chan mode val]..."; +/*----------------------------------------------------------------------------- + resetbasicchannels [chan1 Mode1 nbr1 chan2 Mode2 nbr2 ...] + + Set the list of MIDI basic channels with mode + This list replace any previous basic channels list. + + With no parameters the function set one channel basic at + basicchan 0 mode 0 (Omni On Poly) (i.e all the MIDI channel are polyphonic). +*/ +int fluid_handle_resetbasicchannels (fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int result; + int i,n = 0; + fluid_basic_channel_infos_t * bci = NULL; + + if (ac ) + { /* parameters for list entries */ + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "resetbasicchannels:%s\n",InvalidArg); + return -1; + } + } + n = ac / 3; /* number of basic channel information */ + if (ac % 3) + { /* each entry needs 3 parameters: basicchan,mode,val */ + fluid_ostream_printf(out, "resetbasicchannels:chan %d,%s\n", + atoi(av[(n * 3)]),TooFewArg); + return -1; + } + /* alloc bci table and fill */ + bci = FLUID_ARRAY(fluid_basic_channel_infos_t, n ); + for (i = 0; i < n; i++) + { + bci[i].basicchan = atoi(av[(i * 3)]); + bci[i].mode = atoi(av[(i * 3)+1]); + bci[i].val = atoi(av[(i * 3)+2]); + } + } + /* set list of basic channels */ + result = fluid_synth_reset_basic_channels(synth,n, bci); + if(bci) free(bci); + if (result==FLUID_POLYMONO_WARNING) + fluid_ostream_printf(out, WarningMsg); + if (result == FLUID_FAILED) + fluid_ostream_printf(out, "resetbasicchannels:%s\n",InvalidArg); + return 0; +} + +char * WarningMsg1 ="warning:\n\ +-(1) val of the previous basic channel has been narrowed or\n\ +-(2) Number of channels in one entry overlaps the next basic channel.\n\ +Anyway, the command succeeds and restricts nbr to the right value.\n"; + +/*----------------------------------------------------------------------------- + setbasicchannels chan1 Mode1 nbr1 [chan2 Mode2 nbr2..] + + Change or add basic channel 1 and 2 + + -if chan is already a basic channel, his mode is changed. + -If chan is not a basic channel, a new basic channel part is inserted + between the previous basic channel and the next basic channel. + val value of the previous basic channel will be narrowed if necessary. + +*/ +int fluid_handle_setbasicchannels (fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int result; + int i,n ; + + if (ac ) + { /* parameters for list entries */ + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "setbasicchannels:%s\n",InvalidArg); + return -1; + } + } + } + n = ac / 3; /* number of basic channel information */ + if(!ac || ac % 3) + { /* each entry needs 3 parameters: basicchan,mode,val */ + fluid_ostream_printf(out, "setbasicchannels:chan %d,%s\n", + atoi(av[(n * 3)]),TooFewArg); + return -1; + } + + for (i = 0; i < n; i++) + { + int basicchan = atoi(av[(i * 3)]); + int mode = atoi(av[(i * 3)+1]); + int val = atoi(av[(i * 3)+2]); + /* change basic channels */ + + result = fluid_synth_set_basic_channel(synth,basicchan,mode,val); + if (result==FLUID_POLYMONO_WARNING) + fluid_ostream_printf(out,"channel:%3d, mode:%3d, nbr:%3d, %s", + basicchan,mode, val, WarningMsg1); + if (result == FLUID_FAILED) + fluid_ostream_printf(out,"channel:%3d, mode:%3d, nbr:%3d, %s\n", + basicchan,mode, val, InvalidArg); + } + return 0; +} + +/*----------------------------------------------------------------------------- + channelsmode + Print channel mode of all MIDI channels (Poly/mono, Enabled, Basic Channel) + example + + channel: 0, disabled + channel: 1, disabled + channel: 2, disabled + channel: 3, disabled + channel: 4, disabled + channel: 5, enabled, basic channel, mono omni off(3), nbr: 2 + channel: 6, enabled, -- , mono , -- + channel: 7, disabled + channel: 8, disabled + channel: 9, disabled + channel: 10, enabled, basic channel, mono omni off(3), nbr: 4 + channel: 11, enabled, -- , mono , -- + channel: 12, enabled, -- , mono , -- + channel: 13, enabled, -- , mono , -- + channel: 14, disabled + channel: 15, disabled + + channelsmode chan1 chan2 + Print only channel mode of MIDI channel chan1, chan2 +*/ +int fluid_handle_channelsmode (fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + fluid_basic_channel_infos_t bci; /* basic channels infos */ + int i,result; + int n,nChan= synth->midi_channels; + + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "channelsmode:%s\n",InvalidArg); + return -1; + } + } + if (ac ) n = ac; /* print ac MIDI channels number */ + else n= nChan; /* print all MIDI channels number */ + /* print header */ + fluid_ostream_printf(out,"Channel , Status , Type , Mode , Nbr of channels\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + result = fluid_synth_get_channel_mode(synth, chan, &bci); + if (result == FLUID_OK) + { + if(IsModeChanEn(bci.mode)) + { /* This channel is enabled */ + char * basicchannel1="basic channel"; /* field basic channel */ + char * bcmsg; /* field basic channel */ + char * polymsg ="poly"; /* field mode */ + char * monomsg ="mono"; /* field mode */ + char * pMode; /* field mode */ + char * blank="--"; + char nbr1[10]; /* field Nbr */ + char *pNbr; /* field Nbr */ + int mode = GetModeMode(bci.mode); + if (IsModeBasicChan(bci.mode)) + { /* This channel is a basic channel */ + bcmsg = basicchannel1; + sprintf(nbr1,"nbr:%3d",bci.val); + pNbr = nbr1; + pMode = modename[mode]; + } + else + { /* This channel is member of a part */ + bcmsg = blank; pNbr = blank; + if(IsModeMono(mode)) pMode = monomsg; + else pMode = polymsg; + } + fluid_ostream_printf(out, + "channel:%3d, enabled, %-13s, %-16s, %s\n", + bci.basicchan, + bcmsg, + pMode, + pNbr); + } + else fluid_ostream_printf(out, "channel:%3d, disabled\n",bci.basicchan); + } + else fluid_ostream_printf(out, + "channel:%3d is is outside MIDI channel count(%d)\n", + chan,nChan); + } + return 0; +} + +/** commands mono legato mode ***********************************************/ +/*----------------------------------------------------------------------------- + legatomode + Print legato mode of all MIDI channels + example + + channel: 0, (2)single-trigger_0 + channel: 1, (1)multi-retrigger + channel: 2, (0)retrigger_0 + channel: 3, (3)single-trigger_1 + ..... + + legatomode chan1 chan2 + Print only legato mode of MIDI channel chan1, chan2 +*/ +char * nameLegatomode[LEGATOMODE_NBR]={ + "(0)retrigger_0 (fast release)","(1)retrigger_1 (normal release)", + "(2)multi-retrigger","(3)single-trigger_0","(4)single-trigger_1" +}; + +int fluid_handle_legatomode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int mode; + int i,result; + int n,nChan= synth->midi_channels; + + for (i = 0; i < ac; i++) { + if (!fluid_is_number(av[i])) { + fluid_ostream_printf(out, "legatomode:%s\n",InvalidArg); + return -1; + } + } + if (ac ) n = ac; /* print ac MIDI channels number */ + else n= nChan; /* print all MIDI channels number */ + /* print header */ + fluid_ostream_printf(out,"Channel , legato mode\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + result = fluid_synth_get_legato_mode(synth, chan, &mode); + if (result == FLUID_OK) + fluid_ostream_printf(out,"channel:%3d, %s\n",chan, + nameLegatomode[mode]); + else fluid_ostream_printf(out, + "channel:%3d is is outside MIDI channel count(%d)\n", + chan,nChan); + } + return 0; +} + + +/*----------------------------------------------------------------------------- + setlegatomode chan1 Mode1 [chan2 Mode2 ..] + + Change legato mode for channels chan1 and [chan2] +*/ +char *TooFewArgChanMode = " too few argument, chan mode [chan mode]..."; +int fluid_handle_setlegatomode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int result; + int i,n ; + + if (ac ) + { /* parameters for list entries */ + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "setlegatomode:%s\n",InvalidArg); + return -1; + } + } + } + n = ac / 2; /* number of legato information */ + if(!ac || ac % 2) + { /* each entry needs 2 parameters: chan,mode */ + fluid_ostream_printf(out, "setlegatomode:chan %d,%s\n", + atoi(av[(n * 2)]),TooFewArgChanMode); + return -1; + } + + for (i = 0; i < n; i++) + { + int chan = atoi(av[(i * 2)]); + int mode = atoi(av[(i * 2)+1]); + /* change legato mode */ + + result = fluid_synth_set_legato_mode(synth,chan,mode); + if (result == FLUID_FAILED) + fluid_ostream_printf(out,"chan:%3d, mode:%3d, %s\n", + chan,mode, InvalidArg); + } + return 0; +} + +/*----------------------------------------------------------------------------- + portamentomode + Print portamento mode of all MIDI channels + example + + channel: 0, (2)staccato only + channel: 1, (1)legato only + channel: 2, (0)each note + channel: 3, (1)legato only + ..... + + portamentotomode chan1 chan2 + Print only portamentoto mode of MIDI channel chan1, chan2 +*/ +char * namePortamentomode[PORTAMENTOMODE_NBR]={ + "(0)each note","(1)legato only", + "(2)staccato only" +}; + +int fluid_handle_portamentomode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int mode; + int i,result; + int n,nChan= synth->midi_channels; + + for (i = 0; i < ac; i++) { + if (!fluid_is_number(av[i])) { + fluid_ostream_printf(out, "portamentomode:%s\n",InvalidArg); + return -1; + } + } + if (ac ) n = ac; /* print ac MIDI channels number */ + else n= nChan; /* print all MIDI channels number */ + /* print header */ + fluid_ostream_printf(out,"Channel , portamentoto mode\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + result = fluid_synth_get_portamento_mode(synth, chan, &mode); + if (result == FLUID_OK) + fluid_ostream_printf(out,"channel:%3d, %s\n",chan, + namePortamentomode[mode]); + else fluid_ostream_printf(out, + "channel:%3d is is outside MIDI channel count(%d)\n", + chan,nChan); + } + return 0; +} + + +/*----------------------------------------------------------------------------- + setportamentomode chan1 Mode1 [chan2 Mode2 ..] + + Change portamento mode for channels chan1 and [chan2] +*/ +int fluid_handle_setportamentomode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int result; + int i,n ; + + if (ac ) + { /* parameters for list entries */ + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "setportamentomode:%s\n",InvalidArg); + return -1; + } + } + } + n = ac / 2; /* number of portamento information */ + if(!ac || ac % 2) + { /* each entry needs 2 parameters: chan,mode */ + fluid_ostream_printf(out, "setportamentomode:chan %d,%s\n", + atoi(av[(n * 2)]),TooFewArgChanMode); + return -1; + } + + for (i = 0; i < n; i++) + { + int chan = atoi(av[(i * 2)]); + int mode = atoi(av[(i * 2)+1]); + /* change portamento mode */ + + result = fluid_synth_set_portamento_mode(synth,chan,mode); + if (result == FLUID_FAILED) + fluid_ostream_printf(out,"chan:%3d, mode:%3d, %s\n", + chan,mode, InvalidArg); + } + return 0; +} + + +/*----------------------------------------------------------------------------- + breathmode + Print breath options of all MIDI channels (poly on/off, mono on/off + breath + example + + Channel , poly breath , mono breath , breath sync + channel: 0, off , off , off + channel: 1, off , off , off + channel: 2, off , off , off + ..... + + breathmode chan1 chan2 + Print only breath mode of MIDI channel chan1, chan2 +*/ +char * Onmsg ="on"; +char * Offmsg ="off"; +int fluid_handle_breathmode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int breathmode; + int i,result; + int n,nChan= synth->midi_channels; + + for (i = 0; i < ac; i++) { + if (!fluid_is_number(av[i])) { + fluid_ostream_printf(out, "breathmode:%s\n",InvalidArg); + return -1; + } + } + if (ac ) n = ac; /* print ac MIDI channels number */ + else n= nChan; /* print all MIDI channels number */ + /* print header */ + fluid_ostream_printf(out,"Channel , poly breath , mono breath , breath sync\n"); + for (i = 0; i < n; i++) + { + int chan = ac ? atoi(av[i]): i; + result = fluid_synth_get_breath_mode(synth, chan, &breathmode); + if (result == FLUID_OK) + { + char * msgPolyBreath, * msgMonoBreath, * msgBreathSync; + if (IsPolyDefaultBreath(breathmode)) + msgPolyBreath =Onmsg; + else msgPolyBreath = Offmsg; + if (IsMonoDefaultBreath(breathmode)) + msgMonoBreath =Onmsg; + else msgMonoBreath = Offmsg; + if (IsBreathSync(breathmode)) + msgBreathSync =Onmsg; + else msgBreathSync = Offmsg; + fluid_ostream_printf(out,"channel:%3d, %-12s, %-12s, %-11s\n",chan, + msgPolyBreath, msgMonoBreath, msgBreathSync); + } + else fluid_ostream_printf(out, + "channel:%3d is is outside MIDI channel count(%d)\n", + chan,nChan); + } + return 0; +} + +/*----------------------------------------------------------------------------- + setbreathmode chan1 poly_breath_mod(1/0) mono_breath_mod mono_breath_sync(1/0) + + Change breath options for channels chan1 and [chan2...] +*/ +char *TooFewArgBreath = +" too few argument:\nchan 1/0(breath poly) 1/0(breath mono) 1/0(breath sync mono)[..]"; +int fluid_handle_setbreathmode(fluid_synth_t* synth, int ac, char** av, + fluid_ostream_t out) +{ + int result; + int i,n, nChan= synth->midi_channels; + + if (ac ) + { /* parameters for list entries */ + for (i = 0; i < ac; i++) + { + if (!fluid_is_number(av[i])) + { + fluid_ostream_printf(out, "setbreathmode:%s\n",InvalidArg); + return -1; + } + } + } + n = ac / 4; /* number of default breath informations */ + if(!ac || ac % 4) + { /* each entry needs 3 parameters: chan,chan1 poly_breath(1/0) mono_breath(1/0) */ + fluid_ostream_printf(out, "setbreathmode:chan %d,%s\n", + atoi(av[(n * 3)]),TooFewArgBreath); + return -1; + } + + for (i = 0; i < n; i++) + { + int chan = atoi(av[(i * 4)]); + int poly_breath = atoi(av[(i * 4)+1]); + int mono_breath = atoi(av[(i * 4)+2]); + int breath_sync = atoi(av[(i * 4)+3]); + int breath_infos = 0; + /* change ldefault breath */ + if(poly_breath) SetPolyDefaultBreath(breath_infos); + if(mono_breath) SetMonoDefaultBreath(breath_infos); + if(breath_sync) SetBreathSync(breath_infos); + result = fluid_synth_set_breath_mode(synth,chan,breath_infos); + if (result == FLUID_FAILED) + fluid_ostream_printf(out, + "channel:%3d is is outside MIDI channel count(%d)\n", + chan,nChan); + } + return 0; +} + diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_voice.c ./fluid-polymono-0003/src/synth/fluid_voice.c --- ./fluidsynth-1.1.6/src/synth/fluid_voice.c Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_voice.c Wed Jul 20 19:21:06 2016 @@ -27,6 +27,7 @@ #include "fluid_sys.h" #include "fluid_sfont.h" #include "fluid_rvoice_event.h" +#include "fluid_defsfont.h" /* used for filter turn off optimization - if filter cutoff is above the specified value and filter q is below the other value, turn filter off */ @@ -236,9 +237,13 @@ /* fluid_voice_init * * Initialize the synthesis process + * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange + * of the voice. + * When playing legato (n1,n2) in mono mode, n2 will use n1 voices + * as far as n2 still enters in Keyrange,Velrange of n1. */ int -fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, +fluid_voice_init(fluid_voice_t* voice, fluid_inst_zone_t *inst_zone, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int start_time, fluid_real_t gain) { @@ -247,6 +252,7 @@ * the 'working memory' of the voice (position in envelopes, history * of IIR filters, position in sample etc) is initialized. */ int i; + fluid_sample_t* sample; if (!voice->can_access_rvoice) { if (voice->can_access_overflow_rvoice) @@ -261,6 +267,8 @@ if (voice->sample) fluid_voice_off(voice); + sample = fluid_inst_zone_get_sample(inst_zone); + voice->inst_zone = inst_zone; /* Instrument Zone for legato */ voice->id = id; voice->chan = fluid_channel_get_num(channel); voice->key = (unsigned char) key; @@ -464,14 +472,18 @@ voice->channel->synth->active_voice_count++; } -void -fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) +/* Useful to return the nominal pitch of a key */ +/* The nominal pitch is dependant of voice->root_pitch,tuning, and + GEN_SCALETUNE generator. + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. +*/ +fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t* voice, int key) { fluid_tuning_t* tuning; - fluid_real_t x; + fluid_real_t x,pitch; - /* The GEN_PITCH is a hack to fit the pitch bend controller into the - * modulator paradigm. Now the nominal pitch of the key is set. + /* Now the nominal pitch of the key is returned. * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a * non-realtime parameter. So we don't allow modulation (as opposed * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied, @@ -480,15 +492,23 @@ if (fluid_channel_has_tuning(voice->channel)) { tuning = fluid_channel_get_tuning (voice->channel); x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f)); - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f * - (fluid_tuning_get_pitch (tuning, voice->key) - x) + x; + pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch (tuning, key) - x) + x; } else { - voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val - * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch; + pitch = voice->gen[GEN_SCALETUNE].val + * (key - voice->root_pitch / 100.0f) + voice->root_pitch; } + return pitch; +} +void +fluid_voice_calculate_gen_pitch(fluid_voice_t* voice) +{ + voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, + voice->key); } + /* * fluid_voice_calculate_runtime_synthesis_parameters * @@ -603,6 +623,17 @@ fluid_voice_update_param(voice, list_of_generators_to_initialize[i]); } + /* Start portamento if enabled */ + { /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. + When fromkey is set to ValidNote , portamento is started */ + /* Return fromkey portamento */ + int fromkey = voice->channel->synth->fromkey_portamento; + if(IsValidNote(fromkey)) + { /* Send portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice,fromkey, voice->key); + } + } + /* Make an estimate on how loud this voice can get at any time (attenuation). */ UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, fluid_voice_get_lower_boundary_for_attenuation(voice)); @@ -1182,6 +1213,134 @@ return FLUID_OK; } +/** legato update function ---------------------------------------------------*/ +/* Update portamento parameter */ +void fluid_voice_update_portamento (fluid_voice_t* voice, int fromkey,int tokey) + +{ + fluid_channel_t* channel= voice->channel; + fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice,fromkey); + fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice,tokey); + fluid_real_t pitchoffset = PitchBeg - PitchEnd; + /* Increment number is function of PortamentoTime (ms)*/ + unsigned int countinc = (int)(((fluid_real_t)voice->output_rate * + 0.001f * + (fluid_real_t)fluid_channel_portamentotime(channel)) / + (fluid_real_t)FLUID_BUFSIZE +0.5); + UPDATE_RVOICE2(fluid_rvoice_set_portamento, countinc, pitchoffset); +} + +/*---------------------------------------------------------------*/ +extern fluid_gen_info_t fluid_gen_info[]; +/* force in the release section for legato mode retrigger 0 and 1: + flags: + 0: fast release. + 1: normal release. RELEASE generators are forced to the minimum possible +*/ +void fluid_update_release(fluid_voice_t* voice, unsigned char flags) +{ + if (!flags) + { + /* force possible minimum Release value */ + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, + fluid_gen_info[GEN_MODENVRELEASE].min); + fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, FLUID_MIN_VOLENVRELEASE); + /* update */ + fluid_voice_update_param(voice, GEN_MODENVRELEASE); + fluid_voice_update_param(voice, GEN_VOLENVRELEASE); + } + /* Skip in release section */ + /* Skip immediately in release section */ + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, 0); + voice->has_noteoff = 1; // voice is marked as noteoff occured +} + +/*---------------------------------------------------------------*/ +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_update_multi_retrigger_attack(fluid_voice_t* voice, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Update dependent generator of velocity */ + /* Modulate GEN_ATTENUATION (and others ) before calling + fluid_rvoice_multi_retrigger_attack().*/ + fluid_voice_modulate(voice,0,FLUID_MOD_VELOCITY); + + /* Update dependent generator of key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + /* update adrs generator */ + UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); +} + +/*---------------------------------------------------------------*/ +/* force in the current section for legato mode single_trigger: 2*/ +void fluid_update_single_trigger0(fluid_voice_t* voice,int fromkey, + int tokey, int vel) +{ + int decaycount; /* decay data count */ + int dholdcount; /* difference hold data count with fromkey */ + + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Update dependent generator of velocity */ + /* Modulate GEN_ATTENUATION (and others ) before calling + fluid_rvoice_single_trigger().*/ + fluid_voice_modulate(voice,0,FLUID_MOD_VELOCITY); + + /* Update dependent generator of key for tokey*/ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + /* Attention GEN_KEYTOVOLENVHOLD, GEN_KEYTOVOLENVDECAY + fluid_update_single_trigger0() need only get data count + section HOLD et DECAY updated. + (max,min, increment, must not be changed !) */ + /* calculate decaycount for tokey (1 for decay )*/ + decaycount = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, + GEN_KEYTOVOLENVDECAY, 1); + + /* calculate dholdcount for tokey (0 for hold) */ + dholdcount = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, + GEN_KEYTOVOLENVHOLD, 0); + /* now the difference with hold count for fromkey (0 for hold)*/ + voice->key = fromkey; /* fromkey note */ + dholdcount -= calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, + GEN_KEYTOVOLENVHOLD, 0); + voice->key = tokey; /* come back to tokey */ + /* update Pitch */ + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + /* update adrs generator */ + UPDATE_RVOICE2(fluid_rvoice_single_trigger, dholdcount, decaycount); +} + +/*---------------------------------------------------------------*/ +/* force in the current section for legato mode single_trigger: 2*/ +void fluid_update_single_trigger1(fluid_voice_t* voice,int fromkey, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Update dependent generator of velocity */ + /* Modulate GEN_ATTENUATION (and others ) */ + fluid_voice_modulate(voice,0,FLUID_MOD_VELOCITY); + + /* Update dependent generator of key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); +} + +/** end of legato update function */ + /* Force the voice into release stage. Useful anywhere a voice needs to be damped even if pedals (sustain sostenuto) are depressed. @@ -1199,12 +1358,15 @@ /* * fluid_voice_noteoff + * The function is convenient for polyphonic or monophonic note + * On return, The function return True if the voice is sustained + * (by Sustain or Sostenuto). */ int fluid_voice_noteoff(fluid_voice_t* voice) { fluid_channel_t* channel; - + int IsSustained = 0; fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref); channel = voice->channel; @@ -1214,16 +1376,18 @@ channel->sostenuto_orderid > voice->id) { // Sostenuto depressed after note voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; + IsSustained = 1; } /* Or sustain a note under Sustain pedal */ else if (fluid_channel_sustained(channel)) { - voice->status = FLUID_VOICE_SUSTAINED; + voice->status = FLUID_VOICE_SUSTAINED; + IsSustained = 1; } /* Or force the voice to release stage */ else fluid_voice_release(voice); - return FLUID_OK; + return IsSustained; } /* diff -Naur ./fluidsynth-1.1.6/src/synth/fluid_voice.h ./fluid-polymono-0003/src/synth/fluid_voice.h --- ./fluidsynth-1.1.6/src/synth/fluid_voice.h Tue May 19 12:27:02 2015 +++ ./fluid-polymono-0003/src/synth/fluid_voice.h Wed Jul 20 19:09:34 2016 @@ -69,6 +69,7 @@ fluid_gen_t gen[GEN_LAST]; fluid_mod_t mod[FLUID_NUM_MOD]; int mod_count; + fluid_inst_zone_t *inst_zone; /* Instrument Zone */ fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */ int has_noteoff; /* Flag set when noteoff has been sent */ @@ -120,7 +121,8 @@ int fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf); -int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, +//int fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, +int fluid_voice_init(fluid_voice_t* voice, fluid_inst_zone_t *inst_zone, fluid_channel_t* channel, int key, int vel, unsigned int id, unsigned int time, fluid_real_t gain); @@ -142,6 +144,19 @@ already operating voice. Most applications will not need this function.*/ void fluid_voice_update_param(fluid_voice_t* voice, int gen); + +/** legato modes */ +/* force in the release section for legato mode retrigger: 0 and 1 */ +void fluid_update_release(fluid_voice_t* voice, unsigned char flags); +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_update_multi_retrigger_attack(fluid_voice_t* voice,int tokey, int vel); +/* force in the current section for legato mode single_trigger: 2*/ +void fluid_update_single_trigger0(fluid_voice_t* voice, int fromkey, int tokey, int vel); +/* force in the current section for legato mode single_trigger: 2*/ +void fluid_update_single_trigger1(fluid_voice_t* voice, int fromkey, int tokey, int vel); +/* Update portamento parameter */ +void fluid_voice_update_portamento (fluid_voice_t* voice, int fromkey, int tokey); + /** fluid_voice_release Force the voice into release stage. Usefuf anywhere a voice