summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_voice.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_voice.c')
-rw-r--r--libs/fluidsynth/src/fluid_voice.c2692
1 files changed, 1527 insertions, 1165 deletions
diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c
index e6efbac899..51c1ebf655 100644
--- a/libs/fluidsynth/src/fluid_voice.c
+++ b/libs/fluidsynth/src/fluid_voice.c
@@ -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 */
@@ -36,299 +37,349 @@
/* 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_t* voice);
+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_t* voice)
+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_t* voice)
+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_t* voice, 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_t* voice, 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_t* voice, 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_t* voice, 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_t* voice)
+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_t* voice)
+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_t* voice, 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_t* voice, 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_t* voice, 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_t* voice)
+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_t* voice)
+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_t* voice)
+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_t* voice)
+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_t* voice)
+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_t* voice, 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_t* voice, 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_t* s)
+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_t* score,
- 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);
}
+