summaryrefslogtreecommitdiff
path: root/libs/fluidsynth
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2019-01-02 15:33:23 +0100
committerRobin Gareus <robin@gareus.org>2019-01-02 16:48:03 +0100
commitac9329f907bf011395968764c82d7c7c13675b7c (patch)
treecd91d325e839b30e743d2a7aff61153e08a443f2 /libs/fluidsynth
parent754591e2ee23a322a1ad3cb66ca65ce7e21d3d33 (diff)
Update Fluidsynth to v2.0.3
see https://github.com/FluidSynth/fluidsynth/releases/tag/v2.0.3
Diffstat (limited to 'libs/fluidsynth')
-rw-r--r--libs/fluidsynth/README9
-rw-r--r--libs/fluidsynth/fluidsynth/midi.h4
-rw-r--r--libs/fluidsynth/fluidsynth/sfont.h6
-rw-r--r--libs/fluidsynth/src/fluid_defsfont.c945
-rw-r--r--libs/fluidsynth/src/fluid_midi.c93
-rw-r--r--libs/fluidsynth/src/fluid_mod.c199
-rw-r--r--libs/fluidsynth/src/fluid_mod.h1
-rw-r--r--libs/fluidsynth/src/fluid_rev.c1566
-rw-r--r--libs/fluidsynth/src/fluid_rvoice.c58
-rw-r--r--libs/fluidsynth/src/fluid_samplecache.c36
-rw-r--r--libs/fluidsynth/src/fluid_sffile.c275
-rw-r--r--libs/fluidsynth/src/fluid_sfont.c15
-rw-r--r--libs/fluidsynth/src/fluid_sfont.h2
-rw-r--r--libs/fluidsynth/src/fluid_synth.c40
-rw-r--r--libs/fluidsynth/src/fluid_sys.c71
-rw-r--r--libs/fluidsynth/src/fluid_sys.h8
-rw-r--r--libs/fluidsynth/src/fluid_voice.c212
-rw-r--r--libs/fluidsynth/src/fluid_voice.h1
-rw-r--r--libs/fluidsynth/src/fluidsynth_priv.h4
-rw-r--r--libs/fluidsynth/wscript2
20 files changed, 2340 insertions, 1207 deletions
diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README
index 451f88e55f..3fcf3159f0 100644
--- a/libs/fluidsynth/README
+++ b/libs/fluidsynth/README
@@ -1,7 +1,14 @@
This is a stripped down version of fluidsynth (library only)
from git://github.com/FluidSynth/fluidsynth.git
-rev. v2.0.2 (6e9d84f02a7a0f7e436c2adffc4a065608f490ba)
+rev. v2.0.3-16-g8b18205
fluidsynth is licensed in terms of the LGPL-2+, see individual source
files for (C) holders.
+
+---
+
+The source is imported in the repository in order to allow static linking,
+with hidden visibility. This is required for a-fluidsynth.lv2 (plugin must
+be self-contained and not rely on external shared libs) and to make it
+available to to libardour on all platforms that Ardour is.
diff --git a/libs/fluidsynth/fluidsynth/midi.h b/libs/fluidsynth/fluidsynth/midi.h
index cd30e2088b..9ddeef054a 100644
--- a/libs/fluidsynth/fluidsynth/midi.h
+++ b/libs/fluidsynth/fluidsynth/midi.h
@@ -53,8 +53,12 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *dat
int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt,
void *data, int size, int dynamic);
+FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt,
+ void **data, int *size);
FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt,
void *data, int size, int dynamic);
+FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt,
+ void **data, int *size);
/**
* MIDI router rule type.
diff --git a/libs/fluidsynth/fluidsynth/sfont.h b/libs/fluidsynth/fluidsynth/sfont.h
index 55413a9e24..0df4d6d4a8 100644
--- a/libs/fluidsynth/fluidsynth/sfont.h
+++ b/libs/fluidsynth/fluidsynth/sfont.h
@@ -184,12 +184,10 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont);
/**
* Virtual SoundFont preset iteration function.
* @param sfont Virtual SoundFont
- * @param preset Caller supplied uninitialized buffer to fill in with current preset information
* @return NULL when no more presets are available, otherwise the a pointer to the current preset
*
- * Should store preset information to the caller supplied \a preset structure
- * and advance the internal iteration state to the next preset for subsequent
- * calls.
+ * Returns preset information to the caller. The returned buffer is only valid until a subsequent
+ * call to this function.
*/
typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont);
diff --git a/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c
index 6e19eb71af..d7960cbb9c 100644
--- a/libs/fluidsynth/src/fluid_defsfont.c
+++ b/libs/fluidsynth/src/fluid_defsfont.c
@@ -682,6 +682,140 @@ fluid_defpreset_next(fluid_defpreset_t *defpreset)
return defpreset->next;
}
+/*
+ * Adds global and local modulators list to the voice. This is done in 2 steps:
+ * - Step 1: Local modulators replace identic global modulators.
+ * - Step 2: global + local modulators are added to the voice using mode.
+ *
+ * Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE.
+ * Preset zone list (local/global) must be added using FLUID_VOICE_ADD.
+ *
+ * @param voice voice instance.
+ * @param global_mod global list of modulators.
+ * @param local_mod local list of modulators.
+ * @param mode Determines how to handle an existing identical modulator.
+ * #FLUID_VOICE_ADD to add (offset) the modulator amounts,
+ * #FLUID_VOICE_OVERWRITE to replace the modulator,
+*/
+static void
+fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice,
+ fluid_mod_t *global_mod, fluid_mod_t *local_mod,
+ int mode)
+{
+ fluid_mod_t *mod;
+ /* list for 'sorting' global/local modulators */
+ fluid_mod_t *mod_list[FLUID_NUM_MOD];
+ int mod_list_count, i;
+
+ /* identity_limit_count is the modulator upper limit number to handle with
+ * existing identical modulators.
+ * When identity_limit_count is below the actual number of modulators, this
+ * will restrict identity check to this upper limit,
+ * This is useful when we know by advance that there is no duplicate with
+ * modulators at index above this limit. This avoid wasting cpu cycles at
+ * noteon.
+ */
+ int identity_limit_count;
+
+ /* Step 1: Local modulators replace identic global modulators. */
+
+ /* local (instrument zone/preset zone), modulators: Put them all into a list. */
+ mod_list_count = 0;
+
+ while(local_mod)
+ {
+ /* As modulators number in local_mod list was limited to FLUID_NUM_MOD at
+ soundfont loading time (fluid_limit_mod_list()), here we don't need
+ to check if mod_list is full.
+ */
+ mod_list[mod_list_count++] = local_mod;
+ local_mod = local_mod->next;
+ }
+
+ /* global (instrument zone/preset zone), modulators.
+ * Replace modulators with the same definition in the global list:
+ * (Instrument zone: SF 2.01 page 69, 'bullet' 8)
+ * (Preset zone: SF 2.01 page 69, second-last bullet).
+ *
+ * mod_list contains local modulators. Now we know that there
+ * is no global modulator identic to another global modulator (this has
+ * been checked at soundfont loading time). So global modulators
+ * are only checked against local modulators number.
+ */
+
+ /* Restrict identity check to the number of local modulators */
+ identity_limit_count = mod_list_count;
+
+ while(global_mod)
+ {
+ /* 'Identical' global modulators are ignored.
+ * SF2.01 section 9.5.1
+ * page 69, 'bullet' 3 defines 'identical'. */
+
+ for(i = 0; i < identity_limit_count; i++)
+ {
+ if(fluid_mod_test_identity(global_mod, mod_list[i]))
+ {
+ break;
+ }
+ }
+
+ /* Finally add the new modulator to the list. */
+ if(i >= identity_limit_count)
+ {
+ /* Although local_mod and global_mod lists was limited to
+ FLUID_NUM_MOD at soundfont loading time, it is possible that
+ local + global modulators exceeds FLUID_NUM_MOD.
+ So, checks if mod_list_count reachs the limit.
+ */
+ if(mod_list_count >= FLUID_NUM_MOD)
+ {
+ /* mod_list is full, we silently forget this modulator and
+ next global modulators. When mod_list will be added to the
+ voice, a warning will be displayed if the voice list is full.
+ (see fluid_voice_add_mod_local()).
+ */
+ break;
+ }
+
+ mod_list[mod_list_count++] = global_mod;
+ }
+
+ global_mod = global_mod->next;
+ }
+
+ /* Step 2: global + local modulators are added to the voice using mode. */
+
+ /*
+ * mod_list contains local and global modulators, we know that:
+ * - there is no global modulator identic to another global modulator,
+ * - there is no local modulator identic to another local modulator,
+ * So these local/global modulators are only checked against
+ * actual number of voice modulators.
+ */
+
+ /* Restrict identity check to the actual number of voice modulators */
+ /* Acual number of voice modulators : defaults + [instruments] */
+ identity_limit_count = voice->mod_count;
+
+ for(i = 0; i < mod_list_count; i++)
+ {
+
+ mod = mod_list[i];
+ /* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */
+ /* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */
+
+ if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0))
+ {
+ /* Instrument modulators -supersede- existing (default) modulators.
+ SF 2.01 page 69, 'bullet' 6 */
+
+ /* Preset modulators -add- to existing instrument modulators.
+ SF2.01 page 70 first bullet on page */
+ fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count);
+ }
+ }
+}
/*
* fluid_defpreset_noteon
@@ -695,9 +829,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
fluid_voice_zone_t *voice_zone;
fluid_list_t *list;
fluid_voice_t *voice;
- fluid_mod_t *mod;
- fluid_mod_t *mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
- int mod_list_count;
int i;
global_preset_zone = fluid_defpreset_get_global_zone(defpreset);
@@ -769,63 +900,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
} /* for all generators */
- /* global instrument zone, modulators: Put them all into a
- * list. */
-
- mod_list_count = 0;
-
- if(global_inst_zone)
- {
- mod = global_inst_zone->mod;
-
- while(mod)
- {
- mod_list[mod_list_count++] = mod;
- mod = mod->next;
- }
- }
-
- /* local instrument zone, modulators.
- * Replace modulators with the same definition in the list:
- * SF 2.01 page 69, 'bullet' 8
- */
- mod = inst_zone->mod;
-
- while(mod)
- {
-
- /* 'Identical' modulators will be deleted by setting their
- * list entry to NULL. The list length is known, NULL
- * entries will be ignored later. SF2.01 section 9.5.1
- * page 69, 'bullet' 3 defines 'identical'. */
-
- for(i = 0; i < mod_list_count; i++)
- {
- if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i]))
- {
- mod_list[i] = NULL;
- }
- }
-
- /* Finally add the new modulator to to the list. */
- mod_list[mod_list_count++] = mod;
- mod = mod->next;
- }
-
- /* Add instrument modulators (global / local) to the voice. */
- for(i = 0; i < mod_list_count; i++)
- {
-
- mod = mod_list[i];
-
- if(mod != NULL) /* disabled modulators CANNOT be skipped. */
- {
-
- /* Instrument modulators -supersede- existing (default)
- * modulators. SF 2.01 page 69, 'bullet' 6 */
- fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE);
- }
- }
+ /* Adds instrument zone modulators (global and local) to the voice.*/
+ fluid_defpreset_noteon_add_mod_to_voice(voice,
+ /* global instrument modulators */
+ global_inst_zone ? global_inst_zone->mod : NULL,
+ inst_zone->mod, /* local instrument modulators */
+ FLUID_VOICE_OVERWRITE); /* mode */
/* Preset level, generators */
@@ -868,57 +948,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
}
} /* for all generators */
-
- /* Global preset zone, modulators: put them all into a
- * list. */
- mod_list_count = 0;
-
- if(global_preset_zone)
- {
- mod = global_preset_zone->mod;
-
- while(mod)
- {
- mod_list[mod_list_count++] = mod;
- mod = mod->next;
- }
- }
-
- /* Process the modulators of the local preset zone. Kick
- * out all identical modulators from the global preset zone
- * (SF 2.01 page 69, second-last bullet) */
-
- mod = preset_zone->mod;
-
- while(mod)
- {
- for(i = 0; i < mod_list_count; i++)
- {
- if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i]))
- {
- mod_list[i] = NULL;
- }
- }
-
- /* Finally add the new modulator to the list. */
- mod_list[mod_list_count++] = mod;
- mod = mod->next;
- }
-
- /* Add preset modulators (global / local) to the voice. */
- for(i = 0; i < mod_list_count; i++)
- {
- mod = mod_list[i];
-
- if((mod != NULL) && (mod->amount != 0)) /* disabled modulators can be skipped. */
- {
-
- /* Preset modulators -add- to existing instrument /
- * default modulators. SF2.01 page 70 first bullet on
- * page */
- fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD);
- }
- }
+ /* Adds preset zone modulators (global and local) to the voice.*/
+ fluid_defpreset_noteon_add_mod_to_voice(voice,
+ /* global preset modulators */
+ global_preset_zone ? global_preset_zone->mod : NULL,
+ preset_zone->mod, /* local preset modulators */
+ FLUID_VOICE_ADD); /* mode */
/* add the synthesis process to the synthesis loop. */
fluid_synth_start_voice(synth, voice);
@@ -930,7 +965,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
* class - for example when using stereo samples)
*/
}
-
}
}
@@ -1105,17 +1139,11 @@ new_fluid_preset_zone(char *name)
}
/*
- * delete_fluid_preset_zone
+ * delete list of modulators.
*/
-void
-delete_fluid_preset_zone(fluid_preset_zone_t *zone)
+static void delete_fluid_list_mod(fluid_mod_t *mod)
{
- fluid_mod_t *mod, *tmp;
- fluid_list_t *list;
-
- fluid_return_if_fail(zone != NULL);
-
- mod = zone->mod;
+ fluid_mod_t *tmp;
while(mod) /* delete the modulators */
{
@@ -1123,6 +1151,19 @@ delete_fluid_preset_zone(fluid_preset_zone_t *zone)
mod = mod->next;
delete_fluid_mod(tmp);
}
+}
+
+/*
+ * delete_fluid_preset_zone
+ */
+void
+delete_fluid_preset_zone(fluid_preset_zone_t *zone)
+{
+ fluid_list_t *list;
+
+ fluid_return_if_fail(zone != NULL);
+
+ delete_fluid_list_mod(zone->mod);
for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list))
{
@@ -1186,71 +1227,269 @@ static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone
return FLUID_OK;
}
+/**
+ * Checks if modulator mod is identic to another modulator in the list
+ * (specs SF 2.0X 7.4, 7.8).
+ * @param mod, modulator list.
+ * @param name, if not NULL, pointer on a string displayed as warning.
+ * @return TRUE if mod is identic to another modulator, FALSE otherwise.
+ */
+static int
+fluid_zone_is_mod_identic(fluid_mod_t *mod, char *name)
+{
+ fluid_mod_t *next = mod->next;
+
+ while(next)
+ {
+ /* is mod identic to next ? */
+ if(fluid_mod_test_identity(mod, next))
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, "Ignoring identic modulator %s", name);
+ }
+
+ return TRUE;
+ }
+
+ next = next->next;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Limits the number of modulators in a modulator list.
+ * This is appropriate to internal synthesizer modulators tables
+ * which have a fixed size (FLUID_NUM_MOD).
+ *
+ * @param zone_name, zone name
+ * @param list_mod, address of pointer on modulator list.
+ */
+static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod)
+{
+ int mod_idx = 0; /* modulator index */
+ fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */
+ fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */
+
+ while(mod)
+ {
+ if((mod_idx + 1) > FLUID_NUM_MOD)
+ {
+ /* truncation of list_mod */
+ if(mod_idx)
+ {
+ prev_mod->next = NULL;
+ }
+ else
+ {
+ *list_mod = NULL;
+ }
+
+ delete_fluid_list_mod(mod);
+ FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name,
+ FLUID_NUM_MOD);
+ break;
+ }
+
+ mod_idx++;
+ prev_mod = mod;
+ mod = mod->next;
+ }
+}
+
+/**
+ * Checks and remove invalid modulators from a zone modulators list.
+ * - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1).
+ * - checks identic modulators in the list (specs SF 2.01 7.4, 7.8).
+ * @param zone_name, zone name.
+ * @param list_mod, address of pointer on modulators list.
+ */
+static void
+fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod)
+{
+ fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */
+ fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */
+ int mod_idx = 0; /* modulator index */
+
+ while(mod)
+ {
+ char zone_mod_name[256];
+ fluid_mod_t *next = mod->next;
+
+ /* prepare modulator name: zonename/#modulator */
+ FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx);
+
+ /* has mod invalid sources ? */
+ if(!fluid_mod_check_sources(mod, zone_mod_name)
+ /* or is mod identic to any following modulator ? */
+ || fluid_zone_is_mod_identic(mod, zone_mod_name))
+ {
+ /* the modulator is useless so we remove it */
+ if(prev_mod)
+ {
+ prev_mod->next = next;
+ }
+ else
+ {
+ *list_mod = next;
+ }
+
+ delete_fluid_mod(mod); /* freeing */
+ }
+ else
+ {
+ prev_mod = mod;
+ }
+
+ mod = next;
+ mod_idx++;
+ }
+
+ /* limits the size of modulators list */
+ fluid_limit_mod_list(zone_name, list_mod);
+}
+
/*
- * fluid_preset_zone_import_sfont
+ * fluid_zone_gen_import_sfont
+ * Imports generators from sfzone to gen and range.
+ * @param gen, pointer on destination generators table.
+ * @param range, pointer on destination range generators.
+ * @param sfzone, pointer on soundfont zone generators.
*/
-int
-fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont)
+static void
+fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone *sfzone)
{
fluid_list_t *r;
SFGen *sfgen;
- SFInst *sfinst;
- int count;
- for(count = 0, r = sfzone->gen; r != NULL; count++)
+ for(r = sfzone->gen; r != NULL;)
{
sfgen = (SFGen *)fluid_list_get(r);
switch(sfgen->id)
{
case GEN_KEYRANGE:
- zone->range.keylo = sfgen->amount.range.lo;
- zone->range.keyhi = sfgen->amount.range.hi;
+ range->keylo = sfgen->amount.range.lo;
+ range->keyhi = sfgen->amount.range.hi;
break;
case GEN_VELRANGE:
- zone->range.vello = sfgen->amount.range.lo;
- zone->range.velhi = sfgen->amount.range.hi;
+ range->vello = sfgen->amount.range.lo;
+ range->velhi = sfgen->amount.range.hi;
break;
case GEN_ATTENUATION:
/* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
* preset and instrument level */
- zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
- zone->gen[sfgen->id].flags = GEN_SET;
+ gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
+ gen[sfgen->id].flags = GEN_SET;
break;
default:
/* FIXME: some generators have an unsigne word amount value but i don't know which ones */
- zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
- zone->gen[sfgen->id].flags = GEN_SET;
+ gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ gen[sfgen->id].flags = GEN_SET;
break;
}
r = fluid_list_next(r);
}
+}
- if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL))
+/*
+ * fluid_zone_mod_source_import_sfont
+ * Imports source information from sf_source to src and flags.
+ * @param src, pointer on destination modulator source.
+ * @param flags, pointer on destination modulator flags.
+ * @param sf_source, soundfont modulator source.
+ * @return return TRUE if success, FALSE if source type is unknow.
+ */
+static int
+fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source)
+{
+ int type;
+ unsigned char flags_dest; /* destination flags */
+
+ /* sources */
+ *src = sf_source & 127; /* index of source, seven-bit value, SF2.01 section 8.2, page 50 */
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ flags_dest = 0;
+
+ if(sf_source & (1 << 7))
{
- sfinst = sfzone->instsamp->data;
+ flags_dest |= FLUID_MOD_CC;
+ }
+ else
+ {
+ flags_dest |= FLUID_MOD_GC;
+ }
- zone->inst = find_inst_by_idx(defsfont, sfinst->idx);
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if(sf_source & (1 << 8))
+ {
+ flags_dest |= FLUID_MOD_NEGATIVE;
+ }
+ else
+ {
+ flags_dest |= FLUID_MOD_POSITIVE;
+ }
- if(zone->inst == NULL)
- {
- zone->inst = fluid_inst_import_sfont(zone, sfinst, defsfont);
- }
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if(sf_source & (1 << 9))
+ {
+ flags_dest |= FLUID_MOD_BIPOLAR;
+ }
+ else
+ {
+ flags_dest |= FLUID_MOD_UNIPOLAR;
+ }
- if(zone->inst == NULL)
- {
- return FLUID_FAILED;
- }
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = sf_source >> 10;
+ type &= 63; /* type is a 6-bit value */
- if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED)
- {
- return FLUID_FAILED;
- }
+ if(type == 0)
+ {
+ flags_dest |= FLUID_MOD_LINEAR;
}
+ else if(type == 1)
+ {
+ flags_dest |= FLUID_MOD_CONCAVE;
+ }
+ else if(type == 2)
+ {
+ flags_dest |= FLUID_MOD_CONVEX;
+ }
+ else if(type == 3)
+ {
+ flags_dest |= FLUID_MOD_SWITCH;
+ }
+ else
+ {
+ *flags = flags_dest;
+ /* This shouldn't happen - unknown type! */
+ return FALSE;
+ }
+
+ *flags = flags_dest;
+ return TRUE;
+}
+
+/*
+ * fluid_zone_mod_import_sfont
+ * Imports modulators from sfzone to modulators list mod.
+ * @param zone_name, zone name.
+ * @param mod, address of pointer on modulators list to return.
+ * @param sfzone, pointer on soundfont zone.
+ * @return FLUID_OK if success, FLUID_FAILED otherwise.
+ */
+static int
+fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone)
+{
+ fluid_list_t *r;
+ int count;
/* Import the modulators (only SF2.1 and higher) */
for(count = 0, r = sfzone->mod; r != NULL; count++)
@@ -1258,7 +1497,6 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_
SFMod *mod_src = (SFMod *)fluid_list_get(r);
fluid_mod_t *mod_dest = new_fluid_mod();
- int type;
if(mod_dest == NULL)
{
@@ -1271,128 +1509,45 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_
mod_dest->amount = mod_src->amount;
/* *** Source *** */
- mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
- mod_dest->flags1 = 0;
-
- /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
- if(mod_src->src & (1 << 7))
- {
- mod_dest->flags1 |= FLUID_MOD_CC;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_GC;
- }
-
- /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
- if(mod_src->src & (1 << 8))
- {
- mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_POSITIVE;
- }
-
- /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
- if(mod_src->src & (1 << 9))
- {
- mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
- }
-
- /* modulator source types: SF2.01 section 8.2.1 page 52 */
- type = (mod_src->src) >> 10;
- type &= 63; /* type is a 6-bit value */
-
- if(type == 0)
- {
- mod_dest->flags1 |= FLUID_MOD_LINEAR;
- }
- else if(type == 1)
- {
- mod_dest->flags1 |= FLUID_MOD_CONCAVE;
- }
- else if(type == 2)
- {
- mod_dest->flags1 |= FLUID_MOD_CONVEX;
- }
- else if(type == 3)
- {
- mod_dest->flags1 |= FLUID_MOD_SWITCH;
- }
- else
+ if(!fluid_zone_mod_source_import_sfont(&mod_dest->src1, &mod_dest->flags1, mod_src->src))
{
/* This shouldn't happen - unknown type!
* Deactivate the modulator by setting the amount to 0. */
mod_dest->amount = 0;
+ }
+
+ /* Note: When primary source input (src1) is set to General Controller 'No Controller',
+ output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()).
+ That means that the minimum value of the modulator will be always 0.0.
+ We need to force amount value to 0 to ensure a correct evaluation of the minimum
+ value later (see fluid_voice_get_lower_boundary_for_attenuation()).
+ */
+ if(((mod_dest->flags1 & FLUID_MOD_CC) == FLUID_MOD_GC) &&
+ (mod_dest->src1 == FLUID_MOD_NONE))
+ {
+ mod_dest->amount = 0;
}
/* *** Dest *** */
mod_dest->dest = mod_src->dest; /* index of controlled generator */
/* *** Amount source *** */
- mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */
- mod_dest->flags2 = 0;
-
- /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
- if(mod_src->amtsrc & (1 << 7))
- {
- mod_dest->flags2 |= FLUID_MOD_CC;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_GC;
- }
-
- /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
- if(mod_src->amtsrc & (1 << 8))
- {
- mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_POSITIVE;
- }
-
- /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
- if(mod_src->amtsrc & (1 << 9))
- {
- mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
- }
-
- /* modulator source types: SF2.01 section 8.2.1 page 52 */
- type = (mod_src->amtsrc) >> 10;
- type &= 63; /* type is a 6-bit value */
-
- if(type == 0)
- {
- mod_dest->flags2 |= FLUID_MOD_LINEAR;
- }
- else if(type == 1)
- {
- mod_dest->flags2 |= FLUID_MOD_CONCAVE;
- }
- else if(type == 2)
- {
- mod_dest->flags2 |= FLUID_MOD_CONVEX;
- }
- else if(type == 3)
- {
- mod_dest->flags2 |= FLUID_MOD_SWITCH;
- }
- else
+ if(!fluid_zone_mod_source_import_sfont(&mod_dest->src2, &mod_dest->flags2, mod_src->amtsrc))
{
/* This shouldn't happen - unknown type!
* Deactivate the modulator by setting the amount to 0. */
mod_dest->amount = 0;
+ }
+ /* Note: When secondary source input (src2) is set to General Controller 'No Controller',
+ output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()).
+ That means that this source will behave unipolar only. We need to force the
+ unipolar flag to ensure to ensure a correct evaluation of the minimum
+ value later (see fluid_voice_get_lower_boundary_for_attenuation()).
+ */
+ if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) &&
+ (mod_dest->src2 == FLUID_MOD_NONE))
+ {
+ mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR;
}
/* *** Transform *** */
@@ -1410,11 +1565,11 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_
* in amount. */
if(count == 0)
{
- zone->mod = mod_dest;
+ *mod = mod_dest;
}
else
{
- fluid_mod_t *last_mod = zone->mod;
+ fluid_mod_t *last_mod = *mod;
/* Find the end of the list */
while(last_mod->next != NULL)
@@ -1428,10 +1583,47 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_
r = fluid_list_next(r);
} /* foreach modulator */
+ /* checks and removes invalid modulators in modulators list*/
+ fluid_zone_check_mod(zone_name, mod);
return FLUID_OK;
}
/*
+ * fluid_preset_zone_import_sfont
+ */
+int
+fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont)
+{
+ /* import the generators */
+ fluid_zone_gen_import_sfont(zone->gen, &zone->range, sfzone);
+
+ if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL))
+ {
+ SFInst *sfinst = sfzone->instsamp->data;
+
+ zone->inst = find_inst_by_idx(defsfont, sfinst->idx);
+
+ if(zone->inst == NULL)
+ {
+ zone->inst = fluid_inst_import_sfont(zone, sfinst, defsfont);
+ }
+
+ if(zone->inst == NULL)
+ {
+ return FLUID_FAILED;
+ }
+
+ if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED)
+ {
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Import the modulators (only SF2.1 and higher) */
+ return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone);
+}
+
+/*
* fluid_preset_zone_get_inst
*/
fluid_inst_t *
@@ -1541,7 +1733,9 @@ fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_
{
sfzone = (SFZone *)fluid_list_get(p);
- FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", inst->name, count);
+ /* integrates preset zone name in instrument zone name */
+ FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%s/%d", preset_zone->name,
+ inst->name, count);
inst_zone = new_fluid_inst_zone(zone_name);
@@ -1661,18 +1855,9 @@ new_fluid_inst_zone(char *name)
void
delete_fluid_inst_zone(fluid_inst_zone_t *zone)
{
- fluid_mod_t *mod, *tmp;
-
fluid_return_if_fail(zone != NULL);
- mod = zone->mod;
-
- while(mod) /* delete the modulators */
- {
- tmp = mod;
- mod = mod->next;
- delete_fluid_mod(tmp);
- }
+ delete_fluid_list_mod(zone->mod);
FLUID_FREE(zone->name);
FLUID_FREE(zone);
@@ -1693,43 +1878,8 @@ fluid_inst_zone_next(fluid_inst_zone_t *zone)
int
fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont)
{
- fluid_list_t *r;
- SFGen *sfgen;
- int count;
-
- for(count = 0, r = sfzone->gen; r != NULL; count++)
- {
- sfgen = (SFGen *)fluid_list_get(r);
-
- switch(sfgen->id)
- {
- case GEN_KEYRANGE:
- inst_zone->range.keylo = sfgen->amount.range.lo;
- inst_zone->range.keyhi = sfgen->amount.range.hi;
- break;
-
- case GEN_VELRANGE:
- inst_zone->range.vello = sfgen->amount.range.lo;
- inst_zone->range.velhi = sfgen->amount.range.hi;
- break;
-
- case GEN_ATTENUATION:
- /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
- * preset and instrument level */
- inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
- inst_zone->gen[sfgen->id].flags = GEN_SET;
- break;
-
- default:
- /* FIXME: some generators have an unsigned word amount value but
- i don't know which ones */
- inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
- inst_zone->gen[sfgen->id].flags = GEN_SET;
- break;
- }
-
- r = fluid_list_next(r);
- }
+ /* import the generators */
+ fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, sfzone);
/* FIXME */
/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */
@@ -1743,182 +1893,7 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid
}
/* Import the modulators (only SF2.1 and higher) */
- for(count = 0, r = sfzone->mod; r != NULL; count++)
- {
- SFMod *mod_src = (SFMod *)fluid_list_get(r);
- int type;
- fluid_mod_t *mod_dest;
-
- mod_dest = new_fluid_mod();
-
- if(mod_dest == NULL)
- {
- return FLUID_FAILED;
- }
-
- mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
-
- /* *** Amount *** */
- mod_dest->amount = mod_src->amount;
-
- /* *** Source *** */
- mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
- mod_dest->flags1 = 0;
-
- /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
- if(mod_src->src & (1 << 7))
- {
- mod_dest->flags1 |= FLUID_MOD_CC;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_GC;
- }
-
- /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
- if(mod_src->src & (1 << 8))
- {
- mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_POSITIVE;
- }
-
- /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
- if(mod_src->src & (1 << 9))
- {
- mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
- }
- else
- {
- mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
- }
-
- /* modulator source types: SF2.01 section 8.2.1 page 52 */
- type = (mod_src->src) >> 10;
- type &= 63; /* type is a 6-bit value */
-
- if(type == 0)
- {
- mod_dest->flags1 |= FLUID_MOD_LINEAR;
- }
- else if(type == 1)
- {
- mod_dest->flags1 |= FLUID_MOD_CONCAVE;
- }
- else if(type == 2)
- {
- mod_dest->flags1 |= FLUID_MOD_CONVEX;
- }
- else if(type == 3)
- {
- mod_dest->flags1 |= FLUID_MOD_SWITCH;
- }
- else
- {
- /* This shouldn't happen - unknown type!
- * Deactivate the modulator by setting the amount to 0. */
- mod_dest->amount = 0;
- }
-
- /* *** Dest *** */
- mod_dest->dest = mod_src->dest; /* index of controlled generator */
-
- /* *** Amount source *** */
- mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */
- mod_dest->flags2 = 0;
-
- /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
- if(mod_src->amtsrc & (1 << 7))
- {
- mod_dest->flags2 |= FLUID_MOD_CC;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_GC;
- }
-
- /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
- if(mod_src->amtsrc & (1 << 8))
- {
- mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_POSITIVE;
- }
-
- /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
- if(mod_src->amtsrc & (1 << 9))
- {
- mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
- }
- else
- {
- mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
- }
-
- /* modulator source types: SF2.01 section 8.2.1 page 52 */
- type = (mod_src->amtsrc) >> 10;
- type &= 63; /* type is a 6-bit value */
-
- if(type == 0)
- {
- mod_dest->flags2 |= FLUID_MOD_LINEAR;
- }
- else if(type == 1)
- {
- mod_dest->flags2 |= FLUID_MOD_CONCAVE;
- }
- else if(type == 2)
- {
- mod_dest->flags2 |= FLUID_MOD_CONVEX;
- }
- else if(type == 3)
- {
- mod_dest->flags2 |= FLUID_MOD_SWITCH;
- }
- else
- {
- /* This shouldn't happen - unknown type!
- * Deactivate the modulator by setting the amount to 0. */
- mod_dest->amount = 0;
- }
-
- /* *** Transform *** */
- /* SF2.01 only uses the 'linear' transform (0).
- * Deactivate the modulator by setting the amount to 0 in any other case.
- */
- if(mod_src->trans != 0)
- {
- mod_dest->amount = 0;
- }
-
- /* Store the new modulator in the zone
- * The order of modulators will make a difference, at least in an instrument context:
- * The second modulator overwrites the first one, if they only differ in amount. */
- if(count == 0)
- {
- inst_zone->mod = mod_dest;
- }
- else
- {
- fluid_mod_t *last_mod = inst_zone->mod;
-
- /* Find the end of the list */
- while(last_mod->next != NULL)
- {
- last_mod = last_mod->next;
- }
-
- last_mod->next = mod_dest;
- }
-
- r = fluid_list_next(r);
- } /* foreach modulator */
-
- return FLUID_OK;
+ return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone);
}
/*
diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c
index b82bfdf618..8deb0b0d1b 100644
--- a/libs/fluidsynth/src/fluid_midi.c
+++ b/libs/fluidsynth/src/fluid_midi.c
@@ -36,6 +36,7 @@ static long fluid_getlength(unsigned char *s);
*/
static char *fluid_file_read_full(fluid_file fp, size_t *length);
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic);
+static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size);
#define READ_FULL_INITIAL_BUFLEN 1024
static fluid_track_t *new_fluid_track(int num);
@@ -82,6 +83,41 @@ static int fluid_midi_file_get_division(fluid_midi_file *midifile);
*/
/**
+ * Check if a file is a MIDI file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a MIDI file, FALSE otherwise
+ *
+ * The current implementation only checks for the "MThd" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int fluid_is_midifile(const char *filename)
+{
+ FILE *fp = FLUID_FOPEN(filename, "rb");
+ uint32_t id;
+ int retcode = FALSE;
+
+ do
+ {
+ if(fp == NULL)
+ {
+ return retcode;
+ }
+
+ if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1)
+ {
+ break;
+ }
+
+ retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd'));
+ }
+ while(0);
+
+ FLUID_FCLOSE(fp);
+
+ return retcode;
+}
+
+/**
* Return a new MIDI file handle for parsing an already-loaded MIDI file.
* @internal
* @param buffer Pointer to full contents of MIDI file (borrows the pointer).
@@ -1271,8 +1307,6 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
* should be freed when the event is freed (only applies if event gets destroyed
* with delete_fluid_midi_event())
* @return Always returns #FLUID_OK
- *
- * @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@@ -1291,7 +1325,6 @@ fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dy
* @return Always returns #FLUID_OK
*
* @since 2.0.0
- * @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@@ -1301,6 +1334,25 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
}
/**
+ * Get the text of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to return text data on.
+ * @param size Pointer to return text size on.
+ * @return Returns #FLUID_OK if \p data and \p size previously set by
+ * fluid_midi_event_set_text() have been successfully retrieved.
+ * Else #FLUID_FAILED is returned and \p data and \p size are not changed.
+ * @since 2.0.3
+ */
+int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size)
+{
+ fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED);
+
+ fluid_midi_event_get_sysex_LOCAL(evt, data, size);
+ return FLUID_OK;
+}
+
+/**
* Assign lyric data to a MIDI event structure.
* @param evt MIDI event structure
* @param data Pointer to lyric data
@@ -1310,7 +1362,6 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
* @return Always returns #FLUID_OK
*
* @since 2.0.0
- * @note Unlike the other event assignment functions, this one sets evt->type.
*/
int
fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@@ -1319,6 +1370,25 @@ fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int d
return FLUID_OK;
}
+/**
+ * Get the lyric of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to return lyric data on.
+ * @param size Pointer to return lyric size on.
+ * @return Returns #FLUID_OK if \p data and \p size previously set by
+ * fluid_midi_event_set_lyrics() have been successfully retrieved.
+ * Else #FLUID_FAILED is returned and \p data and \p size are not changed.
+ * @since 2.0.3
+ */
+int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size)
+{
+ fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED);
+
+ fluid_midi_event_get_sysex_LOCAL(evt, data, size);
+ return FLUID_OK;
+}
+
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic)
{
evt->type = type;
@@ -1327,6 +1397,19 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type,
evt->param2 = dynamic;
}
+static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size)
+{
+ if(data)
+ {
+ *data = evt->paramptr;
+ }
+
+ if(size)
+ {
+ *size = evt->param1;
+ }
+}
+
/******************************************************
*
* fluid_track_t
@@ -1598,7 +1681,7 @@ new_fluid_player(fluid_synth_t *synth)
fluid_settings_getint(synth->settings, "player.reset-synth", &i);
fluid_player_handle_reset_synth(player, NULL, i);
-
+
fluid_settings_callback_int(synth->settings, "player.reset-synth",
fluid_player_handle_reset_synth, player);
diff --git a/libs/fluidsynth/src/fluid_mod.c b/libs/fluidsynth/src/fluid_mod.c
index 9a48ed45d8..5e57455d4d 100644
--- a/libs/fluidsynth/src/fluid_mod.c
+++ b/libs/fluidsynth/src/fluid_mod.c
@@ -245,10 +245,7 @@ fluid_mod_get_source_value(const unsigned char mod_src,
static fluid_real_t
fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range)
{
- /* normalized value, i.e. usually in the range [0;1]
- *
- * if val was retrieved from pitch_bend then [-0.5;0.5]
- */
+ /* normalized value, i.e. usually in the range [0;1] */
const fluid_real_t val_norm = val / range;
/* we could also only switch case the lower nibble of mod_flags, however
@@ -364,7 +361,22 @@ fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, cons
}
/*
- * fluid_mod_get_value
+ * fluid_mod_get_value.
+ * Computes and return modulator output following SF2.01
+ * (See SoundFont Modulator Controller Model Chapter 9.5).
+ *
+ * Output = Transform(Amount * Map(primary source input) * Map(secondary source input))
+ *
+ * Notes:
+ * 1)fluid_mod_get_value, ignores the Transform operator. The result is:
+ *
+ * Output = Amount * Map(primary source input) * Map(secondary source input)
+ *
+ * 2)When primary source input (src1) is set to General Controller 'No Controller',
+ * output is forced to 0.
+ *
+ * 3)When secondary source input (src2) is set to General Controller 'No Controller',
+ * output is forced to +1.0
*/
fluid_real_t
fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
@@ -418,6 +430,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the input value */
v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1);
}
+ /* When primary source input (src1) is set to General Controller 'No Controller',
+ output is forced to 0.0
+ */
else
{
return 0.0;
@@ -437,6 +452,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the second input value */
v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2);
}
+ /* When secondary source input (src2) is set to General Controller 'No Controller',
+ output is forced to +1.0
+ */
else
{
v2 = 1.0f;
@@ -487,6 +505,177 @@ size_t fluid_mod_sizeof()
}
/**
+ * Checks if modulator with source 1 other than CC is FLUID_MOD_NONE.
+ *
+ * @param mod, modulator.
+ * @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise.
+ */
+static int
+fluid_mod_is_src1_none(const fluid_mod_t *mod)
+{
+ return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE));
+}
+
+/**
+ * Checks if modulators source other than CC source is invalid.
+ * (specs SF 2.01 7.4, 7.8, 8.2.1)
+ *
+ * @param mod, modulator.
+ * @param src1_select, source input selection to check.
+ * 1 to check src1 source.
+ * 0 to check src2 source.
+ * @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise.
+ */
+static int
+fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
+{
+ unsigned char flags, src;
+
+ if(src1_select)
+ {
+ flags = mod->flags1;
+ src = mod->src1;
+ }
+ else
+ {
+ flags = mod->flags2;
+ src = mod->src2;
+ }
+
+ return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */
+ /* SF2.01 section 8.2.1: Constant value */
+ || ((src == FLUID_MOD_NONE)
+ || (src == FLUID_MOD_VELOCITY) /* Note-on velocity */
+ || (src == FLUID_MOD_KEY) /* Note-on key number */
+ || (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */
+ || (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
+ || (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
+ || (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */
+ ));
+}
+
+/**
+ * Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1).
+ * @param mod, modulator.
+ * @src1_select, source input selection:
+ * 1 to check src1 source or
+ * 0 to check src2 source.
+ * @return FALSE if selected modulator's source CC is invalid, TRUE otherwise.
+ */
+static int
+fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
+{
+ unsigned char flags, src;
+
+ if(src1_select)
+ {
+ flags = mod->flags1;
+ src = mod->src1;
+ }
+ else
+ {
+ flags = mod->flags2;
+ src = mod->src2;
+ }
+
+ return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */
+ || ((src != BANK_SELECT_MSB)
+ && (src != BANK_SELECT_LSB)
+ && (src != DATA_ENTRY_MSB)
+ && (src != DATA_ENTRY_LSB)
+ /* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */
+ && ((src < NRPN_LSB) || (RPN_MSB < src))
+ /* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */
+ /* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */
+ && (src < ALL_SOUND_OFF)
+ /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1)
+ However, as long fluidsynth will use only CC 7 bits resolution,
+ it is safe to ignore these SF recommendations on CC receive.
+ See explanations in fluid_synth_cc_LOCAL() */
+ /* uncomment next line to forbid CC lsb */
+ /* && ((src < 32) || (63 < src)) */
+ ));
+}
+
+/**
+ * Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1)
+ * @param mod, modulator.
+ * @param name,if not NULL, pointer on a string displayed as a warning.
+ * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.
+ */
+int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name)
+{
+ static const char *invalid_non_cc_src =
+ "Invalid modulator, using non-CC source %s.src%d=%d";
+ static const char *invalid_cc_src =
+ "Invalid modulator, using CC source %s.src%d=%d";
+ static const char *src1_is_none =
+ "Modulator with source 1 none %s.src1=%d";
+
+ /* checks valid non cc sources */
+ if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1);
+ }
+
+ return FALSE;
+ }
+
+ /*
+ When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but
+ the output of this modulator will be forced to 0 at synthesis time.
+ Also this modulator cannot be used to overwrite a default modulator (as
+ there is no default modulator with src1 source equal to FLUID_MOD_NONE).
+ Consequently it is useful to return FALSE to indicate this modulator
+ being useless. It will be removed later with others invalid modulators.
+ */
+ if(fluid_mod_is_src1_none(mod))
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1);
+ }
+
+ return FALSE;
+ }
+
+ if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2);
+ }
+
+ return FALSE;
+ }
+
+ /* checks valid cc sources */
+ if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1);
+ }
+
+ return FALSE;
+ }
+
+ if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */
+ {
+ if(name)
+ {
+ FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2);
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
* Checks if two modulators are identical in sources, flags and destination.
* @param mod1 First modulator
* @param mod2 Second modulator
diff --git a/libs/fluidsynth/src/fluid_mod.h b/libs/fluidsynth/src/fluid_mod.h
index e834baa513..ec8e967a35 100644
--- a/libs/fluidsynth/src/fluid_mod.h
+++ b/libs/fluidsynth/src/fluid_mod.h
@@ -44,6 +44,7 @@ struct _fluid_mod_t
};
fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
+int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name);
#ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod);
diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c
index 894afc5a05..9acee96150 100644
--- a/libs/fluidsynth/src/fluid_rev.c
+++ b/libs/fluidsynth/src/fluid_rev.c
@@ -1,548 +1,1358 @@
-/*
+/******************************************************************************
+ * 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 Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ *
+ * FDN REVERB
+ *
+ * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on
+ * Schroeder-Moorer reverberator:
+ * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html
+ *
+ * This FDN reverberation is based on jot FDN reverberator.
+ * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html
+ * Like Freeverb it is a late reverb which is convenient for Fluidsynth.
+ *
+ *
+ * .-------------------.
+ * .-----------------| |
+ * | - | Feedback |
+ * | .--------------| Matrix |
+ * | | |___________________|
+ * | | /|\ /|\
+ * \|/ | .---------. .-------. | - | .------.
+ * .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->| |-> out
+ * .---------. | | |_________| |_______| | | | left
+ * |Tone | | | - - | |Stereo|
+ * In ->|corrector|--* | - - | | unit |
+ * mono |_________| | \|/ .---------. .-------. | | |-> out
+ * ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->| | right
+ * |_________| |_______| |______|
+ * /|\ /|\ /|\ /|\
+ * | | | |
+ * roomsize --/ | width --/ |
+ * damp ------/ level ------/
+ *
+ * It takes a monophonic input and produces a stereo output.
+ *
+ * The parameters are the same than for Freeverb.
+ * Also the default response of these parameters are the same than for Freeverb:
+ * - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s.
+ * This reverberation time is ofen called T60DC.
+ *
+ * - damp (0 to 1): controls the reverb time frequency dependency.
+ * This controls the reverb time for the frequency sample rate/2
+ *
+ * When 0, the reverb time for high frequencies is the same as
+ * for DC frequency.
+ * When > 0, high frequencies have less reverb time than lower frequencies.
+ *
+ * - width (0 to 100): controls the left/right output separation.
+ * When 0, there are no separation and the signal on left and right.
+ * output is the same. This sounds like a monophonic signal.
+ * When 100, the separation between left and right is maximum.
+ *
+ * - level (0 to 1), controls the ouput level reverberation.
+ *
+ * This FDN reverb produces a better quality reverberation tail than Freeverb with
+ * far less ringing by using modulated delay lines that help to cancel
+ * the building of a lot of resonances in the reverberation tail even when
+ * using only 8 delays lines (NBR_DELAYS = 8) (default).
+ *
+ * Although 8 lines give good result, using 12 delays lines brings the overall
+ * frequency density quality a bit higher. This quality augmentation is noticeable
+ * particularly when using long reverb time (roomsize = 1) on solo instrument with
+ * long release time. Of course the cpu load augmentation is +50% relatively
+ * to 8 lines.
+ *
+ * As a general rule the reverberation tail quality is easier to perceive by ear
+ * when using:
+ * - percussive instruments (i.e piano and others).
+ * - long reverb time (roomsize = 1).
+ * - no damping (damp = 0).
+ *
+ *
+ * The cpu load for 8 lines is a bit lower than for freeverb (- 3%),
+ * but higher for 12 lines (+ 41%).
+ *
+ *
+ * The memory consumption is less than for freeverb. This saves 147480 bytes
+ * for 8 lines (- 72%) and 110152 bytes for 12 lines (- 54 %).
+ * (see the results table below).
+ *
+ * Two macros are usable at compiler time:
+ * - NBR_DELAYS: number of delay lines. 8 (default) or 12.
+ * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of
+ * roomsize parameter.
+ * When this macro is not defined (the default), roomsize has the same
+ * response that Freeverb, that is:
+ * - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s).
+ *
+ * When this macro is defined, roomsize behaves linearly:
+ * - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s).
+ * This linear response is convenient when using GUI controls.
+ *
+ * --------------------------------------------------------------------------
+ * Compare table:
+ * Note: the cpu load in % are relative each to other. These values are
+ * given by the fluidsynth profile commands.
+ * --------------------------------------------------------------------------
+ * reverb | NBR_DELAYS | Performances | memory size | quality
+ * | | (cpu_load: %) | (bytes) |
+ * ==========================================================================
+ * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing
+ * | 2 x 4 all-pass | | |
+ * ----------|---------------------------------------------------------------
+ * FDN | 8 | 0.650 % | 57136 | far less
+ * modulated | |(feeverb - 3%) |(freeverb - 72%) | ringing
+ * |---------------------------------------------------------------
+ * | 12 | 0.942 % | 94464 | best than
+ * | |(freeverb + 41%) |(freeverb - 54%) | 8 lines
+ *---------------------------------------------------------------------------
+ *
+ *
+ *----------------------------------------------------------------------------
+ * 'Denormalise' method to avoid loss of performance.
+ * --------------------------------------------------
+ * According to music-dsp thread 'Denormalise', Pentium processors
+ * have a hardware 'feature', that is of interest here, related to
+ * numeric underflow. We have a recursive filter. The output decays
+ * exponentially, if the input stops. So the numbers get smaller and
+ * smaller... At some point, they reach 'denormal' level. This will
+ * lead to drastic spikes in the CPU load. The effect was reproduced
+ * with the reverb - sometimes the average load over 10 s doubles!!.
+ *
+ * The 'undenormalise' macro fixes the problem: As soon as the number
+ * is close enough to denormal level, the macro forces the number to
+ * 0.0f. The original macro is:
+ *
+ * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f
+ *
+ * This will zero out a number when it reaches the denormal level.
+ * Advantage: Maximum dynamic range Disadvantage: We'll have to check
+ * every sample, expensive. The alternative macro comes from a later
+ * mail from Jon Watte. It will zap a number before it reaches
+ * denormal level. Jon suggests to run it once per block instead of
+ * every sample.
+ */
- Freeverb
+/* Denormalising part II:
+ *
+ * Another method fixes the problem cheaper: Use a small DC-offset in
+ * the filter calculations. Now the signals converge not against 0,
+ * but against the offset. The constant offset is invisible from the
+ * outside world (i.e. it does not appear at the output. There is a
+ * very small turn-on transient response, which should not cause
+ * problems.
+ */
+#include "fluid_rev.h"
+#include "fluid_sys.h"
- Written by Jezar at Dreampoint, June 2000
- http://www.dreampoint.co.uk
- This code is public domain
+/*----------------------------------------------------------------------------
+ Configuration macros at compiler time.
+
+ 3 macros are usable at compiler time:
+ - NBR_DELAYs: number of delay lines. 8 (default) or 12.
+ - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for
+ roomsize parameter.
+ - DENORMALISING enable denormalising handling.
+-----------------------------------------------------------------------------*/
+/* Number of delay lines (must be only 8 or 12)
+ 8 is the default.
+ 12 produces a better quality but is +50% cpu expensive
+*/
+#define NBR_DELAYS 8 /* default*/
+
+/* response curve of parameter roomsize */
+/*
+ The default response is the same as Freeverb:
+ - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s).
- Translated to C by Peter Hanappe, Mai 2001
+ when ROOMSIZE_RESPONSE_LINEAR is defined, the response is:
+ - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s).
*/
+//#define ROOMSIZE_RESPONSE_LINEAR
-#include "fluid_sys.h"
-#include "fluid_rev.h"
+/* DENORMALISING enable denormalising handling */
+#define DENORMALISING
-/***************************************************************
- *
- * REVERB
- */
+#ifdef DENORMALISING
+#define DC_OFFSET 1e-8
+#else
+#define DC_OFFSET 0.0
+#endif
-/* Denormalising:
- *
- * We have a recursive filter. The output decays exponentially, if the input
- * stops. So the numbers get smaller and smaller... At some point, they reach
- * 'denormal' level. On some platforms this will lead to drastic spikes in the
- * CPU load. This is especially noticable on some older Pentium (especially
- * Pentium 3) processors, but even more modern Intel Core processors still show
- * reduced performance with denormals. While there are compile-time switches to
- * treat denormals as zero for a lot of processors, those are not available or
- * effective on all platforms.
- *
- * The fix used here: Use a small DC-offset in the filter calculations. Now
- * the signals converge not against 0, but against the offset. The constant
- * offset is invisible from the outside world (i.e. it does not appear at the
- * output. There is a very small turn-on transient response, which should not
- * cause problems.
+/*----------------------------------------------------------------------------
+ Initial internal reverb settings (at reverb creation time)
+-----------------------------------------------------------------------------*/
+/* SCALE_WET_WIDTH is a compensation weight factor to get an output
+ amplitude (wet) rather independent of the width setting.
+ 0: the output amplitude is fully dependant on the width setting.
+ >0: the output amplitude is less dependant on the width setting.
+ With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather
+ independent of width setting (see fluid_revmodel_update()).
*/
-#define DC_OFFSET ((fluid_real_t)1e-8)
+#define SCALE_WET_WIDTH 0.2f
-typedef struct _fluid_allpass fluid_allpass;
-typedef struct _fluid_comb fluid_comb;
+/* It is best to inject the input signal less ofen. This contributes to obtain
+a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */
+#define FIXED_GAIN 0.1f /* input gain */
-struct _fluid_allpass
-{
- fluid_real_t feedback;
- fluid_real_t *buffer;
- int bufsize;
- int bufidx;
-};
+/* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */
+#define SCALE_WET 5.0f /* scale output gain */
-void fluid_allpass_init(fluid_allpass *allpass);
-void fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val);
-fluid_real_t fluid_allpass_getfeedback(fluid_allpass *allpass);
+/*----------------------------------------------------------------------------
+ Internal FDN late reverb settings
+-----------------------------------------------------------------------------*/
-static void
-fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
-{
- allpass->bufidx = 0;
- allpass->buffer = FLUID_ARRAY(fluid_real_t, size);
- allpass->bufsize = size;
-}
+/*-- Reverberation time settings ----------------------------------
+ MIN_DC_REV_TIME est defined egal to the minimum value of freeverb:
+ MAX_DC_REV_TIME est defined egal to the maximum value of freeverb:
+ T60DC is computed from gi and the longuest delay line in freeverb: L8 = 1617
+ T60 = -3 * Li * T / log10(gi)
+ T60 = -3 * Li * / (log10(gi) * sr)
-static void
-fluid_allpass_release(fluid_allpass *allpass)
-{
- FLUID_FREE(allpass->buffer);
-}
+ - Li: length of comb filter delay line.
+ - sr: sample rate.
+ - gi: the feedback gain.
-void
-fluid_allpass_init(fluid_allpass *allpass)
-{
- int i;
- int len = allpass->bufsize;
- fluid_real_t *buf = allpass->buffer;
+ The minimum value for freeverb correspond to gi = 0.7.
+ with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s
- for(i = 0; i < len; i++)
- {
- buf[i] = DC_OFFSET; /* this is not 100 % correct. */
- }
-}
+ The maximum value for freeverb correspond to gi = 0.98.
+ with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s
+*/
-void
-fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val)
-{
- allpass->feedback = val;
-}
+#define MIN_DC_REV_TIME 0.7f /* minimum T60DC reverb time: seconds */
+#define MAX_DC_REV_TIME 12.5f /* maximumm T60DC time in seconds */
+#define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME)
-fluid_real_t
-fluid_allpass_getfeedback(fluid_allpass *allpass)
-{
- return allpass->feedback;
-}
+/* macro to compute internal reverberation time versus roomsize parameter */
+#define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize)
-#define fluid_allpass_process(_allpass, _input) \
-{ \
- fluid_real_t output; \
- fluid_real_t bufout; \
- bufout = _allpass.buffer[_allpass.bufidx]; \
- output = bufout-_input; \
- _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \
- if (++_allpass.bufidx >= _allpass.bufsize) { \
- _allpass.bufidx = 0; \
- } \
- _input = output; \
-}
+/*-- Modulation related settings ----------------------------------*/
+/* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be:
-struct _fluid_comb
+ MOD_DEPTH: [3..6] (in samples).
+ MOD_FREQ: [0.5 ..2.0] (in Hz).
+
+ Values below the lower limits are often not sufficient to cancel unwanted
+ "ringing"(resonant frequency).
+ Values above upper limits augment the unwanted "chorus".
+
+ With NBR_DELAYS to 8:
+ MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6].
+ With NBR_DELAYS to 12:
+ MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6]
+*/
+#define MOD_DEPTH 4 /* modulation depth (samples)*/
+#define MOD_RATE 50 /* modulation rate (samples)*/
+#define MOD_FREQ 1.0f /* modulation frequency (Hz) */
+/*
+ Number of samples to add to the desired length of a delay line. This
+ allow to take account of modulation interpolation.
+ 1 is sufficient with MOD_DEPTH equal to 6.
+*/
+#define INTERP_SAMPLES_NBR 1
+
+/* phase offset between modulators waveform */
+#define MOD_PHASE (360.0/(float) NBR_DELAYS)
+
+#if (NBR_DELAYS == 8)
+ #define DELAY_L0 601
+ #define DELAY_L1 691
+ #define DELAY_L2 773
+ #define DELAY_L3 839
+ #define DELAY_L4 919
+ #define DELAY_L5 997
+ #define DELAY_L6 1061
+ #define DELAY_L7 1129
+#elif (NBR_DELAYS == 12)
+ #define DELAY_L0 601
+ #define DELAY_L1 691
+ #define DELAY_L2 773
+ #define DELAY_L3 839
+ #define DELAY_L4 919
+ #define DELAY_L5 997
+ #define DELAY_L6 1061
+ #define DELAY_L7 1093
+ #define DELAY_L8 1129
+ #define DELAY_L9 1151
+ #define DELAY_L10 1171
+ #define DELAY_L11 1187
+#endif
+
+/* Delay lines length table (in samples) */
+static const int delay_length[NBR_DELAYS] =
{
- fluid_real_t feedback;
- fluid_real_t filterstore;
- fluid_real_t damp1;
- fluid_real_t damp2;
- fluid_real_t *buffer;
- int bufsize;
- int bufidx;
+ DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3,
+ DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7,
+#if (NBR_DELAYS == 12)
+ DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11
+#endif
};
-void fluid_comb_setbuffer(fluid_comb *comb, int size);
-void fluid_comb_release(fluid_comb *comb);
-void fluid_comb_init(fluid_comb *comb);
-void fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val);
-fluid_real_t fluid_comb_getdamp(fluid_comb *comb);
-void fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val);
-fluid_real_t fluid_comb_getfeedback(fluid_comb *comb);
-void
-fluid_comb_setbuffer(fluid_comb *comb, int size)
-{
- comb->filterstore = 0;
- comb->bufidx = 0;
- comb->buffer = FLUID_ARRAY(fluid_real_t, size);
- comb->bufsize = size;
-}
+/*---------------------------------------------------------------------------*/
+/* The FDN late feed back matrix: A
+ T
+ A = P - 2 / N * u * u
+ N N N N
-void
-fluid_comb_release(fluid_comb *comb)
+ N: the matrix dimension (i.e NBR_DELAYS).
+ P: permutation matrix.
+ u: is a colomn vector of 1.
+
+*/
+#define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS)
+
+/*----------------------------------------------------------------------------
+ Internal FDN late structures and static functions
+-----------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------------
+ Delay absorbent low pass filter
+-----------------------------------------------------------------------------*/
+typedef struct
{
- FLUID_FREE(comb->buffer);
+ fluid_real_t buffer;
+ fluid_real_t b0, a1; /* filter coefficients */
+} fdn_delay_lpf;
+
+/*-----------------------------------------------------------------------------
+ Sets coefficients for delay absorbent low pass filter.
+ @param lpf pointer on low pass filter structure.
+ @param b0,a1 coefficients.
+-----------------------------------------------------------------------------*/
+static void set_fdn_delay_lpf(fdn_delay_lpf *lpf,
+ fluid_real_t b0, fluid_real_t a1)
+{
+ lpf->b0 = b0;
+ lpf->a1 = a1;
}
-void
-fluid_comb_init(fluid_comb *comb)
+/*-----------------------------------------------------------------------------
+ Process delay absorbent low pass filter.
+ @param mod_delay modulated delay line.
+ @param in, input sample.
+ @param out output sample.
+-----------------------------------------------------------------------------*/
+/* process low pass damping filter (input, output, delay) */
+#define process_damping_filter(in,out,mod_delay) \
+{\
+ out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \
+ mod_delay->dl.damping.a1;\
+ mod_delay->dl.damping.buffer = out;\
+}\
+
+
+/*-----------------------------------------------------------------------------
+ Delay line :
+ The delay line is composed of the line plus an absorbent low pass filter
+ to get frequency dependant reverb time.
+-----------------------------------------------------------------------------*/
+typedef struct
+{
+ fluid_real_t *line; /* buffer line */
+ int size; /* effective internal size (in samples) */
+ /*-------------*/
+ int line_in; /* line in position */
+ int line_out; /* line out position */
+ /*-------------*/
+ fdn_delay_lpf damping; /* damping low pass filter */
+} delay_line;
+
+
+/*-----------------------------------------------------------------------------
+ Clears a delay line to DC_OFFSET float value.
+ @param dl pointer on delay line structure
+-----------------------------------------------------------------------------*/
+static void clear_delay_line(delay_line *dl)
{
int i;
- fluid_real_t *buf = comb->buffer;
- int len = comb->bufsize;
- for(i = 0; i < len; i++)
+ for(i = 0; i < dl->size; i++)
{
- buf[i] = DC_OFFSET; /* This is not 100 % correct. */
+ dl->line[i] = DC_OFFSET;
}
}
-void
-fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val)
+/*-----------------------------------------------------------------------------
+ Push a sample val into the delay line
+-----------------------------------------------------------------------------*/
+#define push_in_delay_line(dl, val) \
+{\
+ dl->line[dl->line_in] = val;\
+ /* Incrementation and circular motion if necessary */\
+ if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\
+}\
+
+/*-----------------------------------------------------------------------------
+ Modulator for modulated delay line
+-----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ Sinusoidal modulator
+-----------------------------------------------------------------------------*/
+/* modulator are integrated in modulated delay line */
+typedef struct
{
- comb->damp1 = val;
- comb->damp2 = 1 - val;
-}
-
-fluid_real_t
-fluid_comb_getdamp(fluid_comb *comb)
+ fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */
+ fluid_real_t buffer1; /* buffer1 */
+ fluid_real_t buffer2; /* buffer2 */
+ fluid_real_t reset_buffer2;/* reset value of buffer2 */
+} sinus_modulator;
+
+/*-----------------------------------------------------------------------------
+ Sets the frequency of sinus oscillator.
+
+ @param mod pointer on modulator structure.
+ @param freq frequency of the oscillator in Hz.
+ @param sample_rate sample rate on audio output in Hz.
+ @param phase initial phase of the oscillator in degree (0 to 360).
+-----------------------------------------------------------------------------*/
+static void set_mod_frequency(sinus_modulator *mod,
+ float freq, float sample_rate, float phase)
{
- return comb->damp1;
-}
+ fluid_real_t w = 2 * M_PI * freq / sample_rate; /* intial angle */
-void
-fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val)
-{
- comb->feedback = val;
+ mod->a1 = 2 * cos(w);
+ mod->buffer2 = sin(2 * M_PI * phase / 360 - w); /* y(n-1) = sin(-intial angle) */
+ mod->buffer1 = sin(2 * M_PI * phase / 360); /* y(n) = sin(initial phase) */
+ mod->reset_buffer2 = sin(M_PI / 2.0 - w); /* reset value for PI/2 */
}
-fluid_real_t
-fluid_comb_getfeedback(fluid_comb *comb)
+/*-----------------------------------------------------------------------------
+ Gets current value of sinus modulator:
+ y(n) = a1 . y(n-1) - y(n-2)
+ out = a1 . buffer1 - buffer2
+
+ @param pointer on modulator structure.
+ @return current value of the modulator sine wave.
+-----------------------------------------------------------------------------*/
+static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod)
{
- return comb->feedback;
-}
+ fluid_real_t out;
+ out = mod->a1 * mod->buffer1 - mod->buffer2;
+ mod->buffer2 = mod->buffer1;
-#define fluid_comb_process(_comb, _input, _output) \
-{ \
- fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \
- _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \
- _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \
- if (++_comb.bufidx >= _comb.bufsize) { \
- _comb.bufidx = 0; \
- } \
- _output += _tmp; \
-}
+ if(out >= 1.0) /* reset in case of instability near PI/2 */
+ {
+ out = 1.0; /* forces output to the right value */
+ mod->buffer2 = mod->reset_buffer2;
+ }
-#define numcombs 8
-#define numallpasses 4
-#define fixedgain 0.015f
-/* scale_wet_width is a compensation weight factor to get an output
- amplitude (wet) rather independent of the width setting.
- 0: the output amplitude is fully dependant on the width setting.
- >0: the output amplitude is less dependant on the width setting.
- With a scale_wet_width of 0.2 the output amplitude is rather
- independent of width setting (see fluid_revmodel_update()).
- */
-#define scale_wet_width 0.2f
-#define scalewet 3.0f
-#define scaledamp 1.0f
-#define scaleroom 0.28f
-#define offsetroom 0.7f
-#define stereospread 23
+ if(out <= -1.0) /* reset in case of instability near -PI/2 */
+ {
+ out = -1.0; /* forces output to the right value */
+ mod->buffer2 = - mod->reset_buffer2;
+ }
-/*
- These values assume 44.1KHz sample rate
- they will probably be OK for 48KHz sample rate
- but would need scaling for 96KHz (or other) sample rates.
- The values were obtained by listening tests.
-*/
-#define combtuningL1 1116
-#define combtuningR1 (1116 + stereospread)
-#define combtuningL2 1188
-#define combtuningR2 (1188 + stereospread)
-#define combtuningL3 1277
-#define combtuningR3 (1277 + stereospread)
-#define combtuningL4 1356
-#define combtuningR4 (1356 + stereospread)
-#define combtuningL5 1422
-#define combtuningR5 (1422 + stereospread)
-#define combtuningL6 1491
-#define combtuningR6 (1491 + stereospread)
-#define combtuningL7 1557
-#define combtuningR7 (1557 + stereospread)
-#define combtuningL8 1617
-#define combtuningR8 (1617 + stereospread)
-#define allpasstuningL1 556
-#define allpasstuningR1 (556 + stereospread)
-#define allpasstuningL2 441
-#define allpasstuningR2 (441 + stereospread)
-#define allpasstuningL3 341
-#define allpasstuningR3 (341 + stereospread)
-#define allpasstuningL4 225
-#define allpasstuningR4 (225 + stereospread)
+ mod->buffer1 = out;
+ return out;
+}
-struct _fluid_revmodel_t
+/*-----------------------------------------------------------------------------
+ Modulated delay line. The line is composed of:
+ - the delay line with its damping low pass filter.
+ - the sinusoidal modulator.
+ - center output position modulated by the modulator.
+ - variable rate control of center output position.
+ - first order All-Pass interpolator.
+-----------------------------------------------------------------------------*/
+typedef struct
{
- fluid_real_t roomsize;
- fluid_real_t damp;
- fluid_real_t level, wet1, wet2;
- fluid_real_t width;
- fluid_real_t gain;
- /*
- The following are all declared inline
- to remove the need for dynamic allocation
- with its subsequent error-checking messiness
- */
- /* Comb filters */
- fluid_comb combL[numcombs];
- fluid_comb combR[numcombs];
- /* Allpass filters */
- fluid_allpass allpassL[numallpasses];
- fluid_allpass allpassR[numallpasses];
-};
-
-static void fluid_revmodel_update(fluid_revmodel_t *rev);
-static void fluid_revmodel_init(fluid_revmodel_t *rev);
-void fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate);
-
-fluid_revmodel_t *
-new_fluid_revmodel(fluid_real_t sample_rate)
+ /* delay line with damping low pass filter member */
+ delay_line dl; /* delayed line */
+ /*---------------------------*/
+ /* Sinusoidal modulator member */
+ sinus_modulator mod; /* sinus modulator */
+ /*-------------------------*/
+ /* center output position members */
+ fluid_real_t center_pos_mod; /* center output position modulated by modulator */
+ int mod_depth; /* modulation depth (in samples) */
+ /*-------------------------*/
+ /* variable rate control of center output position */
+ int index_rate; /* index rate to know when to update center_pos_mod */
+ int mod_rate; /* rate at which center_pos_mod is updated */
+ /*-------------------------*/
+ /* first order All-Pass interpolator members */
+ fluid_real_t frac_pos_mod; /* fractional position part between samples) */
+ /* previous value used when interpolating using fractional */
+ fluid_real_t buffer;
+} mod_delay_line;
+
+
+/*-----------------------------------------------------------------------------
+ Modulated delay line initialization.
+
+ Sets the length line ( alloc delay samples).
+ Remark: the function sets the internal size accordling to the length delay_length.
+ As the delay line is a modulated line, its internal size is augmented by mod_depth.
+ The size is also augmented by INTERP_SAMPLES_NBR to take account of interpolation.
+
+ @param mdl, pointer on modulated delay line.
+ @param delay_length the length of the delay line in samples.
+ @param mod_depth depth of the modulation in samples (amplitude of the sine wave).
+ @param mod_rate the rate of the modulation in samples.
+ @return FLUID_OK if success , FLUID_FAILED if memory error.
+
+ Return FLUID_OK if success, FLUID_FAILED if memory error.
+-----------------------------------------------------------------------------*/
+static int set_mod_delay_line(mod_delay_line *mdl,
+ int delay_length,
+ int mod_depth,
+ int mod_rate
+ )
{
- fluid_revmodel_t *rev;
- rev = FLUID_NEW(fluid_revmodel_t);
+ /*-----------------------------------------------------------------------*/
+ /* checks parameter */
+ if(delay_length < 1)
+ {
+ return FLUID_FAILED;
+ }
- if(rev == NULL)
+ /* limits mod_depth to the requested delay length */
+ if(mod_depth >= delay_length)
{
- return NULL;
+ FLUID_LOG(FLUID_INFO,
+ "fdn reverb: modulation depth has been limited");
+ mod_depth = delay_length - 1;
}
- fluid_set_revmodel_buffers(rev, sample_rate);
+ mdl->mod_depth = mod_depth;
+ /*-----------------------------------------------------------------------
+ allocates delay_line and initialize members:
+ - line, size, line_in, line_out...
+ */
+ {
+ /* total size of the line:
+ size = INTERP_SAMPLES_NBR + mod_depth + delay_length */
+ mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR;
+ mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size);
- /* Set default values */
- fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f);
- fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f);
+ if(! mdl->dl.line)
+ {
+ return FLUID_FAILED;
+ }
- rev->gain = fixedgain;
+ clear_delay_line(&mdl->dl); /* clears the buffer */
- return rev;
+ /* Initializes line_in to the start of the buffer */
+ mdl->dl.line_in = 0;
+ /* Initializes line_out index INTERP_SAMPLES_NBR samples after line_in */
+ /* so that the delay between line_out and line_in is:
+ mod_depth + delay_length */
+ mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR;
+ }
+
+ /* Damping low pass filter -------------------*/
+ mdl->dl.damping.buffer = 0;
+ /*------------------------------------------------------------------------
+ Initializes modulation members:
+ - modulated center position: center_pos_mod
+ - index rate to know when to update center_pos_mod:index_rate
+ - modulation rate (the speed at which center_pos_mod is modulated: mod_rate
+ - interpolator member: buffer, frac_pos_mod
+ -------------------------------------------------------------------------*/
+ /* Sets the modulation rate. This rate defines how often
+ the center position (center_pos_mod ) is modulated .
+ The value is expressed in samples. The default value is 1 that means that
+ center_pos_mod is updated at every sample.
+ For example with a value of 2, the center position position will be
+ updated only one time every 2 samples only.
+ */
+ mdl->mod_rate = 1; /* default modulation rate: every one sample */
+
+ if(mod_rate > mdl->dl.size)
+ {
+ FLUID_LOG(FLUID_INFO,
+ "fdn reverb: modulation rate is out of range");
+ }
+ else
+ {
+ mdl->mod_rate = mod_rate;
+ }
+
+ /* Initializes the modulated center position (center_pos_mod) so that:
+ - the delay between line_out and center_pos_mod is mod_depth.
+ - the delay between center_pos_mod and line_in is delay_length.
+ */
+ mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth;
+
+ /* index rate to control when to update center_pos_mod */
+ /* Important: must be set to get center_pos_mod immediatly used for the
+ reading of first sample (see get_mod_delay()) */
+ mdl->index_rate = mdl->mod_rate;
+
+ /* initializes 1st order All-Pass interpolator members */
+ mdl->buffer = 0; /* previous delay sample value */
+ mdl->frac_pos_mod = 0; /* fractional position (between consecutives sample) */
+ return FLUID_OK;
}
-void
-delete_fluid_revmodel(fluid_revmodel_t *rev)
+/*-----------------------------------------------------------------------------
+ Reads the sample value out of the modulated delay line.
+ @param mdl, pointer on modulated delay line.
+ @return the sample value.
+-----------------------------------------------------------------------------*/
+static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl)
{
- int i;
- fluid_return_if_fail(rev != NULL);
+ fluid_real_t out_index; /* new modulated index position */
+ int int_out_index; /* integer part of out_index */
+ fluid_real_t out; /* value to return */
+
+ /* Checks if the modulator must be updated (every mod_rate samples). */
+ /* Important: center_pos_mod must be used immediatly for the
+ first sample. So, mdl->index_rate must be initialized
+ to mdl->mod_rate (set_mod_delay_line()) */
- for(i = 0; i < numcombs; i++)
+ if(++mdl->index_rate >= mdl->mod_rate)
{
- fluid_comb_release(&rev->combL[i]);
- fluid_comb_release(&rev->combR[i]);
+ mdl->index_rate = 0;
+
+ /* out_index = center position (center_pos_mod) + sinus waweform */
+ out_index = mdl->center_pos_mod +
+ get_mod_sinus(&mdl->mod) * mdl->mod_depth;
+
+ /* extracts integer part in int_out_index */
+ if(out_index >= 0.0f)
+ {
+ int_out_index = (int)out_index; /* current integer part */
+
+ /* forces read index (line_out) with integer modulation value */
+ /* Boundary check and circular motion as needed */
+ if((mdl->dl.line_out = int_out_index) >= mdl->dl.size)
+ {
+ mdl->dl.line_out -= mdl->dl.size;
+ }
+ }
+ else /* negative */
+ {
+ int_out_index = (int)(out_index - 1); /* previous integer part */
+ /* forces read index (line_out) with integer modulation value */
+ /* circular motion as needed */
+ mdl->dl.line_out = int_out_index + mdl->dl.size;
+ }
+
+ /* extracts fractionnal part. (it will be used when interpolating
+ between line_out and line_out +1) and memorize it.
+ Memorizing is necessary for modulation rate above 1 */
+ mdl->frac_pos_mod = out_index - int_out_index;
+
+ /* updates center position (center_pos_mod) to the next position
+ specified by modulation rate */
+ if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size)
+ {
+ mdl->center_pos_mod -= mdl->dl.size;
+ }
}
- for(i = 0; i < numallpasses; i++)
+ /* First order all-pass interpolation ----------------------------------*/
+ /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */
+ /* begins interpolation: read current sample */
+ out = mdl->dl.line[mdl->dl.line_out];
+
+ /* updates line_out to the next sample.
+ Boundary check and circular motion as needed */
+ if(++mdl->dl.line_out >= mdl->dl.size)
{
- fluid_allpass_release(&rev->allpassL[i]);
- fluid_allpass_release(&rev->allpassR[i]);
+ mdl->dl.line_out -= mdl->dl.size;
}
- FLUID_FREE(rev);
+ /* Fractional interpolation beetween next sample (at next position) and
+ previous output added to current sample.
+ */
+ out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer);
+ mdl->buffer = out; /* memorizes current output */
+ return out;
}
-void
-fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate)
+/*-----------------------------------------------------------------------------
+ Late structure
+-----------------------------------------------------------------------------*/
+struct _fluid_late
{
+ fluid_real_t samplerate; /* sample rate */
+ /*----- High pass tone corrector -------------------------------------*/
+ fluid_real_t tone_buffer;
+ fluid_real_t b1, b2;
+ /*----- Modulated delay lines lines ----------------------------------*/
+ mod_delay_line mod_delay_lines[NBR_DELAYS];
+ /*-----------------------------------------------------------------------*/
+ /* Output coefficients for separate Left and right stereo outputs */
+ fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */
+ fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/
+};
- float srfactor = sample_rate / 44100.0f;
-
- fluid_comb_setbuffer(&rev->combL[0], combtuningL1 * srfactor);
- fluid_comb_setbuffer(&rev->combR[0], combtuningR1 * srfactor);
- fluid_comb_setbuffer(&rev->combL[1], combtuningL2 * srfactor);
- fluid_comb_setbuffer(&rev->combR[1], combtuningR2 * srfactor);
- fluid_comb_setbuffer(&rev->combL[2], combtuningL3 * srfactor);
- fluid_comb_setbuffer(&rev->combR[2], combtuningR3 * srfactor);
- fluid_comb_setbuffer(&rev->combL[3], combtuningL4 * srfactor);
- fluid_comb_setbuffer(&rev->combR[3], combtuningR4 * srfactor);
- fluid_comb_setbuffer(&rev->combL[4], combtuningL5 * srfactor);
- fluid_comb_setbuffer(&rev->combR[4], combtuningR5 * srfactor);
- fluid_comb_setbuffer(&rev->combL[5], combtuningL6 * srfactor);
- fluid_comb_setbuffer(&rev->combR[5], combtuningR6 * srfactor);
- fluid_comb_setbuffer(&rev->combL[6], combtuningL7 * srfactor);
- fluid_comb_setbuffer(&rev->combR[6], combtuningR7 * srfactor);
- fluid_comb_setbuffer(&rev->combL[7], combtuningL8 * srfactor);
- fluid_comb_setbuffer(&rev->combR[7], combtuningR8 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4 * srfactor);
- fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4 * srfactor);
-
- /* Clear all buffers */
- fluid_revmodel_init(rev);
-}
+typedef struct _fluid_late fluid_late;
+/*-----------------------------------------------------------------------------
+ fluidsynth reverb structure
+-----------------------------------------------------------------------------*/
+struct _fluid_revmodel_t
+{
+ /* reverb parameters */
+ fluid_real_t roomsize; /* acting on reverb time */
+ fluid_real_t damp; /* acting on frequency dependent reverb time */
+ fluid_real_t level, wet1, wet2; /* output level */
+ fluid_real_t width; /* width stereo separation */
+
+ /* fdn reverberation structure */
+ fluid_late late;
+};
+/*-----------------------------------------------------------------------------
+ Updates Reverb time and absorbent filters coefficients from parameters:
-static void
-fluid_revmodel_init(fluid_revmodel_t *rev)
+ @param late pointer on late structure.
+ @param roomsize (0 to 1): acting on reverb time.
+ @param damping (0 to 1): acting on absorbent damping filter.
+
+ Design formulas:
+ https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html
+ https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html
+-----------------------------------------------------------------------------*/
+static void update_rev_time_damping(fluid_late *late,
+ fluid_real_t roomsize, fluid_real_t damp)
{
int i;
+ fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */
+ fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */
+
+ fluid_real_t alpha;
- for(i = 0; i < numcombs; i++)
+ /*--------------------------------------------
+ Computes dc_rev_time and alpha
+ ----------------------------------------------*/
{
- fluid_comb_init(&rev->combL[i]);
- fluid_comb_init(&rev->combR[i]);
+ fluid_real_t gi_tmp, ai_tmp;
+#ifdef ROOMSIZE_RESPONSE_LINEAR
+ /* roomsize parameter behave linearly:
+ * - roomsize (0 to 1) controls reverb time linearly (0.7 to 10 s).
+ * This linear response is convenient when using GUI controls.
+ */
+ /*-----------------------------------------
+ Computes dc_rev_time
+ ------------------------------------------*/
+ dc_rev_time = GET_DC_REV_TIME(roomsize);
+ /* computes gi_tmp from dc_rev_time using relation E2 */
+ gi_tmp = (fluid_real_t) pow(10, -3 * delay_length[NBR_DELAYS - 1] *
+ sample_period / dc_rev_time); /* E2 */
+#else
+ /* roomsize parameters have the same response that Freeverb, that is:
+ * - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s).
+ */
+ {
+ /*-----------------------------------------
+ Computes dc_rev_time
+ ------------------------------------------*/
+ fluid_real_t gi_min, gi_max;
+ /* values gi_min et gi_max are computed using E2 for the line with
+ maximum delay */
+ gi_max = (fluid_real_t)pow(10, -3 * delay_length[NBR_DELAYS - 1] *
+ sample_period / MAX_DC_REV_TIME); /* E2 */
+ gi_min = (fluid_real_t)pow(10, -3 * delay_length[NBR_DELAYS - 1] *
+ sample_period / MIN_DC_REV_TIME); /* E2 */
+ /* gi = f(roomsize, gi_max, gi_min) */
+ gi_tmp = gi_min + roomsize * (gi_max - gi_min);
+ /* Computes T60DC from gi using inverse of relation E2.*/
+ dc_rev_time = -3 * delay_length[NBR_DELAYS - 1] * sample_period / log10(gi_tmp);
+ }
+#endif /* ROOMSIZE_RESPONSE_LINEAR */
+ /*--------------------------------------------
+ Computes alpha
+ ----------------------------------------------*/
+ /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */
+ /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */
+ ai_tmp = 1.0 * damp;
+ alpha = sqrt(1 / (1 - ai_tmp / (20 * log10(gi_tmp) * log(10) / 80))); /* R */
}
- for(i = 0; i < numallpasses; i++)
+ /* updates tone corrector coefficients b1,b2 from alpha */
{
- fluid_allpass_init(&rev->allpassL[i]);
- fluid_allpass_init(&rev->allpassR[i]);
+ /*
+ Beta = (1 - alpha) / (1 + alpha)
+ b1 = 1/(1-beta)
+ b2 = beta * b1
+ */
+ fluid_real_t beta = (1 - alpha) / (1 + alpha);
+ late->b1 = 1 / (1 - beta);
+ late->b2 = beta * late->b1;
+ late->tone_buffer = 0.0f;
}
-}
-void
-fluid_revmodel_reset(fluid_revmodel_t *rev)
-{
- fluid_revmodel_init(rev);
+ /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */
+ for(i = 0; i < NBR_DELAYS; i++)
+ {
+ /* iir low pass filter gain */
+ fluid_real_t gi = (fluid_real_t)pow(10, -3 * delay_length[i] *
+ sample_period / dc_rev_time);
+
+ /* iir low pass filter feedback gain */
+ fluid_real_t ai = (fluid_real_t)(20 * log10(gi) * log(10) / 80 *
+ (1 - 1 / pow(alpha, 2)));
+ /* b0 = gi * (1 - ai), a1 = - ai */
+ set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping,
+ gi * (1 - ai), -ai);
+ }
}
-void
-fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in,
- fluid_real_t *left_out, fluid_real_t *right_out)
+/*-----------------------------------------------------------------------------
+ Updates stereo coefficients
+ @param late pointer on late structure
+ @param wet level integrated in stereo coefficients.
+-----------------------------------------------------------------------------*/
+static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1)
{
- int i, k = 0;
- fluid_real_t outL, outR, input;
+ int i;
- for(k = 0; k < FLUID_BUFSIZE; k++)
+ for(i = 0; i < NBR_DELAYS; i++)
{
+ /* delay lines output gains vectors Left and Right
+
+ L R
+ 0 | 1 1|
+ 1 |-1 1|
+ 2 | 1 -1|
+ 3 |-1 -1|
+
+ 4 | 1 1|
+ 5 |-1 1|
+ stereo gain = 6 | 1 -1|
+ 7 |-1 -1|
- outL = outR = 0;
+ 8 | 1 1|
+ 9 |-1 1|
+ 10| 1 -1|
+ 11|-1 -1|
+ */
- /* The original Freeverb code expects a stereo signal and 'input'
- * is set to the sum of the left and right input sample. Since
- * this code works on a mono signal, 'input' is set to twice the
- * input sample. */
- input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
+ late->out_left_gain[i] = wet1;
- /* Accumulate comb filters in parallel */
- for(i = 0; i < numcombs; i++)
+ /* Sets Left coefficients first */
+ if(i % 2) /* Left is 1,-1, 1,-1, 1,-1,.... */
{
- fluid_comb_process(rev->combL[i], input, outL);
- fluid_comb_process(rev->combR[i], input, outR);
+ late->out_left_gain[i] *= -1;
}
- /* Feed through allpasses in series */
- for(i = 0; i < numallpasses; i++)
+ /* Now sets right gain as function of Left */
+ /* for right line 1,2,5,6,9,10,13,14, right = - left */
+ if((i == 1) || (i == 2) || (i == 5) || (i == 6) || (i == 9) || (i == 10) || (i == 13) || (i == 14))
{
- fluid_allpass_process(rev->allpassL[i], outL);
- fluid_allpass_process(rev->allpassR[i], outR);
+ /* Right is reverse of Left */
+ late->out_right_gain[i] = -1 * late->out_left_gain[i];
}
+ else /* for Right : line 0,3,4,7,8,11,12,15 */
+ {
+ /* Right is same as Left */
+ late->out_right_gain[i] = late->out_left_gain[i];
+ }
+ }
+}
- /* Remove the DC offset */
- outL -= DC_OFFSET;
- outR -= DC_OFFSET;
+/*-----------------------------------------------------------------------------
+ fluid_late destructor.
+ @param late pointer on late structure.
+-----------------------------------------------------------------------------*/
+static void delete_fluid_rev_late(fluid_late *late)
+{
+ int i;
+ fluid_return_if_fail(late != NULL);
- /* Calculate output REPLACING anything already there */
- left_out[k] = outL * rev->wet1 + outR * rev->wet2;
- right_out[k] = outR * rev->wet1 + outL * rev->wet2;
+ /* free the delay lines */
+ for(i = 0; i < NBR_DELAYS; i++)
+ {
+ FLUID_FREE(late->mod_delay_lines[i].dl.line);
}
}
-void
-fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
- fluid_real_t *left_out, fluid_real_t *right_out)
+/*-----------------------------------------------------------------------------
+ Creates the fdn reverb.
+ @param late, pointer on the fnd late reverb to initialize.
+ @param sample_rate the sample rate.
+ @return FLUID_OK if success, FLUID_FAILED otherwise.
+-----------------------------------------------------------------------------*/
+static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate)
{
- int i, k = 0;
- fluid_real_t outL, outR, input;
+ int result; /* return value */
+ int i;
- for(k = 0; k < FLUID_BUFSIZE; k++)
- {
+ FLUID_MEMSET(late, 0, sizeof(fluid_late));
- outL = outR = 0;
+ late->samplerate = sample_rate;
- /* The original Freeverb code expects a stereo signal and 'input'
- * is set to the sum of the left and right input sample. Since
- * this code works on a mono signal, 'input' is set to twice the
- * input sample. */
- input = (2.0f * in[k] + DC_OFFSET) * rev->gain;
+ /*--------------------------------------------------------------------------
+ First initialize the modulated delay lines
+ */
- /* Accumulate comb filters in parallel */
- for(i = 0; i < numcombs; i++)
- {
- fluid_comb_process(rev->combL[i], input, outL);
- fluid_comb_process(rev->combR[i], input, outR);
- }
+ for(i = 0; i < NBR_DELAYS; i++)
+ {
+ /* sets local delay lines's parameters */
+ result = set_mod_delay_line(&late->mod_delay_lines[i],
+ delay_length[i], MOD_DEPTH, MOD_RATE);
- /* Feed through allpasses in series */
- for(i = 0; i < numallpasses; i++)
+ if(result == FLUID_FAILED)
{
- fluid_allpass_process(rev->allpassL[i], outL);
- fluid_allpass_process(rev->allpassR[i], outR);
+ return FLUID_FAILED;
}
- /* Remove the DC offset */
- outL -= DC_OFFSET;
- outR -= DC_OFFSET;
+ /* Sets local Modulators parameters: frequency and phase
+ Each modulateur are shifted of MOD_PHASE degree
+ */
+ set_mod_frequency(&late->mod_delay_lines[i].mod,
+ MOD_FREQ * MOD_RATE,
+ sample_rate,
+ (float)(MOD_PHASE * i));
+ }
+
+ /*-----------------------------------------------------------------------*/
+ update_stereo_coefficient(late, 1.0f);
+ return FLUID_OK;
+}
+
+/*
+ Clears the delay lines.
+
+ @param rev pointer on the reverb.
+*/
+static void
+fluid_revmodel_init(fluid_revmodel_t *rev)
+{
+ int i;
- /* Calculate output MIXING with anything already there */
- left_out[k] += outL * rev->wet1 + outR * rev->wet2;
- right_out[k] += outR * rev->wet1 + outL * rev->wet2;
+ /* clears all the delay lines */
+ for(i = 0; i < NBR_DELAYS; i ++)
+ {
+ clear_delay_line(&rev->late.mod_delay_lines[i].dl);
}
}
+
+/*
+ updates internal parameters.
+
+ @param rev pointer on the reverb.
+*/
static void
fluid_revmodel_update(fluid_revmodel_t *rev)
{
- /* Recalculate internal values after parameter change */
- int i;
+ /* Recalculate internal values after parameters change */
/* The stereo amplitude equation (wet1 and wet2 below) have a
tendency to produce high amplitude with high width values ( 1 < width < 100).
This results in an unwanted noisy output clipped by the audio card.
- To avoid this dependency, we divide by (1 + rev->width * scale_wet_width)
- Actually, with a scale_wet_width of 0.2, (regardless of level setting),
+ To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH)
+ Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting),
the output amplitude (wet) seems rather independent of width setting */
- fluid_real_t wet = (rev->level * scalewet) /
- (1.0f + rev->width * scale_wet_width);
+ fluid_real_t wet = (rev->level * SCALE_WET) /
+ (1.0f + rev->width * SCALE_WET_WIDTH);
/* wet1 and wet2 are used by the stereo effect controled by the width setting
for producing a stereo ouptput from a monophonic reverb signal.
Please see the note above about a side effect tendency */
+
rev->wet1 = wet * (rev->width / 2.0f + 0.5f);
rev->wet2 = wet * ((1.0f - rev->width) / 2.0f);
- for(i = 0; i < numcombs; i++)
+ /* integrates wet1 in stereo coefficient (this will save one multiply) */
+ update_stereo_coefficient(&rev->late, rev->wet1);
+
+ if(rev->wet1 > 0.0)
{
- fluid_comb_setfeedback(&rev->combL[i], rev->roomsize);
- fluid_comb_setfeedback(&rev->combR[i], rev->roomsize);
+ rev->wet2 /= rev->wet1;
}
- for(i = 0; i < numcombs; i++)
+ /* Reverberation time and damping */
+ update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
+}
+
+/*----------------------------------------------------------------------------
+ Reverb API
+-----------------------------------------------------------------------------*/
+
+/*
+* Creates a reverb.
+* @param sample_rate sample rate in Hz.
+* @return pointer on the new reverb or NULL if memory error.
+* Reverb API.
+*/
+fluid_revmodel_t *
+new_fluid_revmodel(fluid_real_t sample_rate)
+{
+ fluid_revmodel_t *rev;
+ rev = FLUID_NEW(fluid_revmodel_t);
+
+ if(rev == NULL)
+ {
+ return NULL;
+ }
+
+ /* create fdn reverb */
+ if(create_fluid_rev_late(&rev->late, sample_rate) != FLUID_OK)
{
- fluid_comb_setdamp(&rev->combL[i], rev->damp);
- fluid_comb_setdamp(&rev->combR[i], rev->damp);
+ delete_fluid_revmodel(rev);
+ return NULL;
}
+
+ return rev;
}
-/**
- * Set one or more reverb parameters.
- * @param rev Reverb instance
- * @param set One or more flags from #fluid_revmodel_set_t indicating what
- * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters)
- * @param roomsize Reverb room size
- * @param damping Reverb damping
- * @param width Reverb width
- * @param level Reverb level
- */
+/*
+* free the reverb.
+* @param pointer on rev to free.
+* Reverb API.
+*/
+void
+delete_fluid_revmodel(fluid_revmodel_t *rev)
+{
+ fluid_return_if_fail(rev != NULL);
+ delete_fluid_rev_late(&rev->late);
+ FLUID_FREE(rev);
+}
+
+/*
+* Sets one or more reverb parameters.
+* @param rev Reverb instance.
+* @param set One or more flags from #fluid_revmodel_set_t indicating what
+* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters).
+* @param roomsize Reverb room size.
+* @param damping Reverb damping.
+* @param width Reverb width.
+* @param level Reverb level.
+*
+* Reverb API.
+*/
void
fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
fluid_real_t damping, fluid_real_t width, fluid_real_t level)
{
+ /*-----------------------------------*/
if(set & FLUID_REVMODEL_SET_ROOMSIZE)
{
- /* With upper limit above 1.07, the output amplitude will grow
- exponentially. So, keeping this upper limit to 1.0 seems sufficient
- as it produces yet a long reverb time */
fluid_clip(roomsize, 0.0f, 1.0f);
- rev->roomsize = (roomsize * scaleroom) + offsetroom;
+ rev->roomsize = roomsize;
}
+ /*-----------------------------------*/
if(set & FLUID_REVMODEL_SET_DAMPING)
{
- rev->damp = damping * scaledamp;
+ fluid_clip(damping, 0.0f, 1.0f);
+ rev->damp = damping;
}
+ /*-----------------------------------*/
if(set & FLUID_REVMODEL_SET_WIDTH)
{
rev->width = width;
}
+ /*-----------------------------------*/
if(set & FLUID_REVMODEL_SET_LEVEL)
{
fluid_clip(level, 0.0f, 1.0f);
rev->level = level;
}
+ /* updates internal parameters */
fluid_revmodel_update(rev);
}
+/*
+* Applies a sample rate change on the reverb.
+* @param rev the reverb.
+* @param sample_rate new sample rate value.
+*
+* Reverb API.
+*/
void
fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
{
int i;
- for(i = 0; i < numcombs; i++)
+ /* updates modulator frequency according to sample rate change */
+ for(i = 0; i < NBR_DELAYS; i++)
{
- fluid_comb_release(&rev->combL[i]);
- fluid_comb_release(&rev->combR[i]);
+ set_mod_frequency(&rev->late.mod_delay_lines[i].mod,
+ MOD_FREQ * MOD_RATE,
+ sample_rate,
+ (float)(MOD_PHASE * i));
}
- for(i = 0; i < numallpasses; i++)
+ /* updates damping filter coefficients according to sample rate change */
+ rev->late.samplerate = sample_rate;
+ update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
+
+ /* clears all delay lines */
+ fluid_revmodel_init(rev);
+}
+
+/*
+* Damps the reverb by clearing the delay lines.
+* @param rev the reverb.
+*
+* Reverb API.
+*/
+void
+fluid_revmodel_reset(fluid_revmodel_t *rev)
+{
+ fluid_revmodel_init(rev);
+}
+
+/*-----------------------------------------------------------------------------
+* fdn reverb process replace.
+* @param rev pointer on reverb.
+* @param in monophonic buffer input (FLUID_BUFSIZE sample).
+* @param left_out stereo left processed output (FLUID_BUFSIZE sample).
+* @param right_out stereo right processed output (FLUID_BUFSIZE sample).
+*
+* The processed reverb is replacing anything there in out.
+* Reverb API.
+-----------------------------------------------------------------------------*/
+void
+fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k;
+
+ fluid_real_t xn; /* mono input x(n) */
+ fluid_real_t out_tone_filter; /* tone corrector output */
+ fluid_real_t out_left, out_right; /* output stereo Left and Right */
+ fluid_real_t matrix_factor; /* partial matrix computation */
+ fluid_real_t delay_out_s; /* sample */
+ fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */
+
+ for(k = 0; k < FLUID_BUFSIZE; k++)
{
- fluid_allpass_release(&rev->allpassL[i]);
- fluid_allpass_release(&rev->allpassR[i]);
+ /* stereo output */
+ out_left = out_right = 0;
+
+#ifdef DENORMALISING
+ /* Input is adjusted by DC_OFFSET. */
+ xn = (in[k]) * FIXED_GAIN + DC_OFFSET;
+#else
+ xn = (in[k]) * FIXED_GAIN;
+#endif
+
+ /*--------------------------------------------------------------------
+ tone correction.
+ */
+ out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer;
+ rev->late.tone_buffer = xn;
+ xn = out_tone_filter;
+ /*--------------------------------------------------------------------
+ process feedback delayed network:
+ - xn is the input signal.
+ - before inserting in the line input we first we get the delay lines
+ output, filter them and compute output in delay_out[].
+ - also matrix_factor is computed (to simplify further matrix product)
+ ---------------------------------------------------------------------*/
+ /* We begin with the modulated output delay line + damping filter */
+ matrix_factor = 0;
+
+ for(i = 0; i < NBR_DELAYS; i++)
+ {
+ mod_delay_line *mdl = &rev->late.mod_delay_lines[i];
+ /* get current modulated output */
+ delay_out_s = get_mod_delay(mdl);
+
+ /* process low pass damping filter
+ (input:delay_out_s, output:delay_out_s) */
+ process_damping_filter(delay_out_s, delay_out_s, mdl);
+
+ /* Result in delay_out[], and matrix_factor.
+ These wil be use later during input line process */
+ delay_out[i] = delay_out_s; /* result in delay_out[] */
+ matrix_factor += delay_out_s; /* result in matrix_factor */
+
+ /* Process stereo output */
+ /* stereo left = left + out_left_gain * delay_out */
+ out_left += rev->late.out_left_gain[i] * delay_out_s;
+ /* stereo right= right+ out_right_gain * delay_out */
+ out_right += rev->late.out_right_gain[i] * delay_out_s;
+ }
+
+ /* now we process the input delay line.Each input is a combination of
+ - xn: input signal
+ - delay_out[] the output of a delay line given by a permutation matrix P
+ - and matrix_factor.
+ This computes: in_delay_line = xn + (delay_out[] * matrix A) with
+ an algorithm equivalent but faster than using a product with matrix A.
+ */
+ /* matrix_factor = output sum * (-2.0)/N */
+ matrix_factor *= FDN_MATRIX_FACTOR;
+ matrix_factor += xn; /* adds reverb input signal */
+
+ for(i = 1; i < NBR_DELAYS; i++)
+ {
+ /* delay_in[i-1] = delay_out[i] + matrix_factor */
+ delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl;
+ push_in_delay_line(dl, delay_out[i] + matrix_factor);
+ }
+
+ /* last line input (NB_DELAY-1) */
+ /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */
+ {
+ delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl;
+ push_in_delay_line(dl, delay_out[0] + matrix_factor);
+ }
+
+ /*-------------------------------------------------------------------*/
+#ifdef DENORMALISING
+ /* Removes the DC offset */
+ out_left -= DC_OFFSET;
+ out_right -= DC_OFFSET;
+#endif
+
+ /* Calculates stereo output REPLACING anything already there: */
+ /*
+ left_out[k] = out_left * rev->wet1 + out_right * rev->wet2;
+ right_out[k] = out_right * rev->wet1 + out_left * rev->wet2;
+
+ As wet1 is integrated in stereo coefficient wet 1 is now
+ integrated in out_left and out_right we simplify previous
+ relation by suppression of one multiply as this:
+
+ left_out[k] = out_left + out_right * rev->wet2;
+ right_out[k] = out_right + out_left * rev->wet2;
+ */
+ left_out[k] = out_left + out_right * rev->wet2;
+ right_out[k] = out_right + out_left * rev->wet2;
}
+}
+
+
+/*-----------------------------------------------------------------------------
+* fdn reverb process mix.
+* @param rev pointer on reverb.
+* @param in monophonic buffer input (FLUID_BUFSIZE samples).
+* @param left_out stereo left processed output (FLUID_BUFSIZE samples).
+* @param right_out stereo right processed output (FLUID_BUFSIZE samples).
+*
+* The processed reverb is mixed in out with samples already there in out.
+* Reverb API.
+-----------------------------------------------------------------------------*/
+void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
+ fluid_real_t *left_out, fluid_real_t *right_out)
+{
+ int i, k;
+
+ fluid_real_t xn; /* mono input x(n) */
+ fluid_real_t out_tone_filter; /* tone corrector output */
+ fluid_real_t out_left, out_right; /* output stereo Left and Right */
+ fluid_real_t matrix_factor; /* partial matrix term */
+ fluid_real_t delay_out_s; /* sample */
+ fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */
+
+ for(k = 0; k < FLUID_BUFSIZE; k++)
+ {
+ /* stereo output */
+ out_left = out_right = 0;
+#ifdef DENORMALISING
+ /* Input is adjusted by DC_OFFSET. */
+ xn = (in[k]) * FIXED_GAIN + DC_OFFSET;
+#else
+ xn = (in[k]) * FIXED_GAIN;
+#endif
+
+ /*--------------------------------------------------------------------
+ tone correction
+ */
+ out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer;
+ rev->late.tone_buffer = xn;
+ xn = out_tone_filter;
+ /*--------------------------------------------------------------------
+ process feedback delayed network:
+ - xn is the input signal.
+ - before inserting in the line input we first we get the delay lines
+ output, filter them and compute output in local delay_out[].
+ - also matrix_factor is computed (to simplify further matrix product).
+ ---------------------------------------------------------------------*/
+ /* We begin with the modulated output delay line + damping filter */
+ matrix_factor = 0;
+
+ for(i = 0; i < NBR_DELAYS; i++)
+ {
+ mod_delay_line *mdl = &rev->late.mod_delay_lines[i];
+ /* get current modulated output */
+ delay_out_s = get_mod_delay(mdl);
+
+ /* process low pass damping filter
+ (input:delay_out_s, output:delay_out_s) */
+ process_damping_filter(delay_out_s, delay_out_s, mdl);
+
+ /* Result in delay_out[], and matrix_factor.
+ These wil be use later during input line process */
+ delay_out[i] = delay_out_s; /* result in delay_out[] */
+ matrix_factor += delay_out_s; /* result in matrix_factor */
+
+ /* Process stereo output */
+ /* stereo left = left + out_left_gain * delay_out */
+ out_left += rev->late.out_left_gain[i] * delay_out_s;
+ /* stereo right= right+ out_right_gain * delay_out */
+ out_right += rev->late.out_right_gain[i] * delay_out_s;
+ }
+
+ /* now we process the input delay line. Each input is a combination of:
+ - xn: input signal
+ - delay_out[] the output of a delay line given by a permutation matrix P
+ - and matrix_factor.
+ This computes: in_delay_line = xn + (delay_out[] * matrix A) with
+ an algorithm equivalent but faster than using a product with matrix A.
+ */
+ /* matrix_factor = output sum * (-2.0)/N */
+ matrix_factor *= FDN_MATRIX_FACTOR;
+ matrix_factor += xn; /* adds reverb input signal */
+
+ for(i = 1; i < NBR_DELAYS; i++)
+ {
+ /* delay_in[i-1] = delay_out[i] + matrix_factor */
+ delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl;
+ push_in_delay_line(dl, delay_out[i] + matrix_factor);
+ }
- fluid_set_revmodel_buffers(rev, sample_rate);
+ /* last line input (NB_DELAY-1) */
+ /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */
+ {
+ delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl;
+ push_in_delay_line(dl, delay_out[0] + matrix_factor);
+ }
+
+ /*-------------------------------------------------------------------*/
+#ifdef DENORMALISING
+ /* Removes the DC offset */
+ out_left -= DC_OFFSET;
+ out_right -= DC_OFFSET;
+#endif
+ /* Calculates stereo output MIXING anything already there: */
+ /*
+ left_out[k] += out_left * rev->wet1 + out_right * rev->wet2;
+ right_out[k] += out_right * rev->wet1 + out_left * rev->wet2;
+
+ As wet1 is integrated in stereo coefficient wet 1 is now
+ integrated in out_left and out_right we simplify previous
+ relation by suppression of one multiply as this:
+
+ left_out[k] += out_left + out_right * rev->wet2;
+ right_out[k] += out_right + out_left * rev->wet2;
+ */
+ left_out[k] += out_left + out_right * rev->wet2;
+ right_out[k] += out_right + out_left * rev->wet2;
+ }
}
diff --git a/libs/fluidsynth/src/fluid_rvoice.c b/libs/fluidsynth/src/fluid_rvoice.c
index f6565bf0b1..6b64c9a979 100644
--- a/libs/fluidsynth/src/fluid_rvoice.c
+++ b/libs/fluidsynth/src/fluid_rvoice.c
@@ -305,6 +305,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
{
int ticks = voice->envlfo.ticks;
int count, is_looping;
+ fluid_real_t modenv_val;
/******************* sample sanity check **********/
@@ -361,6 +362,12 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
/******************* phase **********************/
+ /* SF2.04 section 8.1.2 #26:
+ * attack of modEnv is convex ?!?
+ */
+ modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
+ ? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv))
+ : fluid_adsr_env_get_val(&voice->envlfo.modenv);
/* Calculate the number of samples, that the DSP loop advances
* through the original waveform with each step in the output
* buffer. It is the ratio between the frequencies of original
@@ -369,7 +376,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
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)
+ + modenv_val * voice->envlfo.modenv_to_pitch)
/ voice->dsp.root_pitch_hz;
/******************* portamento ****************/
@@ -455,7 +462,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
- fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
+ modenv_val * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
@@ -585,7 +592,7 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
/* A voice is turned off during the attack section of the volume
* envelope. The attack section ramps up linearly with
* amplitude. The other sections use logarithmic scaling. Calculate new
- * volenv_val to achieve equievalent amplitude during the release phase
+ * volenv_val to achieve equivalent amplitude during the release phase
* for seamless volume transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)
@@ -598,6 +605,24 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
}
}
+ if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
+ {
+ /* A voice is turned off during the attack section of the modulation
+ * envelope. The attack section use convex scaling with pitch and filter
+ * frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)
+ * The other sections use linear scaling: modenv_val = modenv.val
+ *
+ * Calculate new modenv.val to achieve equivalent modenv_val during the release phase
+ * for seamless pitch and filter frequency cutoff transition.
+ */
+ if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)
+ {
+ fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));
+ fluid_clip(env_value, 0.0, 1.0);
+ fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);
+ }
+ }
+
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
}
@@ -650,11 +675,12 @@ static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voi
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
{
fluid_rvoice_t *voice = obj;
- int section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
+ int section; /* volume or modulation section */
/*-------------------------------------------------------------------------
Section skip for volume envelope
--------------------------------------------------------------------------*/
+ section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new
@@ -672,16 +698,30 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
/* skips to Attack section from any section */
/* Update vol and attack data */
fluid_rvoice_local_retrigger_attack(voice);
+
/*-------------------------------------------------------------------------
Section skip for modulation envelope
--------------------------------------------------------------------------*/
+ section = fluid_adsr_env_get_section(&voice->envlfo.modenv);
+ if(section >= FLUID_VOICE_ENVHOLD)
+ {
+ /* DECAY, SUSTAIN,RELEASE section use linear scaling.
+ Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section
+ uses convex shape (see fluid_rvoice_write() - fluid_convex()).
+ Calculate new modenv value (new_value) for seamless attack transition.
+ Here we need the inverse of fluid_convex() function defined as:
+ new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)
+ For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with
+ val = (1 – current_val) . FLUID_PEAK_ATTENUATION / 2.0
+ */
+ fluid_real_t new_value; /* new modenv value */
+ new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))
+ * FLUID_PEAK_ATTENUATION / 2.0);
+ fluid_clip(new_value, 0.0, 1.0);
+ fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);
+ }
/* Skips 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 issue #153 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 to do this */
}
/**
diff --git a/libs/fluidsynth/src/fluid_samplecache.c b/libs/fluidsynth/src/fluid_samplecache.c
index 773e19feea..036964f584 100644
--- a/libs/fluidsynth/src/fluid_samplecache.c
+++ b/libs/fluidsynth/src/fluid_samplecache.c
@@ -60,8 +60,10 @@ struct _fluid_samplecache_entry_t
static fluid_list_t *samplecache_list = NULL;
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
-static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
-static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
+static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start,
+ unsigned int sample_end, int sample_type, time_t mtime);
+static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start,
+ unsigned int sample_end, int sample_type, time_t mtime);
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
@@ -75,14 +77,20 @@ int fluid_samplecache_load(SFData *sf,
{
fluid_samplecache_entry_t *entry;
int ret;
+ time_t mtime;
fluid_mutex_lock(samplecache_mutex);
- entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type);
+ if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
+ {
+ mtime = 0;
+ }
+
+ entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL)
{
- entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
+ entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL)
{
@@ -180,7 +188,8 @@ unlock_exit:
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_end,
- int sample_type)
+ int sample_type,
+ time_t mtime)
{
fluid_samplecache_entry_t *entry;
@@ -202,12 +211,6 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
goto error_exit;
}
- if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
- {
- FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
- entry->modification_time = 0;
- }
-
entry->sf_samplepos = sf->samplepos;
entry->sf_samplesize = sf->samplesize;
entry->sf_sample24pos = sf->sample24pos;
@@ -215,6 +218,7 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
entry->sample_start = sample_start;
entry->sample_end = sample_end;
entry->sample_type = sample_type;
+ entry->modification_time = mtime;
entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
&entry->sample_data, &entry->sample_data24);
@@ -244,18 +248,12 @@ static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_end,
- int sample_type)
+ int sample_type,
+ time_t mtime)
{
- time_t mtime;
fluid_list_t *entry_list;
fluid_samplecache_entry_t *entry;
- if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
- {
- FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
- mtime = 0;
- }
-
entry_list = samplecache_list;
while(entry_list)
diff --git a/libs/fluidsynth/src/fluid_sffile.c b/libs/fluidsynth/src/fluid_sffile.c
index 40055e8189..302eea72a7 100644
--- a/libs/fluidsynth/src/fluid_sffile.c
+++ b/libs/fluidsynth/src/fluid_sffile.c
@@ -34,75 +34,79 @@
Borrowed from Smurf SoundFont Editor by Josh Green
=================================================================*/
-/*
- functions for loading data from sfont files, with appropriate byte swapping
- on big endian machines. Sfont IDs are not swapped because the ID read is
- equivalent to the matching ID list in memory regardless of LE/BE machine
-*/
-
-/* sf file chunk IDs */
-enum
-{
- UNKN_ID,
- RIFF_ID,
- LIST_ID,
- SFBK_ID,
- INFO_ID,
- SDTA_ID,
- PDTA_ID, /* info/sample/preset */
-
- IFIL_ID,
- ISNG_ID,
- INAM_ID,
- IROM_ID, /* info ids (1st byte of info strings) */
- IVER_ID,
- ICRD_ID,
- IENG_ID,
- IPRD_ID, /* more info ids */
- ICOP_ID,
- ICMT_ID,
- ISFT_ID, /* and yet more info ids */
-
- SNAM_ID,
- SMPL_ID, /* sample ids */
- PHDR_ID,
- PBAG_ID,
- PMOD_ID,
- PGEN_ID, /* preset ids */
- IHDR_ID,
- IBAG_ID,
- IMOD_ID,
- IGEN_ID, /* instrument ids */
- SHDR_ID, /* sample info */
- SM24_ID
-};
+/* FOURCC definitions */
+#define RIFF_FCC FLUID_FOURCC('R','I','F','F')
+#define LIST_FCC FLUID_FOURCC('L','I','S','T')
+#define SFBK_FCC FLUID_FOURCC('s','f','b','k')
+#define INFO_FCC FLUID_FOURCC('I','N','F','O')
+#define SDTA_FCC FLUID_FOURCC('s','d','t','a')
+#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */
+
+#define IFIL_FCC FLUID_FOURCC('i','f','i','l')
+#define ISNG_FCC FLUID_FOURCC('i','s','n','g')
+#define INAM_FCC FLUID_FOURCC('I','N','A','M')
+#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */
+#define IVER_FCC FLUID_FOURCC('i','v','e','r')
+#define ICRD_FCC FLUID_FOURCC('I','C','R','D')
+#define IENG_FCC FLUID_FOURCC('I','E','N','G')
+#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */
+#define ICOP_FCC FLUID_FOURCC('I','C','O','P')
+#define ICMT_FCC FLUID_FOURCC('I','C','M','T')
+#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */
+
+#define SNAM_FCC FLUID_FOURCC('s','n','a','m')
+#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */
+#define PHDR_FCC FLUID_FOURCC('p','h','d','r')
+#define PBAG_FCC FLUID_FOURCC('p','b','a','g')
+#define PMOD_FCC FLUID_FOURCC('p','m','o','d')
+#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */
+#define IHDR_FCC FLUID_FOURCC('i','n','s','t')
+#define IBAG_FCC FLUID_FOURCC('i','b','a','g')
+#define IMOD_FCC FLUID_FOURCC('i','m','o','d')
+#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */
+#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */
+#define SM24_FCC FLUID_FOURCC('s','m','2','4')
+
+/* Set when the FCC code is unknown */
+#define UNKN_ID FLUID_N_ELEMENTS(idlist)
/*
- * This declares a char array containing the SF2 chunk identifiers. This
- * array is being accessed like an uint32 below to simplify id comparison.
- * To make sure it is suitably aligned for uint32 access, we must wrap it
- * inside a union along with a uint32 telling the compiler to align it
- * for integer access and avoiding undefined behaviour.
- * This basically is the C89 equivalent to what is written in C11 as:
- * alignas(uint32_t) static const char idlist[] = {};
- *
- * See: EXP36-C. Do not cast pointers into more strictly aligned pointer
- * types - SEI CERT C Coding Standard
+ * This declares a uint32_t array containing the SF2 chunk identifiers.
*/
-static const union fluid_idlist
+static const uint32_t idlist[] =
{
- /*
- * Cannot be char c[ ], because in C89, arrays wraped in unions
- * must have a fixed size. Otherwise the size of the union would depend
- * on the initialization of its first member, which results in
- * different sizes for different instances of the same union type.
- */
- char c[116];
- uint32_t i;
-} idlist = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
- "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24"
- };
-
+ RIFF_FCC,
+ LIST_FCC,
+ SFBK_FCC,
+ INFO_FCC,
+ SDTA_FCC,
+ PDTA_FCC,
+
+ IFIL_FCC,
+ ISNG_FCC,
+ INAM_FCC,
+ IROM_FCC,
+ IVER_FCC,
+ ICRD_FCC,
+ IENG_FCC,
+ IPRD_FCC,
+ ICOP_FCC,
+ ICMT_FCC,
+ ISFT_FCC,
+
+ SNAM_FCC,
+ SMPL_FCC,
+ PHDR_FCC,
+ PBAG_FCC,
+ PMOD_FCC,
+ PGEN_FCC,
+ IHDR_FCC,
+ IBAG_FCC,
+ IMOD_FCC,
+ IGEN_FCC,
+ SHDR_FCC,
+ SM24_FCC
+};
/* generator types */
typedef enum
@@ -205,8 +209,6 @@ static const unsigned short invalid_preset_gen[] =
};
-#define CHNKIDSTR(id) &idlist.c[(id - 1) * 4]
-
/* sfont file chunk sizes */
#define SF_PHDR_SIZE (38)
#define SF_BAG_SIZE (4)
@@ -322,6 +324,56 @@ static void delete_zone(SFZone *zone);
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data);
static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24);
+/**
+ * Check if a file is a SoundFont file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a SoundFont, FALSE otherwise
+ *
+ * @note The current implementation only checks for the "RIFF" and "sfbk" headers in
+ * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
+ */
+int fluid_is_soundfont(const char *filename)
+{
+ FILE *fp = FLUID_FOPEN(filename, "rb");
+ uint32_t fcc;
+ int retcode = FALSE;
+
+ do
+ {
+ if(fp == NULL)
+ {
+ return retcode;
+ }
+
+ if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
+ {
+ break;
+ }
+
+ if(fcc != RIFF_FCC)
+ {
+ break;
+ }
+
+ if(FLUID_FSEEK(fp, 4, SEEK_CUR))
+ {
+ break;
+ }
+
+ if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
+ {
+ break;
+ }
+
+ retcode = (fcc == SFBK_FCC);
+ }
+ while(0);
+
+ FLUID_FCLOSE(fp);
+
+ return retcode;
+}
+
/*
* Open a SoundFont file and parse it's contents into a SFData structure.
*
@@ -511,17 +563,17 @@ void fluid_sffile_close(SFData *sf)
static int chunkid(uint32_t id)
{
unsigned int i;
- const uint32_t *p = &idlist.i;
- for(i = 0; i < sizeof(idlist) / sizeof(idlist.i); i++, p += 1)
+ for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++)
{
- if(*p == id)
+ if(idlist[i] == id)
{
- return (i + 1);
+ break;
}
}
- return UNKN_ID;
+ /* Return chunk id or UNKN_ID if not found */
+ return i;
}
static int load_header(SFData *sf)
@@ -530,7 +582,7 @@ static int load_header(SFData *sf)
READCHUNK(sf, &chunk); /* load RIFF chunk */
- if(chunkid(chunk.id) != RIFF_ID)
+ if(chunk.id != RIFF_FCC)
{
/* error if not RIFF */
FLUID_LOG(FLUID_ERR, "Not a RIFF file");
@@ -539,7 +591,7 @@ static int load_header(SFData *sf)
READID(sf, &chunk.id); /* load file ID */
- if(chunkid(chunk.id) != SFBK_ID)
+ if(chunk.id != SFBK_FCC)
{
/* error if not SFBK_ID */
FLUID_LOG(FLUID_ERR, "Not a SoundFont file");
@@ -558,7 +610,7 @@ static int load_header(SFData *sf)
return FALSE;
}
- if(chunkid(chunk.id) != INFO_ID)
+ if(chunk.id != INFO_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk");
return FALSE;
@@ -575,7 +627,7 @@ static int load_header(SFData *sf)
return FALSE;
}
- if(chunkid(chunk.id) != SDTA_ID)
+ if(chunk.id != SDTA_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk");
return FALSE;
@@ -592,7 +644,7 @@ static int load_header(SFData *sf)
return FALSE;
}
- if(chunkid(chunk.id) != PDTA_ID)
+ if(chunk.id != PDTA_FCC)
{
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
return FALSE;
@@ -637,7 +689,7 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
{
READCHUNK(sf, chunk); /* read list chunk */
- if(chunkid(chunk->id) != LIST_ID) /* error if ! list chunk */
+ if(chunk->id != LIST_FCC) /* error if ! list chunk */
{
FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse");
return FALSE;
@@ -651,8 +703,11 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
static int process_info(SFData *sf, int size)
{
SFChunk chunk;
- unsigned char id;
- char *item;
+ union
+ {
+ char *chr;
+ uint32_t *fcc;
+ } item;
unsigned short ver;
while(size > 0)
@@ -660,9 +715,7 @@ static int process_info(SFData *sf, int size)
READCHUNK(sf, &chunk);
size -= 8;
- id = chunkid(chunk.id);
-
- if(id == IFIL_ID)
+ if(chunk.id == IFIL_FCC)
{
/* sound font version chunk? */
if(chunk.size != 4)
@@ -703,7 +756,7 @@ static int process_info(SFData *sf, int size)
return FALSE;
}
}
- else if(id == IVER_ID)
+ else if(chunk.id == IVER_FCC)
{
/* ROM version chunk? */
if(chunk.size != 4)
@@ -717,34 +770,35 @@ static int process_info(SFData *sf, int size)
READW(sf, ver);
sf->romver.minor = ver;
}
- else if(id != UNKN_ID)
+ else if(chunkid(chunk.id) != UNKN_ID)
{
- if((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
+ if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
{
FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes",
&chunk.id, chunk.size);
return FALSE;
}
- /* alloc for chunk id and da chunk */
- if(!(item = FLUID_MALLOC(chunk.size + 1)))
+ /* alloc for chunk fcc and da chunk */
+ if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1)))
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE;
}
/* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */
- sf->info = fluid_list_append(sf->info, item);
+ sf->info = fluid_list_append(sf->info, item.fcc);
- *(unsigned char *)item = id;
+ /* save chunk fcc and update pointer to data value */
+ *item.fcc++ = chunk.id;
- if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED)
+ if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED)
{
return FALSE;
}
- /* force terminate info item (don't forget uint8 info ID) */
- *(item + chunk.size) = '\0';
+ /* force terminate info item */
+ item.chr[chunk.size] = '\0';
}
else
{
@@ -777,7 +831,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk);
size -= 8;
- if(chunkid(chunk.id) != SMPL_ID)
+ if(chunk.id != SMPL_FCC)
{
FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead");
return FALSE;
@@ -810,7 +864,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk);
size -= 8;
- if(chunkid(chunk.id) == SM24_ID)
+ if(chunk.id == SM24_FCC)
{
int sm24size, sdtahalfsize;
@@ -850,29 +904,24 @@ ret:
static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size)
{
- unsigned int id;
- const char *expstr;
-
- expstr = CHNKIDSTR(expid); /* in case we need it */
-
READCHUNK(sf, chunk);
*size -= 8;
- if((id = chunkid(chunk->id)) != expid)
+ if(chunk->id != expid)
{
- FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr);
+ FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", &expid);
return FALSE;
}
if(chunk->size % reclen) /* valid chunk size? */
{
- FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", expstr, reclen);
+ FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", &expid, reclen);
return FALSE;
}
if((*size -= chunk->size) < 0)
{
- FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", expstr);
+ FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", &expid);
return FALSE;
}
@@ -883,7 +932,7 @@ static int process_pdta(SFData *sf, int size)
{
SFChunk chunk;
- if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -893,7 +942,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -903,7 +952,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -913,7 +962,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -923,7 +972,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -933,7 +982,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -943,7 +992,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -953,7 +1002,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{
return FALSE;
}
@@ -963,7 +1012,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE;
}
- if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size))
+ if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size))
{
return FALSE;
}
diff --git a/libs/fluidsynth/src/fluid_sfont.c b/libs/fluidsynth/src/fluid_sfont.c
index 405bec6c64..548a80abce 100644
--- a/libs/fluidsynth/src/fluid_sfont.c
+++ b/libs/fluidsynth/src/fluid_sfont.c
@@ -512,9 +512,13 @@ delete_fluid_sample(fluid_sample_t *sample)
/**
* Returns the size of the fluid_sample_t structure.
*
- * Useful in low latency scenarios e.g. to allocate a sample on the stack.
+ * Useful in low latency scenarios e.g. to allocate a pool of samples.
*
* @return Size of fluid_sample_t in bytes
+ *
+ * @note It is recommend to zero initialize the memory before using the object.
+ *
+ * @warning Do NOT allocate samples on the stack and assign them to a voice!
*/
size_t fluid_sample_sizeof()
{
@@ -563,16 +567,17 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
fluid_return_val_if_fail(data != NULL, FLUID_FAILED);
- fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED);
+ fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED);
/* in case we already have some data */
if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)
{
FLUID_FREE(sample->data);
FLUID_FREE(sample->data24);
- sample->data = NULL;
- sample->data24 = NULL;
}
+
+ sample->data = NULL;
+ sample->data24 = NULL;
if(copy_data)
{
@@ -635,6 +640,8 @@ error_rec:
FLUID_LOG(FLUID_ERR, "Out of memory");
FLUID_FREE(sample->data);
FLUID_FREE(sample->data24);
+ sample->data = NULL;
+ sample->data24 = NULL;
return FLUID_FAILED;
#undef SAMPLE_LOOP_MARGIN
diff --git a/libs/fluidsynth/src/fluid_sfont.h b/libs/fluidsynth/src/fluid_sfont.h
index 24773353df..28609e96aa 100644
--- a/libs/fluidsynth/src/fluid_sfont.h
+++ b/libs/fluidsynth/src/fluid_sfont.h
@@ -183,8 +183,6 @@ struct _fluid_sample_t
* @return Should return #FLUID_OK
*/
int (*notify)(fluid_sample_t *sample, int reason);
-
- void *userdata; /**< User defined data */
};
diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c
index 79c5a9c87c..b4524a2acf 100644
--- a/libs/fluidsynth/src/fluid_synth.c
+++ b/libs/fluidsynth/src/fluid_synth.c
@@ -227,7 +227,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
#else
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0);
#endif
-
+
fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);
@@ -1331,6 +1331,13 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
+
+ /* Checks if modulators sources are valid */
+ if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod"))
+ {
+ return FLUID_FAILED;
+ }
+
fluid_synth_api_enter(synth);
default_mod = synth->default_mod;
@@ -1902,7 +1909,7 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
int bank = 0, prog, channels;
double tunedata[128];
int keys[128];
- char name[17];
+ char name[17]={0};
int note, frac, frac2;
uint8_t chksum;
int i, count, index;
@@ -1973,7 +1980,8 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
}
*resptr++ = prog;
- FLUID_STRNCPY(resptr, name, 16);
+ /* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */
+ FLUID_MEMCPY(resptr, name, 16);
resptr += 16;
for(i = 0; i < 128; i++)
@@ -3368,7 +3376,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(left != NULL, FLUID_FAILED);
fluid_return_val_if_fail(right != NULL, FLUID_FAILED);
-
+
/* First, take what's still available in the buffer */
count = 0;
num = synth->cur;
@@ -3521,7 +3529,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
/**
* mixes the samples of \p in to \p out
- *
+ *
* @param out the output sample buffer to mix to
* @param ooff sample offset in \p out
* @param in the rvoice_mixer input sample buffer to mix from
@@ -3539,6 +3547,7 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out
if(out != NULL)
{
int j;
+
for(j = 0; j < num; j++)
{
out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];
@@ -3687,7 +3696,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
-
+
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);
@@ -3730,10 +3739,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
-
+
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);
-
+
out_buf = fx[(buf_idx * 2 + 1) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);
}
@@ -3784,7 +3793,7 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
float cpu_load;
fluid_profile_ref_var(prof_ref);
-
+
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
@@ -3853,10 +3862,12 @@ static FLUID_INLINE int16_t
round_clip_to_i16(float x)
{
long i;
+
if(x >= 0.0f)
{
i = (long)(x + 0.5f);
- if (FLUID_UNLIKELY(i > 32767))
+
+ if(FLUID_UNLIKELY(i > 32767))
{
i = 32767;
}
@@ -3864,12 +3875,13 @@ round_clip_to_i16(float x)
else
{
i = (long)(x - 0.5f);
- if (FLUID_UNLIKELY(i < -32768))
+
+ if(FLUID_UNLIKELY(i < -32768))
{
i = -32768;
}
}
-
+
return (int16_t)i;
}
@@ -4347,11 +4359,11 @@ fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int
)
{
// Replacement of default_vel2att modulator by custom_breath2att_modulator
- fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT);
+ fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);
}
else
{
- fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT);
+ fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);
}
// Next default modulator to add to the voice
diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c
index cce778b3c4..28911ee7fc 100644
--- a/libs/fluidsynth/src/fluid_sys.c
+++ b/libs/fluidsynth/src/fluid_sys.c
@@ -276,71 +276,6 @@ fluid_error()
}
/**
- * Check if a file is a MIDI file.
- * @param filename Path to the file to check
- * @return TRUE if it could be a MIDI file, FALSE otherwise
- *
- * The current implementation only checks for the "MThd" header in the file.
- * It is useful only to distinguish between SoundFont and MIDI files.
- */
-int
-fluid_is_midifile(const char *filename)
-{
- FILE *fp = fopen(filename, "rb");
- char id[4];
-
- if(fp == NULL)
- {
- return 0;
- }
-
- if(fread((void *) id, 1, 4, fp) != 4)
- {
- fclose(fp);
- return 0;
- }
-
- fclose(fp);
-
- return FLUID_STRNCMP(id, "MThd", 4) == 0;
-}
-
-/**
- * Check if a file is a SoundFont file.
- * @param filename Path to the file to check
- * @return TRUE if it could be a SoundFont, FALSE otherwise
- *
- * @note The current implementation only checks for the "RIFF" and "sfbk" headers in
- * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
- */
-int
-fluid_is_soundfont(const char *filename)
-{
- FILE *fp = fopen(filename, "rb");
- char riff_id[4], sfbk_id[4];
-
- if(fp == NULL)
- {
- return 0;
- }
-
- if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) ||
- (fseek(fp, 4, SEEK_CUR) != 0) ||
- (fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id)))
- {
- goto error_rec;
- }
-
- fclose(fp);
- return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) &&
- (FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0);
-
-error_rec:
- fclose(fp);
- return 0;
-}
-
-/**
* Suspend the execution of the current thread for the specified amount of time.
* @param milliseconds to wait.
*/
@@ -1280,7 +1215,11 @@ fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt,
FLUID_SNPRINTF(buf, len, "%s", line);
buf[len - 1] = 0;
- add_history(buf);
+
+ if(buf[0] != '\0')
+ {
+ add_history(buf);
+ }
free(line);
return 1;
diff --git a/libs/fluidsynth/src/fluid_sys.h b/libs/fluidsynth/src/fluid_sys.h
index d95f6159f2..1229386550 100644
--- a/libs/fluidsynth/src/fluid_sys.h
+++ b/libs/fluidsynth/src/fluid_sys.h
@@ -77,6 +77,14 @@
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
#define FLUID_LE16TOH(x) GINT16_FROM_LE(x)
+#if FLUID_IS_BIG_ENDIAN
+#define FLUID_FOURCC(_a, _b, _c, _d) \
+ (uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d))
+#else
+#define FLUID_FOURCC(_a, _b, _c, _d) \
+ (uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a))
+#endif
+
#define fluid_return_if_fail(cond) \
if(cond) \
diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c
index 2f146ba02d..7c6265745b 100644
--- a/libs/fluidsynth/src/fluid_voice.c
+++ b/libs/fluidsynth/src/fluid_voice.c
@@ -516,7 +516,6 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t *voice)
voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice));
}
-
/*
* fluid_voice_calculate_runtime_synthesis_parameters
*
@@ -1153,7 +1152,9 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* Recalculate voice parameters for a given control.
* @param voice the synthesis voice
* @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...)
- * @param ctrl the control number
+ * @param ctrl the control number:
+ * when >=0, only modulators's destination having ctrl as source are updated.
+ * when -1, all modulators's destination are updated (regardless of ctrl).
*
* In this implementation, I want to make sure that all controllers
* are event based: the parameter values of the DSP algorithm should
@@ -1161,57 +1162,83 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* iteration of the audio cycle (which would probably be feasible if
* the synth was made in silicon).
*
- * The update is done in three steps:
- *
- * - first, we look for all the modulators that have the changed
- * controller as a source. This will yield a list of generators that
- * will be changed because of the controller event.
+ * The update is done in two steps:
*
- * - For every changed generator, calculate its new value. This is the
- * sum of its original value plus the values of al the attached
- * modulators.
+ * - step 1: first, we look for all the modulators that have the changed
+ * controller as a source. This will yield a generator that will be changed
+ * because of the controller event.
*
- * - For every changed generator, convert its value to the correct
- * unit of the corresponding DSP parameter
+ * - step 2: For this generator, calculate its new value. This is the
+ * sum of its original value plus the values of all the attached modulators.
+ * The generator flag is set to indicate the parameters must be updated.
+ * This avoid the risk to call 'fluid_voice_update_param' several
+ * times for the same generator if several modulators have that generator as
+ * destination. So every changed generators are updated only once.
+ */
+
+ /* bit table for each generator being updated. The bits are packed in variables
+ Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2.
+ The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT.
+
+ Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu)
+ but this could be set to 6 for 64 bits cpu.
*/
+
+#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */
+#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2)
+#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1)
+#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR)
+
+#define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
+#define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
+
int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
{
int i, k;
fluid_mod_t *mod;
- int gen;
+ uint32_t gen;
fluid_real_t modval;
+ /* Clears registered bits table of updated generators */
+ uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0};
+
/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */
for(i = 0; i < voice->mod_count; i++)
{
-
mod = &voice->mod[i];
/* step 1: find all the modulators that have the changed controller
- * as input source. */
- if(fluid_mod_has_source(mod, cc, ctrl))
+ as input source. When ctrl is -1 all modulators destination
+ are updated */
+ if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl))
{
-
gen = fluid_mod_get_dest(mod);
- modval = 0.0;
- /* step 2: for every changed modulator, calculate the modulation
- * value of its associated generator */
- for(k = 0; k < voice->mod_count; k++)
+ /* Skip if this generator has already been updated */
+ if(!is_gen_updated(updated_gen_bit, gen))
{
- if(fluid_mod_has_dest(&voice->mod[k], gen))
+ modval = 0.0;
+
+ /* step 2: for every attached modulator, calculate the modulation
+ * value for the generator gen */
+ for(k = 0; k < voice->mod_count; k++)
{
- modval += fluid_mod_get_value(&voice->mod[k], voice);
+ if(fluid_mod_has_dest(&voice->mod[k], gen))
+ {
+ modval += fluid_mod_get_value(&voice->mod[k], voice);
+ }
}
- }
- fluid_gen_set_mod(&voice->gen[gen], modval);
+ fluid_gen_set_mod(&voice->gen[gen], modval);
- /* step 3: now that we have the new value of the generator,
- * recalculate the parameter values that are derived from the
- * generator */
- fluid_voice_update_param(voice, gen);
+ /* now recalculate the parameter values that are derived from the
+ generator */
+ fluid_voice_update_param(voice, gen);
+
+ /* set the bit that indicates this generator is updated */
+ set_gen_updated(updated_gen_bit, gen);
+ }
}
}
@@ -1222,47 +1249,11 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
* Update all the modulators. This function is called after a
* ALL_CTRL_OFF MIDI message has been received (CC 121).
*
+ * All destination of all modulators must be updated.
*/
int fluid_voice_modulate_all(fluid_voice_t *voice)
{
- fluid_mod_t *mod;
- int i, k, gen;
- fluid_real_t modval;
-
- /* Loop through all the modulators.
-
- FIXME: we should loop through the set of generators instead of
- the set of modulators. We risk to call 'fluid_voice_update_param'
- several times for the same generator if several modulators have
- that generator as destination. It's not an error, just a wast of
- energy (think polution, global warming, unhappy musicians,
- ...) */
-
- for(i = 0; i < voice->mod_count; i++)
- {
-
- mod = &voice->mod[i];
- gen = fluid_mod_get_dest(mod);
- modval = 0.0;
-
- /* Accumulate the modulation values of all the modulators with
- * destination generator 'gen' */
- for(k = 0; k < voice->mod_count; k++)
- {
- if(fluid_mod_has_dest(&voice->mod[k], gen))
- {
- modval += fluid_mod_get_value(&voice->mod[k], voice);
- }
- }
-
- fluid_gen_set_mod(&voice->gen[gen], modval);
-
- /* Update the parameter values that are depend on the generator
- * 'gen' */
- fluid_voice_update_param(voice, gen);
- }
-
- return FLUID_OK;
+ return fluid_voice_modulate(voice, 0, -1);
}
/** legato update functions --------------------------------------------------*/
@@ -1474,10 +1465,10 @@ fluid_voice_stop(fluid_voice_t *voice)
}
/**
- * Adds a modulator to the voice.
- * @param voice Voice instance
- * @param mod Modulator info (copied)
- * @param mode Determines how to handle an existing identical modulator
+ * Adds a modulator to the voice if the modulator has valid sources.
+ * @param voice Voice instance.
+ * @param mod Modulator info (copied).
+ * @param mode Determines how to handle an existing identical modulator.
* #FLUID_VOICE_ADD to add (offset) the modulator amounts,
* #FLUID_VOICE_OVERWRITE to replace the modulator,
* #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should
@@ -1486,32 +1477,42 @@ fluid_voice_stop(fluid_voice_t *voice)
void
fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
- int i;
+ /* Ignore the modulator if its sources inputs are invalid */
+ if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod"))
+ {
+ fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD);
+ }
+}
- /*
- * Some soundfonts come with a huge number of non-standard
- * controllers, because they have been designed for one particular
- * sound card. Discard them, maybe print a warning.
- */
+/**
+ * Adds a modulator to the voice.
+ * local version of fluid_voice_add_mod function. Called at noteon time.
+ * @param voice, mod, mode, same as for fluid_voice_add_mod() (see above).
+ * @param check_limit_count is the modulator number limit to handle with existing
+ * identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD).
+ * - When FLUID_NUM_MOD, all the voices modulators (since the previous call)
+ * are checked for identity.
+ * - When check_count_limit is below the actual number of voices modulators
+ * (voice->mod_count), this will restrict identity check to this number,
+ * This is usefull when we know by advance that there is no duplicate with
+ * modulators at index above this limit. This avoid wasting cpu cycles at noteon.
+ */
+void
+fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count)
+{
+ int i;
- if(((mod->flags1 & FLUID_MOD_CC) == 0)
- && ((mod->src1 != FLUID_MOD_NONE) /* SF2.01 section 8.2.1: Constant value */
- && (mod->src1 != FLUID_MOD_VELOCITY) /* Note-on velocity */
- && (mod->src1 != FLUID_MOD_KEY) /* Note-on key number */
- && (mod->src1 != FLUID_MOD_KEYPRESSURE) /* Poly pressure */
- && (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
- && (mod->src1 != FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
- && (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
+ /* check_limit_count cannot be above voice->mod_count */
+ if(check_limit_count > voice->mod_count)
{
- FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
- return;
+ check_limit_count = voice->mod_count;
}
if(mode == FLUID_VOICE_ADD)
{
/* if identical modulator exists, add them */
- for(i = 0; i < voice->mod_count; i++)
+ for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{
@@ -1526,7 +1527,7 @@ fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* if identical modulator exists, replace it (only the amount has to be changed) */
- for(i = 0; i < voice->mod_count; i++)
+ for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{
@@ -1704,9 +1705,10 @@ int fluid_voice_get_velocity(const fluid_voice_t *voice)
* A lower boundary for the attenuation (as in 'the minimum
* attenuation of this voice, with volume pedals, modulators
* etc. resulting in minimum attenuation, cannot fall below x cB) is
- * calculated. This has to be called during fluid_voice_init, after
+ * calculated. This has to be called during fluid_voice_start, after
* all modulators have been run on the voice once. Also,
* voice->attenuation has to be initialized.
+ * (see fluid_voice_calculate_runtime_synthesis_parameters())
*/
static fluid_real_t
fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
@@ -1733,30 +1735,42 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
{
fluid_real_t current_val = fluid_mod_get_value(mod, voice);
- fluid_real_t v = fabs(mod->amount);
+ /* min_val is the possible minimum value for this modulator.
+ it depends of 3 things :
+ 1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar
+ or 0 if mapping is unipolar).
+ 2)the sign of amount.
+ 3)absolute value of amount.
+
+ When at least one source mapping is bipolar:
+ min_val is -|amount| regardless the sign of amount.
+ When both sources mapping are unipolar:
+ min_val is -|amount|, if amount is negative.
+ min_val is 0, if amount is positive
+ */
+ fluid_real_t min_val = fabs(mod->amount);
- if((mod->src1 == FLUID_MOD_PITCHWHEEL)
- || (mod->flags1 & FLUID_MOD_BIPOLAR)
+ /* Can this modulator produce a negative contribution? */
+ if((mod->flags1 & FLUID_MOD_BIPOLAR)
|| (mod->flags2 & FLUID_MOD_BIPOLAR)
|| (mod->amount < 0))
{
- /* Can this modulator produce a negative contribution? */
- v *= -1.0;
+ min_val *= -1.0; /* min_val = - |amount|*/
}
else
{
/* No negative value possible. But still, the minimum contribution is 0. */
- v = 0;
+ min_val = 0;
}
/* For example:
* - current_val=100
* - min_val=-4000
- * - possible_att_reduction_cB += 4100
+ * - possible reduction contribution of this modulator = current_val - min_val = 4100
*/
- if(current_val > v)
+ if(current_val > min_val)
{
- possible_att_reduction_cB += (current_val - v);
+ possible_att_reduction_cB += (current_val - min_val);
}
}
}
diff --git a/libs/fluidsynth/src/fluid_voice.h b/libs/fluidsynth/src/fluid_voice.h
index 6038a1a9f3..fb0b28239f 100644
--- a/libs/fluidsynth/src/fluid_voice.h
+++ b/libs/fluidsynth/src/fluid_voice.h
@@ -157,6 +157,7 @@ void fluid_voice_release(fluid_voice_t *voice);
void fluid_voice_noteoff(fluid_voice_t *voice);
void fluid_voice_off(fluid_voice_t *voice);
void fluid_voice_stop(fluid_voice_t *voice);
+void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice);
int fluid_voice_kill_excl(fluid_voice_t *voice);
diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h
index 5de758dc0a..384785fccd 100644
--- a/libs/fluidsynth/src/fluidsynth_priv.h
+++ b/libs/fluidsynth/src/fluidsynth_priv.h
@@ -103,7 +103,7 @@
#endif
#if HAVE_IO_H
-#include <io.h>
+#include <io.h> // _open(), _close(), read(), write() on windows
#endif
#if HAVE_SIGNAL_H
@@ -262,7 +262,7 @@ typedef FILE *fluid_file;
#define FLUID_STRNCPY(_dst,_src,_n) \
do { strncpy(_dst,_src,_n); \
- (_dst)[(_n)-1]=0; \
+ (_dst)[(_n)-1]='\0'; \
}while(0)
#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
diff --git a/libs/fluidsynth/wscript b/libs/fluidsynth/wscript
index 3531e2e3d7..724e3aaf6c 100644
--- a/libs/fluidsynth/wscript
+++ b/libs/fluidsynth/wscript
@@ -65,7 +65,7 @@ def build(bld):
'src/fluid_synth_monopoly.c',
'src/fluid_sys.c'
],
- cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden', '-std=gnu99' ],
+ cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden', '-std=gnu99', '-Wno-unused-function' ],
includes = ['.', 'src/' ],
target = 'libfluidsynth',
use = 'libfluidsynth_includes',