#include "fluidsynth_priv.h"
#include "fluid_voice.h"
#include "fluid_mod.h"
#include "fluid_chan.h"
#include "fluid_conv.h"
#include "fluid_synth.h"
#include "fluid_sys.h"
#include "fluid_sfont.h"
#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f
#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f
#define FLUID_NOISE_FLOOR 0.00003
#define FLUID_MIN_LOOP_SIZE 2
#define FLUID_MIN_LOOP_PAD 0
#define FLUID_MIN_VOLENVRELEASE -7200.0f
static void fluid_voice_effects (fluid_voice_t *voice, int count,
fluid_real_t* dsp_left_buf,
fluid_real_t* dsp_right_buf,
fluid_real_t* dsp_reverb_buf,
fluid_real_t* dsp_chorus_buf);
fluid_voice_t*
new_fluid_voice(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->status = FLUID_VOICE_CLEAN;
voice->chan = NO_CHANNEL;
voice->key = 0;
voice->vel = 0;
voice->channel = NULL;
voice->sample = NULL;
voice->output_rate = output_rate;
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;
return voice;
}
int
delete_fluid_voice(fluid_voice_t* voice)
{
if (voice == NULL) {
return FLUID_OK;
}
FLUID_FREE(voice);
return FLUID_OK;
}
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)
{
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->sample = sample;
voice->start_time = start_time;
voice->ticks = 0;
voice->noteoff_ticks = 0;
voice->debug = 0;
voice->has_looped = 0;
voice->last_fres = -1;
voice->filter_startup = 1;
voice->interp_method = fluid_channel_get_interp_method(voice->channel);
voice->volenv_count = 0;
voice->volenv_section = 0;
voice->volenv_val = 0.0f;
voice->amp = 0.0f;
voice->modenv_count = 0;
voice->modenv_section = 0;
voice->modenv_val = 0.0f;
voice->modlfo_val = 0.0;
voice->viblfo_val = 0.0f;
voice->hist1 = 0;
voice->hist2 = 0;
fluid_gen_init(&voice->gen[0], channel);
voice->synth_gain = gain;
if (voice->synth_gain < 0.0000001){
voice->synth_gain = 0.0000001;
}
voice->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain;
voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain;
fluid_sample_incr_ref(voice->sample);
return FLUID_OK;
}
void fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
{
voice->gen[i].val = val;
voice->gen[i].flags = GEN_SET;
}
void fluid_voice_gen_incr(fluid_voice_t* voice, int i, float val)
{
voice->gen[i].val += val;
voice->gen[i].flags = GEN_SET;
}
float fluid_voice_gen_get(fluid_voice_t* voice, int gen)
{
return voice->gen[gen].val;
}
fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num)
{
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);
}
}
int
fluid_voice_write(fluid_voice_t* voice,
fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf,
fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf)
{
fluid_real_t fres;
fluid_real_t target_amp;
int count;
fluid_real_t dsp_buf[FLUID_BUFSIZE];
fluid_env_data_t* env_data;
fluid_real_t x;
if (!_PLAYING(voice)) return FLUID_OK;
if (voice->sample == NULL)
{
fluid_voice_off(voice);
return FLUID_OK;
}
if (voice->noteoff_ticks != 0 && voice->ticks >= voice->noteoff_ticks)
{
fluid_voice_noteoff(voice);
}
fluid_voice_check_sample_sanity (voice);
env_data = &voice->volenv_data[voice->volenv_section];
while (voice->volenv_count >= env_data->count)
{
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;
}
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)
{
fluid_voice_off (voice);
return FLUID_OK;
}
env_data = &voice->modenv_data[voice->modenv_section];
while (voice->modenv_count >= env_data->count)
{
env_data = &voice->modenv_data[++voice->modenv_section];
voice->modenv_count = 0;
}
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++;
if (voice->ticks >= voice->modlfo_delay)
{
voice->modlfo_val += voice->modlfo_incr;
if (voice->modlfo_val > 1.0)
{
voice->modlfo_incr = -voice->modlfo_incr;
voice->modlfo_val = (fluid_real_t) 2.0 - voice->modlfo_val;
}
else if (voice->modlfo_val < -1.0)
{
voice->modlfo_incr = -voice->modlfo_incr;
voice->modlfo_val = (fluid_real_t) -2.0 - voice->modlfo_val;
}
}
if (voice->ticks >= voice->viblfo_delay)
{
voice->viblfo_val += voice->viblfo_incr;
if (voice->viblfo_val > (fluid_real_t) 1.0)
{
voice->viblfo_incr = -voice->viblfo_incr;
voice->viblfo_val = (fluid_real_t) 2.0 - voice->viblfo_val;
}
else if (voice->viblfo_val < -1.0)
{
voice->viblfo_incr = -voice->viblfo_incr;
voice->viblfo_val = (fluid_real_t) -2.0 - voice->viblfo_val;
}
}
if (voice->volenv_section == FLUID_VOICE_ENVDELAY)
goto post_process;
if (voice->volenv_section == FLUID_VOICE_ENVATTACK)
{
target_amp = fluid_atten2amp (voice->attenuation)
* fluid_cb2amp (voice->modlfo_val * -voice->modlfo_to_vol)
* voice->volenv_val;
}
else
{
fluid_real_t amplitude_that_reaches_noise_floor;
fluid_real_t amp_max;
target_amp = fluid_atten2amp (voice->attenuation)
* fluid_cb2amp (960.0f * (1.0f - voice->volenv_val)
+ voice->modlfo_val * -voice->modlfo_to_vol);
if (voice->has_looped)
amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop;
else
amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_nonloop;
amp_max = fluid_atten2amp (voice->min_attenuation_cB) * voice->volenv_val;
if (amp_max < amplitude_that_reaches_noise_floor)
{
fluid_voice_off (voice);
goto post_process;
}
}
voice->amp_incr = (target_amp - voice->amp) / FLUID_BUFSIZE;
if ((voice->amp == 0.0f) && (voice->amp_incr == 0.0f))
goto post_process;
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;
if (voice->phase_incr == 0) voice->phase_incr = 1;
fres = fluid_ct2hz(voice->fres
+ voice->modlfo_val * voice->modlfo_to_fc
+ voice->modenv_val * voice->modenv_to_fc);
if (fres > 0.45f * voice->output_rate)
fres = 0.45f * voice->output_rate;
else if (fres < 5)
fres = 5;
if ((fabs(fres - voice->last_fres) > 0.01))
{
fluid_real_t omega = (fluid_real_t) (2.0 * M_PI * (fres / ((float) voice->output_rate)));
fluid_real_t sin_coeff = (fluid_real_t) sin(omega);
fluid_real_t cos_coeff = (fluid_real_t) cos(omega);
fluid_real_t alpha_coeff = sin_coeff / (2.0f * voice->q_lin);
fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff);
fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv;
fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv;
fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * voice->filter_gain;
fluid_real_t b02_temp = b1_temp * 0.5f;
if (voice->filter_startup)
{
voice->a1 = a1_temp;
voice->a2 = a2_temp;
voice->b02 = b02_temp;
voice->b1 = b1_temp;
voice->filter_coeff_incr_count = 0;
voice->filter_startup = 0;
}
else
{
#define FILTER_TRANSITION_SAMPLES (FLUID_BUFSIZE)
voice->a1_incr = (a1_temp - voice->a1) / FILTER_TRANSITION_SAMPLES;
voice->a2_incr = (a2_temp - voice->a2) / FILTER_TRANSITION_SAMPLES;
voice->b02_incr = (b02_temp - voice->b02) / FILTER_TRANSITION_SAMPLES;
voice->b1_incr = (b1_temp - voice->b1) / FILTER_TRANSITION_SAMPLES;
voice->filter_coeff_incr_count = FILTER_TRANSITION_SAMPLES;
}
voice->last_fres = fres;
}
voice->dsp_buf = dsp_buf;
switch (voice->interp_method)
{
case FLUID_INTERP_NONE:
count = fluid_dsp_float_interpolate_none (voice);
break;
case FLUID_INTERP_LINEAR:
count = fluid_dsp_float_interpolate_linear (voice);
break;
case FLUID_INTERP_4THORDER:
default:
count = fluid_dsp_float_interpolate_4th_order (voice);
break;
case FLUID_INTERP_7THORDER:
count = fluid_dsp_float_interpolate_7th_order (voice);
break;
}
if (count > 0)
fluid_voice_effects (voice, count, dsp_left_buf, dsp_right_buf,
dsp_reverb_buf, dsp_chorus_buf);
if (count < FLUID_BUFSIZE)
{
fluid_voice_off(voice);
}
post_process:
voice->ticks += FLUID_BUFSIZE;
return FLUID_OK;
}
static void
fluid_voice_effects (fluid_voice_t *voice, int count,
fluid_real_t* dsp_left_buf, fluid_real_t* dsp_right_buf,
fluid_real_t* dsp_reverb_buf, fluid_real_t* dsp_chorus_buf)
{
fluid_real_t dsp_hist1 = voice->hist1;
fluid_real_t dsp_hist2 = voice->hist2;
fluid_real_t dsp_a1 = voice->a1;
fluid_real_t dsp_a2 = voice->a2;
fluid_real_t dsp_b02 = voice->b02;
fluid_real_t dsp_b1 = voice->b1;
fluid_real_t dsp_a1_incr = voice->a1_incr;
fluid_real_t dsp_a2_incr = voice->a2_incr;
fluid_real_t dsp_b02_incr = voice->b02_incr;
fluid_real_t dsp_b1_incr = voice->b1_incr;
int dsp_filter_coeff_incr_count = voice->filter_coeff_incr_count;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_centernode;
int dsp_i;
float v;
if (fabs (dsp_hist1) < 1e-20) dsp_hist1 = 0.0f;
if (dsp_filter_coeff_incr_count > 0)
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
{
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
dsp_hist2 = dsp_hist1;
dsp_hist1 = dsp_centernode;
if (dsp_filter_coeff_incr_count-- > 0)
{
dsp_a1 += dsp_a1_incr;
dsp_a2 += dsp_a2_incr;
dsp_b02 += dsp_b02_incr;
dsp_b1 += dsp_b1_incr;
}
}
}
else
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
{
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
dsp_hist2 = dsp_hist1;
dsp_hist1 = dsp_centernode;
}
}
if ((-0.5 < voice->pan) && (voice->pan < 0.5))
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
{
v = voice->amp_left * dsp_buf[dsp_i];
dsp_left_buf[dsp_i] += v;
dsp_right_buf[dsp_i] += v;
}
}
else
{
if (voice->amp_left != 0.0)
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i];
}
if (voice->amp_right != 0.0)
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i];
}
}
if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0))
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i];
}
if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0))
{
for (dsp_i = 0; dsp_i < count; dsp_i++)
dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i];
}
voice->hist1 = dsp_hist1;
voice->hist2 = dsp_hist2;
voice->a1 = dsp_a1;
voice->a2 = dsp_a2;
voice->b02 = dsp_b02;
voice->b1 = dsp_b1;
voice->filter_coeff_incr_count = dsp_filter_coeff_incr_count;
}
fluid_channel_t*
fluid_voice_get_channel(fluid_voice_t* voice)
{
return voice->channel;
}
void fluid_voice_start(fluid_voice_t* voice)
{
fluid_voice_calculate_runtime_synthesis_parameters(voice);
voice->check_sample_sanity_flag=FLUID_SAMPLESANITY_STARTUP;
voice->status = FLUID_VOICE_ON;
}
int
fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
{
int i;
int list_of_generators_to_initialize[35] = {
GEN_STARTADDROFS,
GEN_ENDADDROFS,
GEN_STARTLOOPADDROFS,
GEN_ENDLOOPADDROFS,
GEN_MODLFOTOPITCH,
GEN_VIBLFOTOPITCH,
GEN_MODENVTOPITCH,
GEN_FILTERFC,
GEN_FILTERQ,
GEN_MODLFOTOFILTERFC,
GEN_MODENVTOFILTERFC,
GEN_MODLFOTOVOL,
GEN_CHORUSSEND,
GEN_REVERBSEND,
GEN_PAN,
GEN_MODLFODELAY,
GEN_MODLFOFREQ,
GEN_VIBLFODELAY,
GEN_VIBLFOFREQ,
GEN_MODENVDELAY,
GEN_MODENVATTACK,
GEN_MODENVHOLD,
GEN_MODENVDECAY,
GEN_MODENVRELEASE,
GEN_VOLENVDELAY,
GEN_VOLENVATTACK,
GEN_VOLENVHOLD,
GEN_VOLENVDECAY,
GEN_VOLENVRELEASE,
GEN_KEYNUM,
GEN_VELOCITY,
GEN_ATTENUATION,
GEN_OVERRIDEROOTKEY,
GEN_PITCH,
-1};
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;
}
if (fluid_channel_has_tuning(voice->channel)) {
#define __pitch(_k) fluid_tuning_get_pitch(tuning, _k)
fluid_tuning_t* tuning = fluid_channel_get_tuning(voice->channel);
voice->gen[GEN_PITCH].val = (__pitch(60) + (voice->gen[GEN_SCALETUNE].val / 100.0f *
(__pitch(voice->key) - __pitch(60))));
} else {
voice->gen[GEN_PITCH].val = (voice->gen[GEN_SCALETUNE].val * (voice->key - 60.0f)
+ 100.0f * 60.0f);
}
for (i = 0; list_of_generators_to_initialize[i] != -1; i++) {
fluid_voice_update_param(voice, list_of_generators_to_initialize[i]);
}
voice->min_attenuation_cB = fluid_voice_get_lower_boundary_for_attenuation(voice);
return FLUID_OK;
}
int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
int gen_key2base, int is_decay)
{
fluid_real_t timecents;
fluid_real_t seconds;
int buffers;
timecents = (_GEN(voice, gen_base) + _GEN(voice, gen_key2base) * (60.0 - voice->key));
if (is_decay){
if (timecents > 8000.0) {
timecents = 8000.0;
}
} else {
if (timecents > 5000) {
timecents = 5000.0;
}
if (timecents <= -32768.) {
return 0;
}
}
if (timecents < -12000.0) {
timecents = -12000.0;
}
seconds = fluid_tc2sec(timecents);
buffers = (int)(((fluid_real_t)voice->output_rate * seconds)
/ (fluid_real_t)FLUID_BUFSIZE
+0.5);
return buffers;
}
void
fluid_voice_update_param(fluid_voice_t* voice, int gen)
{
double q_dB;
fluid_real_t x;
fluid_real_t y;
unsigned int count;
static const float ALT_ATTENUATION_SCALE = 0.4;
switch (gen) {
case GEN_PAN:
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;
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;
fluid_clip(voice->attenuation, 0.0, 1440.0);
break;
case GEN_PITCH:
case GEN_COARSETUNE:
case GEN_FINETUNE:
voice->pitch = (_GEN(voice, GEN_PITCH)
+ 100.0f * _GEN(voice, GEN_COARSETUNE)
+ _GEN(voice, GEN_FINETUNE));
break;
case GEN_REVERBSEND:
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;
break;
case GEN_CHORUSSEND:
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;
break;
case GEN_OVERRIDEROOTKEY:
if (voice->gen[GEN_OVERRIDEROOTKEY].val > -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;
}
voice->root_pitch = fluid_ct2hz(voice->root_pitch);
if (voice->sample != NULL) {
voice->root_pitch *= (fluid_real_t) voice->output_rate / voice->sample->samplerate;
}
break;
case GEN_FILTERFC:
voice->fres = _GEN(voice, GEN_FILTERFC);
voice->last_fres = -1.0f;
break;
case GEN_FILTERQ:
q_dB = _GEN(voice, GEN_FILTERQ) / 10.0f;
fluid_clip(q_dB, 0.0f, 96.0f);
q_dB -= 3.01f;
voice->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f));
voice->filter_gain = (fluid_real_t) (1.0 / sqrt(voice->q_lin));
voice->last_fres = -1.;
break;
case GEN_MODLFOTOPITCH:
voice->modlfo_to_pitch = _GEN(voice, GEN_MODLFOTOPITCH);
fluid_clip(voice->modlfo_to_pitch, -12000.0, 12000.0);
break;
case GEN_MODLFOTOVOL:
voice->modlfo_to_vol = _GEN(voice, GEN_MODLFOTOVOL);
fluid_clip(voice->modlfo_to_vol, -960.0, 960.0);
break;
case GEN_MODLFOTOFILTERFC:
voice->modlfo_to_fc = _GEN(voice, GEN_MODLFOTOFILTERFC);
fluid_clip(voice->modlfo_to_fc, -12000, 12000);
break;
case GEN_MODLFODELAY:
x = _GEN(voice, GEN_MODLFODELAY);
fluid_clip(x, -12000.0f, 5000.0f);
voice->modlfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
break;
case GEN_MODLFOFREQ:
x = _GEN(voice, GEN_MODLFOFREQ);
fluid_clip(x, -16000.0f, 4500.0f);
voice->modlfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
break;
case GEN_VIBLFOFREQ:
x = _GEN(voice, GEN_VIBLFOFREQ);
fluid_clip(x, -16000.0f, 4500.0f);
voice->viblfo_incr = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
break;
case GEN_VIBLFODELAY:
x = _GEN(voice,GEN_VIBLFODELAY);
fluid_clip(x, -12000.0f, 5000.0f);
voice->viblfo_delay = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
break;
case GEN_VIBLFOTOPITCH:
voice->viblfo_to_pitch = _GEN(voice, GEN_VIBLFOTOPITCH);
fluid_clip(voice->viblfo_to_pitch, -12000.0, 12000.0);
break;
case GEN_KEYNUM:
x = _GEN(voice, GEN_KEYNUM);
if (x >= 0){
voice->key = x;
}
break;
case GEN_VELOCITY:
x = _GEN(voice, GEN_VELOCITY);
if (x > 0) {
voice->vel = x;
}
break;
case GEN_MODENVTOPITCH:
voice->modenv_to_pitch = _GEN(voice, GEN_MODENVTOPITCH);
fluid_clip(voice->modenv_to_pitch, -12000.0, 12000.0);
break;
case GEN_MODENVTOFILTERFC:
voice->modenv_to_fc = _GEN(voice,GEN_MODENVTOFILTERFC);
fluid_clip(voice->modenv_to_fc, -12000.0, 12000.0);
break;
case GEN_STARTADDROFS:
case GEN_STARTADDRCOARSEOFS:
if (voice->sample != NULL) {
voice->start = (voice->sample->start
+ (int) _GEN(voice, GEN_STARTADDROFS)
+ 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
}
break;
case GEN_ENDADDROFS:
case GEN_ENDADDRCOARSEOFS:
if (voice->sample != NULL) {
voice->end = (voice->sample->end
+ (int) _GEN(voice, GEN_ENDADDROFS)
+ 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
}
break;
case GEN_STARTLOOPADDROFS:
case GEN_STARTLOOPADDRCOARSEOFS:
if (voice->sample != NULL) {
voice->loopstart = (voice->sample->loopstart
+ (int) _GEN(voice, GEN_STARTLOOPADDROFS)
+ 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
}
break;
case GEN_ENDLOOPADDROFS:
case GEN_ENDLOOPADDRCOARSEOFS:
if (voice->sample != NULL) {
voice->loopend = (voice->sample->loopend
+ (int) _GEN(voice, GEN_ENDLOOPADDROFS)
+ 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK;
}
break;
#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)
case GEN_VOLENVDELAY:
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;
break;
case GEN_VOLENVATTACK:
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;
break;
case GEN_VOLENVHOLD:
case GEN_KEYTOVOLENVHOLD:
count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0);
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;
break;
case GEN_VOLENVDECAY:
case GEN_VOLENVSUSTAIN:
case GEN_KEYTOVOLENVDECAY:
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);
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;
break;
case GEN_VOLENVRELEASE:
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;
break;
case GEN_MODENVDELAY:
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;
break;
case GEN_MODENVATTACK:
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;
break;
case GEN_MODENVHOLD:
case GEN_KEYTOMODENVHOLD:
count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0);
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;
break;
case GEN_MODENVDECAY:
case GEN_MODENVSUSTAIN:
case GEN_KEYTOMODENVDECAY:
count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1);
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;
break;
case GEN_MODENVRELEASE:
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;
break;
}
}
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;
for (i = 0; i < voice->mod_count; i++) {
mod = &voice->mod[i];
if (fluid_mod_has_source(mod, cc, ctrl)) {
gen = fluid_mod_get_dest(mod);
modval = 0.0;
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);
fluid_voice_update_param(voice, gen);
}
}
return FLUID_OK;
}
int fluid_voice_modulate_all(fluid_voice_t* voice)
{
fluid_mod_t* mod;
int i, k, gen;
fluid_real_t modval;
for (i = 0; i < voice->mod_count; i++) {
mod = &voice->mod[i];
gen = fluid_mod_get_dest(mod);
modval = 0.0;
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);
fluid_voice_update_param(voice, gen);
}
return FLUID_OK;
}
int
fluid_voice_noteoff(fluid_voice_t* voice)
{
unsigned int at_tick;
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
if (at_tick > voice->ticks) {
voice->noteoff_ticks = at_tick;
return FLUID_OK;
}
if (voice->channel && fluid_channel_sustained(voice->channel)) {
voice->status = FLUID_VOICE_SUSTAINED;
} else {
if (voice->volenv_section == FLUID_VOICE_ENVATTACK) {
if (voice->volenv_val > 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 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;
}
}
voice->volenv_section = FLUID_VOICE_ENVRELEASE;
voice->volenv_count = 0;
voice->modenv_section = FLUID_VOICE_ENVRELEASE;
voice->modenv_count = 0;
}
return FLUID_OK;
}
int
fluid_voice_kill_excl(fluid_voice_t* voice){
if (!_PLAYING(voice)) {
return FLUID_OK;
}
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0);
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;
}
fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200);
fluid_voice_update_param(voice, GEN_VOLENVRELEASE);
fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200);
fluid_voice_update_param(voice, GEN_MODENVRELEASE);
return FLUID_OK;
}
int
fluid_voice_off(fluid_voice_t* voice)
{
voice->chan = NO_CHANNEL;
voice->volenv_section = FLUID_VOICE_ENVFINISHED;
voice->volenv_count = 0;
voice->modenv_section = FLUID_VOICE_ENVFINISHED;
voice->modenv_count = 0;
voice->status = FLUID_VOICE_OFF;
if (voice->sample) {
fluid_sample_decr_ref(voice->sample);
voice->sample = NULL;
}
return FLUID_OK;
}
void
fluid_voice_add_mod(fluid_voice_t* voice, fluid_mod_t* mod, int mode)
{
int i;
if (((mod->flags1 & FLUID_MOD_CC) == 0)
&& ((mod->src1 != 0)
&& (mod->src1 != 2)
&& (mod->src1 != 3)
&& (mod->src1 != 10)
&& (mod->src1 != 13)
&& (mod->src1 != 14)
&& (mod->src1 != 16))) {
FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
return;
}
if (mode == FLUID_VOICE_ADD) {
for (i = 0; i < voice->mod_count; i++) {
if (fluid_mod_test_identity(&voice->mod[i], mod)) {
voice->mod[i].amount += mod->amount;
return;
}
}
} else if (mode == FLUID_VOICE_OVERWRITE) {
for (i = 0; i < voice->mod_count; i++) {
if (fluid_mod_test_identity(&voice->mod[i], mod)) {
voice->mod[i].amount = mod->amount;
return;
}
}
}
if (voice->mod_count < FLUID_NUM_MOD) {
fluid_mod_clone(&voice->mod[voice->mod_count++], mod);
}
}
unsigned int fluid_voice_get_id(fluid_voice_t* voice)
{
return voice->id;
}
int fluid_voice_is_playing(fluid_voice_t* voice)
{
return _PLAYING(voice);
}
fluid_real_t 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];
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)) {
v *= -1.0;
} else {
v = 0;
}
if (current_val > v){
possible_att_reduction_cB += (current_val - v);
}
}
}
lower_bound = voice->attenuation-possible_att_reduction_cB;
if (lower_bound < 0) {
lower_bound = 0;
}
return lower_bound;
}
void fluid_voice_check_sample_sanity(fluid_voice_t* voice)
{
int min_index_nonloop=(int) voice->sample->start;
int max_index_nonloop=(int) voice->sample->end;
int min_index_loop=(int) voice->sample->start + FLUID_MIN_LOOP_PAD;
int max_index_loop=(int) voice->sample->end - FLUID_MIN_LOOP_PAD + 1;
if (!voice->check_sample_sanity_flag){
return;
}
#if 0#endif
if (voice->start < min_index_nonloop){
voice->start = min_index_nonloop;
} else if (voice->start > max_index_nonloop){
voice->start = max_index_nonloop;
}
if (voice->end < min_index_nonloop){
voice->end = min_index_nonloop;
} else if (voice->end > max_index_nonloop){
voice->end = max_index_nonloop;
}
if (voice->start > voice->end){
int temp = voice->start;
voice->start = voice->end;
voice->end = temp;
}
if (voice->start == voice->end){
fluid_voice_off(voice);
return;
}
if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
if (voice->loopstart < min_index_loop){
voice->loopstart = min_index_loop;
} else if (voice->loopstart > max_index_loop){
voice->loopstart = max_index_loop;
}
if (voice->loopend < min_index_loop){
voice->loopend = min_index_loop;
} else if (voice->loopend > max_index_loop){
voice->loopend = max_index_loop;
}
if (voice->loopstart > voice->loopend){
int temp = voice->loopstart;
voice->loopstart = voice->loopend;
voice->loopend = temp;
}
if (voice->loopend < voice->loopstart + FLUID_MIN_LOOP_SIZE){
voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED;
}
if ((int)voice->loopstart >= (int)voice->sample->loopstart
&& (int)voice->loopend <= (int)voice->sample->loopend){
if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){
voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain;
} else {
voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop;
};
};
}
if (voice->check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
if ((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE)
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)){
voice->gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED;
}
}
fluid_phase_set_int(voice->phase, voice->start);
}
if (((_SAMPLEMODE(voice) == FLUID_LOOP_UNTIL_RELEASE) && (voice->volenv_section < FLUID_VOICE_ENVRELEASE))
|| (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) {
int index_in_sample = fluid_phase_index(voice->phase);
if (index_in_sample >= voice->loopend){
fluid_phase_set_int(voice->phase, voice->loopstart);
}
}
voice->check_sample_sanity_flag=0;
#if 0#endif
}
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;
}
int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
{
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;
return FLUID_OK;
}
int 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;
if (!s->valid || (s->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
return (FLUID_OK);
if (!s->amplitude_that_reaches_noise_floor_is_valid){
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;
}
}
if (peak_max > -peak_min){
peak = peak_max;
} else {
peak = -peak_min;
};
if (peak == 0){
peak = 1;
};
normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.;
result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
s->amplitude_that_reaches_noise_floor = (double)result;
s->amplitude_that_reaches_noise_floor_is_valid = 1;
#if 0#endif
};
return FLUID_OK;
}