From 98b115fe2522f8f841cfb9120c0ddb574e67146b Mon Sep 17 00:00:00 2001 From: spessasus Date: Sat, 26 Oct 2024 20:34:14 +0200 Subject: [PATCH] Add support for absolute transform (#1409) Add support for absolute transform modulators --------- Co-authored-by: derselbst --- doc/recent_changes.txt | 1 + include/fluidsynth/mod.h | 11 +++++++++ src/sfloader/fluid_defsfont.c | 16 ++++++++---- src/synth/fluid_mod.c | 46 ++++++++++++++++++++++++++++++++--- src/synth/fluid_mod.h | 1 + 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/doc/recent_changes.txt b/doc/recent_changes.txt index fb45b2f8e..081039f0d 100644 --- a/doc/recent_changes.txt +++ b/doc/recent_changes.txt @@ -5,6 +5,7 @@ - synth.device-id now has a default value of 16 - synth.gain now has a default value of 0.6 - Default values of reverb and chorus settings have been tuned +- fluid_mod_get_transform() and fluid_mod_set_transform() for SoundFont 2.04 compliant modulators \section NewIn2_3_2 What's new in 2.3.2? diff --git a/include/fluidsynth/mod.h b/include/fluidsynth/mod.h index a0965d000..001b790a5 100644 --- a/include/fluidsynth/mod.h +++ b/include/fluidsynth/mod.h @@ -57,6 +57,15 @@ enum fluid_mod_flags FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */ }; +/** + * Transform types for the SoundFont2 modulators as defined by SoundFont 2.04 section 8.3. + */ +enum fluid_mod_transforms +{ + FLUID_MOD_TRANSFORM_LINEAR = 0, /**< Linear: directly add the computed value to summing node */ + FLUID_MOD_TRANSFORM_ABS = 2 /**< Abs: add the absolute value of the computed to summing node */ +}; + /** * General controller (if #FLUID_MOD_GC in flags). This * corresponds to SoundFont 2.04 PDF section 8.2.1 @@ -83,6 +92,7 @@ FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags); FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags); FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t *mod, int dst); FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t *mod, double amount); +FLUIDSYNTH_API void fluid_mod_set_transform(fluid_mod_t *mod, int type); FLUIDSYNTH_API int fluid_mod_get_source1(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod); @@ -90,6 +100,7 @@ FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod); FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_transform(fluid_mod_t *mod); FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2); FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl); diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 84eaefce2..15335d452 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1639,7 +1639,7 @@ fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) /* 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 + unipolar flag 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) && @@ -1648,13 +1648,19 @@ fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR; } - /* *** Transform *** */ - /* SF2.01 only uses the 'linear' transform (0). - * Deactivate the modulator by setting the amount to 0 in any other case. + /** + * *** Transform Type *** + * Only 2 types of transform are defined in the sf2 specification. */ - if(mod_src->trans != 0) + if(mod_src->trans != FLUID_MOD_TRANSFORM_LINEAR && mod_src->trans != FLUID_MOD_TRANSFORM_ABS) { + /* disable the modulator as the transform is invalid */ mod_dest->amount = 0; + mod_dest->trans = FLUID_MOD_TRANSFORM_LINEAR; + } + else + { + mod_dest->trans = mod_src->trans; } /* Store the new modulator in the zone The order of modulators diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index c4e2c0114..63d602b1c 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -39,6 +39,7 @@ fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src) mod->src2 = src->src2; mod->flags2 = src->flags2; mod->amount = src->amount; + mod->trans = src->trans; } /** @@ -97,6 +98,24 @@ fluid_mod_set_amount(fluid_mod_t *mod, double amount) mod->amount = (double) amount; } +/** + * Set the transform type of a modulator. + * + * @param mod The modulator instance + * @param type Transform type, see #fluid_mod_transforms + */ +void +fluid_mod_set_transform(fluid_mod_t *mod, int type) +{ + unsigned char flag = (unsigned char) type; + if(flag != FLUID_MOD_TRANSFORM_LINEAR && flag != FLUID_MOD_TRANSFORM_ABS) + { + FLUID_LOG(FLUID_ERR, "fluid_mod_set_transform() called with invalid transform type %d", type); + return; + } + mod->trans = flag; +} + /** * Get the primary source value from a modulator. * @@ -169,6 +188,18 @@ fluid_mod_get_amount(const fluid_mod_t *mod) return (double) mod->amount; } +/** + * Get the transform type of a modulator. + * + * @param mod The modulator instance + * @param type Transform type, see #fluid_mod_transforms + */ +int +fluid_mod_get_transform(fluid_mod_t *mod) +{ + return (int) mod->trans; +} + /* * retrieves the initial value from the given source of the modulator */ @@ -382,6 +413,7 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) extern fluid_mod_t default_vel2filter_mod; fluid_real_t v1 = 0.0, v2 = 1.0; + fluid_real_t final_value; /* The wording of the default modulators refers to a range of 127/128. * And the table in section 9.5.3 suggests, that this mapping should be applied * to all unipolar and bipolar mappings respectively. @@ -470,8 +502,15 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) v2 = 1.0f; } - /* it's as simple as that: */ - return (fluid_real_t) mod->amount * v1 * v2; + /* it indeed is as simple as that: */ + final_value = (fluid_real_t) mod->amount * v1 * v2; + + /* check for absolute value transform */ + if(mod->trans == FLUID_MOD_TRANSFORM_ABS) + { + final_value = FLUID_FABS(final_value); + } + return final_value; } /** @@ -489,7 +528,8 @@ new_fluid_mod(void) FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } - + // for the sake of backward compatibility + mod->trans = FLUID_MOD_TRANSFORM_LINEAR; return mod; } diff --git a/src/synth/fluid_mod.h b/src/synth/fluid_mod.h index 3e7661741..8c8cfbc17 100644 --- a/src/synth/fluid_mod.h +++ b/src/synth/fluid_mod.h @@ -34,6 +34,7 @@ struct _fluid_mod_t unsigned char flags1; /**< Source controller 1 flags */ unsigned char src2; /**< Source controller 2 */ unsigned char flags2; /**< Source controller 2 flags */ + unsigned char trans; /**< Output transform flag */ double amount; /**< Multiplier amount */ /* The 'next' field allows to link modulators into a list. It is * not used in fluid_voice.c, there each voice allocates memory for a