diff --git a/fluidsynth/TODO b/fluidsynth/TODO index 18429cbb..d5aae628 100644 --- a/fluidsynth/TODO +++ b/fluidsynth/TODO @@ -17,24 +17,27 @@ already been done. - Josh Green 2003-08-25 - Windows DirectMusic component - Multi-channel audio output -- fluid_synth_program_select() with name of soundfont instead of font_id -- set loop on/off on a sample (1 - with name sample, 2 - name sf and name preset) +- fluid_synth_program_select() with name of soundfont instead of font_id OK +- set loop on/off on a sample (1 - with name sample, 2 - name sf and name preset) Use set_gen? + + set_gen: GEN_SAMPLEMODE (54): + Loop during release: 1, + Loop: 3 + No loop: 0 and all other values, + - add function to get initial soundfont generator value -- add function to set generator value in absolute value (offset with + + + +- add function to set generator value in absolute value (offset with testing initial soundfont generator value) -- add function to set absolute and relative generator value on a scale +- add function to set absolute and relative generator value on a scale testing of 0 to 1 - - - -inst zone -- attack (set to -- scale tune -- modLfoToPitch - - Write documention on tuning +- Remove dependency of settings on audio driver and other (see + fluid_settings_init()) Synthesis related @@ -58,7 +61,6 @@ Drivers Fluid 1.0: -------------------------------------------- -- deb package - Mac and Windows binaries - Xtra binaries diff --git a/fluidsynth/include/fluidsynth/gen.h b/fluidsynth/include/fluidsynth/gen.h index f9d2f1cc..bdd4857a 100644 --- a/fluidsynth/include/fluidsynth/gen.h +++ b/fluidsynth/include/fluidsynth/gen.h @@ -113,7 +113,8 @@ typedef struct _fluid_gen_t enum fluid_gen_flags { GEN_UNUSED, - GEN_SET + GEN_SET, + GEN_ABS_NRPN }; /** Reset an array of generators to the SF2.01 default values */ diff --git a/fluidsynth/src/fluid_chan.c b/fluidsynth/src/fluid_chan.c index 40385b38..315f5301 100644 --- a/fluidsynth/src/fluid_chan.c +++ b/fluidsynth/src/fluid_chan.c @@ -73,6 +73,7 @@ fluid_channel_init_ctrl(fluid_channel_t* chan) for (i = 0; i < GEN_LAST; i++) { chan->gen[i] = 0.0f; + chan->gen_abs[i] = 0; } for (i = 0; i < 128; i++) { @@ -226,7 +227,7 @@ fluid_channel_cc(fluid_channel_t* chan, int num, int value) /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100)) { - float val = fluid_gen_map_nrpn(chan->nrpn_select, data); + float val = fluid_gen_scale_nrpn(chan->nrpn_select, data); FLUID_LOG(FLUID_WARN, "%s: %d: Data = %d, value = %f", __FILE__, __LINE__, data, val); fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val); } diff --git a/fluidsynth/src/fluid_chan.h b/fluidsynth/src/fluid_chan.h index 3de91cde..788efb6b 100644 --- a/fluidsynth/src/fluid_chan.h +++ b/fluidsynth/src/fluid_chan.h @@ -53,7 +53,24 @@ struct _fluid_channel_t /* NRPN system */ short nrpn_select; + + /* The values of the generators, set by NRPN messages, or by + * fluid_synth_set_gen(), are cached in the channel so they can be + * applied to future notes. They are copied to a voice's generators + * in fluid_voice_init(), wihich calls fluid_gen_init(). */ fluid_real_t gen[GEN_LAST]; + + /* By default, the NRPN values are relative to the values of the + * generators set in the SoundFont. For example, if the NRPN + * specifies an attack of 100 msec then 100 msec will be added to the + * combined attack time of the sound font and the modulators. + * + * However, it is useful to be able to specify the generator value + * absolutely, completely ignoring the generators of the sound font + * and the values of modulators. The gen_abs field, is a boolean + * flag indicating whether the NRPN value is absolute or not. + */ + char gen_abs[GEN_LAST]; }; fluid_channel_t* new_fluid_channel(fluid_synth_t* synth, int num); @@ -77,13 +94,12 @@ int fluid_channel_get_num(fluid_channel_t* chan); void fluid_channel_set_interp_method(fluid_channel_t* chan, int new_method); int fluid_channel_get_interp_method(fluid_channel_t* chan); -#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } -#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) -#define fluid_channel_get_tuning(_c) ((_c)->tuning) - -#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) - -#define fluid_channel_set_gen(_c, _n, _v) { (_c)->gen[_n] = _v; } -#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) +#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } +#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) +#define fluid_channel_get_tuning(_c) ((_c)->tuning) +#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) +#define fluid_channel_set_gen(_c, _n, _v, _a) { (_c)->gen[_n] = _v; (_c)->gen_abs[_n] = _a; } +#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) +#define fluid_channel_get_gen_abs(_c, _n) ((_c)->gen_abs[_n]) #endif /* _FLUID_CHAN_H */ diff --git a/fluidsynth/src/fluid_defsfont.c b/fluidsynth/src/fluid_defsfont.c index 5aee342f..2bf49173 100644 --- a/fluidsynth/src/fluid_defsfont.c +++ b/fluidsynth/src/fluid_defsfont.c @@ -647,7 +647,7 @@ fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan if (inst_zone->gen[i].flags){ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); - } else if (global_inst_zone != NULL && global_inst_zone->gen[i].flags){ + } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) { fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); } else { @@ -733,9 +733,9 @@ fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan * generator. The effect is -added- to the destination * summing node -> voice_gen_incr */ - if (preset_zone->gen[i].flags){ + if (preset_zone->gen[i].flags) { fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); - } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags){ + } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) { fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); } else { /* The generator has not been defined in this preset diff --git a/fluidsynth/src/fluid_gen.c b/fluidsynth/src/fluid_gen.c index a9d1281c..24c4cc32 100644 --- a/fluidsynth/src/fluid_gen.c +++ b/fluidsynth/src/fluid_gen.c @@ -22,6 +22,73 @@ #include "fluid_gen.h" #include "fluid_chan.h" + +/* See SFSpec21 $8.1.3 */ +fluid_gen_info_t fluid_gen_info[] = { + /* number/name init scale min max def */ + { GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f }, + { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, + { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, + { GEN_ENDLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, + { GEN_STARTADDRCOARSEOFS, 0, 1, 0.0f, 1e10f, 0.0f }, + { GEN_MODLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_VIBLFOTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_MODENVTOPITCH, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_FILTERFC, 1, 2, 1500.0f, 13500.0f, 13500.0f }, + { GEN_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }, + { GEN_MODLFOTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_MODENVTOFILTERFC, 1, 2, -12000.0f, 12000.0f, 0.0f }, + { GEN_ENDADDRCOARSEOFS, 0, 1, -1e10f, 0.0f, 0.0f }, + { GEN_MODLFOTOVOL, 1, 1, -960.0f, 960.0f, 0.0f }, + { GEN_UNUSED1, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_CHORUSSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_REVERBSEND, 1, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_PAN, 1, 1, -500.0f, 500.0f, 0.0f }, + { GEN_UNUSED2, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_UNUSED3, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_UNUSED4, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_MODLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, + { GEN_VIBLFODELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VIBLFOFREQ, 1, 4, -16000.0f, 4500.0f, 0.0f }, + { GEN_MODENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_MODENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_MODENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_MODENVSUSTAIN, 0, 1, 0.0f, 1000.0f, 0.0f }, + { GEN_MODENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_KEYTOMODENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_KEYTOMODENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_VOLENVDELAY, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VOLENVATTACK, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_VOLENVHOLD, 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { GEN_VOLENVDECAY, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_VOLENVSUSTAIN, 0, 1, 0.0f, 1440.0f, 0.0f }, + { GEN_VOLENVRELEASE, 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { GEN_KEYTOVOLENVHOLD, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_KEYTOVOLENVDECAY, 0, 1, -1200.0f, 1200.0f, 0.0f }, + { GEN_INSTRUMENT, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_RESERVED1, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_KEYRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, + { GEN_VELRANGE, 0, 0, 0.0f, 127.0f, 0.0f }, + { GEN_STARTLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, + { GEN_KEYNUM, 1, 0, 0.0f, 127.0f, -1.0f }, + { GEN_VELOCITY, 1, 1, 0.0f, 127.0f, -1.0f }, + { GEN_ATTENUATION, 1, 1, 0.0f, 1440.0f, 0.0f }, + { GEN_RESERVED2, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_ENDLOOPADDRCOARSEOFS, 0, 1, -1e10f, 1e10f, 0.0f }, + { GEN_COARSETUNE, 0, 1, -120.0f, 120.0f, 0.0f }, + { GEN_FINETUNE, 0, 1, -99.0f, 99.0f, 0.0f }, + { GEN_SAMPLEID, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_SAMPLEMODE, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_RESERVED3, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_SCALETUNE, 0, 1, 0.0f, 1200.0f, 100.0f }, + { GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f }, + { GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f }, + { GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f } +}; + + /* fluid_gen_set_default_values * * Set an array of generators to their initial value @@ -29,72 +96,16 @@ int fluid_gen_set_default_values(fluid_gen_t* gen) { - int i; + int i; - for (i = 0; i < GEN_LAST; i++) { - gen[i].flags = GEN_UNUSED; - gen[i].mod = 0.0; - gen[i].nrpn = 0.0; - } + for (i = 0; i < GEN_LAST; i++) { + gen[i].flags = GEN_UNUSED; + gen[i].mod = 0.0; + gen[i].nrpn = 0.0; + gen[i].val = fluid_gen_info[i].def; + } - gen[GEN_STARTADDROFS].val = (fluid_real_t) 0.0; /* SF2.01 section 8.1.3 #0 */ - gen[GEN_ENDADDROFS].val = (fluid_real_t) 0.0f; /* # 1 */ - gen[GEN_STARTLOOPADDROFS].val = (fluid_real_t) 0.0f; /* # 2 */ - gen[GEN_ENDLOOPADDROFS].val = (fluid_real_t) 0.0f; /* # 3 */ - gen[GEN_STARTADDRCOARSEOFS].val = (fluid_real_t) 0.0f; /* # 4 */ - gen[GEN_MODLFOTOPITCH].val = (fluid_real_t) 0.0f; /* # 5 */ - gen[GEN_VIBLFOTOPITCH].val = (fluid_real_t) 0.0f; /* # 6 */ - gen[GEN_MODENVTOPITCH].val = (fluid_real_t) 0.0f; /* # 7 */ - gen[GEN_FILTERFC].val = (fluid_real_t) 13500.0f; /* # 8 */ - gen[GEN_FILTERQ].val = (fluid_real_t) 0.0f; /* # 9 */ - gen[GEN_MODLFOTOFILTERFC].val = (fluid_real_t) 0.0f; /* # 10 */ - gen[GEN_MODENVTOFILTERFC].val = (fluid_real_t) 0.0f; /* # 11 */ - gen[GEN_ENDADDRCOARSEOFS].val = (fluid_real_t) 0.0f; /* # 12 */ - gen[GEN_MODLFOTOVOL].val = (fluid_real_t) 0.0f; /* # 13 */ - /* # 14 */ - gen[GEN_CHORUSSEND].val = (fluid_real_t) 0.0f; /* # 15 */ - gen[GEN_REVERBSEND].val = (fluid_real_t) 0.0f; /* # 16 */ - gen[GEN_PAN].val = (fluid_real_t) 0.0f; /* # 17 */ - /* # 18 - # 20 */ - gen[GEN_MODLFODELAY].val = (fluid_real_t) -12000.0f; /* # 21 => instantaneous */ - gen[GEN_MODLFOFREQ].val = (fluid_real_t) 0.0f; /* # 22 => 8.176 Hz */ - gen[GEN_VIBLFODELAY].val = (fluid_real_t) -12000.0f; /* # 23 => instantaneous */ - gen[GEN_VIBLFOFREQ].val = (fluid_real_t) 0.0f; /* # 24 => 8.176 Hz */ - gen[GEN_MODENVDELAY].val = (fluid_real_t) -12000.0f; /* # 25 => instantaneous */ - gen[GEN_MODENVATTACK].val = (fluid_real_t) -12000.0f; /* # 26 => instantaneous */ - gen[GEN_MODENVHOLD].val = (fluid_real_t) -12000.0f; /* # 27 => instantaneous */ - gen[GEN_MODENVDECAY].val = (fluid_real_t) -12000.0f; /* # 28 => instantaneous */ - gen[GEN_MODENVSUSTAIN].val = (fluid_real_t) 0.0f; /* # 29 => 0 dB */ - gen[GEN_MODENVRELEASE].val = (fluid_real_t) -12000.0f; /* # 30 => instantaneous */ - gen[GEN_KEYTOMODENVHOLD].val = (fluid_real_t) 0.0f; /* # 31 */ - gen[GEN_KEYTOMODENVDECAY].val = (fluid_real_t) 0.0f; /* # 32 */ - gen[GEN_VOLENVDELAY].val = (fluid_real_t) -12000.0f; /* # 33 */ - gen[GEN_VOLENVATTACK].val = (fluid_real_t) -12000.0f; /* # 34 */ - gen[GEN_VOLENVHOLD].val = (fluid_real_t) -12000.0f; /* # 35 */ - gen[GEN_VOLENVDECAY].val = (fluid_real_t) -12000.0f; /* # 36 */ - gen[GEN_VOLENVSUSTAIN].val = (fluid_real_t) 0.0f; /* # 37 */ - gen[GEN_VOLENVRELEASE].val = (fluid_real_t) -12000.0f; /* # 38 */ - gen[GEN_KEYTOVOLENVHOLD].val = (fluid_real_t) 0.0f; /* # 39 */ - gen[GEN_KEYTOVOLENVDECAY].val = (fluid_real_t) 0.0f; /* # 40 */ - /* # 41 - # 42 */ - /* GEN_KEYRANGE, GEN_VELRANGE are handled in fluid_defsfont.c # 43 - # 44 */ - gen[GEN_STARTLOOPADDRCOARSEOFS].val = (fluid_real_t) 0.0f; /* # 45 */ - gen[GEN_KEYNUM].val = (fluid_real_t) -1.0f; /* # 46 => disabled */ - gen[GEN_VELOCITY].val = (fluid_real_t) -1.0f; /* # 47 => disabled */ - gen[GEN_ATTENUATION].val = (fluid_real_t) 0.0f; /* # 48 => 0 dB */ - /* # 49 */ - gen[GEN_ENDLOOPADDRCOARSEOFS].val = (fluid_real_t) 0.0f; /* # 50 */ - gen[GEN_COARSETUNE].val = (fluid_real_t) 0.0f; /* # 51 */ - gen[GEN_FINETUNE].val = (fluid_real_t) 0.0f; /* # 52 */ - gen[GEN_SAMPLEID].val = (fluid_real_t) 0.0f; /* # 53 ??? */ - gen[GEN_SAMPLEMODE].val = (fluid_real_t) 0.0f; /* # 54 => no loop */ - /* # 55 */ - gen[GEN_SCALETUNE].val = (fluid_real_t) 100.0f; /* # 56 => 1 semitone / key */ - gen[GEN_EXCLUSIVECLASS].val = (fluid_real_t) 0.0f; /* # 57 => no exclusive class */ - gen[GEN_OVERRIDEROOTKEY].val = (fluid_real_t) -1.0f; /* # 58 => disabled */ - gen[GEN_PITCH].val = 0.0f; - - return FLUID_OK; + return FLUID_OK; } @@ -105,87 +116,33 @@ fluid_gen_set_default_values(fluid_gen_t* gen) int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel) { - int i; + int i; - fluid_gen_set_default_values(gen); + fluid_gen_set_default_values(gen); - for (i = 0; i < GEN_LAST; i++) { - gen[i].nrpn = fluid_channel_get_gen(channel, i); - } + for (i = 0; i < GEN_LAST; i++) { + gen[i].nrpn = fluid_channel_get_gen(channel, i); - return FLUID_OK; + /* This is an extension to the SoundFont standard. More + * documentation is available at the fluid_synth_set_gen2() + * function. */ + if (fluid_channel_get_gen_abs(channel, i)) { + gen[i].flags = GEN_ABS_NRPN; + } + } + + return FLUID_OK; } +fluid_real_t fluid_gen_scale(int gen, float value) +{ + return (fluid_gen_info[gen].min + + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); +} -fluid_real_t fluid_gen_map_nrpn(int gen, int data) +fluid_real_t fluid_gen_scale_nrpn(int gen, int data) { - fluid_real_t value = (float) data - 8192.0f; - - fluid_clip(value, -8192, 8192); - - switch (gen) { - default: - case GEN_OVERRIDEROOTKEY: - case GEN_PITCH: - case GEN_KEYRANGE: - case GEN_VELRANGE: - case GEN_SAMPLEMODE: - case GEN_SAMPLEID: - case GEN_INSTRUMENT: - case GEN_KEYNUM: - case GEN_EXCLUSIVECLASS: - return 0.0; - - case GEN_STARTADDROFS: - case GEN_ENDADDROFS: - case GEN_STARTLOOPADDROFS: - case GEN_ENDLOOPADDROFS: - case GEN_STARTADDRCOARSEOFS: - case GEN_FILTERQ: - case GEN_ENDADDRCOARSEOFS: - case GEN_MODLFOTOVOL: - case GEN_CHORUSSEND: - case GEN_REVERBSEND: - case GEN_PAN: - case GEN_KEYTOMODENVHOLD: - case GEN_KEYTOMODENVDECAY: - case GEN_MODENVSUSTAIN: - case GEN_VOLENVSUSTAIN: - case GEN_KEYTOVOLENVHOLD: - case GEN_KEYTOVOLENVDECAY: - case GEN_STARTLOOPADDRCOARSEOFS: - case GEN_ENDLOOPADDRCOARSEOFS: - case GEN_VELOCITY: - case GEN_ATTENUATION: - case GEN_COARSETUNE: - case GEN_FINETUNE: - case GEN_SCALETUNE: - return value; - - case GEN_MODLFOTOPITCH: - case GEN_VIBLFOTOPITCH: - case GEN_MODENVTOPITCH: - case GEN_FILTERFC: - case GEN_MODLFOTOFILTERFC: - case GEN_MODENVTOFILTERFC: - case GEN_MODLFODELAY: - case GEN_VIBLFODELAY: - case GEN_MODENVDELAY: - case GEN_MODENVATTACK: - case GEN_MODENVHOLD: - case GEN_MODENVDECAY: - case GEN_MODENVRELEASE: - case GEN_VOLENVDELAY: - case GEN_VOLENVATTACK: - case GEN_VOLENVHOLD: - case GEN_VOLENVDECAY: - case GEN_VOLENVRELEASE: - return 2 * value; - - case GEN_MODLFOFREQ: - case GEN_VIBLFOFREQ: - return 4 * value; - } - - return 0.0f; + fluid_real_t value = (float) data - 8192.0f; + fluid_clip(value, -8192, 8192); + return value * (float) fluid_gen_info[gen].nrpn_scale; } diff --git a/fluidsynth/src/fluid_gen.h b/fluidsynth/src/fluid_gen.h index 06c0763c..de446c85 100644 --- a/fluidsynth/src/fluid_gen.h +++ b/fluidsynth/src/fluid_gen.h @@ -24,11 +24,21 @@ #include "fluidsynth_priv.h" +typedef struct _fluid_gen_info_t { + char num; /* Generator number */ + char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */ + char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ + float min; /* The minimum value */ + float max; /* The maximum value */ + float def; /* The default value (cfr. fluid_gen_set_default_values()) */ +} fluid_gen_info_t; + #define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } #define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } -fluid_real_t fluid_gen_map_nrpn(int gen, int nrpn); - +fluid_real_t fluid_gen_scale(int gen, float value); +fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); int fluid_gen_init(fluid_gen_t* gen, fluid_channel_t* channel); + #endif /* _FLUID_GEN_H */ diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 403b8601..e6ec3827 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -47,6 +47,10 @@ fluid_sfont_t* fluid_synth_get_sfont_by_name(fluid_synth_t* synth, char *name); int fluid_synth_get_pitch_wheel_sens(fluid_synth_t* synth, int chan, int* pval); +int fluid_synth_set_gen2(fluid_synth_t* synth, int chan, + int param, float value, + int absolute, int normalized); + /*************************************************************** * @@ -2739,12 +2743,67 @@ fluid_synth_set_gen(fluid_synth_t* synth, int chan, int param, float value) return FLUID_FAILED; } - fluid_channel_set_gen(synth->channel[chan], param, value); + fluid_channel_set_gen(synth->channel[chan], param, value, 0); for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; if (voice->chan == chan) { - fluid_voice_set_param(voice, param, value); + fluid_voice_set_param(voice, param, value, 0); + } + } + + return FLUID_OK; +} + +/** Change the value of a generator. This function allows to control + all synthesis parameters in real-time. The changes are additive, + i.e. they add up to the existing parameter value. This function is + similar to sending an NRPN message to the synthesizer. The + function accepts a float as the value of the parameter. The + parameter numbers and ranges are described in the SoundFont 2.01 + specification, paragraph 8.1.3, page 48. See also + 'fluid_gen_type'. + + Using the fluid_synth_set_gen2() function, it is possible to set + the absolute value of a generator. This is an extension to the + SoundFont standard. If 'absolute' is non-zero, the value of the + generator specified in the SoundFont is completely ignored and the + generator is fixed to the value passed as argument. To undo this + behavior, you must call fluid_synth_set_gen2 again, with + 'absolute' set to 0 (and possibly 'value' set to zero). + + If 'normalized' is non-zero, the value is supposed to be + normalized between 0 and 1. Before applying the value, it will be + scaled and shifted to the range defined in the SoundFont + specifications. + + */ +int +fluid_synth_set_gen2(fluid_synth_t* synth, int chan, int param, + float value, int absolute, int normalized) +{ + int i; + fluid_voice_t* voice; + float v; + + if ((chan < 0) || (chan >= synth->midi_channels)) { + FLUID_LOG(FLUID_WARN, "Channel out of range"); + return FLUID_FAILED; + } + + if ((param < 0) || (param >= GEN_LAST)) { + FLUID_LOG(FLUID_WARN, "Parameter number out of range"); + return FLUID_FAILED; + } + + v = (normalized)? fluid_gen_scale(param, value) : value; + + fluid_channel_set_gen(synth->channel[chan], param, v, absolute); + + for (i = 0; i < synth->nvoice; i++) { + voice = synth->voice[i]; + if (voice->chan == chan) { + fluid_voice_set_param(voice, param, v, absolute); } } diff --git a/fluidsynth/src/fluid_voice.c b/fluidsynth/src/fluid_voice.c index dc971fc6..47a4944a 100644 --- a/fluidsynth/src/fluid_voice.c +++ b/fluidsynth/src/fluid_voice.c @@ -295,6 +295,18 @@ float fluid_voice_gen_get(fluid_voice_t* voice, int gen) return voice->gen[gen].val; } +fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num) +{ + /* This is an extension to the SoundFont standard. More + * documentation is available at the fluid_synth_set_gen2() + * function. */ + if (voice->gen[num].flags == GEN_ABS_NRPN) { + return (fluid_real_t) voice->gen[num].nrpn; + } else { + return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); + } +} + /* * fluid_voice_write @@ -960,7 +972,7 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice) * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided * by removing all but one generator from the list of voice - * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the + * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the * initialisation list contains only GEN_XXX. */ @@ -1948,9 +1960,10 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice) } -int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value) +int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t nrpn_value, int abs) { voice->gen[gen].nrpn = nrpn_value; + voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET; fluid_voice_update_param(voice, gen); return FLUID_OK; } diff --git a/fluidsynth/src/fluid_voice.h b/fluidsynth/src/fluid_voice.h index 062aee22..fc90b924 100644 --- a/fluidsynth/src/fluid_voice.h +++ b/fluidsynth/src/fluid_voice.h @@ -208,7 +208,7 @@ int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl); int fluid_voice_modulate_all(fluid_voice_t* voice); /** Set the NRPN value of a generator. */ -int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value); +int fluid_voice_set_param(fluid_voice_t* voice, int gen, fluid_real_t value, int abs); /** Set the gain. */ @@ -239,13 +239,18 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice); #define _PLAYING(voice) (((voice)->status == FLUID_VOICE_ON) || ((voice)->status == FLUID_VOICE_SUSTAINED)) -/* A voice is 'ON', if it has not yet received a noteoff event. Sending a noteoff event will advance the envelopes to section 5 (release). */ +/* A voice is 'ON', if it has not yet received a noteoff + * event. Sending a noteoff event will advance the envelopes to + * section 5 (release). */ #define _ON(voice) ((voice)->status == FLUID_VOICE_ON && (voice)->volenv_section < FLUID_VOICE_ENVRELEASE) #define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED) #define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)) #define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) #define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) + +fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num); + #define _GEN(_voice, _n) \ ((fluid_real_t)(_voice)->gen[_n].val \ + (fluid_real_t)(_voice)->gen[_n].mod \