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.
This commit is contained in:
Tamás Korodi 2021-07-05 00:47:47 +02:00 committed by GitHub
parent 7ff164d8ab
commit e096919477
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 3 deletions

View file

@ -151,3 +151,4 @@ Sven Meier
Marcus Weseloh
Jean-jacques Ceresa
Vladimir Davidovich
Tamás Korodi

View file

@ -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;

View file

@ -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:

View file

@ -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) \

View file

@ -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)