Update Fluidsynth to 2.0.1
[ardour.git] / libs / fluidsynth / src / fluid_voice.c
index e6efbac89979eacb72c9997b105e6ee2c819ae73..51c1ebf655680ed090b229eeddfc3e00a5ef14a8 100644 (file)
@@ -3,16 +3,16 @@
  * Copyright (C) 2003  Peter Hanappe and others.
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public License
- * as published by the Free Software Foundation; either version 2 of
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA
@@ -27,6 +27,7 @@
 #include "fluid_sys.h"
 #include "fluid_sfont.h"
 #include "fluid_rvoice_event.h"
+#include "fluid_defsfont.h"
 
 /* used for filter turn off optimization - if filter cutoff is above the
    specified value and filter q is below the other value, turn filter off */
 /* min vol envelope release (to stop clicks) in SoundFont timecents */
 #define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */
 
-static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice);
-static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
+
+static const int32_t INT24_MAX = (1 << (16 + 8 - 1));
+
+static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice);
+static int calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base,
                                         int gen_key2base, int is_decay);
 static fluid_real_t
-fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_tvoice);
+fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice);
 
 #define UPDATE_RVOICE0(proc) \
   do { \
-    if (voice->can_access_rvoice) proc(voice->rvoice); \
-    else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
-      proc, voice->rvoice, 0, 0.0f); \
-  } while (0)
-
-#define UPDATE_RVOICE_PTR(proc, obj) \
-  do { \
-    if (voice->can_access_rvoice) proc(voice->rvoice, obj); \
-    else fluid_rvoice_eventhandler_push_ptr(voice->channel->synth->eventhandler, \
-      proc, voice->rvoice, obj); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \
   } while (0)
 
-
 #define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \
   do { \
-    if (voice->can_access_rvoice) proc(obj, rarg); \
-    else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
-      proc, obj, 0, rarg); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].real = rarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
 #define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \
   do { \
-    if (voice->can_access_rvoice) proc(obj, iarg); \
-    else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
-      proc, obj, iarg, 0.0f); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
-#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
+#define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \
   do { \
-    if (voice->can_access_rvoice) proc(obj, iarg, rarg); \
-    else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
-      proc, obj, iarg, rarg); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg1; \
+      param[1].i = iarg2; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
-#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \
+#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
   do { \
-    if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \
-    else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \
-      proc, obj, iarg, r1, r2, r3, r4, r5); \
+      fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \
+      param[0].i = iarg; \
+      param[1].real = rarg; \
+      fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \
   } while (0)
 
 
-#define UPDATE_RVOICE_VOLENV(section, arg1, arg2, arg3, arg4, arg5) \
-  do { \
-    fluid_adsr_env_set_data(&voice->volenv, section, arg1, arg2, arg3, arg4, arg5) \
-    UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.volenv, section, arg1, arg2, arg3, arg4, arg5) \
-  } while(0)
-
-#define UPDATE_RVOICE_MODENV(section, arg1, arg2, arg3, arg4, arg5) \
-  UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.modenv, section, arg1, arg2, arg3, arg4, arg5)
-
 #define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1)
 #define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1)
-#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1)
 
-#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg)
-#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
-#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) 
-#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) 
+#define UPDATE_RVOICE_BUFFERS_AMP(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
+#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg)
+#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg)
 
-static inline void
-fluid_voice_update_volenv(fluid_voice_t* voice, 
-                         fluid_adsr_env_section_t section,
+static FLUID_INLINE void
+fluid_voice_update_volenv(fluid_voice_t *voice,
+                          int enqueue,
+                          fluid_adsr_env_section_t section,
                           unsigned int count,
                           fluid_real_t coeff,
                           fluid_real_t increment,
                           fluid_real_t min,
                           fluid_real_t max)
 {
-  fluid_adsr_env_set_data(&voice->volenv, section, count, coeff, increment, 
-                         min, max);
-  UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, 
-                           &voice->rvoice->envlfo.volenv, section, count, 
-                           coeff, increment, min, max);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    param[0].i = section;
+    param[1].i = count;
+    param[2].real = coeff;
+    param[3].real = increment;
+    param[4].real = min;
+    param[5].real = max;
+
+    if(enqueue)
+    {
+        fluid_rvoice_eventhandler_push(voice->eventhandler,
+                                       fluid_adsr_env_set_data,
+                                       &voice->rvoice->envlfo.volenv,
+                                       param);
+    }
+    else
+    {
+        fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param);
+    }
 }
 
-static inline void
-fluid_voice_update_modenv(fluid_voice_t* voice, 
-                         fluid_adsr_env_section_t section,
+static FLUID_INLINE void
+fluid_voice_update_modenv(fluid_voice_t *voice,
+                          int enqueue,
+                          fluid_adsr_env_section_t section,
                           unsigned int count,
                           fluid_real_t coeff,
                           fluid_real_t increment,
                           fluid_real_t min,
                           fluid_real_t max)
 {
-  UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, 
-                           &voice->rvoice->envlfo.modenv, section, count,
-                           coeff, increment, min, max);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    param[0].i = section;
+    param[1].i = count;
+    param[2].real = coeff;
+    param[3].real = increment;
+    param[4].real = min;
+    param[5].real = max;
+
+    if(enqueue)
+    {
+        fluid_rvoice_eventhandler_push(voice->eventhandler,
+                                       fluid_adsr_env_set_data,
+                                       &voice->rvoice->envlfo.modenv,
+                                       param);
+    }
+    else
+    {
+        fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param);
+    }
 }
 
-static inline void fluid_sample_null_ptr(fluid_sample_t** sample)
+static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t **sample)
 {
-  if (*sample != NULL) {
-    fluid_sample_decr_ref(*sample);
-    *sample = NULL;
-  }
+    if(*sample != NULL)
+    {
+        fluid_sample_decr_ref(*sample);
+        *sample = NULL;
+    }
 }
 
 /*
  * Swaps the current rvoice with the current overflow_rvoice
  */
-static void fluid_voice_swap_rvoice(fluid_voice_tvoice)
+static void fluid_voice_swap_rvoice(fluid_voice_t *voice)
 {
-  fluid_rvoice_t* rtemp = voice->rvoice;
-  int ctemp = voice->can_access_rvoice;
-  voice->rvoice = voice->overflow_rvoice;
-  voice->can_access_rvoice = voice->can_access_overflow_rvoice;
-  voice->overflow_rvoice = rtemp;
-  voice->can_access_overflow_rvoice = ctemp;
+    fluid_rvoice_t *rtemp = voice->rvoice;
+    int ctemp = voice->can_access_rvoice;
+    voice->rvoice = voice->overflow_rvoice;
+    voice->can_access_rvoice = voice->can_access_overflow_rvoice;
+    voice->overflow_rvoice = rtemp;
+    voice->can_access_overflow_rvoice = ctemp;
 }
 
-static void fluid_voice_initialize_rvoice(fluid_voice_t* voice)
+static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate)
 {
-  FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t));
-
-  /* The 'sustain' and 'finished' segments of the volume / modulation
-   * envelope are constant. They are never affected by any modulator
-   * or generator. Therefore it is enough to initialize them once
-   * during the lifetime of the synth.
-   */
-  fluid_voice_update_volenv(voice, FLUID_VOICE_ENVSUSTAIN, 
-                          0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
-  fluid_voice_update_volenv(voice, FLUID_VOICE_ENVFINISHED, 
-                          0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
-  fluid_voice_update_modenv(voice, FLUID_VOICE_ENVSUSTAIN, 
-                          0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
-  fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED, 
-                          0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+    fluid_rvoice_param_t param[MAX_EVENT_PARAMS];
+
+    FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t));
+
+    /* The 'sustain' and 'finished' segments of the volume / modulation
+     * envelope are constant. They are never affected by any modulator
+     * or generator. Therefore it is enough to initialize them once
+     * during the lifetime of the synth.
+     */
+    fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN,
+                              0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+    fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED,
+                              0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+    fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN,
+                              0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
+    fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED,
+                              0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
+
+    param[0].i = FLUID_IIR_LOWPASS;
+    param[1].i = 0;
+    fluid_iir_filter_init(&voice->rvoice->resonant_filter, param);
+
+    param[0].i = FLUID_IIR_DISABLED;
+    fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param);
+
+    param[0].real = output_rate;
+    fluid_rvoice_set_output_rate(voice->rvoice, param);
 }
 
 /*
  * new_fluid_voice
  */
-fluid_voice_t*
-new_fluid_voice(fluid_real_t output_rate)
+fluid_voice_t *
+new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate)
 {
-  fluid_voice_t* voice;
-  voice = FLUID_NEW(fluid_voice_t);
-  if (voice == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    return NULL;
-  }
-  voice->rvoice = FLUID_NEW(fluid_rvoice_t);
-  voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t);
-  if (voice->rvoice == NULL || voice->overflow_rvoice == NULL) {
-    FLUID_LOG(FLUID_ERR, "Out of memory");
-    FLUID_FREE(voice->rvoice);
-    FLUID_FREE(voice);
-    return NULL;
-  }
-
-  voice->status = FLUID_VOICE_CLEAN;
-  voice->chan = NO_CHANNEL;
-  voice->key = 0;
-  voice->vel = 0;
-  voice->channel = NULL;
-  voice->sample = NULL;
-
-  /* Initialize both the rvoice and overflow_rvoice */
-  voice->can_access_rvoice = 1; 
-  voice->can_access_overflow_rvoice = 1; 
-  fluid_voice_initialize_rvoice(voice);
-  fluid_voice_swap_rvoice(voice);
-  fluid_voice_initialize_rvoice(voice);
-
-  fluid_voice_set_output_rate(voice, output_rate);
-
-  return voice;
+    fluid_voice_t *voice;
+    voice = FLUID_NEW(fluid_voice_t);
+
+    if(voice == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    voice->can_access_rvoice = TRUE;
+    voice->can_access_overflow_rvoice = TRUE;
+
+    voice->rvoice = FLUID_NEW(fluid_rvoice_t);
+    voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t);
+
+    if(voice->rvoice == NULL || voice->overflow_rvoice == NULL)
+    {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        delete_fluid_voice(voice);
+        return NULL;
+    }
+
+    voice->status = FLUID_VOICE_CLEAN;
+    voice->chan = NO_CHANNEL;
+    voice->key = 0;
+    voice->vel = 0;
+    voice->eventhandler = handler;
+    voice->channel = NULL;
+    voice->sample = NULL;
+    voice->output_rate = output_rate;
+
+    /* Initialize both the rvoice and overflow_rvoice */
+    fluid_voice_initialize_rvoice(voice, output_rate);
+    fluid_voice_swap_rvoice(voice);
+    fluid_voice_initialize_rvoice(voice, output_rate);
+
+    return voice;
 }
 
 /*
  * delete_fluid_voice
  */
-int
-delete_fluid_voice(fluid_voice_tvoice)
+void
+delete_fluid_voice(fluid_voice_t *voice)
 {
-  if (voice == NULL) {
-    return FLUID_OK;
-  }
-  if (!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) {
-    /* stop rvoice before deleting voice! */
-    return FLUID_FAILED;
-  }
-  FLUID_FREE(voice->overflow_rvoice);
-  FLUID_FREE(voice->rvoice);
-  FLUID_FREE(voice);
-  return FLUID_OK;
+    fluid_return_if_fail(voice != NULL);
+
+    if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice)
+    {
+        FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id);
+    }
+
+    FLUID_FREE(voice->overflow_rvoice);
+    FLUID_FREE(voice->rvoice);
+    FLUID_FREE(voice);
 }
 
 /* fluid_voice_init
  *
  * Initialize the synthesis process
+ * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange
+ * of the voice.
+ * When playing legato (n1,n2) in mono mode, n2 will use n1 voices
+ * as far as n2 still enters in Keyrange,Velrange of n1.
  */
 int
-fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
-                fluid_channel_t* channel, int key, int vel, unsigned int id,
-                unsigned int start_time, fluid_real_t gain)
+fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample,
+                 fluid_zone_range_t *inst_zone_range,
+                 fluid_channel_t *channel, int key, int vel, unsigned int id,
+                 unsigned int start_time, fluid_real_t gain)
 {
-  /* Note: The voice parameters will be initialized later, when the
-   * generators have been retrieved from the sound font. Here, only
-   * the 'working memory' of the voice (position in envelopes, history
-   * of IIR filters, position in sample etc) is initialized. */
-  int i;
-
-  if (!voice->can_access_rvoice) {
-    if (voice->can_access_overflow_rvoice) 
-      fluid_voice_swap_rvoice(voice);
-    else {
-      FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!");
-      return FLUID_FAILED;
+    /* Note: The voice parameters will be initialized later, when the
+     * generators have been retrieved from the sound font. Here, only
+     * the 'working memory' of the voice (position in envelopes, history
+     * of IIR filters, position in sample etc) is initialized. */
+    int i;
+
+    if(!voice->can_access_rvoice)
+    {
+        if(voice->can_access_overflow_rvoice)
+        {
+            fluid_voice_swap_rvoice(voice);
+        }
+        else
+        {
+            FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!");
+            return FLUID_FAILED;
+        }
     }
-  }
-  /* We are now guaranteed to have access to the rvoice */
-
-  if (voice->sample)
-    fluid_voice_off(voice);
-
-  voice->id = id;
-  voice->chan = fluid_channel_get_num(channel);
-  voice->key = (unsigned char) key;
-  voice->vel = (unsigned char) vel;
-  voice->channel = channel;
-  voice->mod_count = 0;
-  voice->start_time = start_time;
-  voice->debug = 0;
-  voice->has_noteoff = 0;
-  UPDATE_RVOICE0(fluid_rvoice_reset);
-
-  /* Increment the reference count of the sample to prevent the
-     unloading of the soundfont while this voice is playing,
-     once for us and once for the rvoice. */
-  fluid_sample_incr_ref(sample);
-  UPDATE_RVOICE_PTR(fluid_rvoice_set_sample, sample);
-  fluid_sample_incr_ref(sample);
-  voice->sample = sample;
-
-  i = fluid_channel_get_interp_method(channel);
-  UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i);
-
-  /* Set all the generators to their default value, according to SF
-   * 2.01 section 8.1.3 (page 48). The value of NRPN messages are
-   * copied from the channel to the voice's generators. The sound font
-   * loader overwrites them. The generator values are later converted
-   * into voice parameters in
-   * fluid_voice_calculate_runtime_synthesis_parameters.  */
-  fluid_gen_init(&voice->gen[0], channel);
-  UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice));
-
-  voice->synth_gain = gain;
-  /* avoid division by zero later*/
-  if (voice->synth_gain < 0.0000001){
-    voice->synth_gain = 0.0000001;
-  }
-  UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
-
-  /* Set up buffer mapping, should be done more flexible in the future. */
-  i = channel->synth->audio_groups;
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL);
-  i = 2 * (voice->chan % i);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1);
-
-  return FLUID_OK;
+
+    /* We are now guaranteed to have access to the rvoice */
+
+    if(voice->sample)
+    {
+        fluid_voice_off(voice);
+    }
+
+    voice->zone_range = inst_zone_range; /* Instrument zone range for legato */
+    voice->id = id;
+    voice->chan = fluid_channel_get_num(channel);
+    voice->key = (unsigned char) key;
+    voice->vel = (unsigned char) vel;
+    voice->channel = channel;
+    voice->mod_count = 0;
+    voice->start_time = start_time;
+    voice->has_noteoff = 0;
+    UPDATE_RVOICE0(fluid_rvoice_reset);
+
+    /* Increment the reference count of the sample to prevent the
+       unloading of the soundfont while this voice is playing,
+       once for us and once for the rvoice. */
+    fluid_sample_incr_ref(sample);
+    fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample);
+    fluid_sample_incr_ref(sample);
+    voice->sample = sample;
+
+    i = fluid_channel_get_interp_method(channel);
+    UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i);
+
+    /* Set all the generators to their default value, according to SF
+     * 2.01 section 8.1.3 (page 48). The value of NRPN messages are
+     * copied from the channel to the voice's generators. The sound font
+     * loader overwrites them. The generator values are later converted
+     * into voice parameters in
+     * fluid_voice_calculate_runtime_synthesis_parameters.  */
+    fluid_gen_init(&voice->gen[0], channel);
+    UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice));
+
+    voice->synth_gain = gain;
+
+    /* avoid division by zero later*/
+    if(voice->synth_gain < 0.0000001)
+    {
+        voice->synth_gain = 0.0000001;
+    }
+
+    UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
+
+    /* Set up buffer mapping, should be done more flexible in the future. */
+    i = 2 * channel->synth->audio_groups;
+    i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels;
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL);
+
+    i = 2 * (voice->chan % channel->synth->audio_groups);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i);
+    UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1);
+
+    return FLUID_OK;
 }
 
 
 /**
- * Update sample rate. 
- * NOTE: If the voice is active, it will be turned off.
+ * Update sample rate.
+ * @note If the voice is active, it will be turned off.
  */
-int 
-fluid_voice_set_output_rate(fluid_voice_tvoice, fluid_real_t value)
+void
+fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value)
 {
-  if (_PLAYING(voice))
-    fluid_voice_off(voice);
-  
-  voice->output_rate = value;
-  UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value);
-  /* Update the other rvoice as well */
-  fluid_voice_swap_rvoice(voice);
-  UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, value);
-  fluid_voice_swap_rvoice(voice);
-
-  return FLUID_FAILED;
+    if(fluid_voice_is_playing(voice))
+    {
+        fluid_voice_off(voice);
+    }
+
+    voice->output_rate = value;
+    UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value);
+    UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value);
 }
 
 
@@ -339,12 +390,15 @@ fluid_voice_set_output_rate(fluid_voice_t* voice, fluid_real_t value)
  * @param val Generator value
  */
 void
-fluid_voice_gen_set(fluid_voice_tvoice, int i, float val)
+fluid_voice_gen_set(fluid_voice_t *voice, int i, float val)
 {
-  voice->gen[i].val = val;
-  voice->gen[i].flags = GEN_SET;
-  if (i == GEN_SAMPLEMODE)
-    UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val);
+    voice->gen[i].val = val;
+    voice->gen[i].flags = GEN_SET;
+
+    if(i == GEN_SAMPLEMODE)
+    {
+        UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val);
+    }
 }
 
 /**
@@ -354,10 +408,10 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
  * @param val Value to add to the existing value
  */
 void
-fluid_voice_gen_incr(fluid_voice_tvoice, int i, float val)
+fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val)
 {
-  voice->gen[i].val += val;
-  voice->gen[i].flags = GEN_SET;
+    voice->gen[i].val += val;
+    voice->gen[i].flags = GEN_SET;
 }
 
 /**
@@ -367,128 +421,102 @@ fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val)
  * @return Current generator value
  */
 float
-fluid_voice_gen_get(fluid_voice_tvoice, int gen)
+fluid_voice_gen_get(fluid_voice_t *voice, int gen)
 {
-  return voice->gen[gen].val;
+    return voice->gen[gen].val;
 }
 
-fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num)
+fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num)
 {
-       /* This is an extension to the SoundFont standard. More
-        * documentation is available at the fluid_synth_set_gen2()
-        * function. */
-       if (voice->gen[num].flags == GEN_ABS_NRPN) {
-               return (fluid_real_t) voice->gen[num].nrpn;
-       } else {
-               return (fluid_real_t) (voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn);
-       }
+    /* This is an extension to the SoundFont standard. More
+     * documentation is available at the fluid_synth_set_gen2()
+     * function. */
+    if(voice->gen[num].flags == GEN_ABS_NRPN)
+    {
+        return (fluid_real_t) voice->gen[num].nrpn;
+    }
+    else
+    {
+        return (fluid_real_t)(voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn);
+    }
 }
 
-
-/**
- * Synthesize a voice to a buffer.
- *
- * @param voice Voice to synthesize
- * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
- * @return Count of samples written to dsp_buf (can be 0)
- *
- * Panning, reverb and chorus are processed separately. The dsp interpolation
- * routine is in (fluid_dsp_float.c).
+/*
+ * fluid_voice_start
  */
-int
-fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
+void fluid_voice_start(fluid_voice_t *voice)
 {
-  int result;
-  if (!voice->can_access_rvoice) 
-    return 0;
+    /* The maximum volume of the loop is calculated and cached once for each
+     * sample with its nominal loop settings. This happens, when the sample is used
+     * for the first time.*/
 
-  result = fluid_rvoice_write(voice->rvoice, dsp_buf);
+    fluid_voice_calculate_runtime_synthesis_parameters(voice);
 
-  if (result == -1)
-    return 0;
+#ifdef WITH_PROFILING
+    voice->ref = fluid_profile_ref();
+#endif
 
-  if ((result < FLUID_BUFSIZE) && _PLAYING(voice)) /* Voice finished by itself */
-    fluid_voice_off(voice);
+    voice->status = FLUID_VOICE_ON;
 
-  return result;
+    /* Increment voice count */
+    voice->channel->synth->active_voice_count++;
 }
 
-
 /**
- * Mix voice data to left/right (panning), reverb and chorus buffers.
- * @param count Number of samples
- * @param dsp_buf Source buffer
- * @param voice Voice to mix
- * @param left_buf Left audio buffer
- * @param right_buf Right audio buffer
- * @param reverb_buf Reverb buffer
- * @param chorus_buf Chorus buffer
+ * Calculate the amplitude of a voice.
  *
+ * @param gain The gain value in the range [0.0 ; 1.0]
+ * @return An amplitude used by rvoice_mixer's buffers
  */
-void
-fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
-                fluid_real_t* left_buf, fluid_real_t* right_buf,
-                fluid_real_t* reverb_buf, fluid_real_t* chorus_buf)
+static FLUID_INLINE fluid_real_t
+fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain)
 {
-  fluid_rvoice_buffers_t buffers;
-  fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf};
-
-  fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left);
-  fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right);
-  fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb);
-  fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus);
-  fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4);
-
-  fluid_check_fpe ("voice_mix");
+    /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float
+     * samples to [0.0;1.0] divide samples by the max. value of an int24 and
+     * amplify them with the gain */
+    return gain * voice->synth_gain / (INT24_MAX * 1.0f);
 }
 
-
-
-/*
- * fluid_voice_start
- */
-void fluid_voice_start(fluid_voice_t* voice)
+/* Useful to return the nominal pitch of a key */
+/* The nominal pitch is dependant of voice->root_pitch,tuning, and
+   GEN_SCALETUNE generator.
+   This is useful to set the value of GEN_PITCH generator on noteOn.
+   This is useful to get the beginning/ending pitch for portamento.
+*/
+fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key)
 {
-  /* The maximum volume of the loop is calculated and cached once for each
-   * sample with its nominal loop settings. This happens, when the sample is used
-   * for the first time.*/
-
-  fluid_voice_calculate_runtime_synthesis_parameters(voice);
-
-  voice->ref = fluid_profile_ref();
-
-  voice->status = FLUID_VOICE_ON;
+    fluid_tuning_t *tuning;
+    fluid_real_t x, pitch;
+
+    /* Now the nominal pitch of the key is returned.
+     * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a
+     * non-realtime parameter. So we don't allow modulation (as opposed
+     * to fluid_voice_gen_value(voice, GEN_SCALETUNE) When the scale tuning is varied,
+     * one key remains fixed. Here C3 (MIDI number 60) is used.
+     */
+    if(fluid_channel_has_tuning(voice->channel))
+    {
+        tuning = fluid_channel_get_tuning(voice->channel);
+        x = fluid_tuning_get_pitch(tuning, (int)(voice->root_pitch / 100.0f));
+        pitch = voice->gen[GEN_SCALETUNE].val / 100.0f *
+                (fluid_tuning_get_pitch(tuning, key) - x) + x;
+    }
+    else
+    {
+        pitch = voice->gen[GEN_SCALETUNE].val
+                * (key - voice->root_pitch / 100.0f) + voice->root_pitch;
+    }
 
-  /* Increment voice count */
-  voice->channel->synth->active_voice_count++;
+    return pitch;
 }
 
-void 
-fluid_voice_calculate_gen_pitch(fluid_voice_tvoice)
+void
+fluid_voice_calculate_gen_pitch(fluid_voice_t *voice)
 {
-  fluid_tuning_t* tuning;
-  fluid_real_t x;
-
-  /* The GEN_PITCH is a hack to fit the pitch bend controller into the
-   * modulator paradigm.  Now the nominal pitch of the key is set.
-   * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a
-   * non-realtime parameter. So we don't allow modulation (as opposed
-   * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied,
-   * one key remains fixed. Here C3 (MIDI number 60) is used.
-   */
-  if (fluid_channel_has_tuning(voice->channel)) {
-    tuning = fluid_channel_get_tuning (voice->channel);
-    x = fluid_tuning_get_pitch (tuning, (int)(voice->root_pitch / 100.0f));
-    voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val / 100.0f *
-      (fluid_tuning_get_pitch (tuning, voice->key) - x) + x;
-  } else {
-    voice->gen[GEN_PITCH].val = voice->gen[GEN_SCALETUNE].val
-      * (voice->key - voice->root_pitch / 100.0f) + voice->root_pitch;
-  }
-
+    voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice));
 }
 
+
 /*
  * fluid_voice_calculate_runtime_synthesis_parameters
  *
@@ -500,176 +528,206 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t* voice)
  * example, for the pitch since it is modulated by the controllers in
  * cents. */
 static int
-fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_tvoice)
+fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice)
 {
-  int i;
-
-  int list_of_generators_to_initialize[35] = {
-    GEN_STARTADDROFS,                    /* SF2.01 page 48 #0   */
-    GEN_ENDADDROFS,                      /*                #1   */
-    GEN_STARTLOOPADDROFS,                /*                #2   */
-    GEN_ENDLOOPADDROFS,                  /*                #3   */
-    /* GEN_STARTADDRCOARSEOFS see comment below [1]        #4   */
-    GEN_MODLFOTOPITCH,                   /*                #5   */
-    GEN_VIBLFOTOPITCH,                   /*                #6   */
-    GEN_MODENVTOPITCH,                   /*                #7   */
-    GEN_FILTERFC,                        /*                #8   */
-    GEN_FILTERQ,                         /*                #9   */
-    GEN_MODLFOTOFILTERFC,                /*                #10  */
-    GEN_MODENVTOFILTERFC,                /*                #11  */
-    /* GEN_ENDADDRCOARSEOFS [1]                            #12  */
-    GEN_MODLFOTOVOL,                     /*                #13  */
-    /* not defined                                         #14  */
-    GEN_CHORUSSEND,                      /*                #15  */
-    GEN_REVERBSEND,                      /*                #16  */
-    GEN_PAN,                             /*                #17  */
-    /* not defined                                         #18  */
-    /* not defined                                         #19  */
-    /* not defined                                         #20  */
-    GEN_MODLFODELAY,                     /*                #21  */
-    GEN_MODLFOFREQ,                      /*                #22  */
-    GEN_VIBLFODELAY,                     /*                #23  */
-    GEN_VIBLFOFREQ,                      /*                #24  */
-    GEN_MODENVDELAY,                     /*                #25  */
-    GEN_MODENVATTACK,                    /*                #26  */
-    GEN_MODENVHOLD,                      /*                #27  */
-    GEN_MODENVDECAY,                     /*                #28  */
-    /* GEN_MODENVSUSTAIN [1]                               #29  */
-    GEN_MODENVRELEASE,                   /*                #30  */
-    /* GEN_KEYTOMODENVHOLD [1]                             #31  */
-    /* GEN_KEYTOMODENVDECAY [1]                            #32  */
-    GEN_VOLENVDELAY,                     /*                #33  */
-    GEN_VOLENVATTACK,                    /*                #34  */
-    GEN_VOLENVHOLD,                      /*                #35  */
-    GEN_VOLENVDECAY,                     /*                #36  */
-    /* GEN_VOLENVSUSTAIN [1]                               #37  */
-    GEN_VOLENVRELEASE,                   /*                #38  */
-    /* GEN_KEYTOVOLENVHOLD [1]                             #39  */
-    /* GEN_KEYTOVOLENVDECAY [1]                            #40  */
-    /* GEN_STARTLOOPADDRCOARSEOFS [1]                      #45  */
-    GEN_KEYNUM,                          /*                #46  */
-    GEN_VELOCITY,                        /*                #47  */
-    GEN_ATTENUATION,                     /*                #48  */
-    /* GEN_ENDLOOPADDRCOARSEOFS [1]                        #50  */
-    /* GEN_COARSETUNE           [1]                        #51  */
-    /* GEN_FINETUNE             [1]                        #52  */
-    GEN_OVERRIDEROOTKEY,                 /*                #58  */
-    GEN_PITCH,                           /*                ---  */
-    -1};                                 /* end-of-list marker  */
-
-  /* When the voice is made ready for the synthesis process, a lot of
-   * voice-internal parameters have to be calculated.
-   *
-   * At this point, the sound font has already set the -nominal- value
-   * for all generators (excluding GEN_PITCH). Most generators can be
-   * modulated - they include a nominal value and an offset (which
-   * changes with velocity, note number, channel parameters like
-   * aftertouch, mod wheel...) Now this offset will be calculated as
-   * follows:
-   *
-   *  - Process each modulator once.
-   *  - Calculate its output value.
-   *  - Find the target generator.
-   *  - Add the output value to the modulation value of the generator.
-   *
-   * Note: The generators have been initialized with
-   * fluid_gen_set_default_values.
-   */
-
-  for (i = 0; i < voice->mod_count; i++) {
-    fluid_mod_t* mod = &voice->mod[i];
-    fluid_real_t modval = fluid_mod_get_value(mod, voice->channel, voice);
-    int dest_gen_index = mod->dest;
-    fluid_gen_t* dest_gen = &voice->gen[dest_gen_index];
-    dest_gen->mod += modval;
-    /*      fluid_dump_modulator(mod); */
-  }
-
-  /* Now the generators are initialized, nominal and modulation value.
-   * The voice parameters (which depend on generators) are calculated
-   * with fluid_voice_update_param. Processing the list of generator
-   * changes will calculate each voice parameter once.
-   *
-   * Note [1]: Some voice parameters depend on several generators. For
-   * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and
-   * GEN_PITCH.  voice->pitch.  Unnecessary recalculation is avoided
-   * by removing all but one generator from the list of voice
-   * parameters.  Same with GEN_XXX and GEN_XXXCOARSE: the
-   * initialisation list contains only GEN_XXX.
-   */
-
-  /* Calculate the voice parameter(s) dependent on each generator. */
-  for (i = 0; list_of_generators_to_initialize[i] != -1; i++) {
-    fluid_voice_update_param(voice, list_of_generators_to_initialize[i]);
-  }
-
-  /* Make an estimate on how loud this voice can get at any time (attenuation). */
-  UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, 
-                 fluid_voice_get_lower_boundary_for_attenuation(voice)); 
-  return FLUID_OK;
+    int i;
+    unsigned int n;
+
+    static int const list_of_generators_to_initialize[] =
+    {
+        GEN_STARTADDROFS,                    /* SF2.01 page 48 #0   */
+        GEN_ENDADDROFS,                      /*                #1   */
+        GEN_STARTLOOPADDROFS,                /*                #2   */
+        GEN_ENDLOOPADDROFS,                  /*                #3   */
+        /* GEN_STARTADDRCOARSEOFS see comment below [1]        #4   */
+        GEN_MODLFOTOPITCH,                   /*                #5   */
+        GEN_VIBLFOTOPITCH,                   /*                #6   */
+        GEN_MODENVTOPITCH,                   /*                #7   */
+        GEN_FILTERFC,                        /*                #8   */
+        GEN_FILTERQ,                         /*                #9   */
+        GEN_MODLFOTOFILTERFC,                /*                #10  */
+        GEN_MODENVTOFILTERFC,                /*                #11  */
+        /* GEN_ENDADDRCOARSEOFS [1]                            #12  */
+        GEN_MODLFOTOVOL,                     /*                #13  */
+        /* not defined                                         #14  */
+        GEN_CHORUSSEND,                      /*                #15  */
+        GEN_REVERBSEND,                      /*                #16  */
+        GEN_PAN,                             /*                #17  */
+        /* not defined                                         #18  */
+        /* not defined                                         #19  */
+        /* not defined                                         #20  */
+        GEN_MODLFODELAY,                     /*                #21  */
+        GEN_MODLFOFREQ,                      /*                #22  */
+        GEN_VIBLFODELAY,                     /*                #23  */
+        GEN_VIBLFOFREQ,                      /*                #24  */
+        GEN_MODENVDELAY,                     /*                #25  */
+        GEN_MODENVATTACK,                    /*                #26  */
+        GEN_MODENVHOLD,                      /*                #27  */
+        GEN_MODENVDECAY,                     /*                #28  */
+        /* GEN_MODENVSUSTAIN [1]                               #29  */
+        GEN_MODENVRELEASE,                   /*                #30  */
+        /* GEN_KEYTOMODENVHOLD [1]                             #31  */
+        /* GEN_KEYTOMODENVDECAY [1]                            #32  */
+        GEN_VOLENVDELAY,                     /*                #33  */
+        GEN_VOLENVATTACK,                    /*                #34  */
+        GEN_VOLENVHOLD,                      /*                #35  */
+        GEN_VOLENVDECAY,                     /*                #36  */
+        /* GEN_VOLENVSUSTAIN [1]                               #37  */
+        GEN_VOLENVRELEASE,                   /*                #38  */
+        /* GEN_KEYTOVOLENVHOLD [1]                             #39  */
+        /* GEN_KEYTOVOLENVDECAY [1]                            #40  */
+        /* GEN_STARTLOOPADDRCOARSEOFS [1]                      #45  */
+        GEN_KEYNUM,                          /*                #46  */
+        GEN_VELOCITY,                        /*                #47  */
+        GEN_ATTENUATION,                     /*                #48  */
+        /* GEN_ENDLOOPADDRCOARSEOFS [1]                        #50  */
+        /* GEN_COARSETUNE           [1]                        #51  */
+        /* GEN_FINETUNE             [1]                        #52  */
+        GEN_OVERRIDEROOTKEY,                 /*                #58  */
+        GEN_PITCH,                           /*                ---  */
+        GEN_CUSTOM_BALANCE,                  /*                ---  */
+        GEN_CUSTOM_FILTERFC,                 /*                ---  */
+        GEN_CUSTOM_FILTERQ                   /*                ---  */
+    };
+
+    /* When the voice is made ready for the synthesis process, a lot of
+     * voice-internal parameters have to be calculated.
+     *
+     * At this point, the sound font has already set the -nominal- value
+     * for all generators (excluding GEN_PITCH). Most generators can be
+     * modulated - they include a nominal value and an offset (which
+     * changes with velocity, note number, channel parameters like
+     * aftertouch, mod wheel...) Now this offset will be calculated as
+     * follows:
+     *
+     *  - Process each modulator once.
+     *  - Calculate its output value.
+     *  - Find the target generator.
+     *  - Add the output value to the modulation value of the generator.
+     *
+     * Note: The generators have been initialized with
+     * fluid_gen_set_default_values.
+     */
+
+    for(i = 0; i < voice->mod_count; i++)
+    {
+        fluid_mod_t *mod = &voice->mod[i];
+        fluid_real_t modval = fluid_mod_get_value(mod, voice);
+        int dest_gen_index = mod->dest;
+        fluid_gen_t *dest_gen = &voice->gen[dest_gen_index];
+        dest_gen->mod += modval;
+        /*      fluid_dump_modulator(mod); */
+    }
+
+    /* Now the generators are initialized, nominal and modulation value.
+     * The voice parameters (which depend on generators) are calculated
+     * with fluid_voice_update_param. Processing the list of generator
+     * changes will calculate each voice parameter once.
+     *
+     * Note [1]: Some voice parameters depend on several generators. For
+     * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and
+     * GEN_PITCH.  voice->pitch.  Unnecessary recalculation is avoided
+     * by removing all but one generator from the list of voice
+     * parameters.  Same with GEN_XXX and GEN_XXXCOARSE: the
+     * initialisation list contains only GEN_XXX.
+     */
+
+    /* Calculate the voice parameter(s) dependent on each generator. */
+    for(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++)
+    {
+        fluid_voice_update_param(voice, list_of_generators_to_initialize[n]);
+    }
+
+    /* Start portamento if enabled */
+    {
+        /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector.
+        When fromkey is set to ValidNote , portamento is started */
+        /* Return fromkey portamento */
+        int fromkey = voice->channel->synth->fromkey_portamento;
+
+        if(fluid_channel_is_valid_note(fromkey))
+        {
+            /* Send portamento parameters to the voice dsp */
+            fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice));
+        }
+    }
+
+    /* Make an estimate on how loud this voice can get at any time (attenuation). */
+    UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB,
+                     fluid_voice_get_lower_boundary_for_attenuation(voice));
+    return FLUID_OK;
 }
 
 /*
  * calculate_hold_decay_buffers
  */
 static int
-calculate_hold_decay_buffers(fluid_voice_tvoice, int gen_base,
+calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base,
                              int gen_key2base, int is_decay)
 {
-  /* Purpose:
-   *
-   * Returns the number of DSP loops, that correspond to the hold
-   * (is_decay=0) or decay (is_decay=1) time.
-   * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD,
-   * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD,
-   * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY
-   */
-
-  fluid_real_t timecents;
-  fluid_real_t seconds;
-  int buffers;
-
-  /* SF2.01 section 8.4.3 # 31, 32, 39, 40
-   * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'.
-   * The unit of the generator is timecents per key number.
-   * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72)
-   * will cause (60-72)*100=-1200 timecents of time variation.
-   * The time is cut in half.
-   */
-  timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key));
-
-  /* Range checking */
-  if (is_decay){
-    /* SF 2.01 section 8.1.3 # 28, 36 */
-    if (timecents > 8000.0) {
-      timecents = 8000.0;
+    /* Purpose:
+     *
+     * Returns the number of DSP loops, that correspond to the hold
+     * (is_decay=0) or decay (is_decay=1) time.
+     * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD,
+     * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD,
+     * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY
+     */
+
+    fluid_real_t timecents;
+    fluid_real_t seconds;
+    int buffers;
+
+    /* SF2.01 section 8.4.3 # 31, 32, 39, 40
+     * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'.
+     * The unit of the generator is timecents per key number.
+     * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72)
+     * 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) * (60.0 - fluid_voice_get_actual_key(voice)));
+
+    /* Range checking */
+    if(is_decay)
+    {
+        /* SF 2.01 section 8.1.3 # 28, 36 */
+        if(timecents > 8000.0)
+        {
+            timecents = 8000.0;
+        }
     }
-  } else {
-    /* SF 2.01 section 8.1.3 # 27, 35 */
-    if (timecents > 5000) {
-      timecents = 5000.0;
+    else
+    {
+        /* SF 2.01 section 8.1.3 # 27, 35 */
+        if(timecents > 5000)
+        {
+            timecents = 5000.0;
+        }
+
+        /* SF 2.01 section 8.1.2 # 27, 35:
+         * The most negative number indicates no hold time
+         */
+        if(timecents <= -32768.)
+        {
+            return 0;
+        }
     }
-    /* SF 2.01 section 8.1.2 # 27, 35:
-     * The most negative number indicates no hold time
-     */
-    if (timecents <= -32768.) {
-      return 0;
+
+    /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */
+    if(timecents < -12000.0)
+    {
+        timecents = -12000.0;
     }
-  }
-  /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */
-  if (timecents < -12000.0) {
-    timecents = -12000.0;
-  }
 
-  seconds = fluid_tc2sec(timecents);
-  /* Each DSP loop processes FLUID_BUFSIZE samples. */
+    seconds = fluid_tc2sec(timecents);
+    /* Each DSP loop processes FLUID_BUFSIZE samples. */
 
-  /* round to next full number of buffers */
-  buffers = (int)(((fluid_real_t)voice->output_rate * seconds)
-                 / (fluid_real_t)FLUID_BUFSIZE
-                 +0.5);
+    /* round to next full number of buffers */
+    buffers = (int)(((fluid_real_t)voice->output_rate * seconds)
+                    / (fluid_real_t)FLUID_BUFSIZE
+                    + 0.5);
 
-  return buffers;
+    return buffers;
 }
 
 /*
@@ -684,7 +742,7 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
  *
  * Note: The generator holds three values: The base value .val, an
  * offset caused by modulators .mod, and an offset caused by the
- * NRPN system. _GEN(voice, generator_enumerator) returns the sum
+ * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum
  * of all three.
  */
 /**
@@ -696,236 +754,245 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
  * Most applications will not need this function.
  */
 void
-fluid_voice_update_param(fluid_voice_tvoice, int gen)
+fluid_voice_update_param(fluid_voice_t *voice, int gen)
 {
-  double q_dB;
-  fluid_real_t x;
-  fluid_real_t y;
-  unsigned int count, z;
-  // Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank.
-  static const float ALT_ATTENUATION_SCALE = 0.4;
-
-  switch (gen) {
-
-  case GEN_PAN:
-    /* range checking is done in the fluid_pan function */
-    voice->pan = _GEN(voice, GEN_PAN);
-    voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f;
-    voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f;
-    UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
-    UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
-    break;
-
-  case GEN_ATTENUATION:
-    voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) +
-    (fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn;
-
-    /* Range: SF2.01 section 8.1.3 # 48
-     * Motivation for range checking:
-     * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */
-    fluid_clip(voice->attenuation, 0.0, 1440.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation);
-    break;
+    unsigned int count, z;
+    fluid_real_t x = fluid_voice_gen_value(voice, gen);
+
+    switch(gen)
+    {
+
+    case GEN_PAN:
+    case GEN_CUSTOM_BALANCE:
+        /* range checking is done in the fluid_pan and fluid_balance functions */
+        voice->pan = fluid_voice_gen_value(voice, GEN_PAN);
+        voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE);
+
+        /* left amp */
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0,
+                                  fluid_voice_calculate_gain_amplitude(voice,
+                                          fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)));
+
+        /* right amp */
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1,
+                                  fluid_voice_calculate_gain_amplitude(voice,
+                                          fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)));
+        break;
+
+    case GEN_ATTENUATION:
+        voice->attenuation = x;
+
+        /* Range: SF2.01 section 8.1.3 # 48
+         * Motivation for range checking:
+         * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */
+        fluid_clip(voice->attenuation, 0.0, 1440.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation);
+        break;
 
     /* The pitch is calculated from three different generators.
      * Read comment in fluidsynth.h about GEN_PITCH.
      */
-  case GEN_PITCH:
-  case GEN_COARSETUNE:
-  case GEN_FINETUNE:
-    /* The testing for allowed range is done in 'fluid_ct2hz' */
-    voice->pitch = (_GEN(voice, GEN_PITCH)
-                   + 100.0f * _GEN(voice, GEN_COARSETUNE)
-                   + _GEN(voice, GEN_FINETUNE));
-    UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch);
-    break;
-
-  case GEN_REVERBSEND:
-    /* The generator unit is 'tenths of a percent'. */
-    voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f;
-    fluid_clip(voice->reverb_send, 0.0, 1.0);
-    voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f;
-    UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
-    break;
-
-  case GEN_CHORUSSEND:
-    /* The generator unit is 'tenths of a percent'. */
-    voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f;
-    fluid_clip(voice->chorus_send, 0.0, 1.0);
-    voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f;
-    UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
-    break;
-
-  case GEN_OVERRIDEROOTKEY:
-    /* This is a non-realtime parameter. Therefore the .mod part of the generator
-     * can be neglected.
-     * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount
-     * which offsets the original rate.  This means that the fine tuning is
-     * inverted with respect to the root note (so subtract it, not add).
-     */
-    if (voice->sample != NULL) {
-      if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1)   //FIXME: use flag instead of -1
-        voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f
-         - voice->sample->pitchadj;
-      else
-        voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
-      x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
-    } else {
-      if (voice->gen[GEN_OVERRIDEROOTKEY].val > -1)    //FIXME: use flag instead of -1
-        voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f;
-      else
-        voice->root_pitch = 0;
-      x = fluid_ct2hz(voice->root_pitch);
-    }
-    /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
-    fluid_voice_calculate_gen_pitch(voice);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x);
-
-    break;
-
-  case GEN_FILTERFC:
-    /* The resonance frequency is converted from absolute cents to
-     * midicents .val and .mod are both used, this permits real-time
-     * modulation.  The allowed range is tested in the 'fluid_ct2hz'
-     * function [PH,20021214]
-     */
-    x = _GEN(voice, GEN_FILTERFC);
-    UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_fres, x);
-    break;
-
-  case GEN_FILTERQ:
-    /* The generator contains 'centibels' (1/10 dB) => divide by 10 to
-     * obtain dB */
-    q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
+    case GEN_PITCH:
+    case GEN_COARSETUNE:
+    case GEN_FINETUNE:
+        /* The testing for allowed range is done in 'fluid_ct2hz' */
+        voice->pitch = (fluid_voice_gen_value(voice, GEN_PITCH)
+                        + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE)
+                        + fluid_voice_gen_value(voice, GEN_FINETUNE));
+        UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch);
+        break;
+
+    case GEN_REVERBSEND:
+        /* The generator unit is 'tenths of a percent'. */
+        voice->reverb_send = x / 1000.0f;
+        fluid_clip(voice->reverb_send, 0.0, 1.0);
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send));
+        break;
+
+    case GEN_CHORUSSEND:
+        /* The generator unit is 'tenths of a percent'. */
+        voice->chorus_send = x / 1000.0f;
+        fluid_clip(voice->chorus_send, 0.0, 1.0);
+        UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send));
+        break;
+
+    case GEN_OVERRIDEROOTKEY:
+
+        /* This is a non-realtime parameter. Therefore the .mod part of the generator
+         * can be neglected.
+         * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount
+         * which offsets the original rate.  This means that the fine tuning is
+         * inverted with respect to the root note (so subtract it, not add).
+         */
+        if(voice->sample != NULL)
+        {
+            if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1)    //FIXME: use flag instead of -1
+            {
+                voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f
+                                    - voice->sample->pitchadj;
+            }
+            else
+            {
+                voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
+            }
+
+            x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
+        }
+        else
+        {
+            if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1)     //FIXME: use flag instead of -1
+            {
+                voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f;
+            }
+            else
+            {
+                voice->root_pitch = 0;
+            }
+
+            x = fluid_ct2hz(voice->root_pitch);
+        }
+
+        /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
+        fluid_voice_calculate_gen_pitch(voice);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x);
+
+        break;
+
+    case GEN_FILTERFC:
+        /* The resonance frequency is converted from absolute cents to
+         * midicents .val and .mod are both used, this permits real-time
+         * modulation.  The allowed range is tested in the 'fluid_ct2hz'
+         * function [PH,20021214]
+         */
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x);
+        break;
+
+    case GEN_FILTERQ:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x);
+        break;
+
+    /* same as the two above, only for the custom filter */
+    case GEN_CUSTOM_FILTERFC:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x);
+        break;
+
+    case GEN_CUSTOM_FILTERQ:
+        UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x);
+        break;
+
+    case GEN_MODLFOTOPITCH:
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
+        break;
+
+    case GEN_MODLFOTOVOL:
+        fluid_clip(x, -960.0, 960.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
+        break;
+
+    case GEN_MODLFOTOFILTERFC:
+        fluid_clip(x, -12000, 12000);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
+        break;
+
+    case GEN_MODLFODELAY:
+        fluid_clip(x, -12000.0f, 5000.0f);
+        z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x));
+        UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z);
+        break;
+
+    case GEN_MODLFOFREQ:
+        /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
+         * - the delay into a sample delay
+         */
+        fluid_clip(x, -16000.0f, 4500.0f);
+        x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
+        UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x);
+        break;
+
+    case GEN_VIBLFOFREQ:
+        /* vib lfo
+         *
+         * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
+         * - the delay into a sample delay
+         */
+        fluid_clip(x, -16000.0f, 4500.0f);
+        x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate;
+        UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x);
+        break;
+
+    case GEN_VIBLFODELAY:
+        fluid_clip(x, -12000.0f, 5000.0f);
+        z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x));
+        UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z);
+        break;
+
+    case GEN_VIBLFOTOPITCH:
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x);
+        break;
+
+    case GEN_KEYNUM:
+        /* GEN_KEYNUM: SF2.01 page 46, item 46
+         *
+         * If this generator is active, it forces the key number to its
+         * value.  Non-realtime controller.
+         *
+         * There is a flag, which should indicate, whether a generator is
+         * enabled or not.  But here we rely on the default value of -1.
+         */
+
+        /* 2017-09-02: do not change the voice's key here, otherwise it will
+         * never be released on a noteoff event
+         */
+#if 0
+        x = fluid_voice_gen_value(voice, GEN_KEYNUM);
 
-    /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
-    fluid_clip(q_dB, 0.0f, 96.0f);
+        if(x >= 0)
+        {
+            voice->key = x;
+        }
 
-    /* Short version: Modify the Q definition in a way, that a Q of 0
-     * dB leads to no resonance hump in the freq. response.
-     *
-     * Long version: From SF2.01, page 39, item 9 (initialFilterQ):
-     * "The gain at the cutoff frequency may be less than zero when
-     * zero is specified".  Assume q_dB=0 / q_lin=1: If we would leave
-     * q as it is, then this results in a 3 dB hump slightly below
-     * fc. At fc, the gain is exactly the DC gain (0 dB).  What is
-     * (probably) meant here is that the filter does not show a
-     * resonance hump for q_dB=0. In this case, the corresponding
-     * q_lin is 1/sqrt(2)=0.707.  The filter should have 3 dB of
-     * attenuation at fc now.  In this case Q_dB is the height of the
-     * resonance peak not over the DC gain, but over the frequency
-     * response of a non-resonant filter.  This idea is implemented as
-     * follows: */
-    q_dB -= 3.01f;
-    UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB);
-
-    break;
-
-  case GEN_MODLFOTOPITCH:
-    x = _GEN(voice, GEN_MODLFOTOPITCH);
-    fluid_clip(x, -12000.0, 12000.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
-    break;
-
-  case GEN_MODLFOTOVOL:
-    x = _GEN(voice, GEN_MODLFOTOVOL);
-    fluid_clip(x, -960.0, 960.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
-    break;
-
-  case GEN_MODLFOTOFILTERFC:
-    x = _GEN(voice, GEN_MODLFOTOFILTERFC);
-    fluid_clip(x, -12000, 12000);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
-    break;
-
-  case GEN_MODLFODELAY:
-    x = _GEN(voice, GEN_MODLFODELAY);
-    fluid_clip(x, -12000.0f, 5000.0f);
-    z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
-    UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z);
-    break;
-
-  case GEN_MODLFOFREQ:
-    /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
-     * - the delay into a sample delay
-     */
-    x = _GEN(voice, GEN_MODLFOFREQ);
-    fluid_clip(x, -16000.0f, 4500.0f);
-    x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
-    UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x);
-    break;
-
-  case GEN_VIBLFOFREQ:
-    /* vib lfo
-     *
-     * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples
-     * - the delay into a sample delay
-     */
-    x = _GEN(voice, GEN_VIBLFOFREQ);
-    fluid_clip(x, -16000.0f, 4500.0f);
-    x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate;
-    UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); 
-    break;
-
-  case GEN_VIBLFODELAY:
-    x = _GEN(voice,GEN_VIBLFODELAY);
-    fluid_clip(x, -12000.0f, 5000.0f);
-    z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x)); 
-    UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); 
-    break;
-
-  case GEN_VIBLFOTOPITCH:
-    x = _GEN(voice, GEN_VIBLFOTOPITCH);
-    fluid_clip(x, -12000.0, 12000.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); 
-    break;
-
-  case GEN_KEYNUM:
-    /* GEN_KEYNUM: SF2.01 page 46, item 46
-     *
-     * If this generator is active, it forces the key number to its
-     * value.  Non-realtime controller.
-     *
-     * There is a flag, which should indicate, whether a generator is
-     * enabled or not.  But here we rely on the default value of -1.
-     * */
-    x = _GEN(voice, GEN_KEYNUM);
-    if (x >= 0){
-      voice->key = x;
-    }
-    break;
+#endif
+        break;
+
+    case GEN_VELOCITY:
+        /* GEN_VELOCITY: SF2.01 page 46, item 47
+         *
+         * If this generator is active, it forces the velocity to its
+         * value. Non-realtime controller.
+         *
+         * There is a flag, which should indicate, whether a generator is
+         * enabled or not. But here we rely on the default value of -1.
+         */
+        /* 2017-09-02: do not change the voice's velocity here, use
+         * fluid_voice_get_actual_velocity() to get the value of this generator
+         * if active.
+         */
+#if 0
+        x = fluid_voice_gen_value(voice, GEN_VELOCITY);
 
-  case GEN_VELOCITY:
-    /* GEN_VELOCITY: SF2.01 page 46, item 47
-     *
-     * If this generator is active, it forces the velocity to its
-     * value. Non-realtime controller.
-     *
-     * There is a flag, which should indicate, whether a generator is
-     * enabled or not. But here we rely on the default value of -1.  */
-    x = _GEN(voice, GEN_VELOCITY);
-    if (x > 0) {
-      voice->vel = x;
-    }
-    break;
+        if(x > 0)
+        {
+            voice->vel = x;
+        }
 
-  case GEN_MODENVTOPITCH:
-    x = _GEN(voice, GEN_MODENVTOPITCH);
-    fluid_clip(x, -12000.0, 12000.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
-    break;
+#endif
+        break;
 
-  case GEN_MODENVTOFILTERFC:
-    x = _GEN(voice,GEN_MODENVTOFILTERFC);
+    case GEN_MODENVTOPITCH:
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
+        break;
 
-    /* Range: SF2.01 section 8.1.3 # 1
-     * Motivation for range checking:
-     * Filter is reported to make funny noises now and then
-     */
-    fluid_clip(x, -12000.0, 12000.0);
-    UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x);
-    break;
+    case GEN_MODENVTOFILTERFC:
+        /* Range: SF2.01 section 8.1.3 # 1
+         * Motivation for range checking:
+         * Filter is reported to make funny noises now and then
+         */
+        fluid_clip(x, -12000.0, 12000.0);
+        UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x);
+        break;
 
 
     /* sample start and ends points
@@ -939,45 +1006,59 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
      * end point ahead of the loop start point => invalid, then
      * move the loop start point forward => valid again.
      */
-  case GEN_STARTADDROFS:              /* SF2.01 section 8.1.3 # 0 */
-  case GEN_STARTADDRCOARSEOFS:        /* SF2.01 section 8.1.3 # 4 */
-    if (voice->sample != NULL) {
-      z = (voice->sample->start
-                            + (int) _GEN(voice, GEN_STARTADDROFS)
-                            + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
-      UPDATE_RVOICE_I1(fluid_rvoice_set_start, z);
-    }
-    break;
-  case GEN_ENDADDROFS:                 /* SF2.01 section 8.1.3 # 1 */
-  case GEN_ENDADDRCOARSEOFS:           /* SF2.01 section 8.1.3 # 12 */
-    if (voice->sample != NULL) {
-      z = (voice->sample->end
-                          + (int) _GEN(voice, GEN_ENDADDROFS)
-                          + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
-      UPDATE_RVOICE_I1(fluid_rvoice_set_end, z);
-    }
-    break;
-  case GEN_STARTLOOPADDROFS:           /* SF2.01 section 8.1.3 # 2 */
-  case GEN_STARTLOOPADDRCOARSEOFS:     /* SF2.01 section 8.1.3 # 45 */
-    if (voice->sample != NULL) {
-      z = (voice->sample->loopstart
-                                 + (int) _GEN(voice, GEN_STARTLOOPADDROFS)
-                                 + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
-      UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z);
-    }
-    break;
-
-  case GEN_ENDLOOPADDROFS:             /* SF2.01 section 8.1.3 # 3 */
-  case GEN_ENDLOOPADDRCOARSEOFS:       /* SF2.01 section 8.1.3 # 50 */
-    if (voice->sample != NULL) {
-      z = (voice->sample->loopend
-                               + (int) _GEN(voice, GEN_ENDLOOPADDROFS)
-                               + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
-      UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z);
-    }
-    break;
-
-    /* Conversion functions differ in range limit */
+    case GEN_STARTADDROFS:              /* SF2.01 section 8.1.3 # 0 */
+    case GEN_STARTADDRCOARSEOFS:        /* SF2.01 section 8.1.3 # 4 */
+        if(voice->sample != NULL)
+        {
+            fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS);
+            fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS);
+
+            z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar;
+            UPDATE_RVOICE_I1(fluid_rvoice_set_start, z);
+        }
+
+        break;
+
+    case GEN_ENDADDROFS:                 /* SF2.01 section 8.1.3 # 1 */
+    case GEN_ENDADDRCOARSEOFS:           /* SF2.01 section 8.1.3 # 12 */
+        if(voice->sample != NULL)
+        {
+            fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS);
+            fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS);
+
+            z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar;
+            UPDATE_RVOICE_I1(fluid_rvoice_set_end, z);
+        }
+
+        break;
+
+    case GEN_STARTLOOPADDROFS:           /* SF2.01 section 8.1.3 # 2 */
+    case GEN_STARTLOOPADDRCOARSEOFS:     /* SF2.01 section 8.1.3 # 45 */
+        if(voice->sample != NULL)
+        {
+            fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS);
+            fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS);
+
+            z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar;
+            UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z);
+        }
+
+        break;
+
+    case GEN_ENDLOOPADDROFS:             /* SF2.01 section 8.1.3 # 3 */
+    case GEN_ENDLOOPADDRCOARSEOFS:       /* SF2.01 section 8.1.3 # 50 */
+        if(voice->sample != NULL)
+        {
+            fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS);
+            fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS);
+
+            z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar;
+            UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z);
+        }
+
+        break;
+
+        /* Conversion functions differ in range limit */
 #define NUM_BUFFERS_DELAY(_v)   (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE)
 #define NUM_BUFFERS_ATTACK(_v)  (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE)
 #define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE)
@@ -988,90 +1069,84 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
      * - sustain is converted to its absolute value
      * - attack, decay and release are converted to their increment per sample
      */
-  case GEN_VOLENVDELAY:                /* SF2.01 section 8.1.3 # 33 */
-    x = _GEN(voice, GEN_VOLENVDELAY);
-    fluid_clip(x, -12000.0f, 5000.0f);
-    count = NUM_BUFFERS_DELAY(x);
-    fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDELAY,
-                            count, 0.0f, 0.0f, -1.0f, 1.0f);
-    break;
-
-  case GEN_VOLENVATTACK:               /* SF2.01 section 8.1.3 # 34 */
-    x = _GEN(voice, GEN_VOLENVATTACK);
-    fluid_clip(x, -12000.0f, 8000.0f);
-    count = 1 + NUM_BUFFERS_ATTACK(x);
-    fluid_voice_update_volenv(voice, FLUID_VOICE_ENVATTACK,
-                            count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
-    break;
-
-  case GEN_VOLENVHOLD:                 /* SF2.01 section 8.1.3 # 35 */
-  case GEN_KEYTOVOLENVHOLD:            /* SF2.01 section 8.1.3 # 39 */
-    count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */
-    fluid_voice_update_volenv(voice, FLUID_VOICE_ENVHOLD,
-                            count, 1.0f, 0.0f, -1.0f, 2.0f);
-    break;
-
-  case GEN_VOLENVDECAY:               /* SF2.01 section 8.1.3 # 36 */
-  case GEN_VOLENVSUSTAIN:             /* SF2.01 section 8.1.3 # 37 */
-  case GEN_KEYTOVOLENVDECAY:          /* SF2.01 section 8.1.3 # 40 */
-    y = 1.0f - 0.001f * _GEN(voice, GEN_VOLENVSUSTAIN);
-    fluid_clip(y, 0.0f, 1.0f);
-    count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */
-    fluid_voice_update_volenv(voice, FLUID_VOICE_ENVDECAY,
-                            count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
-    break;
-
-  case GEN_VOLENVRELEASE:             /* SF2.01 section 8.1.3 # 38 */
-    x = _GEN(voice, GEN_VOLENVRELEASE);
-    fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f);
-    count = 1 + NUM_BUFFERS_RELEASE(x);
-    fluid_voice_update_volenv(voice, FLUID_VOICE_ENVRELEASE,
-                            count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 1.0f);
-    break;
+    case GEN_VOLENVDELAY:                /* SF2.01 section 8.1.3 # 33 */
+        fluid_clip(x, -12000.0f, 5000.0f);
+        count = NUM_BUFFERS_DELAY(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY,
+                                  count, 0.0f, 0.0f, -1.0f, 1.0f);
+        break;
+
+    case GEN_VOLENVATTACK:               /* SF2.01 section 8.1.3 # 34 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_ATTACK(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK,
+                                  count, 1.0f, 1.0f / count, -1.0f, 1.0f);
+        break;
+
+    case GEN_VOLENVHOLD:                 /* SF2.01 section 8.1.3 # 35 */
+    case GEN_KEYTOVOLENVHOLD:            /* SF2.01 section 8.1.3 # 39 */
+        count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVHOLD,
+                                  count, 1.0f, 0.0f, -1.0f, 2.0f);
+        break;
+
+    case GEN_VOLENVDECAY:               /* SF2.01 section 8.1.3 # 36 */
+    case GEN_VOLENVSUSTAIN:             /* SF2.01 section 8.1.3 # 37 */
+    case GEN_KEYTOVOLENVDECAY:          /* SF2.01 section 8.1.3 # 40 */
+        x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN);
+        fluid_clip(x, 0.0f, 1.0f);
+        count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY,
+                                  count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f);
+        break;
+
+    case GEN_VOLENVRELEASE:             /* SF2.01 section 8.1.3 # 38 */
+        fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f);
+        count = 1 + NUM_BUFFERS_RELEASE(x);
+        fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE,
+                                  count, 1.0f, -1.0f / count, 0.0f, 1.0f);
+        break;
 
     /* Modulation envelope */
-  case GEN_MODENVDELAY:               /* SF2.01 section 8.1.3 # 25 */
-    x = _GEN(voice, GEN_MODENVDELAY);
-    fluid_clip(x, -12000.0f, 5000.0f);
-    fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDELAY,
-                            NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f);
-    break;
-
-  case GEN_MODENVATTACK:               /* SF2.01 section 8.1.3 # 26 */
-    x = _GEN(voice, GEN_MODENVATTACK);
-    fluid_clip(x, -12000.0f, 8000.0f);
-    count = 1 + NUM_BUFFERS_ATTACK(x);
-    fluid_voice_update_modenv(voice, FLUID_VOICE_ENVATTACK,
-                            count, 1.0f, count ? 1.0f / count : 0.0f, -1.0f, 1.0f);
-    break;
-
-  case GEN_MODENVHOLD:               /* SF2.01 section 8.1.3 # 27 */
-  case GEN_KEYTOMODENVHOLD:          /* SF2.01 section 8.1.3 # 31 */
-    count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */
-    fluid_voice_update_modenv(voice, FLUID_VOICE_ENVHOLD,
-                            count, 1.0f, 0.0f, -1.0f, 2.0f);
-    break;
-
-  case GEN_MODENVDECAY:                                   /* SF 2.01 section 8.1.3 # 28 */
-  case GEN_MODENVSUSTAIN:                                 /* SF 2.01 section 8.1.3 # 29 */
-  case GEN_KEYTOMODENVDECAY:                              /* SF 2.01 section 8.1.3 # 32 */
-    count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */
-    y = 1.0f - 0.001f * _GEN(voice, GEN_MODENVSUSTAIN);
-    fluid_clip(y, 0.0f, 1.0f);
-    fluid_voice_update_modenv(voice, FLUID_VOICE_ENVDECAY,
-                            count, 1.0f, count ? -1.0f / count : 0.0f, y, 2.0f);
-    break;
-
-  case GEN_MODENVRELEASE:                                  /* SF 2.01 section 8.1.3 # 30 */
-    x = _GEN(voice, GEN_MODENVRELEASE);
-    fluid_clip(x, -12000.0f, 8000.0f);
-    count = 1 + NUM_BUFFERS_RELEASE(x);
-    fluid_voice_update_modenv(voice, FLUID_VOICE_ENVRELEASE,
-                            count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f);
-
-    break;
-
-  } /* switch gen */
+    case GEN_MODENVDELAY:               /* SF2.01 section 8.1.3 # 25 */
+        fluid_clip(x, -12000.0f, 5000.0f);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDELAY,
+                                  NUM_BUFFERS_DELAY(x), 0.0f, 0.0f, -1.0f, 1.0f);
+        break;
+
+    case GEN_MODENVATTACK:               /* SF2.01 section 8.1.3 # 26 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_ATTACK(x);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK,
+                                  count, 1.0f, 1.0f / count, -1.0f, 1.0f);
+        break;
+
+    case GEN_MODENVHOLD:               /* SF2.01 section 8.1.3 # 27 */
+    case GEN_KEYTOMODENVHOLD:          /* SF2.01 section 8.1.3 # 31 */
+        count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVHOLD,
+                                  count, 1.0f, 0.0f, -1.0f, 2.0f);
+        break;
+
+    case GEN_MODENVDECAY:                                   /* SF 2.01 section 8.1.3 # 28 */
+    case GEN_MODENVSUSTAIN:                                 /* SF 2.01 section 8.1.3 # 29 */
+    case GEN_KEYTOMODENVDECAY:                              /* SF 2.01 section 8.1.3 # 32 */
+        count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */
+        x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN);
+        fluid_clip(x, 0.0f, 1.0f);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY,
+                                  count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f);
+        break;
+
+    case GEN_MODENVRELEASE:                                  /* SF 2.01 section 8.1.3 # 30 */
+        fluid_clip(x, -12000.0f, 8000.0f);
+        count = 1 + NUM_BUFFERS_RELEASE(x);
+        fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE,
+                                  count, 1.0f, -1.0f / count, 0.0f, 2.0f);
+
+        break;
+
+    } /* switch gen */
 }
 
 /**
@@ -1099,43 +1174,48 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
  * - For every changed generator, convert its value to the correct
  * unit of the corresponding DSP parameter
  */
-int fluid_voice_modulate(fluid_voice_tvoice, int cc, int ctrl)
+int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
 {
-  int i, k;
-  fluid_mod_t* mod;
-  int gen;
-  fluid_real_t modval;
-
-/*    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)) {
-
-      gen = fluid_mod_get_dest(mod);
-      modval = 0.0;
-
-      /* step 2: for every changed modulator, calculate the modulation
-       * value of its associated generator */
-      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->channel, voice);
-       }
-      }
-
-      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 */
-      fluid_voice_update_param(voice, gen);
+    int i, k;
+    fluid_mod_t *mod;
+    int gen;
+    fluid_real_t modval;
+
+    /*    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))
+        {
+
+            gen = fluid_mod_get_dest(mod);
+            modval = 0.0;
+
+            /* step 2: for every changed modulator, calculate the modulation
+             * value of its associated generator */
+            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);
+
+            /* step 3: now that we have the new value of the generator,
+             * recalculate the parameter values that are derived from the
+             * generator */
+            fluid_voice_update_param(voice, gen);
+        }
     }
-  }
-  return FLUID_OK;
+
+    return FLUID_OK;
 }
 
 /**
@@ -1143,44 +1223,113 @@ int fluid_voice_modulate(fluid_voice_t* voice, int cc, int ctrl)
  * ALL_CTRL_OFF MIDI message has been received (CC 121).
  *
  */
-int fluid_voice_modulate_all(fluid_voice_tvoice)
+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->channel, 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);
     }
 
-    fluid_gen_set_mod(&voice->gen[gen], modval);
+    return FLUID_OK;
+}
 
-    /* Update the parameter values that are depend on the generator
-     * 'gen' */
-    fluid_voice_update_param(voice, gen);
-  }
+/** legato update functions --------------------------------------------------*/
+/* Updates voice portamento parameters
+ *
+ * @voice voice the synthesis voice
+ * @fromkey the beginning pitch of portamento.
+ * @tokey the ending pitch of portamento.
+ *
+ * The function calculates pitch offset and increment, then these parameters
+ * are send to the dsp.
+*/
+void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey)
+
+{
+    fluid_channel_t *channel = voice->channel;
+
+    /* calculates pitch offset */
+    fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey);
+    fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey);
+    fluid_real_t pitchoffset = PitchBeg - PitchEnd;
+
+    /* Calculates increment countinc */
+    /* Increment is function of PortamentoTime (ms)*/
+    unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate *
+                                            0.001f *
+                                            (fluid_real_t)fluid_channel_portamentotime(channel))  /
+                                           (fluid_real_t)FLUID_BUFSIZE  + 0.5);
+
+    /* Send portamento parameters to the voice dsp */
+    UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset);
+}
+
+/*---------------------------------------------------------------*/
+/*legato mode 1: multi_retrigger
+ *
+ * Modulates all generators dependent of key,vel.
+ * Forces the voice envelopes in the attack section (legato mode 1).
+ *
+ * @voice voice the synthesis voice
+ * @tokey the new key to be applied to this voice.
+ * @vel the new velocity to be applied to this voice.
+ */
+void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice,
+        int tokey, int vel)
+{
+    voice->key = tokey;  /* new note */
+    voice->vel = vel; /* new velocity */
+    /* Updates generators dependent of velocity */
+    /* Modulates GEN_ATTENUATION (and others ) before calling
+       fluid_rvoice_multi_retrigger_attack().*/
+    fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY);
+
+    /* Updates generator dependent of voice->key */
+    fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD);
+    fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY);
+    fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD);
+    fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY);
+
+    /* Updates pitch generator  */
+    fluid_voice_calculate_gen_pitch(voice);
+    fluid_voice_update_param(voice, GEN_PITCH);
 
-  return FLUID_OK;
+    /* updates adsr generator */
+    UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack);
 }
+/** end of legato update functions */
 
 /*
  Force the voice into release stage. Useful anywhere a voice
@@ -1190,40 +1339,45 @@ int fluid_voice_modulate_all(fluid_voice_t* voice)
  fluid_voice_noteoff().
 */
 void
-fluid_voice_release(fluid_voice_tvoice)
+fluid_voice_release(fluid_voice_t *voice)
 {
-    unsigned int at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
+    unsigned int at_tick = fluid_channel_get_min_note_length_ticks(voice->channel);
     UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
     voice->has_noteoff = 1; // voice is marked as noteoff occured
 }
 
 /*
  * fluid_voice_noteoff
+ *
+ * Sending a noteoff event will advance the envelopes to section 5 (release).
+ * The function is convenient for polyphonic or monophonic note
  */
-int
-fluid_voice_noteoff(fluid_voice_tvoice)
+void
+fluid_voice_noteoff(fluid_voice_t *voice)
 {
-  fluid_channel_t* channel;
-
-  fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref);
-
-  channel = voice->channel;
-
-  /* Sustain a note under Sostenuto pedal */
-  if (fluid_channel_sostenuto(channel) &&
-      channel->sostenuto_orderid > voice->id)
-  { // Sostenuto depressed after note
-    voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO;
-  }
-  /* Or sustain a note under Sustain pedal */
-  else if (fluid_channel_sustained(channel)) {
-     voice->status = FLUID_VOICE_SUSTAINED;
-  }
-  /* Or force the voice to release stage */
-  else
-    fluid_voice_release(voice);
-
-  return FLUID_OK;
+    fluid_channel_t *channel;
+
+    fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0);
+
+    channel = voice->channel;
+
+    /* Sustain a note under Sostenuto pedal */
+    if(fluid_channel_sostenuto(channel) &&
+            channel->sostenuto_orderid > voice->id)
+    {
+        // Sostenuto depressed after note
+        voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO;
+    }
+    /* Or sustain a note under Sustain pedal */
+    else if(fluid_channel_sustained(channel))
+    {
+        voice->status = FLUID_VOICE_SUSTAINED;
+    }
+    /* Or force the voice to release stage */
+    else
+    {
+        fluid_voice_release(voice);
+    }
 }
 
 /*
@@ -1239,73 +1393,84 @@ fluid_voice_noteoff(fluid_voice_t* voice)
  */
 
 int
-fluid_voice_kill_excl(fluid_voice_t* voice){
+fluid_voice_kill_excl(fluid_voice_t *voice)
+{
 
-  unsigned int at_tick;
+    unsigned int at_tick;
 
-  if (!_PLAYING(voice)) {
-    return FLUID_OK;
-  }
+    if(!fluid_voice_is_playing(voice))
+    {
+        return FLUID_OK;
+    }
 
-  /* Turn off the exclusive class information for this voice,
-     so that it doesn't get killed twice
-  */
-  fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
+    /* Turn off the exclusive class information for this voice,
+       so that it doesn't get killed twice
+    */
+    fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
 
-  /* Speed up the volume envelope */
-  /* The value was found through listening tests with hi-hat samples. */
-  fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200);
-  fluid_voice_update_param(voice, GEN_VOLENVRELEASE);
+    /* Speed up the volume envelope */
+    /* The value was found through listening tests with hi-hat samples. */
+    fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200);
+    fluid_voice_update_param(voice, GEN_VOLENVRELEASE);
 
-  /* Speed up the modulation envelope */
-  fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200);
-  fluid_voice_update_param(voice, GEN_MODENVRELEASE);
+    /* Speed up the modulation envelope */
+    fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200);
+    fluid_voice_update_param(voice, GEN_MODENVRELEASE);
 
-  at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
-  UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
+    at_tick = fluid_channel_get_min_note_length_ticks(voice->channel);
+    UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
 
 
-  return FLUID_OK;
+    return FLUID_OK;
 }
 
 /*
- * Called by fluid_synth when the overflow rvoice can be reclaimed. 
+ * Called by fluid_synth when the overflow rvoice can be reclaimed.
  */
-void fluid_voice_overflow_rvoice_finished(fluid_voice_tvoice)
+void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice)
 {
-  voice->can_access_overflow_rvoice = 1;
-  fluid_sample_null_ptr(&voice->overflow_rvoice->dsp.sample);
+    voice->can_access_overflow_rvoice = 1;
+    fluid_voice_sample_unref(&voice->overflow_rvoice->dsp.sample);
 }
 
-
 /*
  * fluid_voice_off
  *
+ * Force the voice into finished stage. Useful anywhere a voice
+ * needs to be cancelled from MIDI API.
+ */
+void fluid_voice_off(fluid_voice_t *voice)
+{
+    UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */
+}
+
+/*
+ * fluid_voice_stop
+ *
  * Purpose:
- * Turns off a voice, meaning that it is not processed
- * anymore by the DSP loop.
+ * Turns off a voice, meaning that it is not processed anymore by the
+ * DSP loop, i.e. contrary part to fluid_voice_start().
  */
-int
-fluid_voice_off(fluid_voice_t* voice)
+void
+fluid_voice_stop(fluid_voice_t *voice)
 {
-  fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref);
+    fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0);
 
-  voice->chan = NO_CHANNEL;
-  UPDATE_RVOICE0(fluid_rvoice_voiceoff);
-  
-  if (voice->can_access_rvoice)
-    fluid_sample_null_ptr(&voice->rvoice->dsp.sample);
+    voice->chan = NO_CHANNEL;
 
-  voice->status = FLUID_VOICE_OFF;
-  voice->has_noteoff = 1;
+    if(voice->can_access_rvoice)
+    {
+        fluid_voice_sample_unref(&voice->rvoice->dsp.sample);
+    }
 
-  /* Decrement the reference count of the sample. */
-  fluid_sample_null_ptr(&voice->sample);
+    voice->status = FLUID_VOICE_OFF;
+    voice->has_noteoff = 1;
 
-  /* Decrement voice count */
-  voice->channel->synth->active_voice_count--;
+    /* Decrement the reference count of the sample. */
+    fluid_voice_sample_unref(&voice->sample);
 
-  return FLUID_OK;
+    /* Decrement voice count */
+    voice->channel->synth->active_voice_count--;
 }
 
 /**
@@ -1319,57 +1484,70 @@ fluid_voice_off(fluid_voice_t* voice)
  *   exist so don't check.
  */
 void
-fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
+fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
 {
-  int i;
-
-  /*
-   * Some soundfonts come with a huge number of non-standard
-   * controllers, because they have been designed for one particular
-   * sound card.  Discard them, maybe print a warning.
-   */
-
-  if (((mod->flags1 & FLUID_MOD_CC) == 0)
-      && ((mod->src1 != 0)          /* SF2.01 section 8.2.1: Constant value */
-         && (mod->src1 != 2)       /* Note-on velocity */
-         && (mod->src1 != 3)       /* Note-on key number */
-         && (mod->src1 != 10)      /* Poly pressure */
-         && (mod->src1 != 13)      /* Channel pressure */
-         && (mod->src1 != 14)      /* Pitch wheel */
-         && (mod->src1 != 16))) {  /* Pitch wheel sensitivity */
-    FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
-    return;
-  }
-
-  if (mode == FLUID_VOICE_ADD) {
-
-    /* if identical modulator exists, add them */
-    for (i = 0; i < voice->mod_count; i++) {
-      if (fluid_mod_test_identity(&voice->mod[i], mod)) {
-       //              printf("Adding modulator...\n");
-       voice->mod[i].amount += mod->amount;
-       return;
-      }
+    int i;
+
+    /*
+     * Some soundfonts come with a huge number of non-standard
+     * controllers, because they have been designed for one particular
+     * sound card.  Discard them, maybe print a warning.
+     */
+
+    if(((mod->flags1 & FLUID_MOD_CC) == 0)
+            && ((mod->src1 != FLUID_MOD_NONE)            /* SF2.01 section 8.2.1: Constant value */
+                && (mod->src1 != FLUID_MOD_VELOCITY)        /* Note-on velocity */
+                && (mod->src1 != FLUID_MOD_KEY)             /* Note-on key number */
+                && (mod->src1 != FLUID_MOD_KEYPRESSURE)     /* Poly pressure */
+                && (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
+                && (mod->src1 != FLUID_MOD_PITCHWHEEL)      /* Pitch wheel */
+                && (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
+    {
+        FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
+        return;
     }
 
-  } else if (mode == FLUID_VOICE_OVERWRITE) {
+    if(mode == FLUID_VOICE_ADD)
+    {
+
+        /* if identical modulator exists, add them */
+        for(i = 0; i < voice->mod_count; i++)
+        {
+            if(fluid_mod_test_identity(&voice->mod[i], mod))
+            {
+                //             printf("Adding modulator...\n");
+                voice->mod[i].amount += mod->amount;
+                return;
+            }
+        }
 
-    /* if identical modulator exists, replace it (only the amount has to be changed) */
-    for (i = 0; i < voice->mod_count; i++) {
-      if (fluid_mod_test_identity(&voice->mod[i], mod)) {
-       //              printf("Replacing modulator...amount is %f\n",mod->amount);
-       voice->mod[i].amount = mod->amount;
-       return;
-      }
     }
-  }
-
-  /* Add a new modulator (No existing modulator to add / overwrite).
-     Also, default modulators (FLUID_VOICE_DEFAULT) are added without
-     checking, if the same modulator already exists. */
-  if (voice->mod_count < FLUID_NUM_MOD) {
-    fluid_mod_clone(&voice->mod[voice->mod_count++], mod);
-  }
+    else if(mode == FLUID_VOICE_OVERWRITE)
+    {
+
+        /* if identical modulator exists, replace it (only the amount has to be changed) */
+        for(i = 0; i < voice->mod_count; i++)
+        {
+            if(fluid_mod_test_identity(&voice->mod[i], mod))
+            {
+                //             printf("Replacing modulator...amount is %f\n",mod->amount);
+                voice->mod[i].amount = mod->amount;
+                return;
+            }
+        }
+    }
+
+    /* Add a new modulator (No existing modulator to add / overwrite).
+       Also, default modulators (FLUID_VOICE_DEFAULT) are added without
+       checking, if the same modulator already exists. */
+    if(voice->mod_count < FLUID_NUM_MOD)
+    {
+        fluid_mod_clone(&voice->mod[voice->mod_count++], mod);
+    }
+    else
+    {
+        FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id);
+    }
 }
 
 /**
@@ -1388,19 +1566,134 @@ fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
  *
  * Otherwise the voice has finished playing.
  */
-unsigned int fluid_voice_get_id(fluid_voice_t* voice)
+unsigned int fluid_voice_get_id(const fluid_voice_t *voice)
 {
-  return voice->id;
+    return voice->id;
 }
 
 /**
- * Check if a voice is still playing.
+ * Check if a voice is producing sound. This is also true after a voice received a noteoff as it may be playing in release phase.
  * @param voice Voice instance
  * @return TRUE if playing, FALSE otherwise
  */
-int fluid_voice_is_playing(fluid_voice_t* voice)
+int fluid_voice_is_playing(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_ON)
+           || fluid_voice_is_sustained(voice)
+           || fluid_voice_is_sostenuto(voice);
+
+}
+
+/**
+ * Check if a voice is ON. A voice is ON, if it has not yet received a noteoff event.
+ * @param voice Voice instance
+ * @return TRUE if on, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_on(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff);
+}
+
+/**
+ * Check if a voice keeps playing after it has received a noteoff due to being held by sustain.
+ * @param voice Voice instance
+ * @return TRUE if sustained, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_sustained(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_SUSTAINED);
+}
+
+/**
+ * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto.
+ * @param voice Voice instance
+ * @return TRUE if sostenuto, FALSE otherwise
+ * @since 1.1.7
+ */
+int fluid_voice_is_sostenuto(const fluid_voice_t *voice)
+{
+    return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO);
+}
+
+/**
+ * If the voice is playing, gets the midi channel the voice is playing on. Else the result is undefined.
+ * @param voice Voice instance
+ * @return The channel assigned to this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_channel(const fluid_voice_t *voice)
+{
+    return voice->chan;
+}
+
+/**
+ * If the voice is playing, gets the midi key the voice is actually playing at. Else the result is undefined.
+ * If the voice was started from an instrument which uses a fixed key generator, it returns that.
+ * Else returns the same as \c fluid_voice_get_key.
+ * @param voice Voice instance
+ * @return The midi key this voice is playing at
+ * @since 1.1.7
+ */
+int fluid_voice_get_actual_key(const fluid_voice_t *voice)
+{
+    fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM);
+
+    if(x >= 0)
+    {
+        return (int)x;
+    }
+    else
+    {
+        return fluid_voice_get_key(voice);
+    }
+}
+
+/**
+ * If the voice is playing, gets the midi key from the noteon event, by which the voice was initially turned on with.
+ * Else the result is undefined.
+ * @param voice Voice instance
+ * @return The midi key of the noteon event that originally turned on this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_key(const fluid_voice_t *voice)
+{
+    return voice->key;
+}
+
+/**
+ * If the voice is playing, gets the midi velocity the voice is actually playing at. Else the result is undefined.
+ * If the voice was started from an instrument which uses a fixed velocity generator, it returns that.
+ * Else returns the same as \c fluid_voice_get_velocity.
+ * @param voice Voice instance
+ * @return The midi velocity this voice is playing at
+ * @since 1.1.7
+ */
+int fluid_voice_get_actual_velocity(const fluid_voice_t *voice)
+{
+    fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY);
+
+    if(x > 0)
+    {
+        return (int)x;
+    }
+    else
+    {
+        return fluid_voice_get_velocity(voice);
+    }
+}
+
+/**
+ * If the voice is playing, gets the midi velocity from the noteon event, by which the voice was initially
+ * turned on with. Else the result is undefined.
+ * @param voice Voice instance
+ * @return The midi velocity which originally turned on this voice
+ * @since 1.1.7
+ */
+int fluid_voice_get_velocity(const fluid_voice_t *voice)
 {
-  return _PLAYING(voice);
+    return voice->vel;
 }
 
 /*
@@ -1416,85 +1709,105 @@ int fluid_voice_is_playing(fluid_voice_t* voice)
  * voice->attenuation has to be initialized.
  */
 static fluid_real_t
-fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_tvoice)
+fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
 {
-  int i;
-  fluid_mod_t* mod;
-  fluid_real_t possible_att_reduction_cB=0;
-  fluid_real_t lower_bound;
-
-  for (i = 0; i < voice->mod_count; i++) {
-    mod = &voice->mod[i];
-
-    /* Modulator has attenuation as target and can change over time? */
-    if ((mod->dest == GEN_ATTENUATION)
-       && ((mod->flags1 & FLUID_MOD_CC) || (mod->flags2 & FLUID_MOD_CC))) {
-
-      fluid_real_t current_val = fluid_mod_get_value(mod, voice->channel, voice);
-      fluid_real_t v = fabs(mod->amount);
-
-      if ((mod->src1 == FLUID_MOD_PITCHWHEEL)
-         || (mod->flags1 & FLUID_MOD_BIPOLAR)
-         || (mod->flags2 & FLUID_MOD_BIPOLAR)
-         || (mod->amount < 0)) {
-       /* Can this modulator produce a negative contribution? */
-       v *= -1.0;
-      } else {
-       /* No negative value possible. But still, the minimum contribution is 0. */
-       v = 0;
-      }
-
-      /* For example:
-       * - current_val=100
-       * - min_val=-4000
-       * - possible_att_reduction_cB += 4100
-       */
-      if (current_val > v){
-       possible_att_reduction_cB += (current_val - v);
-      }
+    int i;
+    fluid_mod_t *mod;
+    fluid_real_t possible_att_reduction_cB = 0;
+    fluid_real_t lower_bound;
+
+    for(i = 0; i < voice->mod_count; i++)
+    {
+        mod = &voice->mod[i];
+
+        /* Modulator has attenuation as target and can change over time? */
+        if((mod->dest == GEN_ATTENUATION)
+                && ((mod->flags1 & FLUID_MOD_CC)
+                    || (mod->flags2 & FLUID_MOD_CC)
+                    || (mod->src1 == FLUID_MOD_CHANNELPRESSURE)
+                    || (mod->src1 == FLUID_MOD_KEYPRESSURE)
+                    || (mod->src1 == FLUID_MOD_PITCHWHEEL)
+                    || (mod->src2 == FLUID_MOD_CHANNELPRESSURE)
+                    || (mod->src2 == FLUID_MOD_KEYPRESSURE)
+                    || (mod->src2 == FLUID_MOD_PITCHWHEEL)))
+        {
+
+            fluid_real_t current_val = fluid_mod_get_value(mod, voice);
+            fluid_real_t v = fabs(mod->amount);
+
+            if((mod->src1 == FLUID_MOD_PITCHWHEEL)
+                    || (mod->flags1 & FLUID_MOD_BIPOLAR)
+                    || (mod->flags2 & FLUID_MOD_BIPOLAR)
+                    || (mod->amount < 0))
+            {
+                /* Can this modulator produce a negative contribution? */
+                v *= -1.0;
+            }
+            else
+            {
+                /* No negative value possible. But still, the minimum contribution is 0. */
+                v = 0;
+            }
+
+            /* For example:
+             * - current_val=100
+             * - min_val=-4000
+             * - possible_att_reduction_cB += 4100
+             */
+            if(current_val > v)
+            {
+                possible_att_reduction_cB += (current_val - v);
+            }
+        }
     }
-  }
 
-  lower_bound = voice->attenuation-possible_att_reduction_cB;
+    lower_bound = voice->attenuation - possible_att_reduction_cB;
 
-  /* SF2.01 specs do not allow negative attenuation */
-  if (lower_bound < 0) {
-    lower_bound = 0;
-  }
-  return lower_bound;
+    /* SF2.01 specs do not allow negative attenuation */
+    if(lower_bound < 0)
+    {
+        lower_bound = 0;
+    }
+
+    return lower_bound;
 }
 
 
 
 
-int fluid_voice_set_param(fluid_voice_tvoice, int gen, fluid_real_t nrpn_value, int abs)
+int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t nrpn_value, int abs)
 {
-  voice->gen[gen].nrpn = nrpn_value;
-  voice->gen[gen].flags = (abs)? GEN_ABS_NRPN : GEN_SET;
-  fluid_voice_update_param(voice, gen);
-  return FLUID_OK;
+    voice->gen[gen].nrpn = nrpn_value;
+    voice->gen[gen].flags = (abs) ? GEN_ABS_NRPN : GEN_SET;
+    fluid_voice_update_param(voice, gen);
+    return FLUID_OK;
 }
 
-int fluid_voice_set_gain(fluid_voice_tvoice, fluid_real_t gain)
+int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain)
 {
-  /* avoid division by zero*/
-  if (gain < 0.0000001){
-    gain = 0.0000001;
-  }
-
-  voice->synth_gain = gain;
-  voice->amp_left = fluid_pan(voice->pan, 1) * gain / 32768.0f;
-  voice->amp_right = fluid_pan(voice->pan, 0) * gain / 32768.0f;
-  voice->amp_reverb = voice->reverb_send * gain / 32768.0f;
-  voice->amp_chorus = voice->chorus_send * gain / 32768.0f;
-
-  UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
-  UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
-
-  return FLUID_OK;
+    fluid_real_t left, right, reverb, chorus;
+
+    /* avoid division by zero*/
+    if(gain < 0.0000001)
+    {
+        gain = 0.0000001;
+    }
+
+    voice->synth_gain = gain;
+    left = fluid_voice_calculate_gain_amplitude(voice,
+            fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1));
+    right = fluid_voice_calculate_gain_amplitude(voice,
+            fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0));
+    reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send);
+    chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send);
+
+    UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb);
+    UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, chorus);
+
+    return FLUID_OK;
 }
 
 /* - Scan the loop
@@ -1514,113 +1827,162 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
  * fluid_voice_optimize_sample() on each sample once.
  */
 int
-fluid_voice_optimize_sample(fluid_sample_ts)
+fluid_voice_optimize_sample(fluid_sample_t *s)
 {
-  signed short peak_max = 0;
-  signed short peak_min = 0;
-  signed short peak;
-  fluid_real_t normalized_amplitude_during_loop;
-  double result;
-  int i;
-
-  /* ignore ROM and other(?) invalid samples */
-  if (!s->valid) return (FLUID_OK);
-
-  if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */
-    /* Scan the loop */
-    for (i = (int)s->loopstart; i < (int) s->loopend; i ++){
-      signed short val = s->data[i];
-      if (val > peak_max) {
-       peak_max = val;
-      } else if (val < peak_min) {
-       peak_min = val;
-      }
+    int32_t peak_max = 0;
+    int32_t peak_min = 0;
+    int32_t peak;
+    fluid_real_t normalized_amplitude_during_loop;
+    double result;
+    unsigned int i;
+
+    /* ignore disabled samples */
+    if(s->start == s->end)
+    {
+        return (FLUID_OK);
     }
 
-    /* Determine the peak level */
-    if (peak_max >- peak_min){
-      peak = peak_max;
-    } else {
-      peak =- peak_min;
-    };
-    if (peak == 0){
-      /* Avoid division by zero */
-      peak = 1;
-    };
-
-    /* Calculate what factor will make the loop inaudible
-     * For example: Take a peak of 3277 (10 % of 32768).  The
-     * normalized amplitude is 0.1 (10 % of 32768).  An amplitude
-     * factor of 0.0001 (as opposed to the default 0.00001) will
-     * drop this sample to the noise floor.
-     */
-
-    /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */
-    normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.;
-    result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
-
-    /* Store in sample */
-    s->amplitude_that_reaches_noise_floor = (double)result;
-    s->amplitude_that_reaches_noise_floor_is_valid = 1;
+    if(!s->amplitude_that_reaches_noise_floor_is_valid)    /* Only once */
+    {
+        /* Scan the loop */
+        for(i = s->loopstart; i < s->loopend; i++)
+        {
+            int32_t val = fluid_rvoice_get_sample(s->data, s->data24, i);
+
+            if(val > peak_max)
+            {
+                peak_max = val;
+            }
+            else if(val < peak_min)
+            {
+                peak_min = val;
+            }
+        }
+
+        /* Determine the peak level */
+        if(peak_max > -peak_min)
+        {
+            peak = peak_max;
+        }
+        else
+        {
+            peak = -peak_min;
+        }
+
+        if(peak == 0)
+        {
+            /* Avoid division by zero */
+            peak = 1;
+        }
+
+        /* Calculate what factor will make the loop inaudible
+         * For example: Take a peak of 3277 (10 % of 32768).  The
+         * normalized amplitude is 0.1 (10 % of 32768).  An amplitude
+         * factor of 0.0001 (as opposed to the default 0.00001) will
+         * drop this sample to the noise floor.
+         */
+
+        /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */
+        normalized_amplitude_during_loop = ((fluid_real_t)peak) / (INT24_MAX * 1.0f);
+        result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
+
+        /* Store in sample */
+        s->amplitude_that_reaches_noise_floor = (double)result;
+        s->amplitude_that_reaches_noise_floor_is_valid = 1;
 #if 0
-    printf("Sample peak detection: factor %f\n", (double)result);
+        printf("Sample peak detection: factor %f\n", (double)result);
 #endif
-  };
-  return FLUID_OK;
+    }
+
+    return FLUID_OK;
 }
 
-fluid_real_t 
-fluid_voice_get_overflow_prio(fluid_voice_t* voice, 
-                              fluid_overflow_prio_tscore,
-                              unsigned int cur_time)
+float
+fluid_voice_get_overflow_prio(fluid_voice_t *voice,
+                              fluid_overflow_prio_t *score,
+                              unsigned int cur_time)
 {
-  fluid_real_t this_voice_prio = 0;
-
-  /* Are we already overflowing? */
-  if (!voice->can_access_overflow_rvoice) {
-    return OVERFLOW_PRIO_CANNOT_KILL;
-  }
-
-  /* Is this voice on the drum channel?
-   * Then it is very important.
-   * Also skip the released and sustained scores.
-   */
-  if (voice->channel->channel_type == CHANNEL_TYPE_DRUM){
-    this_voice_prio += score->percussion;
-  } 
-  else if (voice->has_noteoff) {
-    /* Noteoff has */
-    this_voice_prio += score->released;
-  } else if (_SUSTAINED(voice) || _HELD_BY_SOSTENUTO(voice)) {
-    /* This voice is still active, since the sustain pedal is held down.
-     * Consider it less important than non-sustained channels.
-     * This decision is somehow subjective. But usually the sustain pedal
-     * is used to play 'more-voices-than-fingers', so it shouldn't hurt
-     * if we kill one voice.
+    float this_voice_prio = 0;
+    int channel;
+
+    /* Are we already overflowing? */
+    if(!voice->can_access_overflow_rvoice)
+    {
+        return OVERFLOW_PRIO_CANNOT_KILL;
+    }
+
+    /* Is this voice on the drum channel?
+     * Then it is very important.
+     * Also skip the released and sustained scores.
      */
-    this_voice_prio += score->sustained;
-  }
-
-  /* We are not enthusiastic about releasing voices, which have just been started.
-   * Otherwise hitting a chord may result in killing notes belonging to that very same
-   * chord. So give newer voices a higher score. */
-  if (score->age) {
-    cur_time -= voice->start_time;
-    if (cur_time < 1) 
-      cur_time = 1; // Avoid div by zero
-    this_voice_prio += (score->age * voice->output_rate) / cur_time;
-  }
-
-  /* take a rough estimate of loudness into account. Louder voices are more important. */
-  if (score->volume) {
-    fluid_real_t a = voice->attenuation;
-    if (voice->has_noteoff) {
-      // FIXME: Should take into account where on the envelope we are...?
+    if(voice->channel->channel_type == CHANNEL_TYPE_DRUM)
+    {
+        this_voice_prio += score->percussion;
     }
-    if (a < 0.1) 
-      a = 0.1; // Avoid div by zero
-      this_voice_prio += score->volume / a;
+    else if(voice->has_noteoff)
+    {
+        /* Noteoff has */
+        this_voice_prio += score->released;
     }
-    
-  return this_voice_prio;
+    else if(fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))
+    {
+        /* This voice is still active, since the sustain pedal is held down.
+         * Consider it less important than non-sustained channels.
+         * This decision is somehow subjective. But usually the sustain pedal
+         * is used to play 'more-voices-than-fingers', so it shouldn't hurt
+         * if we kill one voice.
+         */
+        this_voice_prio += score->sustained;
+    }
+
+    /* We are not enthusiastic about releasing voices, which have just been started.
+     * Otherwise hitting a chord may result in killing notes belonging to that very same
+     * chord. So give newer voices a higher score. */
+    if(score->age)
+    {
+        cur_time -= voice->start_time;
+
+        if(cur_time < 1)
+        {
+            cur_time = 1; // Avoid div by zero
+        }
+
+        this_voice_prio += (score->age * voice->output_rate) / cur_time;
+    }
+
+    /* take a rough estimate of loudness into account. Louder voices are more important. */
+    if(score->volume)
+    {
+        fluid_real_t a = voice->attenuation;
+
+        if(voice->has_noteoff)
+        {
+            // FIXME: Should take into account where on the envelope we are...?
+        }
+
+        if(a < 0.1)
+        {
+            a = 0.1; // Avoid div by zero
+        }
+
+        this_voice_prio += score->volume / a;
+    }
+
+    /* Check if this voice is on an important channel. If so, then add the
+     * score for important channels */
+    channel = fluid_voice_get_channel(voice);
+
+    if(channel < score->num_important_channels && score->important_channels[channel])
+    {
+        this_voice_prio += score->important;
+    }
+
+    return this_voice_prio;
+}
+
+
+void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags)
+{
+    UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags);
 }
+