Refactor volume and modulation envelopes into separate file

This commit is contained in:
David Henningsson 2010-06-20 06:53:00 +00:00
parent cb143c8fdf
commit 933555f2ce
6 changed files with 246 additions and 213 deletions

View file

@ -127,6 +127,8 @@ libfluidsynth_la_SOURCES = \
fluid_voice.c \
fluid_voice.h \
fluid_iir_filter.c \
fluid_iir_filter.h \
fluid_adsr_env.h \
fluid_filerenderer.c \
fluid_aufile.c

View file

@ -0,0 +1,166 @@
/* FluidSynth - A Software Synthesizer
*
* 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
* 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.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#ifndef _FLUID_ADSR_ENVELOPE_H
#define _FLUID_ADSR_ENVELOPE_H
#include "fluidsynth_priv.h"
/*
* envelope data
*/
struct _fluid_env_data_t {
unsigned int count;
fluid_real_t coeff;
fluid_real_t incr;
fluid_real_t min;
fluid_real_t max;
};
/* Indices for envelope tables */
enum fluid_voice_envelope_index_t{
FLUID_VOICE_ENVDELAY,
FLUID_VOICE_ENVATTACK,
FLUID_VOICE_ENVHOLD,
FLUID_VOICE_ENVDECAY,
FLUID_VOICE_ENVSUSTAIN,
FLUID_VOICE_ENVRELEASE,
FLUID_VOICE_ENVFINISHED,
FLUID_VOICE_ENVLAST
};
typedef enum fluid_voice_envelope_index_t fluid_adsr_env_section_t;
typedef struct _fluid_adsr_env_t fluid_adsr_env_t;
struct _fluid_adsr_env_t {
fluid_env_data_t data[FLUID_VOICE_ENVLAST];
unsigned int count;
int section;
fluid_real_t val; /* the current value of the envelope */
};
/* For performance, all functions are inlined */
static inline void
fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
{
fluid_env_data_t* env_data;
fluid_real_t x;
env_data = &env->data[env->section];
/* skip to the next section of the envelope if necessary */
while (env->count >= env_data->count)
{
// If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage
// Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH]
if (env->section == FLUID_VOICE_ENVDECAY && is_volenv)
env->val = env_data->min * env_data->coeff;
env_data = &env->data[++env->section];
env->count = 0;
}
/* calculate the envelope value and check for valid range */
x = env_data->coeff * env->val + env_data->incr;
if (x < env_data->min)
{
x = env_data->min;
env->section++;
env->count = 0;
}
else if (x > env_data->max)
{
x = env_data->max;
env->section++;
env->count = 0;
}
env->val = x;
env->count++;
}
static inline void
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
fluid_adsr_env_section_t section,
unsigned int count,
fluid_real_t coeff,
fluid_real_t incr,
fluid_real_t min,
fluid_real_t max)
{
env->data[section].count = count;
env->data[section].coeff = coeff;
env->data[section].incr = incr;
env->data[section].min = min;
env->data[section].max = max;
}
static inline void
fluid_adsr_env_reset(fluid_adsr_env_t* env)
{
env->count = 0;
env->section = 0;
env->val = 0.0f;
}
static inline fluid_real_t
fluid_adsr_env_get_val(fluid_adsr_env_t* env)
{
return env->val;
}
static inline void
fluid_adsr_env_set_val(fluid_adsr_env_t* env, fluid_real_t val)
{
env->val = val;
}
static inline fluid_adsr_env_section_t
fluid_adsr_env_get_section(fluid_adsr_env_t* env)
{
return env->section;
}
static inline void
fluid_adsr_env_set_section(fluid_adsr_env_t* env,
fluid_adsr_env_section_t section)
{
env->section = section;
env->count = 0;
}
/* Used for determining which voice to kill.
Returns max amplitude from now, and forward in time.
*/
static inline fluid_real_t
fluid_adsr_env_get_max_val(fluid_adsr_env_t* env)
{
if (env->section > FLUID_VOICE_ENVATTACK){
return env->val * 1000;
} else {
return env->data[FLUID_VOICE_ENVATTACK].max;
}
}
#endif

View file

@ -123,6 +123,14 @@ void fluid_dsp_float_config (void)
}
static inline int
fluid_voice_is_looping(fluid_voice_t *voice)
{
return _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE);
}
/* No interpolation. Just take the sample, which is closest to
* the playback pointer. Questionable quality, but very
* efficient. */
@ -144,10 +152,8 @@ fluid_dsp_float_interpolate_none (fluid_voice_t *voice)
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
looping = fluid_voice_is_looping(voice);
end_index = looping ? voice->loopend - 1 : voice->end;
while (1)
@ -209,9 +215,7 @@ fluid_dsp_float_interpolate_linear (fluid_voice_t *voice)
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
looping = fluid_voice_is_looping(voice);
/* last index before 2nd interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
@ -300,9 +304,7 @@ fluid_dsp_float_interpolate_4th_order (fluid_voice_t *voice)
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
looping = fluid_voice_is_looping(voice);
/* last index before 4th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 2;
@ -457,9 +459,7 @@ fluid_dsp_float_interpolate_7th_order (fluid_voice_t *voice)
fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000);
/* voice is currently looping? */
looping = _SAMPLEMODE (voice) == FLUID_LOOP_DURING_RELEASE
|| (_SAMPLEMODE (voice) == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
looping = fluid_voice_is_looping(voice);
/* last index before 7th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 3;

View file

@ -3424,11 +3424,7 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
/* take a rough estimate of loudness into account. Louder voices are more important. */
if (voice->volenv_section > FLUID_VOICE_ENVATTACK){
this_voice_prio += voice->volenv_val * 1000.;
} else {
this_voice_prio += voice->volenv_data[FLUID_VOICE_ENVATTACK].max * 1000.;
}
this_voice_prio += fluid_voice_get_loudness(voice) * 1000.;
/* check if this voice has less priority than the previous candidate. */
if (this_voice_prio < best_prio)

View file

@ -77,29 +77,14 @@ new_fluid_voice(fluid_real_t output_rate)
* or generator. Therefore it is enough to initialize them once
* during the lifetime of the synth.
*/
voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff;
voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f;
voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f;
voice->volenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f;
voice->volenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff;
voice->volenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f;
voice->volenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff;
voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f;
voice->modenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f;
voice->modenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff;
voice->modenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f;
voice->modenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f;
fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVSUSTAIN,
0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVFINISHED,
0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVSUSTAIN,
0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVFINISHED,
0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
return voice;
}
@ -147,17 +132,13 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
voice->interp_method = fluid_channel_get_interp_method(voice->channel);
/* vol env initialization */
voice->volenv_count = 0;
voice->volenv_section = 0;
voice->volenv_val = 0.0f;
fluid_adsr_env_reset(&voice->volenv);
voice->amp = 0.0f; /* The last value of the volume envelope, used to
calculate the volume increment during
processing */
/* mod env initialization*/
voice->modenv_count = 0;
voice->modenv_section = 0;
voice->modenv_val = 0.0f;
fluid_adsr_env_reset(&voice->modenv);
/* mod lfo */
voice->modlfo_val = 0.0;/* Fixme: Retrieve from any other existing
@ -264,8 +245,6 @@ int
fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
{
fluid_real_t target_amp; /* target amplitude */
fluid_env_data_t* env_data;
fluid_real_t x;
int count = 0;
/* Other routines (such as fluid_voice_effects) use the last dsp_buf assigned */
@ -293,38 +272,10 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
/******************* vol env **********************/
env_data = &voice->volenv_data[voice->volenv_section];
fluid_adsr_env_calc(&voice->volenv, 1);
/* skip to the next section of the envelope if necessary */
while (voice->volenv_count >= env_data->count)
{
// If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage
if (env_data && voice->volenv_section == FLUID_VOICE_ENVDECAY)
voice->volenv_val = env_data->min * env_data->coeff;
env_data = &voice->volenv_data[++voice->volenv_section];
voice->volenv_count = 0;
}
/* calculate the envelope value and check for valid range */
x = env_data->coeff * voice->volenv_val + env_data->incr;
if (x < env_data->min)
{
x = env_data->min;
voice->volenv_section++;
voice->volenv_count = 0;
}
else if (x > env_data->max)
{
x = env_data->max;
voice->volenv_section++;
voice->volenv_count = 0;
}
voice->volenv_val = x;
voice->volenv_count++;
if (voice->volenv_section == FLUID_VOICE_ENVFINISHED)
if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVFINISHED)
{
fluid_profile (FLUID_PROF_VOICE_RELEASE, voice->ref);
fluid_voice_off (voice);
@ -335,33 +286,7 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
/******************* mod env **********************/
env_data = &voice->modenv_data[voice->modenv_section];
/* skip to the next section of the envelope if necessary */
while (voice->modenv_count >= env_data->count)
{
env_data = &voice->modenv_data[++voice->modenv_section];
voice->modenv_count = 0;
}
/* calculate the envelope value and check for valid range */
x = env_data->coeff * voice->modenv_val + env_data->incr;
if (x < env_data->min)
{
x = env_data->min;
voice->modenv_section++;
voice->modenv_count = 0;
}
else if (x > env_data->max)
{
x = env_data->max;
voice->modenv_section++;
voice->modenv_count = 0;
}
voice->modenv_val = x;
voice->modenv_count++;
fluid_adsr_env_calc(&voice->modenv, 0);
fluid_check_fpe ("voice_write mod env");
/******************* mod lfo **********************/
@ -411,17 +336,17 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
* - amplitude envelope
*/
if (voice->volenv_section == FLUID_VOICE_ENVDELAY)
if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVDELAY)
goto post_process; /* The volume amplitude is in hold phase. No sound is produced. */
if (voice->volenv_section == FLUID_VOICE_ENVATTACK)
if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVATTACK)
{
/* the envelope is in the attack section: ramp linearly to max value.
* A positive modlfo_to_vol should increase volume (negative attenuation).
*/
target_amp = fluid_atten2amp (voice->attenuation)
* fluid_cb2amp (voice->modlfo_val * -voice->modlfo_to_vol)
* voice->volenv_val;
* fluid_adsr_env_get_val(&voice->volenv);
}
else
{
@ -429,7 +354,7 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
fluid_real_t amp_max;
target_amp = fluid_atten2amp (voice->attenuation)
* fluid_cb2amp (960.0f * (1.0f - voice->volenv_val)
* fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->volenv))
+ voice->modlfo_val * -voice->modlfo_to_vol);
/* We turn off a voice, if the volume has dropped low enough. */
@ -455,7 +380,8 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
* volenv_val can only drop):
*/
amp_max = fluid_atten2amp (voice->min_attenuation_cB) * voice->volenv_val;
amp_max = fluid_atten2amp (voice->min_attenuation_cB) *
fluid_adsr_env_get_val(&voice->volenv);
/* And if amp_max is already smaller than the known amplitude,
* which will attenuate the sample below the noise floor, then we
@ -484,7 +410,7 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
voice->phase_incr = fluid_ct2hz_real
(voice->pitch + voice->modlfo_val * voice->modlfo_to_pitch
+ voice->viblfo_val * voice->viblfo_to_pitch
+ voice->modenv_val * voice->modenv_to_pitch) / voice->root_pitch_hz;
+ fluid_adsr_env_get_val(&voice->modenv) * voice->modenv_to_pitch) / voice->root_pitch_hz;
fluid_check_fpe ("voice_write phase calculation");
@ -494,7 +420,7 @@ fluid_voice_write (fluid_voice_t* voice, fluid_real_t *dsp_buf)
/*************** resonant filter ******************/
fluid_iir_filter_calc(&voice->resonant_filter, voice->output_rate,
voice->modlfo_val * voice->modlfo_to_fc +
voice->modenv_val * voice->modenv_to_fc);
fluid_adsr_env_get_val(&voice->modenv) * voice->modenv_to_fc);
/*********************** run the dsp chain ************************
* The sample is mixed with the output buffer.
@ -1137,32 +1063,23 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
x = _GEN(voice, GEN_VOLENVDELAY);
fluid_clip(x, -12000.0f, 5000.0f);
count = NUM_BUFFERS_DELAY(x);
voice->volenv_data[FLUID_VOICE_ENVDELAY].count = count;
voice->volenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f;
voice->volenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f;
fluid_adsr_env_set_data(&voice->volenv, 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);
voice->volenv_data[FLUID_VOICE_ENVATTACK].count = count;
voice->volenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f;
voice->volenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f;
voice->volenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f;
voice->volenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f;
fluid_adsr_env_set_data(&voice->volenv, 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 */
voice->volenv_data[FLUID_VOICE_ENVHOLD].count = count;
voice->volenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f;
voice->volenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f;
voice->volenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f;
fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVHOLD,
count, 1.0f, 0.0f, -1.0f, 2.0f);
break;
case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */
@ -1171,54 +1088,39 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
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 */
voice->volenv_data[FLUID_VOICE_ENVDECAY].count = count;
voice->volenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f;
voice->volenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f;
voice->volenv_data[FLUID_VOICE_ENVDECAY].min = y;
voice->volenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f;
fluid_adsr_env_set_data(&voice->volenv, 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);
voice->volenv_data[FLUID_VOICE_ENVRELEASE].count = count;
voice->volenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f;
voice->volenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0f;
voice->volenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f;
voice->volenv_data[FLUID_VOICE_ENVRELEASE].max = 1.0f;
fluid_adsr_env_set_data(&voice->volenv, FLUID_VOICE_ENVRELEASE,
count, 1.0f, count ? -1.0f / count : 0.0f, 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);
voice->modenv_data[FLUID_VOICE_ENVDELAY].count = NUM_BUFFERS_DELAY(x);
voice->modenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f;
voice->modenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f;
fluid_adsr_env_set_data(&voice->modenv, 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);
voice->modenv_data[FLUID_VOICE_ENVATTACK].count = count;
voice->modenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f;
voice->modenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f;
voice->modenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f;
fluid_adsr_env_set_data(&voice->modenv, 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 */
voice->modenv_data[FLUID_VOICE_ENVHOLD].count = count;
voice->modenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f;
voice->modenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f;
fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVHOLD,
count, 1.0f, 0.0f, -1.0f, 2.0f);
break;
case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */
@ -1227,22 +1129,17 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
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);
voice->modenv_data[FLUID_VOICE_ENVDECAY].count = count;
voice->modenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f;
voice->modenv_data[FLUID_VOICE_ENVDECAY].min = y;
voice->modenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f;
fluid_adsr_env_set_data(&voice->modenv, 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);
voice->modenv_data[FLUID_VOICE_ENVRELEASE].count = count;
voice->modenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f;
voice->modenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0;
voice->modenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f;
voice->modenv_data[FLUID_VOICE_ENVRELEASE].max = 2.0f;
fluid_adsr_env_set_data(&voice->modenv, FLUID_VOICE_ENVRELEASE,
count, 1.0f, count ? -1.0f / count : 0.0f, 0.0f, 2.0f);
break;
} /* switch gen */
@ -1378,25 +1275,23 @@ fluid_voice_noteoff(fluid_voice_t* voice)
if (voice->channel && fluid_channel_sustained(voice->channel)) {
voice->status = FLUID_VOICE_SUSTAINED;
} else {
if (voice->volenv_section == FLUID_VOICE_ENVATTACK) {
if (fluid_adsr_env_get_section(&voice->volenv) == FLUID_VOICE_ENVATTACK) {
/* A voice is turned off during the attack section of the volume
* envelope. The attack section ramps up linearly with
* amplitude. The other sections use logarithmic scaling. Calculate new
* volenv_val to achieve equievalent amplitude during the release phase
* for seamless volume transition.
*/
if (voice->volenv_val > 0){
if (fluid_adsr_env_get_val(&voice->volenv) > 0){
fluid_real_t lfo = voice->modlfo_val * -voice->modlfo_to_vol;
fluid_real_t amp = voice->volenv_val * pow (10.0, lfo / -200);
fluid_real_t amp = fluid_adsr_env_get_val(&voice->volenv) * pow (10.0, lfo / -200);
fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1);
fluid_clip (env_value, 0.0, 1.0);
voice->volenv_val = env_value;
fluid_adsr_env_set_val(&voice->volenv, env_value);
}
}
voice->volenv_section = FLUID_VOICE_ENVRELEASE;
voice->volenv_count = 0;
voice->modenv_section = FLUID_VOICE_ENVRELEASE;
voice->modenv_count = 0;
fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVRELEASE);
}
return FLUID_OK;
@ -1426,11 +1321,9 @@ fluid_voice_kill_excl(fluid_voice_t* voice){
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
/* If the voice is not yet in release state, put it into release state */
if (voice->volenv_section != FLUID_VOICE_ENVRELEASE){
voice->volenv_section = FLUID_VOICE_ENVRELEASE;
voice->volenv_count = 0;
voice->modenv_section = FLUID_VOICE_ENVRELEASE;
voice->modenv_count = 0;
if (fluid_adsr_env_get_section(&voice->volenv) != FLUID_VOICE_ENVRELEASE){
fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVRELEASE);
}
/* Speed up the volume envelope */
@ -1458,10 +1351,8 @@ fluid_voice_off(fluid_voice_t* voice)
fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref);
voice->chan = NO_CHANNEL;
voice->volenv_section = FLUID_VOICE_ENVFINISHED;
voice->volenv_count = 0;
voice->modenv_section = FLUID_VOICE_ENVFINISHED;
voice->modenv_count = 0;
fluid_adsr_env_set_section(&voice->volenv, FLUID_VOICE_ENVFINISHED);
fluid_adsr_env_set_section(&voice->modenv, FLUID_VOICE_ENVFINISHED);
voice->status = FLUID_VOICE_OFF;
/* Decrement the reference count of the sample. */
@ -1748,7 +1639,8 @@ fluid_voice_check_sample_sanity(fluid_voice_t* voice)
/* Is this voice run in loop mode, or does it run straight to the
end of the waveform data? */
if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) &&
(fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE))
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
/* Yes, it will loop as soon as it reaches the loop point. In
* this case we must prevent, that the playback pointer (phase)
@ -1874,3 +1766,6 @@ fluid_voice_optimize_sample(fluid_sample_t* s)
};
return FLUID_OK;
}

View file

@ -26,6 +26,7 @@
#include "fluid_gen.h"
#include "fluid_mod.h"
#include "fluid_iir_filter.h"
#include "fluid_adsr_env.h"
#define NO_CHANNEL 0xff
@ -38,29 +39,6 @@ enum fluid_voice_status
};
/*
* envelope data
*/
struct _fluid_env_data_t {
unsigned int count;
fluid_real_t coeff;
fluid_real_t incr;
fluid_real_t min;
fluid_real_t max;
};
/* Indices for envelope tables */
enum fluid_voice_envelope_index_t{
FLUID_VOICE_ENVDELAY,
FLUID_VOICE_ENVATTACK,
FLUID_VOICE_ENVHOLD,
FLUID_VOICE_ENVDECAY,
FLUID_VOICE_ENVSUSTAIN,
FLUID_VOICE_ENVRELEASE,
FLUID_VOICE_ENVFINISHED,
FLUID_VOICE_ENVLAST
};
/*
* fluid_voice_t
*/
@ -140,18 +118,12 @@ struct _fluid_voice_t
fluid_real_t synth_gain;
/* vol env */
fluid_env_data_t volenv_data[FLUID_VOICE_ENVLAST];
unsigned int volenv_count;
int volenv_section;
fluid_real_t volenv_val;
fluid_adsr_env_t volenv;
fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
fluid_real_t amplitude_that_reaches_noise_floor_loop;
/* mod env */
fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST];
unsigned int modenv_count;
int modenv_section;
fluid_real_t modenv_val; /* the value of the modulation envelope */
fluid_adsr_env_t modenv;
fluid_real_t modenv_to_fc;
fluid_real_t modenv_to_pitch;
@ -241,7 +213,7 @@ int fluid_voice_kill_excl(fluid_voice_t* voice);
/* A voice is 'ON', if it has not yet received a noteoff
* event. Sending a noteoff event will advance the envelopes to
* section 5 (release). */
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && (voice)->volenv_section < FLUID_VOICE_ENVRELEASE)
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))
#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
@ -251,6 +223,8 @@ int fluid_voice_kill_excl(fluid_voice_t* voice);
/* FIXME - This doesn't seem to be used anywhere - JG */
fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num);
#define fluid_voice_get_loudness(voice) (fluid_adsr_env_get_max_val(&voice->volenv))
#define _GEN(_voice, _n) \
((fluid_real_t)(_voice)->gen[_n].val \
+ (fluid_real_t)(_voice)->gen[_n].mod \