From bf289de34b1dfc73f035f16dcd17e4017318e138 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 15 Jun 2024 12:58:03 +0200 Subject: [PATCH 01/19] Start implementing AWE32 NRPN compat layer --- src/synth/fluid_synth.c | 137 ++++++++++++++++++++++++++++++++++++++++ src/utils/fluid_conv.c | 29 ++++++++- src/utils/fluid_conv.h | 1 + 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 29b3403f2..7659ee45c 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1837,6 +1837,18 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) chan->nrpn_select = 0; /* Reset to 0 */ } + else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs + { + int gen = fluid_channel_get_cc(chan, NRPN_LSB); + if(gen <= 26) // Effect 26 (reverb) is the last effect to select + { + fluid_synth_process_awe32_nrpn_LOCAL(synth, chan, gen, data); + } + else + { + FLUID_LOG(FLUID_INFO, "Ignoring unknown AWE32 NRPN targetting effect %d", gen); + } + } } else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ { @@ -7585,6 +7597,131 @@ fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value } } +/** + * This implementation is based on "Frequently Asked Questions for SB AWE32" http://archive.gamedev.net/archive/reference/articles/article445.html + * as well as on the "SB AWE32 Developer's Information Pack" https://github.com/user-attachments/files/15757220/adip301.pdf + * + * @param gen the AWE32 effect or generator to manipulate + * @param data the composed value of DATA_MSB and DATA_LSB + */ +static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data) +{ + data -= 8192; + + static const fluid_gen_type awe32_to_sf2_gen[] = + { + // assuming LFO1 maps to MODLFO and LFO2 maps to VIBLFO + // observe how nicely most of the AWE32 generators here match up with the order of SF2 generators in fluid_gen_type + GEN_MODLFODELAY, /**< Modulation LFO delay */ + GEN_MODLFOFREQ, /**< Modulation LFO frequency */ + GEN_VIBLFODELAY, /**< Vibrato LFO delay */ + GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ + GEN_MODENVDELAY, /**< Modulation envelope delay */ + GEN_MODENVATTACK, /**< Modulation envelope attack */ + GEN_MODENVHOLD, /**< Modulation envelope hold */ + GEN_MODENVDECAY, /**< Modulation envelope decay */ + GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ + GEN_MODENVRELEASE, /**< Modulation envelope release */ + GEN_VOLENVDELAY, /**< Volume envelope delay */ + GEN_VOLENVATTACK, /**< Volume envelope attack */ + GEN_VOLENVHOLD, /**< Volume envelope hold */ + GEN_VOLENVDECAY, /**< Volume envelope decay */ + GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ + GEN_VOLENVRELEASE, /**< Volume envelope release */ + GEN_PITCH, /**< Initial Pitch */ + GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ + GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ + GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ + GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ + GEN_FILTERFC, /**< Filter cutoff */ + GEN_FILTERQ, /**< Filter Q */ + GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ + GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ + GEN_CHORUSSEND, /**< Chorus send amount */ + GEN_REVERBSEND, /**< Reverb send amount */ + }; + + int sf2_gen = awe32_to_sf2_gen[gen]; + fluid_real_t converted_sf2_generator_value; + switch(sf2_gen) + { + case GEN_MODLFODELAY: + case GEN_VIBLFODELAY: + case GEN_MODENVDELAY: + case GEN_VOLENVDELAY: + fluid_clip(data, 0, 5900); + converted_sf2_generator_value = fluid_sec2tc(data * 4 / 1000); + break; + + case GEN_MODLFOFREQ: + case GEN_VIBLFOFREQ: + break; + + case GEN_MODENVATTACK: + case GEN_VOLENVATTACK: + fluid_clip(data, 0, 5940); + converted_sf2_generator_value = fluid_sec2tc(data * 1 / 1000); + break; + + case GEN_MODENVHOLD: + case GEN_VOLENVHOLD: + fluid_clip(data, 0, 8191); + converted_sf2_generator_value = fluid_sec2tc(data * 1 / 1000); + break; + + case GEN_MODENVDECAY: + case GEN_MODENVRELEASE: + case GEN_VOLENVDECAY: + case GEN_VOLENVRELEASE: + fluid_clip(data, 0, 5940); + converted_sf2_generator_value = fluid_sec2tc(data * 4 / 1000); + break; + + case GEN_MODENVSUSTAIN: + case GEN_VOLENVSUSTAIN: + fluid_clip(data, 0, 127); + break; + + case GEN_PITCH: + converted_sf2_generator_value = data + 8192; + // This has the side effect of manipulating the state of the channel's pitchwheel, but I'll buy it + fluid_synth_pitch_bend(synth, chan, converted_sf2_generator_value); + return; + + case GEN_MODLFOTOPITCH: + case GEN_VIBLFOTOPITCH: + case GEN_MODENVTOPITCH: + break; + + case GEN_MODLFOTOVOL: + break; + + case GEN_FILTERFC: + break; + case GEN_FILTERQ: + break; + case GEN_MODLFOTOFILTERFC: + break; + case GEN_MODENVTOFILTERFC: + break; + + case GEN_REVERBSEND: + fluid_clip(data, 0, 255); + /* transform the input value */ + converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod->flags1, 256); + converted_sf2_generator_value*= fluid_mod_get_amount(default_reverb_mod); + + case GEN_CHORUSSEND: + fluid_clip(data, 0, 255); + /* transform the input value */ + converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod->flags1, 256); + converted_sf2_generator_value*= fluid_mod_get_amount(default_chorus_mod); + break; + } + + fluid_synth_set_gen_LOCAL(synth, chan, sf2_gen, converted_sf2_generator_value); +} + /** * Retrieve the generator NRPN offset assigned to a MIDI channel. * diff --git a/src/utils/fluid_conv.c b/src/utils/fluid_conv.c index 28740ea4f..5b0f4d597 100644 --- a/src/utils/fluid_conv.c +++ b/src/utils/fluid_conv.c @@ -150,7 +150,7 @@ fluid_tc2sec_delay(fluid_real_t tc) tc = (fluid_real_t) 5000.0f; } - return FLUID_POW(2.f, tc / 1200.f); + return fluid_tc2sec(tc); } /* @@ -178,7 +178,7 @@ fluid_tc2sec_attack(fluid_real_t tc) tc = (fluid_real_t) 8000.f; }; - return FLUID_POW(2.f, tc / 1200.f); + return fluid_tc2sec(tc); } /* @@ -191,6 +191,29 @@ fluid_tc2sec(fluid_real_t tc) return FLUID_POW(2.f, tc / 1200.f); } +/* + * fluid_sec2tc + * + * seconds to timecents + */ +fluid_real_t +fluid_sec2tc(fluid_real_t sec) +{ + fluid_real_t res; + if(sec < 0) + { + // would require a complex solution of fluid_tc2sec(), but this is real-only + return -32768.f; + } + + res = (1200.f / M_LN2) * FLUID_LOGF(sec); + if(res < -32768.f) + { + res = -32768.f; + } + return res; +} + /* * fluid_tc2sec_release */ @@ -216,7 +239,7 @@ fluid_tc2sec_release(fluid_real_t tc) tc = (fluid_real_t) 8000.f; }; - return FLUID_POW(2.f, tc / 1200.f); + return fluid_tc2sec(tc); } /* diff --git a/src/utils/fluid_conv.h b/src/utils/fluid_conv.h index 985c02d0b..b331525e2 100644 --- a/src/utils/fluid_conv.h +++ b/src/utils/fluid_conv.h @@ -27,6 +27,7 @@ fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); fluid_real_t fluid_cb2amp(fluid_real_t cb); +fluid_real_t fluid_sec2tc(fluid_real_t sec) fluid_real_t fluid_tc2sec(fluid_real_t tc); fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); From 4e9fe7196328fd6a6ea750d2149a976f4530f422 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 16 Jun 2024 14:15:37 +0200 Subject: [PATCH 02/19] Complete AWE32 NRPN impl. --- src/synth/fluid_synth.c | 63 +++++++++++++++++++++++++++++++++++++++++ src/utils/fluid_conv.c | 15 +++++----- src/utils/fluid_conv.h | 1 + 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 7659ee45c..ab8b8a161 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7597,6 +7597,52 @@ fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value } } +// The "SB AWE32 Developer's Information Pack" provides a lookup table for the filter resonance. +// Instead of a single Q value, a high and low Q value is given. This suggests a variable-Q filter design, which is +// incompatible to fluidsynth's IIR filter. Therefore we need to somehow derive a single Q value. +// Options are: +// * mean +// * geometric distance (sqrt(q_lo * q_hi)) +// * either q_lo or q_hi +static fluid_real_t +calc_awe32_filter_q(int data) +{ + typedef struct + { + fluid_real_t q_lo; + fluid_real_t q_hi; + } awe32_q_table; + + static const awe32_q_table[] = + { + {5.f, 0.f}, /* coef 0 */ + {6.f, 0.5f},/* coef 1 */ + {8.f, 1.f}, /* coef 2 */ + {10.f, 2.f}, /* coef 3 */ + {11.f, 3.f}, /* coef 4 */ + {13.f, 4.f}, /* coef 5 */ + {14.f, 5.f}, /* coef 6 */ + {16.f, 6.f}, /* coef 7 */ + {17.f, 7.f}, /* coef 8 */ + {19.f, 9.f}, /* coef 9 */ + {20.f, 10.f}, /* coef 10 */ + {22.f, 11.f}, /* coef 11 */ + {23.f, 13.f}, /* coef 12 */ + {25.f, 15.f}, /* coef 13 */ + {26.f, 16.f}, /* coef 14 */ + {28.f, 18.f}, /* coef 15 */ + }; + + fluid_real_t q; + awe32_q_table* tab; + + fluid_clip(data, 0, 127); + data /= 8; + tab = &awe32_q_table[data]; + + return (tab->q_lo + tab->q_hi) / 2; +} + /** * This implementation is based on "Frequently Asked Questions for SB AWE32" http://archive.gamedev.net/archive/reference/articles/article445.html * as well as on the "SB AWE32 Developer's Information Pack" https://github.com/user-attachments/files/15757220/adip301.pdf @@ -7655,6 +7701,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOFREQ: case GEN_VIBLFOFREQ: + fluid_clip(data, 0, 127); + converted_sf2_generator_value = fluid_hz2ct(data * 0.084 /* Hz */); break; case GEN_MODENVATTACK: @@ -7680,6 +7728,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODENVSUSTAIN: case GEN_VOLENVSUSTAIN: fluid_clip(data, 0, 127); + converted_sf2_generator_value = data * (0.75 /* dB */ * 10) /* cB */; break; case GEN_PITCH: @@ -7691,18 +7740,31 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOPITCH: case GEN_VIBLFOTOPITCH: case GEN_MODENVTOPITCH: + fluid_clip(data, -127, 127); + converted_sf2_generator_value = data * 9.375 /* cents */; break; case GEN_MODLFOTOVOL: + fluid_clip(data, 0, 127); + converted_sf2_generator_value = data * (0.1875 /* dB */ * 10) /* cB */; break; case GEN_FILTERFC: + fluid_clip(data, 0, 127); + converted_sf2_generator_value = fluid_hz2ct(data * 62 /* Hz */); break; + case GEN_FILTERQ: + converted_sf2_generator_value = calc_awe32_filter_q(data); break; + case GEN_MODLFOTOFILTERFC: + fluid_clip(data, -64, 63); + converted_sf2_generator_value = data * 56.25 /* cents */; break; case GEN_MODENVTOFILTERFC: + fluid_clip(data, -127, 127); + converted_sf2_generator_value = data * 56.25 /* cents */; break; case GEN_REVERBSEND: @@ -7710,6 +7772,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, /* transform the input value */ converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod->flags1, 256); converted_sf2_generator_value*= fluid_mod_get_amount(default_reverb_mod); + break; case GEN_CHORUSSEND: fluid_clip(data, 0, 255); diff --git a/src/utils/fluid_conv.c b/src/utils/fluid_conv.c index 5b0f4d597..59cf2e1dc 100644 --- a/src/utils/fluid_conv.c +++ b/src/utils/fluid_conv.c @@ -242,17 +242,18 @@ fluid_tc2sec_release(fluid_real_t tc) return fluid_tc2sec(tc); } +/** + * The inverse operation, converting from Hertz to cents + */ +fluid_real_t fluid_hz2ct(fluid_real_t f) +{ + return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); +} + /* * fluid_act2hz * * Convert from absolute cents to Hertz - * - * The inverse operation, converting from Hertz to cents, was unused and implemented as - * -fluid_hz2ct(fluid_real_t f) -{ - return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); -} */ double fluid_act2hz(double c) diff --git a/src/utils/fluid_conv.h b/src/utils/fluid_conv.h index b331525e2..6cc0184de 100644 --- a/src/utils/fluid_conv.h +++ b/src/utils/fluid_conv.h @@ -32,6 +32,7 @@ fluid_real_t fluid_tc2sec(fluid_real_t tc); fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); fluid_real_t fluid_tc2sec_release(fluid_real_t tc); +fluid_real_t fluid_hz2ct(fluid_real_t f); double fluid_act2hz(double c); fluid_real_t fluid_pan(fluid_real_t c, int left); fluid_real_t fluid_balance(fluid_real_t balance, int left); From d570552fc252f81adec8b62993ffd61b6eba3d06 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 16 Jun 2024 16:10:20 +0200 Subject: [PATCH 03/19] Fix build --- src/synth/fluid_mod.c | 2 +- src/synth/fluid_mod.h | 2 ++ src/synth/fluid_synth.c | 30 ++++++++++++++++++------------ src/utils/fluid_conv.c | 2 +- src/utils/fluid_conv.h | 2 +- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index 959967208..f511f97d4 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -240,7 +240,7 @@ fluid_mod_get_source_value(const unsigned char mod_src, /** * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0] */ -static fluid_real_t +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] */ diff --git a/src/synth/fluid_mod.h b/src/synth/fluid_mod.h index 3e7661741..375c32b15 100644 --- a/src/synth/fluid_mod.h +++ b/src/synth/fluid_mod.h @@ -45,6 +45,8 @@ 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, char *name); +fluid_real_t fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range); + #ifdef DEBUG void fluid_dump_modulator(fluid_mod_t *mod); diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index ab8b8a161..72624d7a6 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -129,6 +129,7 @@ static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); +static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data); /* Callback handlers for real-time settings */ static void fluid_synth_handle_gain(void *data, const char *name, double value); @@ -7611,9 +7612,9 @@ calc_awe32_filter_q(int data) { fluid_real_t q_lo; fluid_real_t q_hi; - } awe32_q_table; + } awe32_q; - static const awe32_q_table[] = + static const awe32_q awe32_q_table[] = { {5.f, 0.f}, /* coef 0 */ {6.f, 0.5f},/* coef 1 */ @@ -7633,8 +7634,7 @@ calc_awe32_filter_q(int data) {28.f, 18.f}, /* coef 15 */ }; - fluid_real_t q; - awe32_q_table* tab; + awe32_q* tab; fluid_clip(data, 0, 127); data /= 8; @@ -7652,9 +7652,7 @@ calc_awe32_filter_q(int data) */ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data) { - data -= 8192; - - static const fluid_gen_type awe32_to_sf2_gen[] = + static const enum fluid_gen_type awe32_to_sf2_gen[] = { // assuming LFO1 maps to MODLFO and LFO2 maps to VIBLFO // observe how nicely most of the AWE32 generators here match up with the order of SF2 generators in fluid_gen_type @@ -7687,8 +7685,11 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, GEN_REVERBSEND, /**< Reverb send amount */ }; - int sf2_gen = awe32_to_sf2_gen[gen]; + enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen]; fluid_real_t converted_sf2_generator_value; + + data -= 8192; + switch(sf2_gen) { case GEN_MODLFODELAY: @@ -7770,16 +7771,21 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_REVERBSEND: fluid_clip(data, 0, 255); /* transform the input value */ - converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod->flags1, 256); - converted_sf2_generator_value*= fluid_mod_get_amount(default_reverb_mod); + converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod.flags1, 256); + converted_sf2_generator_value*= fluid_mod_get_amount(&default_reverb_mod); break; case GEN_CHORUSSEND: fluid_clip(data, 0, 255); /* transform the input value */ - converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod->flags1, 256); - converted_sf2_generator_value*= fluid_mod_get_amount(default_chorus_mod); + converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod.flags1, 256); + converted_sf2_generator_value*= fluid_mod_get_amount(&default_chorus_mod); break; + + default: + // should not happen + FLUID_LOG(FLUID_WARN, "AWE32 NPRN %d conversion not implemented", gen); + return; } fluid_synth_set_gen_LOCAL(synth, chan, sf2_gen, converted_sf2_generator_value); diff --git a/src/utils/fluid_conv.c b/src/utils/fluid_conv.c index 59cf2e1dc..9b39dfcff 100644 --- a/src/utils/fluid_conv.c +++ b/src/utils/fluid_conv.c @@ -247,7 +247,7 @@ fluid_tc2sec_release(fluid_real_t tc) */ fluid_real_t fluid_hz2ct(fluid_real_t f) { - return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); + return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f); } /* diff --git a/src/utils/fluid_conv.h b/src/utils/fluid_conv.h index 6cc0184de..bd1edb94f 100644 --- a/src/utils/fluid_conv.h +++ b/src/utils/fluid_conv.h @@ -27,7 +27,7 @@ fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); fluid_real_t fluid_cb2amp(fluid_real_t cb); -fluid_real_t fluid_sec2tc(fluid_real_t sec) +fluid_real_t fluid_sec2tc(fluid_real_t sec); fluid_real_t fluid_tc2sec(fluid_real_t tc); fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); From c9a3ed3420d186689d8fcb42417e05309bf3006c Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 25 Jun 2024 23:24:42 +0200 Subject: [PATCH 04/19] Fix impl --- src/sfloader/fluid_defsfont.c | 7 +++- src/synth/fluid_chan.c | 19 +++++++++++ src/synth/fluid_chan.h | 9 ++++++ src/synth/fluid_synth.c | 61 +++++++++++++++++++++++------------ src/synth/fluid_voice.c | 3 -- 5 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 129867d0f..c150c1a05 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -954,7 +954,7 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c for(i = 0; i < GEN_LAST; i++) { - + fluid_real_t awe_val; /* SF 2.01 section 9.4 'bullet' 4: * * A generator in a local instrument zone supersedes a @@ -978,6 +978,11 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c */ } + /* ...unless the default value has been overridden by an AWE32 NRPN */ + if (fluid_channel_get_override_gen_default(synth->channel[chan], i, &awe_val)) + { + fluid_voice_gen_set(voice, i, awe_val); + } } /* for all generators */ /* Adds instrument zone modulators (global and local) to the voice.*/ diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index 84ca446b7..e5b4d0a15 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -128,6 +128,8 @@ fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) for(i = 0; i < GEN_LAST; i++) { chan->gen[i] = 0.0f; + chan->override_gen_default[i].flags = GEN_UNUSED; + chan->override_gen_default[i].val = 0.0f; } if(is_all_ctrl_off) @@ -730,3 +732,20 @@ void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value) chan->previous_cc_breath = value; } + +int fluid_channel_get_override_gen_default(fluid_channel_t *chan, int gen, fluid_real_t* val) +{ + if(chan->override_gen_default[gen].flags != GEN_UNUSED) + { + *val = chan->override_gen_default[gen].val; + return TRUE; + } + + return FALSE; +} + +void fluid_channel_set_override_gen_default(fluid_channel_t *chan, int gen, fluid_real_t val) +{ + chan->override_gen_default[gen].flags = GEN_SET; + chan->override_gen_default[gen].val = val; +} diff --git a/src/synth/fluid_chan.h b/src/synth/fluid_chan.h index 96eb02e37..b9967e5a7 100644 --- a/src/synth/fluid_chan.h +++ b/src/synth/fluid_chan.h @@ -129,6 +129,13 @@ struct _fluid_channel_t * applied to future notes. They are copied to a voice's generators * in fluid_voice_init(), which calls fluid_gen_init(). */ fluid_real_t gen[GEN_LAST]; + + /* Same for AWE32 NRPNs, however they override the gen's default values */ + struct + { + enum fluid_gen_flags flags; + fluid_real_t val; + } override_gen_default[GEN_LAST]; }; fluid_channel_t *new_fluid_channel(fluid_synth_t *synth, int num); @@ -272,5 +279,7 @@ void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan); void fluid_channel_cc_legato(fluid_channel_t *chan, int value); void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value); +int fluid_channel_get_override_gen_default(fluid_channel_t *chan, int gen, fluid_real_t *val); +void fluid_channel_set_override_gen_default(fluid_channel_t *chan, int gen, fluid_real_t val); #endif /* _FLUID_CHAN_H */ diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 72624d7a6..abd1aa49d 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1843,7 +1843,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) int gen = fluid_channel_get_cc(chan, NRPN_LSB); if(gen <= 26) // Effect 26 (reverb) is the last effect to select { - fluid_synth_process_awe32_nrpn_LOCAL(synth, chan, gen, data); + fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data); } else { @@ -7612,35 +7612,37 @@ calc_awe32_filter_q(int data) { fluid_real_t q_lo; fluid_real_t q_hi; + fluid_real_t dc_atten; } awe32_q; + // Q in dB static const awe32_q awe32_q_table[] = { - {5.f, 0.f}, /* coef 0 */ - {6.f, 0.5f},/* coef 1 */ - {8.f, 1.f}, /* coef 2 */ - {10.f, 2.f}, /* coef 3 */ - {11.f, 3.f}, /* coef 4 */ - {13.f, 4.f}, /* coef 5 */ - {14.f, 5.f}, /* coef 6 */ - {16.f, 6.f}, /* coef 7 */ - {17.f, 7.f}, /* coef 8 */ - {19.f, 9.f}, /* coef 9 */ - {20.f, 10.f}, /* coef 10 */ - {22.f, 11.f}, /* coef 11 */ - {23.f, 13.f}, /* coef 12 */ - {25.f, 15.f}, /* coef 13 */ - {26.f, 16.f}, /* coef 14 */ - {28.f, 18.f}, /* coef 15 */ + { 5.f, 0.f, -0.0f }, /* coef 0 */ + { 6.f, 0.5f, -0.5f }, /* coef 1 */ + { 8.f, 1.f, -1.2f }, /* coef 2 */ + { 10.f, 2.f, -1.8f }, /* coef 3 */ + { 11.f, 3.f, -2.5f }, /* coef 4 */ + { 13.f, 4.f, -3.3f }, /* coef 5 */ + { 14.f, 5.f, -4.1f }, /* coef 6 */ + { 16.f, 6.f, -5.5f}, /* coef 7 */ + { 17.f, 7.f, -6.0f }, /* coef 8 */ + { 19.f, 9.f, -6.6f }, /* coef 9 */ + { 20.f, 10.f, -7.2f }, /* coef 10 */ + { 22.f, 11.f, -7.9f }, /* coef 11 */ + { 23.f, 13.f, -8.5f }, /* coef 12 */ + { 25.f, 15.f, -9.3f }, /* coef 13 */ + { 26.f, 16.f, -10.1f },/* coef 14 */ + { 28.f, 18.f, -11.0f}, /* coef 15 */ }; - awe32_q* tab; + const awe32_q* tab; fluid_clip(data, 0, 127); data /= 8; tab = &awe32_q_table[data]; - return (tab->q_lo + tab->q_hi) / 2; + return (tab->q_lo + tab->q_hi) * 10 / 2 /* cB */; } /** @@ -7686,6 +7688,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, }; enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen]; + int is_realtime = FALSE, i; fluid_real_t converted_sf2_generator_value; data -= 8192; @@ -7704,6 +7707,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_VIBLFOFREQ: fluid_clip(data, 0, 127); converted_sf2_generator_value = fluid_hz2ct(data * 0.084 /* Hz */); + is_realtime = TRUE; break; case GEN_MODENVATTACK: @@ -7740,6 +7744,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOPITCH: case GEN_VIBLFOTOPITCH: + is_realtime = TRUE; + /* fallthrough */ case GEN_MODENVTOPITCH: fluid_clip(data, -127, 127); converted_sf2_generator_value = data * 9.375 /* cents */; @@ -7748,11 +7754,13 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOVOL: fluid_clip(data, 0, 127); converted_sf2_generator_value = data * (0.1875 /* dB */ * 10) /* cB */; + is_realtime = TRUE; break; case GEN_FILTERFC: fluid_clip(data, 0, 127); converted_sf2_generator_value = fluid_hz2ct(data * 62 /* Hz */); + is_realtime = TRUE; break; case GEN_FILTERQ: @@ -7762,7 +7770,9 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOFILTERFC: fluid_clip(data, -64, 63); converted_sf2_generator_value = data * 56.25 /* cents */; + is_realtime = TRUE; break; + case GEN_MODENVTOFILTERFC: fluid_clip(data, -127, 127); converted_sf2_generator_value = data * 56.25 /* cents */; @@ -7788,7 +7798,18 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, return; } - fluid_synth_set_gen_LOCAL(synth, chan, sf2_gen, converted_sf2_generator_value); + fluid_channel_set_override_gen_default(synth->channel[chan], sf2_gen, converted_sf2_generator_value); + + for (i = 0; is_realtime && i < synth->polyphony; i++) + { + fluid_voice_t* voice = synth->voice[i]; + + if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan) + { + fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); + } + } + } /** diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index f605832e0..1acd55032 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -1834,9 +1834,6 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) return lower_bound; } - - - int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t nrpn_value) { voice->gen[gen].nrpn = nrpn_value; From c7f92c36f4cec580d204d099b9260b5061516ac6 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 25 Jun 2024 23:47:31 +0200 Subject: [PATCH 05/19] Missing include --- src/synth/fluid_chan.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/synth/fluid_chan.h b/src/synth/fluid_chan.h index b9967e5a7..fc7cf9632 100644 --- a/src/synth/fluid_chan.h +++ b/src/synth/fluid_chan.h @@ -24,6 +24,7 @@ #include "fluidsynth_priv.h" #include "fluid_midi.h" #include "fluid_tuning.h" +#include "fluid_gen.h" /* The mononophonic list is part of the legato detector for monophonic mode */ /* see fluid_synth_monopoly.c about a description of the legato detector device */ From c59d5d0cbe2d6540544dae58314b8b4332d5f8c3 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 13:27:49 +0200 Subject: [PATCH 06/19] Log AWE32 NPRNs in verbose mode --- src/synth/fluid_synth.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 6ba3260de..63b19dfe9 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1599,7 +1599,7 @@ fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) /* chan is enabled */ if(synth->verbose) { - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + FLUID_LOG(FLUID_INFO, "cc\t\t%d\t%d\t%d", chan, num, val); } fluid_channel_set_cc(channel, num, val); @@ -1633,7 +1633,7 @@ fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) { if(synth->verbose) { - FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + FLUID_LOG(FLUID_INFO, "cc\t\t%d\t%d\t%d", i, num, val); } fluid_channel_set_cc(synth->channel[i], num, val); @@ -1841,6 +1841,10 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs { int gen = fluid_channel_get_cc(chan, NRPN_LSB); + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); + } if(gen <= 26) // Effect 26 (reverb) is the last effect to select { fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data); @@ -2870,7 +2874,7 @@ fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val) if(synth->verbose) { - FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + FLUID_LOG(FLUID_INFO, "pitchb\t\t%d\t%d", chan, val); } fluid_channel_set_pitch_bend(synth->channel[chan], val); @@ -3102,7 +3106,7 @@ fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum) if(synth->verbose) { - FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + FLUID_LOG(FLUID_INFO, "prog\t\t%d\t%d\t%d", chan, banknum, prognum); } /* I think this is a hack for MIDI files that do bank changes in GM mode. From acdc353211610faa1a826a4f21837bf0f5360213 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 14:45:25 +0200 Subject: [PATCH 07/19] continue --- src/rvoice/fluid_iir_filter.c | 1 + src/synth/fluid_synth.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index 0535cbf27..7a23d9caf 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -398,6 +398,7 @@ void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, fres = 5.f; } + FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); /* if filter enabled and there is a significant frequency change.. */ if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) { diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 63b19dfe9..f7529616b 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1841,7 +1841,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs { int gen = fluid_channel_get_cc(chan, NRPN_LSB); - if(synth->verbose) + // if(synth->verbose) { FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); } @@ -7646,7 +7646,7 @@ calc_awe32_filter_q(int data) data /= 8; tab = &awe32_q_table[data]; - return (tab->q_lo + tab->q_hi) * 10 / 2 /* cB */; + return (/*tab->q_lo +*/ tab->q_hi) * 10 / 2 /* cB */; } /** @@ -7763,7 +7763,9 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_FILTERFC: fluid_clip(data, 0, 127); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %d Hz",data * 62); converted_sf2_generator_value = fluid_hz2ct(data * 62 /* Hz */); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %f cents", converted_sf2_generator_value); is_realtime = TRUE; break; @@ -7774,18 +7776,21 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOFILTERFC: fluid_clip(data, -64, 63); converted_sf2_generator_value = data * 56.25 /* cents */; + FLUID_LOG(FLUID_INFO, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value); is_realtime = TRUE; break; case GEN_MODENVTOFILTERFC: fluid_clip(data, -127, 127); converted_sf2_generator_value = data * 56.25 /* cents */; + FLUID_LOG(FLUID_INFO, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value); break; case GEN_REVERBSEND: fluid_clip(data, 0, 255); /* transform the input value */ converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod.flags1, 256); + FLUID_LOG(FLUID_INFO, "AWE32 Reverb: %f", converted_sf2_generator_value); converted_sf2_generator_value*= fluid_mod_get_amount(&default_reverb_mod); break; @@ -7793,6 +7798,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, fluid_clip(data, 0, 255); /* transform the input value */ converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod.flags1, 256); + FLUID_LOG(FLUID_INFO, "AWE32 Chorus: %f", converted_sf2_generator_value); converted_sf2_generator_value*= fluid_mod_get_amount(&default_chorus_mod); break; From db2d37aec5dea4d81b8aebdf6edfdff376dfa985 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 15:16:14 +0200 Subject: [PATCH 08/19] move awe32 nrpn to preset level --- src/rvoice/fluid_iir_filter.c | 2 +- src/sfloader/fluid_defsfont.c | 17 +++++++++-------- src/synth/fluid_synth.c | 3 ++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index 7a23d9caf..cb559c045 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -398,7 +398,7 @@ void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, fres = 5.f; } - FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); + // FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); /* if filter enabled and there is a significant frequency change.. */ if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) { diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index c150c1a05..60d4738d4 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -954,7 +954,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c for(i = 0; i < GEN_LAST; i++) { - fluid_real_t awe_val; /* SF 2.01 section 9.4 'bullet' 4: * * A generator in a local instrument zone supersedes a @@ -977,12 +976,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c * Do nothing, leave it at the default. */ } - - /* ...unless the default value has been overridden by an AWE32 NRPN */ - if (fluid_channel_get_override_gen_default(synth->channel[chan], i, &awe_val)) - { - fluid_voice_gen_set(voice, i, awe_val); - } } /* for all generators */ /* Adds instrument zone modulators (global and local) to the voice.*/ @@ -996,7 +989,7 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c for(i = 0; i < GEN_LAST; i++) { - + fluid_real_t awe_val; /* SF 2.01 section 8.5 page 58: If some generators are encountered at preset level, they should be ignored. However this check is not necessary when the soundfont @@ -1031,6 +1024,14 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c * Do nothing, leave it unchanged. */ } + + /* ...unless the default value has been overridden by an AWE32 NRPN */ + if (fluid_channel_get_override_gen_default(synth->channel[chan], i, &awe_val)) + { + if(i == GEN_FILTERQ) + FLUID_LOG(FLUID_INFO,"Init voice Filter Q %f -> %f", fluid_voice_gen_get(voice, i), awe_val); + fluid_voice_gen_set(voice, i, awe_val); + } } /* for all generators */ /* Adds preset zone modulators (global and local) to the voice.*/ diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index f7529616b..b64823336 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7646,7 +7646,7 @@ calc_awe32_filter_q(int data) data /= 8; tab = &awe32_q_table[data]; - return (/*tab->q_lo +*/ tab->q_hi) * 10 / 2 /* cB */; + return (/*tab->q_lo +*/ tab->q_hi) * 10 /* cB */; } /** @@ -7770,6 +7770,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, break; case GEN_FILTERQ: + FLUID_LOG(FLUID_INFO, "AWE32 IIR Q: %d",data); converted_sf2_generator_value = calc_awe32_filter_q(data); break; From 67b93a7431f03edb4a3bd93f49ad327ed080acef Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 16:48:29 +0200 Subject: [PATCH 09/19] linear Q interpolation? --- src/synth/fluid_chan.c | 1 + src/synth/fluid_chan.h | 2 + src/synth/fluid_synth.c | 89 +++++++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index e5b4d0a15..80a839864 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -131,6 +131,7 @@ fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) chan->override_gen_default[i].flags = GEN_UNUSED; chan->override_gen_default[i].val = 0.0f; } + chan->awe32_filter_coeff = -1; if(is_all_ctrl_off) { diff --git a/src/synth/fluid_chan.h b/src/synth/fluid_chan.h index fc7cf9632..28247bfbf 100644 --- a/src/synth/fluid_chan.h +++ b/src/synth/fluid_chan.h @@ -125,6 +125,8 @@ struct _fluid_channel_t enum fluid_gen_type nrpn_select; /* Generator ID of SoundFont NRPN message */ char nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ + char awe32_filter_coeff; + /* The values of the generators, set by NRPN messages, or by * fluid_synth_set_gen(), are cached in the channel so they can be * applied to future notes. They are copied to a voice's generators diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index b64823336..a2b8aa19e 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7609,11 +7609,15 @@ fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value // * mean // * geometric distance (sqrt(q_lo * q_hi)) // * either q_lo or q_hi +// * linear interpolation between low and high fc +// * log interpolation between low and high fc static fluid_real_t -calc_awe32_filter_q(int data) +calc_awe32_filter_q(int data, fluid_real_t* fc) { typedef struct { + fluid_real_t fc_lo; + fluid_real_t fc_hi; fluid_real_t q_lo; fluid_real_t q_hi; fluid_real_t dc_atten; @@ -7622,31 +7626,37 @@ calc_awe32_filter_q(int data) // Q in dB static const awe32_q awe32_q_table[] = { - { 5.f, 0.f, -0.0f }, /* coef 0 */ - { 6.f, 0.5f, -0.5f }, /* coef 1 */ - { 8.f, 1.f, -1.2f }, /* coef 2 */ - { 10.f, 2.f, -1.8f }, /* coef 3 */ - { 11.f, 3.f, -2.5f }, /* coef 4 */ - { 13.f, 4.f, -3.3f }, /* coef 5 */ - { 14.f, 5.f, -4.1f }, /* coef 6 */ - { 16.f, 6.f, -5.5f}, /* coef 7 */ - { 17.f, 7.f, -6.0f }, /* coef 8 */ - { 19.f, 9.f, -6.6f }, /* coef 9 */ - { 20.f, 10.f, -7.2f }, /* coef 10 */ - { 22.f, 11.f, -7.9f }, /* coef 11 */ - { 23.f, 13.f, -8.5f }, /* coef 12 */ - { 25.f, 15.f, -9.3f }, /* coef 13 */ - { 26.f, 16.f, -10.1f },/* coef 14 */ - { 28.f, 18.f, -11.0f}, /* coef 15 */ + {92, 22000, 5.f, 0.f, -0.0f }, /* coef 0 */ + {93, 8500, 6.f, 0.5f, -0.5f }, /* coef 1 */ + {94, 8300, 8.f, 1.f, -1.2f }, /* coef 2 */ + {95, 8200, 10.f, 2.f, -1.8f }, /* coef 3 */ + {96, 8100, 11.f, 3.f, -2.5f }, /* coef 4 */ + {97, 8000, 13.f, 4.f, -3.3f }, /* coef 5 */ + {98, 7900, 14.f, 5.f, -4.1f }, /* coef 6 */ + {99, 7800, 16.f, 6.f, -5.5f}, /* coef 7 */ + {100, 7700, 17.f, 7.f, -6.0f }, /* coef 8 */ + {100, 7500, 19.f, 9.f, -6.6f }, /* coef 9 */ + {100, 7400, 20.f, 10.f, -7.2f }, /* coef 10 */ + {100, 7300, 22.f, 11.f, -7.9f }, /* coef 11 */ + {100, 7200, 23.f, 13.f, -8.5f }, /* coef 12 */ + {100, 7100, 25.f, 15.f, -9.3f }, /* coef 13 */ + {100, 7100, 26.f, 16.f, -10.1f },/* coef 14 */ + {100, 7000, 28.f, 18.f, -11.0f}, /* coef 15 */ }; const awe32_q* tab; + fluid_real_t alpha; fluid_clip(data, 0, 127); data /= 8; tab = &awe32_q_table[data]; + + fluid_clip(*fc, tab->fc_lo, tab->fc_hi); + + alpha = (*fc - tab->fc_lo) / (tab->fc_hi - tab->fc_lo); - return (/*tab->q_lo +*/ tab->q_hi) * 10 /* cB */; + // linearly interpolate between high and low Q + return 10 * /* cB */ (tab->q_lo * (1.0f - alpha) + tab->q_hi * alpha); } /** @@ -7763,16 +7773,15 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_FILTERFC: fluid_clip(data, 0, 127); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %d Hz",data * 62); - converted_sf2_generator_value = fluid_hz2ct(data * 62 /* Hz */); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %f cents", converted_sf2_generator_value); + // conversion continues below! + converted_sf2_generator_value = (data * 62 /* Hz */); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value); is_realtime = TRUE; break; case GEN_FILTERQ: - FLUID_LOG(FLUID_INFO, "AWE32 IIR Q: %d",data); - converted_sf2_generator_value = calc_awe32_filter_q(data); - break; + synth->channel[chan]->awe32_filter_coeff = data; + return; case GEN_MODLFOTOFILTERFC: fluid_clip(data, -64, 63); @@ -7808,7 +7817,28 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, FLUID_LOG(FLUID_WARN, "AWE32 NPRN %d conversion not implemented", gen); return; } + + fluid_real_t q; + int coef = synth->channel[chan]->awe32_filter_coeff; + if(sf2_gen == GEN_FILTERFC) + { + if(coef != -1) + { + q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc (fixed): %f cents", converted_sf2_generator_value); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Q: %f cB", q); + converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); + + // Safe the "true initial Q" + fluid_channel_set_override_gen_default(synth->channel[chan], GEN_FILTERQ, q); + } + else + { + converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); + } + } + fluid_channel_set_override_gen_default(synth->channel[chan], sf2_gen, converted_sf2_generator_value); for (i = 0; is_realtime && i < synth->polyphony; i++) @@ -7817,10 +7847,17 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan) { - fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); + int coef = synth->channel[chan]->awe32_filter_coeff; + if(sf2_gen == GEN_FILTERFC && coef != -1) + { + // sets the adjusted fc + fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); + + // sets the calculated Q + fluid_voice_gen_set(voice, GEN_FILTERQ, q); + } } } - } /** From f8549e2ce818d17b1e41bf28fcb232dc36a52658 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 17:14:48 +0200 Subject: [PATCH 10/19] disable mod to fc generators due to incompatibility --- src/rvoice/fluid_iir_filter.c | 2 +- src/synth/fluid_synth.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index cb559c045..7a23d9caf 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -398,7 +398,7 @@ void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, fres = 5.f; } - // FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); + FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); /* if filter enabled and there is a significant frequency change.. */ if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) { diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index a2b8aa19e..49246822d 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7788,13 +7788,15 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, converted_sf2_generator_value = data * 56.25 /* cents */; FLUID_LOG(FLUID_INFO, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value); is_realtime = TRUE; - break; + // not supported, as this modulates the "phase" rather than the filters cutoff frequency + return; case GEN_MODENVTOFILTERFC: fluid_clip(data, -127, 127); converted_sf2_generator_value = data * 56.25 /* cents */; FLUID_LOG(FLUID_INFO, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value); - break; + // not supported, as this modulates the "phase" rather than the filters cutoff frequency + return; case GEN_REVERBSEND: fluid_clip(data, 0, 255); From e51a52738439bef85e1102d4e6edf63068859025 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Jun 2024 21:56:06 +0200 Subject: [PATCH 11/19] transposed direct form 2 --- src/rvoice/fluid_iir_filter.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index 7a23d9caf..e20241f85 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -83,7 +83,7 @@ fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, * doesn't change. */ - if(dsp_filter_coeff_incr_count > 0) + if(0) { fluid_real_t dsp_a1_incr = iir_filter->a1_incr; fluid_real_t dsp_a2_incr = iir_filter->a2_incr; @@ -123,10 +123,16 @@ fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, for(dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ - dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; - dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; - dsp_hist2 = dsp_hist1; - dsp_hist1 = dsp_centernode; + //dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + //dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + //dsp_hist2 = dsp_hist1; + //dsp_hist1 = dsp_centernode; + + /* The filter is implemented in Transposed Direct Form II */ + fluid_real_t dsp_input = dsp_buf[dsp_i]; + dsp_buf[dsp_i] = dsp_b02 * dsp_input + dsp_hist1; + dsp_hist1 = dsp_b1 * dsp_input - dsp_a1 * dsp_buf[dsp_i] + dsp_hist2; + dsp_hist2 = dsp_b02 * dsp_input - dsp_a2 * dsp_buf[dsp_i]; } } From 37c9c1ba338885a4a12ffe2fbffb1f5078f609a0 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Jun 2024 12:12:34 +0200 Subject: [PATCH 12/19] revert some experiments --- src/rvoice/fluid_iir_filter.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index e20241f85..e7ded6672 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -83,7 +83,7 @@ fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, * doesn't change. */ - if(0) + if(dsp_filter_coeff_incr_count > 0) { fluid_real_t dsp_a1_incr = iir_filter->a1_incr; fluid_real_t dsp_a2_incr = iir_filter->a2_incr; @@ -123,16 +123,16 @@ fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, for(dsp_i = 0; dsp_i < count; dsp_i++) { /* The filter is implemented in Direct-II form. */ - //dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; - //dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; - //dsp_hist2 = dsp_hist1; - //dsp_hist1 = dsp_centernode; + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; /* The filter is implemented in Transposed Direct Form II */ - fluid_real_t dsp_input = dsp_buf[dsp_i]; - dsp_buf[dsp_i] = dsp_b02 * dsp_input + dsp_hist1; - dsp_hist1 = dsp_b1 * dsp_input - dsp_a1 * dsp_buf[dsp_i] + dsp_hist2; - dsp_hist2 = dsp_b02 * dsp_input - dsp_a2 * dsp_buf[dsp_i]; + // fluid_real_t dsp_input = dsp_buf[dsp_i]; + // dsp_buf[dsp_i] = dsp_b02 * dsp_input + dsp_hist1; + // dsp_hist1 = dsp_b1 * dsp_input - dsp_a1 * dsp_buf[dsp_i] + dsp_hist2; + // dsp_hist2 = dsp_b02 * dsp_input - dsp_a2 * dsp_buf[dsp_i]; } } @@ -404,7 +404,7 @@ void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, fres = 5.f; } - FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); + // FLUID_LOG(FLUID_INFO, "%f + %f cents = %f cents = %f Hz | Q: %f", iir_filter->fres, fres_mod, iir_filter->fres + fres_mod, fres, iir_filter->q_lin); /* if filter enabled and there is a significant frequency change.. */ if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) { From ab830a56af141b9f52393b08e1bad28e16017dad Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Jun 2024 13:01:15 +0200 Subject: [PATCH 13/19] fix decl --- src/synth/fluid_synth.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 49246822d..14fccf91b 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7702,8 +7702,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, }; enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen]; - int is_realtime = FALSE, i; - fluid_real_t converted_sf2_generator_value; + int is_realtime = FALSE, i, coef; + fluid_real_t converted_sf2_generator_value, q; data -= 8192; @@ -7820,8 +7820,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, return; } - fluid_real_t q; - int coef = synth->channel[chan]->awe32_filter_coeff; + coef = synth->channel[chan]->awe32_filter_coeff; if(sf2_gen == GEN_FILTERFC) { if(coef != -1) From 2eb8ed8950d7b949fafa15b9c372c0bd83c3eb7d Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Jun 2024 14:22:05 +0200 Subject: [PATCH 14/19] allow data LSB to also update AWE32 NRPN --- src/synth/fluid_synth.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 14fccf91b..de8344ee6 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1812,8 +1812,29 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) break; case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + { + int data = (fluid_channel_get_cc(chan, DATA_ENTRY_MSB) << 7) + value; + // ALTITUDE.MID also manipulates AWE32 NRPNs by only using DATA LSB events - seems to be legal + if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs + { + int gen; + AWE32_NRPN: + gen = fluid_channel_get_cc(chan, NRPN_LSB); + // if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); + } + if(gen <= 26) // Effect 26 (reverb) is the last effect to select + { + fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data); + } + else + { + FLUID_LOG(FLUID_INFO, "Ignoring unknown AWE32 NRPN targetting effect %d", gen); + } + } break; - + } case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ { int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); @@ -1840,19 +1861,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) } else if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs { - int gen = fluid_channel_get_cc(chan, NRPN_LSB); - // if(synth->verbose) - { - FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); - } - if(gen <= 26) // Effect 26 (reverb) is the last effect to select - { - fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data); - } - else - { - FLUID_LOG(FLUID_INFO, "Ignoring unknown AWE32 NRPN targetting effect %d", gen); - } + goto AWE32_NRPN; } } else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ @@ -7780,6 +7789,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, break; case GEN_FILTERQ: + FLUID_LOG(FLUID_INFO, "AWE32 IIR Q Tab: %d",data); synth->channel[chan]->awe32_filter_coeff = data; return; From 676bd1447e57450a33fb39cb71ab1fb9027e7bc5 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Jun 2024 17:20:14 +0200 Subject: [PATCH 15/19] Fix some float divisions --- src/synth/fluid_synth.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index de8344ee6..f38a96de6 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7723,26 +7723,26 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODENVDELAY: case GEN_VOLENVDELAY: fluid_clip(data, 0, 5900); - converted_sf2_generator_value = fluid_sec2tc(data * 4 / 1000); + converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0)); break; case GEN_MODLFOFREQ: case GEN_VIBLFOFREQ: fluid_clip(data, 0, 127); - converted_sf2_generator_value = fluid_hz2ct(data * 0.084 /* Hz */); + converted_sf2_generator_value = fluid_hz2ct(data * (fluid_real_t)0.084 /* Hz */); is_realtime = TRUE; break; case GEN_MODENVATTACK: case GEN_VOLENVATTACK: fluid_clip(data, 0, 5940); - converted_sf2_generator_value = fluid_sec2tc(data * 1 / 1000); + converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0)); break; case GEN_MODENVHOLD: case GEN_VOLENVHOLD: fluid_clip(data, 0, 8191); - converted_sf2_generator_value = fluid_sec2tc(data * 1 / 1000); + converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(1.0 / 1000.0)); break; case GEN_MODENVDECAY: @@ -7750,18 +7750,20 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_VOLENVDECAY: case GEN_VOLENVRELEASE: fluid_clip(data, 0, 5940); - converted_sf2_generator_value = fluid_sec2tc(data * 4 / 1000); + converted_sf2_generator_value = fluid_sec2tc(data * (fluid_real_t)(4.0 / 1000.0)); break; case GEN_MODENVSUSTAIN: case GEN_VOLENVSUSTAIN: fluid_clip(data, 0, 127); - converted_sf2_generator_value = data * (0.75 /* dB */ * 10) /* cB */; + converted_sf2_generator_value = data * (fluid_real_t)(0.75 /* dB */ * 10) /* cB */; break; case GEN_PITCH: converted_sf2_generator_value = data + 8192; - // This has the side effect of manipulating the state of the channel's pitchwheel, but I'll buy it + // This has the side effect of manipulating the modulation state of the channel's pitchwheel, but + // I'll buy it, since pitch bend is not a regular SF2 generator and we do a bit of magic there to + // make it work fluid_synth_pitch_bend(synth, chan, converted_sf2_generator_value); return; @@ -7771,12 +7773,12 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, /* fallthrough */ case GEN_MODENVTOPITCH: fluid_clip(data, -127, 127); - converted_sf2_generator_value = data * 9.375 /* cents */; + converted_sf2_generator_value = data * (fluid_real_t)9.375 /* cents */; break; case GEN_MODLFOTOVOL: fluid_clip(data, 0, 127); - converted_sf2_generator_value = data * (0.1875 /* dB */ * 10) /* cB */; + converted_sf2_generator_value = data * (fluid_real_t)(0.1875 /* dB */ * 10.0) /* cB */; is_realtime = TRUE; break; @@ -7795,7 +7797,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOTOFILTERFC: fluid_clip(data, -64, 63); - converted_sf2_generator_value = data * 56.25 /* cents */; + converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */; FLUID_LOG(FLUID_INFO, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value); is_realtime = TRUE; // not supported, as this modulates the "phase" rather than the filters cutoff frequency @@ -7803,7 +7805,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODENVTOFILTERFC: fluid_clip(data, -127, 127); - converted_sf2_generator_value = data * 56.25 /* cents */; + converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */; FLUID_LOG(FLUID_INFO, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value); // not supported, as this modulates the "phase" rather than the filters cutoff frequency return; From 016d5de41ffccee802081103cb8cea82523084a8 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 21 Sep 2024 16:11:25 +0200 Subject: [PATCH 16/19] fc -= 500 --- src/synth/fluid_synth.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index e9c793033..ac43083e0 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7661,6 +7661,9 @@ calc_awe32_filter_q(int data, fluid_real_t* fc) // linearly interpolate between high and low Q return 10 * /* cB */ (tab->q_lo * (1.0f - alpha) + tab->q_hi * alpha); + + // alternatively: log interpolation + // return 10 * /* cB */ FLUID_POW(tab->q_hi, alpha) * FLUID_POW(tab->q_lo, 1.0f - alpha); } /** @@ -7830,10 +7833,12 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, coef = synth->channel[chan]->awe32_filter_coeff; if(sf2_gen == GEN_FILTERFC) { + // The cutoff at fc seems to be very steep for SoundBlaster! hardware. Listening tests have shown that lowering the cutoff frequency by 500Hz gives a closer signal to the SB! hardware filter... + converted_sf2_generator_value -= 500; if(coef != -1) { q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc (fixed): %f cents", converted_sf2_generator_value); + FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc (fixed): %f Hz", converted_sf2_generator_value); FLUID_LOG(FLUID_INFO, "AWE32 IIR Q: %f cB", q); converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); From e35b0a43324b5d8f1f52f0531428a2c4f819b9d3 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 21 Sep 2024 16:44:37 +0200 Subject: [PATCH 17/19] disable debug logging --- src/sfloader/fluid_defsfont.c | 2 -- src/synth/fluid_synth.c | 18 +++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index ef7307dbc..7e221a724 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1028,8 +1028,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c /* ...unless the default value has been overridden by an AWE32 NRPN */ if (fluid_channel_get_override_gen_default(synth->channel[chan], i, &awe_val)) { - if(i == GEN_FILTERQ) - FLUID_LOG(FLUID_INFO,"Init voice Filter Q %f -> %f", fluid_voice_gen_get(voice, i), awe_val); fluid_voice_gen_set(voice, i, awe_val); } } /* for all generators */ diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index ac43083e0..29e1a5e42 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1845,7 +1845,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) if(fluid_channel_get_cc(chan, NRPN_MSB) == 127) // indicates AWE32 NRPNs { int gen = fluid_channel_get_cc(chan, NRPN_LSB); - // if(synth->verbose) + if(synth->verbose) { FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); } @@ -7784,19 +7784,19 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, fluid_clip(data, 0, 127); // conversion continues below! converted_sf2_generator_value = (data * 62 /* Hz */); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value); is_realtime = TRUE; break; case GEN_FILTERQ: - FLUID_LOG(FLUID_INFO, "AWE32 IIR Q Tab: %d",data); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Q Tab: %d",data); synth->channel[chan]->awe32_filter_coeff = data; return; case GEN_MODLFOTOFILTERFC: fluid_clip(data, -64, 63); converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */; - FLUID_LOG(FLUID_INFO, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 MOD LFO TO FILTER Fc: %f cents", converted_sf2_generator_value); is_realtime = TRUE; // not supported, as this modulates the "phase" rather than the filters cutoff frequency return; @@ -7804,7 +7804,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODENVTOFILTERFC: fluid_clip(data, -127, 127); converted_sf2_generator_value = data * (fluid_real_t)56.25 /* cents */; - FLUID_LOG(FLUID_INFO, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 MOD ENV TO FILTER Fc: %f cents", converted_sf2_generator_value); // not supported, as this modulates the "phase" rather than the filters cutoff frequency return; @@ -7812,7 +7812,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, fluid_clip(data, 0, 255); /* transform the input value */ converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_reverb_mod.flags1, 256); - FLUID_LOG(FLUID_INFO, "AWE32 Reverb: %f", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 Reverb: %f", converted_sf2_generator_value); converted_sf2_generator_value*= fluid_mod_get_amount(&default_reverb_mod); break; @@ -7820,7 +7820,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, fluid_clip(data, 0, 255); /* transform the input value */ converted_sf2_generator_value = fluid_mod_transform_source_value(data, default_chorus_mod.flags1, 256); - FLUID_LOG(FLUID_INFO, "AWE32 Chorus: %f", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 Chorus: %f", converted_sf2_generator_value); converted_sf2_generator_value*= fluid_mod_get_amount(&default_chorus_mod); break; @@ -7838,8 +7838,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, if(coef != -1) { q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Fc (fixed): %f Hz", converted_sf2_generator_value); - FLUID_LOG(FLUID_INFO, "AWE32 IIR Q: %f cB", q); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q); converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); From 6f1778f5fdbf6c0fcce4b5c5e5c6e84af4f4af05 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 6 Oct 2024 11:37:44 +0200 Subject: [PATCH 18/19] Tweak AWE32 impl to support Uplift.mid --- src/synth/fluid_chan.c | 4 ++- src/synth/fluid_synth.c | 58 +++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index 80a839864..be348be66 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -131,7 +131,9 @@ fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) chan->override_gen_default[i].flags = GEN_UNUSED; chan->override_gen_default[i].val = 0.0f; } - chan->awe32_filter_coeff = -1; + // Not all MIDIs initialize the IIR filter coefficient, e.g. Uplift.mid. + // A default value is not documented, hence I'm assuming zero here. + chan->awe32_filter_coeff = 0; if(is_all_ctrl_off) { diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 29e1a5e42..07f5d49a0 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -129,7 +129,7 @@ static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); -static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data); +static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data, int data_lsb); /* Callback handlers for real-time settings */ static void fluid_synth_handle_gain(void *data, const char *name, double value); @@ -1847,11 +1847,11 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) int gen = fluid_channel_get_cc(chan, NRPN_LSB); if(synth->verbose) { - FLUID_LOG(FLUID_INFO, "AWE32 NRPN\t%d\t%d\t%d", channum, gen, data); + FLUID_LOG(FLUID_INFO, "AWE32 NRPN RAW: Chan %d, Gen %d, data %d | 0x%X, MSB: %d, LSB: %d", channum, gen, data, data, msb_value, lsb_value); } if(gen <= 26) // Effect 26 (reverb) is the last effect to select { - fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data); + fluid_synth_process_awe32_nrpn_LOCAL(synth, channum, gen, data, lsb_value); } else { @@ -7673,7 +7673,7 @@ calc_awe32_filter_q(int data, fluid_real_t* fc) * @param gen the AWE32 effect or generator to manipulate * @param data the composed value of DATA_MSB and DATA_LSB */ -static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data) +static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, int gen, int data, int data_lsb) { static const enum fluid_gen_type awe32_to_sf2_gen[] = { @@ -7711,9 +7711,11 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, enum fluid_gen_type sf2_gen = awe32_to_sf2_gen[gen]; int is_realtime = FALSE, i, coef; fluid_real_t converted_sf2_generator_value, q; - + + // The AWE32 NRPN docs say that a value of 8192 is considered to be the middle, i.e. zero. + // However, it looks like for those generators which work in range [0,127], the AWE32 only inspects the DATA_LSB, i.e. and not doing this subtraction. Found while investigating Uplift.mid. data -= 8192; - + switch(sf2_gen) { case GEN_MODLFODELAY: @@ -7726,8 +7728,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODLFOFREQ: case GEN_VIBLFOFREQ: - fluid_clip(data, 0, 127); - converted_sf2_generator_value = fluid_hz2ct(data * (fluid_real_t)0.084 /* Hz */); + fluid_clip(data_lsb, 0, 127); + converted_sf2_generator_value = fluid_hz2ct(data_lsb * (fluid_real_t)0.084 /* Hz */); is_realtime = TRUE; break; @@ -7753,8 +7755,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, case GEN_MODENVSUSTAIN: case GEN_VOLENVSUSTAIN: - fluid_clip(data, 0, 127); - converted_sf2_generator_value = data * (fluid_real_t)(0.75 /* dB */ * 10) /* cB */; + fluid_clip(data_lsb, 0, 127); + converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.75 /* dB */ * 10) /* cB */; break; case GEN_PITCH: @@ -7775,22 +7777,24 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, break; case GEN_MODLFOTOVOL: - fluid_clip(data, 0, 127); - converted_sf2_generator_value = data * (fluid_real_t)(0.1875 /* dB */ * 10.0) /* cB */; + fluid_clip(data_lsb, 0, 127); + converted_sf2_generator_value = data_lsb * (fluid_real_t)(0.1875 /* dB */ * 10.0) /* cB */; is_realtime = TRUE; break; case GEN_FILTERFC: - fluid_clip(data, 0, 127); + fluid_clip(data_lsb, 0, 127); + // Yes, DO NOT use data here, Uplift.mid doesn't set MSB=64, therefore we would always get a negative value after subtracting 8192. + // Since Uplift.mid sounds fine on hardware though, it seems like AWE32 only inspects DATA_LSB in this case. // conversion continues below! - converted_sf2_generator_value = (data * 62 /* Hz */); + converted_sf2_generator_value = (data_lsb * 62 /* Hz */); FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc: %f Hz",converted_sf2_generator_value); is_realtime = TRUE; break; case GEN_FILTERQ: - FLUID_LOG(FLUID_DBG, "AWE32 IIR Q Tab: %d",data); - synth->channel[chan]->awe32_filter_coeff = data; + FLUID_LOG(FLUID_DBG, "AWE32 IIR Q Tab: %d",data_lsb); + synth->channel[chan]->awe32_filter_coeff = data_lsb; return; case GEN_MODLFOTOFILTERFC: @@ -7835,21 +7839,14 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, { // The cutoff at fc seems to be very steep for SoundBlaster! hardware. Listening tests have shown that lowering the cutoff frequency by 500Hz gives a closer signal to the SB! hardware filter... converted_sf2_generator_value -= 500; - if(coef != -1) - { - q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); - FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value); - FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q); + q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value); + FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q); - converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); + converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); - // Safe the "true initial Q" - fluid_channel_set_override_gen_default(synth->channel[chan], GEN_FILTERQ, q); - } - else - { - converted_sf2_generator_value = fluid_hz2ct(converted_sf2_generator_value /* Hz */); - } + // Safe the "true initial Q" + fluid_channel_set_override_gen_default(synth->channel[chan], GEN_FILTERQ, q); } fluid_channel_set_override_gen_default(synth->channel[chan], sf2_gen, converted_sf2_generator_value); @@ -7860,8 +7857,7 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan) { - int coef = synth->channel[chan]->awe32_filter_coeff; - if(sf2_gen == GEN_FILTERFC && coef != -1) + if(sf2_gen == GEN_FILTERFC) { // sets the adjusted fc fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); From 149c24541acdd2910fafa4dbd48b343f90ba77eb Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 13 Oct 2024 13:47:07 +0200 Subject: [PATCH 19/19] adjust realtime nrpn not affecting voices --- src/synth/fluid_synth.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 07f5d49a0..3683b039a 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -7837,8 +7837,8 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, coef = synth->channel[chan]->awe32_filter_coeff; if(sf2_gen == GEN_FILTERFC) { - // The cutoff at fc seems to be very steep for SoundBlaster! hardware. Listening tests have shown that lowering the cutoff frequency by 500Hz gives a closer signal to the SB! hardware filter... - converted_sf2_generator_value -= 500; + // The cutoff at fc seems to be very steep for SoundBlaster! hardware. Listening tests have shown that lowering the cutoff frequency by 1000Hz gives a closer signal to the SB! hardware filter... + converted_sf2_generator_value -= 1000; q = calc_awe32_filter_q(coef, &converted_sf2_generator_value); FLUID_LOG(FLUID_DBG, "AWE32 IIR Fc (corrected): %f Hz", converted_sf2_generator_value); FLUID_LOG(FLUID_DBG, "AWE32 IIR Q: %f cB", q); @@ -7857,13 +7857,16 @@ static void fluid_synth_process_awe32_nrpn_LOCAL(fluid_synth_t *synth, int chan, if (fluid_voice_is_playing(voice) && fluid_voice_get_channel(voice) == chan) { + // sets the adjusted generator + fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); + fluid_voice_update_param(voice, sf2_gen); + + FLUID_LOG(FLUID_DBG, "AWE32 Realtime: adjusting voice id %d, generator %d, chan %d", fluid_voice_get_id(voice), sf2_gen, chan); if(sf2_gen == GEN_FILTERFC) { - // sets the adjusted fc - fluid_voice_gen_set(voice, sf2_gen, converted_sf2_generator_value); - - // sets the calculated Q + // also sets the calculated Q fluid_voice_gen_set(voice, GEN_FILTERQ, q); + fluid_voice_update_param(voice, GEN_FILTERQ); } } }