diff options
Diffstat (limited to 'libs/fluidsynth/src/fluid_voice.c')
-rw-r--r-- | libs/fluidsynth/src/fluid_voice.c | 2692 |
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); } + |