#include "fluid_chorus.h"
#include "fluid_sys.h"
#define MAX_CHORUS 99
#define MAX_DELAY 100
#define MAX_DEPTH 10
#define MIN_SPEED_HZ 0.29
#define MAX_SPEED_HZ 5
#define MAX_SAMPLES_LN2 12
#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1))
#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1)
#define INTERPOLATION_SUBSAMPLES_LN2 8
#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1))
#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1)
#define INTERPOLATION_SAMPLES 5
struct _fluid_chorus_t {
int type;
int new_type;
fluid_real_t depth_ms;
fluid_real_t new_depth_ms;
fluid_real_t level;
fluid_real_t new_level;
fluid_real_t speed_Hz;
fluid_real_t new_speed_Hz;
int number_blocks;
int new_number_blocks;
fluid_real_t *chorusbuf;
int counter;
long phase[MAX_CHORUS];
long modulation_period_samples;
int *lookup_tab;
fluid_real_t sample_rate;
fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES];
};
void fluid_chorus_triangle(int *buf, int len, int depth);
void fluid_chorus_sine(int *buf, int len, int depth);
fluid_chorus_t*
new_fluid_chorus(fluid_real_t sample_rate)
{
int i; int ii;
fluid_chorus_t* chorus;
chorus = FLUID_NEW(fluid_chorus_t);
if (chorus == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
return NULL;
}
FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
chorus->sample_rate = sample_rate;
for (i = 0; i < INTERPOLATION_SAMPLES; i++){
for (ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++){
double i_shifted = ((double) i- ((double) INTERPOLATION_SAMPLES) / 2.
+ (double) ii / (double) INTERPOLATION_SUBSAMPLES);
if (fabs(i_shifted) < 0.000001) {
chorus->sinc_table[i][ii] = (fluid_real_t)1.;
} else {
chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted);
chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES));
};
};
};
chorus->lookup_tab = FLUID_ARRAY(int, (int) (chorus->sample_rate / MIN_SPEED_HZ));
if (chorus->lookup_tab == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
if (chorus->chorusbuf == NULL) {
fluid_log(FLUID_PANIC, "chorus: Out of memory");
goto error_recovery;
}
if (fluid_chorus_init(chorus) != FLUID_OK){
goto error_recovery;
};
return chorus;
error_recovery:
delete_fluid_chorus(chorus);
return NULL;
}
int
fluid_chorus_init(fluid_chorus_t* chorus)
{
int i;
for (i = 0; i < MAX_SAMPLES; i++) {
chorus->chorusbuf[i] = 0.0;
}
fluid_chorus_set_nr(chorus, FLUID_CHORUS_DEFAULT_N);
fluid_chorus_set_level(chorus, FLUID_CHORUS_DEFAULT_LEVEL);
fluid_chorus_set_speed_Hz(chorus, FLUID_CHORUS_DEFAULT_SPEED);
fluid_chorus_set_depth_ms(chorus, FLUID_CHORUS_DEFAULT_DEPTH);
fluid_chorus_set_type(chorus, FLUID_CHORUS_MOD_SINE);
return fluid_chorus_update(chorus);
}
void fluid_chorus_set_nr(fluid_chorus_t* chorus, int nr)
{
chorus->new_number_blocks = nr;
}
int fluid_chorus_get_nr(fluid_chorus_t* chorus)
{
return chorus->number_blocks;
};
void fluid_chorus_set_level(fluid_chorus_t* chorus, fluid_real_t level)
{
chorus->new_level = level;
}
fluid_real_t fluid_chorus_get_level(fluid_chorus_t* chorus)
{
return chorus->level;
};
void fluid_chorus_set_speed_Hz(fluid_chorus_t* chorus, fluid_real_t speed_Hz)
{
chorus->new_speed_Hz = speed_Hz;
}
fluid_real_t fluid_chorus_get_speed_Hz(fluid_chorus_t* chorus)
{
return chorus->speed_Hz;
};
void fluid_chorus_set_depth_ms(fluid_chorus_t* chorus, fluid_real_t depth_ms)
{
chorus->new_depth_ms=depth_ms;
}
fluid_real_t fluid_chorus_get_depth_ms(fluid_chorus_t* chorus)
{
return chorus->depth_ms;
};
void fluid_chorus_set_type(fluid_chorus_t* chorus, int type)
{
chorus->new_type=type;
}
int fluid_chorus_get_type(fluid_chorus_t* chorus)
{
return chorus->type;
};
void
delete_fluid_chorus(fluid_chorus_t* chorus)
{
if (chorus == NULL) {
return;
}
if (chorus->chorusbuf != NULL) {
FLUID_FREE(chorus->chorusbuf);
}
if (chorus->lookup_tab != NULL) {
FLUID_FREE(chorus->lookup_tab);
}
FLUID_FREE(chorus);
}
int
fluid_chorus_update(fluid_chorus_t* chorus)
{
int i;
int modulation_depth_samples;
if (chorus->new_number_blocks < 0) {
fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
chorus->new_number_blocks = 0;
} else if (chorus->new_number_blocks > MAX_CHORUS) {
fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.",
MAX_CHORUS);
chorus->new_number_blocks = MAX_CHORUS;
};
if (chorus->new_speed_Hz < MIN_SPEED_HZ) {
fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.",
(double) MIN_SPEED_HZ);
chorus->new_speed_Hz = MIN_SPEED_HZ;
} else if (chorus->new_speed_Hz > MAX_SPEED_HZ) {
fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.",
(double) MAX_SPEED_HZ);
chorus->new_speed_Hz = MAX_SPEED_HZ;
}
if (chorus->new_depth_ms < 0.0) {
fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0.");
chorus->new_depth_ms = 0.0;
}
if (chorus->new_level < 0.0) {
fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
chorus->new_level = 0.0;
} else if (chorus->new_level > 10) {
fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
"Setting it to 0.1.");
chorus->new_level = 0.1;
}
chorus->modulation_period_samples = chorus->sample_rate / chorus->new_speed_Hz;
modulation_depth_samples = (int)
(chorus->new_depth_ms / 1000.0
* chorus->sample_rate);
if (modulation_depth_samples > MAX_SAMPLES) {
fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
modulation_depth_samples = MAX_SAMPLES;
}
if (chorus->type == FLUID_CHORUS_MOD_SINE) {
fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
} else if (chorus->type == FLUID_CHORUS_MOD_TRIANGLE) {
fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
} else {
fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
chorus->type = FLUID_CHORUS_MOD_SINE;
fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
modulation_depth_samples);
};
for (i = 0; i < chorus->number_blocks; i++) {
chorus->phase[i] = (int) ((double) chorus->modulation_period_samples
* (double) i / (double) chorus->number_blocks);
}
chorus->counter = 0;
chorus->type = chorus->new_type;
chorus->depth_ms = chorus->new_depth_ms;
chorus->level = chorus->new_level;
chorus->speed_Hz = chorus->new_speed_Hz;
chorus->number_blocks = chorus->new_number_blocks;
return FLUID_OK;
}
void fluid_chorus_processmix(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;
int i;
fluid_real_t d_in, d_out;
for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
d_in = in[sample_index];
d_out = 0.0f;
# if 0
left_out[sample_index]=0;
right_out[sample_index]=0;
#endif
chorus->chorusbuf[chorus->counter] = d_in;
for (i = 0; i < chorus->number_blocks; i++) {
int ii;
int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- chorus->lookup_tab[chorus->phase[i]]);
int pos_samples = pos_subsamples/INTERPOLATION_SUBSAMPLES;
pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
* chorus->sinc_table[ii][pos_subsamples];
pos_samples--;
};
chorus->phase[i]++;
chorus->phase[i] %= (chorus->modulation_period_samples);
}
d_out *= chorus->level;
left_out[sample_index] += d_out;
right_out[sample_index] += d_out;
chorus->counter++;
chorus->counter %= MAX_SAMPLES;
}
}
void fluid_chorus_processreplace(fluid_chorus_t* chorus, fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out)
{
int sample_index;
int i;
fluid_real_t d_in, d_out;
for (sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) {
d_in = in[sample_index];
d_out = 0.0f;
# if 0
left_out[sample_index]=0;
right_out[sample_index]=0;
#endif
chorus->chorusbuf[chorus->counter] = d_in;
for (i = 0; i < chorus->number_blocks; i++) {
int ii;
int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- chorus->lookup_tab[chorus->phase[i]]);
int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
for (ii = 0; ii < INTERPOLATION_SAMPLES; ii++){
d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
* chorus->sinc_table[ii][pos_subsamples];
pos_samples--;
};
chorus->phase[i]++;
chorus->phase[i] %= (chorus->modulation_period_samples);
}
d_out *= chorus->level;
left_out[sample_index] = d_out;
right_out[sample_index] = d_out;
chorus->counter++;
chorus->counter %= MAX_SAMPLES;
}
}
void fluid_chorus_sine(int *buf, int len, int depth)
{
int i;
double val;
for (i = 0; i < len; i++) {
val = sin((double) i / (double)len * 2.0 * M_PI);
buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES);
buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
}
}
void fluid_chorus_triangle(int *buf, int len, int depth)
{
int i=0;
int ii=len-1;
double val;
double val2;
while (i <= ii){
val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
buf[i++] = (int) val2;
buf[ii--] = (int) val2;
}
}
void
fluid_chorus_reset(fluid_chorus_t* chorus)
{
fluid_chorus_init(chorus);
}