From e096919477234baafffca4464a4e28924f47fb74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Korodi?= Date: Mon, 5 Jul 2021 00:47:47 +0200 Subject: [PATCH] Select soundfont samples by frequency instead of midi note numbers (#932) For detuned channels it might be better to use another key for Soundfont sample selection giving better approximations for the pitch than the original key. Example: play key 60 on 6370 Hz => use tuned key 64 for sample selection This feature is only enabled for melodic channels. For drum channels we always select Soundfont samples by key numbers. --- AUTHORS | 1 + src/sfloader/fluid_defsfont.c | 23 +++++++++++++++++++++-- src/synth/fluid_chan.c | 21 +++++++++++++++++++++ src/synth/fluid_chan.h | 1 + src/synth/fluid_voice.c | 5 ++++- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index fb6766b6..14fc45be 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,3 +151,4 @@ Sven Meier Marcus Weseloh Jean-jacques Ceresa Vladimir Davidovich +Tamás Korodi diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index c45696fc..f1539808 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -27,6 +27,7 @@ #include "fluid_sys.h" #include "fluid_synth.h" #include "fluid_samplecache.h" +#include "fluid_chan.h" /* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and * instrument level in a soundfont. We apply this factor when loading the generator values to stay @@ -867,8 +868,26 @@ 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; + int tuned_key; int i; + /* For detuned channels it might be better to use another key for Soundfont sample selection + * giving better approximations for the pitch than the original key. + * Example: play key 60 on 6370 Hz => use tuned key 64 for sample selection + * + * This feature is only enabled for melodic channels. + * For drum channels we always select Soundfont samples by key numbers. + */ + + if(synth->channel[chan]->channel_type == CHANNEL_TYPE_MELODIC) + { + tuned_key = (int)(fluid_channel_get_key_pitch(synth->channel[chan], key) / 100.0f + 0.5f); + } + else + { + tuned_key = key; + } + global_preset_zone = fluid_defpreset_get_global_zone(defpreset); /* run thru all the zones of this preset */ @@ -879,7 +898,7 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c /* check if the note falls into the key and velocity range of this preset */ - if(fluid_zone_inside_range(&preset_zone->range, key, vel)) + if(fluid_zone_inside_range(&preset_zone->range, tuned_key, vel)) { inst = fluid_preset_zone_get_inst(preset_zone); @@ -894,7 +913,7 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c the key and velocity range of this instrument zone. An instrument zone must be ignored when its voice is already running played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ - if(fluid_zone_inside_range(&voice_zone->range, key, vel)) + if(fluid_zone_inside_range(&voice_zone->range, tuned_key, vel)) { inst_zone = voice_zone->inst_zone; diff --git a/src/synth/fluid_chan.c b/src/synth/fluid_chan.c index 5f2674b5..b685a766 100644 --- a/src/synth/fluid_chan.c +++ b/src/synth/fluid_chan.c @@ -374,6 +374,27 @@ fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, } } +/** + * Compute the pitch for a key after applying Fluidsynth's tuning functionality + * and channel coarse/fine tunings. + * @param chan fluid_channel_t + * @param key MIDI note number (0-127) + * @return the pitch of the key + */ +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key) +{ + if(chan->tuning) + { + return fluid_tuning_get_pitch(chan->tuning, key) + + 100.0f * fluid_channel_get_gen(chan, GEN_COARSETUNE) + + fluid_channel_get_gen(chan, GEN_FINETUNE); + } + else + { + return key * 100.0f; + } +} + /** * Updates legato/ staccato playing state * The function is called: diff --git a/src/synth/fluid_chan.h b/src/synth/fluid_chan.h index a9ca1bca..96eb02e3 100644 --- a/src/synth/fluid_chan.h +++ b/src/synth/fluid_chan.h @@ -142,6 +142,7 @@ void fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb); void fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb); void fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, int *bank, int *prog); +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key); #define fluid_channel_get_preset(chan) ((chan)->preset) #define fluid_channel_set_cc(chan, num, val) \ diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 47f28d2a..7cd6e752 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -670,6 +670,7 @@ calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY */ + fluid_real_t keysteps; fluid_real_t timecents; fluid_real_t seconds; int buffers; @@ -681,7 +682,9 @@ calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, * will cause (60-72)*100=-1200 timecents of time variation. * The time is cut in half. */ - timecents = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (fluid_real_t)(60 - fluid_voice_get_actual_key(voice))); + + keysteps = 60.0f - fluid_channel_get_key_pitch(voice->channel, fluid_voice_get_actual_key(voice)) / 100.0f; + timecents = fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * keysteps; /* Range checking */ if(is_decay)