From 36da0111916866856bb8dc0b12158b55c9651b3c Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun, 25 Nov 2018 08:21:51 +0100 Subject: [PATCH] Avoid redundant dsp parameter updates upon modulation. (#461) --- src/synth/fluid_voice.c | 102 ++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 2f146ba0..38b4bfe6 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/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 @@ -1163,36 +1164,56 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen) * * The update is done in three steps: * - * - first, we look for all the modulators that have the changed + * - step 1: 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. * - * - 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 2: For every changed 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. * - * - For every changed generator, convert its value to the correct - * unit of the corresponding DSP parameter + * - step 3: We need to 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. + + 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; + /* 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's destination + are updated */ + if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) { - gen = fluid_mod_get_dest(mod); modval = 0.0; @@ -1207,10 +1228,17 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) } 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 */ + /* set the bit that indicates this generator is updated */ + set_gen_updated(updated_gen_bit, gen); + } + } + + /* step 3: now recalculate the parameter values that are derived from the + generator */ + for(gen = 0; gen < GEN_LAST; gen++) + { + if (is_gen_updated(updated_gen_bit, gen)) + { fluid_voice_update_param(voice, gen); } } @@ -1222,47 +1250,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 --------------------------------------------------*/