Make chorus capable of sample rate change. (#639)

This commit is contained in:
jjceresa 2020-04-27 18:07:22 +02:00 committed by GitHub
parent a89399476e
commit ff14432cd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 124 additions and 63 deletions

View file

@ -261,7 +261,7 @@ static void set_sinus_frequency(sinus_modulator *mod,
y(n) = a1 . y(n-1) - y(n-2)
out = a1 . buffer1 - buffer2
@param pointer on modulator structure.
@param mod pointer on modulator structure.
@return current value of the modulator sine wave.
-----------------------------------------------------------------------------*/
static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod)
@ -293,6 +293,11 @@ static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod)
in the period relative to the beginning of the period.
For example: 0 is the beginning of the period, 1/4 is at 1/4 of the period
relative to the beginning.
@param mod pointer on modulator structure.
@param freq frequency of the oscillator in Hz.
@param sample_rate sample rate on audio output in Hz.
@param frac_phase initial phase (see comment above).
-----------------------------------------------------------------------------*/
static void set_triangle_frequency(triang_modulator *mod, float freq,
float sample_rate, float frac_phase)
@ -333,6 +338,9 @@ static void set_triangle_frequency(triang_modulator *mod, float freq,
/*-----------------------------------------------------------------------------
Get current value of triangular oscillator
y(n) = y(n-1) + dy
@param mod pointer on triang_modulator structure.
@return current value.
-----------------------------------------------------------------------------*/
static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod)
{
@ -354,11 +362,13 @@ static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod)
}
/*-----------------------------------------------------------------------------
Reads the sample value out of the modulated delay line.
@param mdl, pointer on modulated delay line.
@return the sample value.
@param chorus pointer on chorus unit.
@param mod pointer on modulator structure.
@return current value.
-----------------------------------------------------------------------------*/
static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus,
modulator *mod)
modulator *mod)
{
fluid_real_t out_index; /* new modulated index position */
int int_out_index; /* integer part of out_index */
@ -431,6 +441,9 @@ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus,
/*-----------------------------------------------------------------------------
Push a sample val into the delay line
@param dl delay line to push value into.
@param val the value to push into dl.
-----------------------------------------------------------------------------*/
#define push_in_delay_line(dl, val) \
{\
@ -444,13 +457,15 @@ static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus,
center_pos_mod is initialized so that the delay between center_pos_mod and
line_in is: mod_depth + INTERP_SAMPLES_NBR.
@param chorus pointer on chorus unit.
-----------------------------------------------------------------------------*/
static void set_center_position(fluid_chorus_t *chorus)
{
int center;
/* Sets the modulation rate. This rate defines how often
the center position (center_pos_mod ) is modulated .
the center position (center_pos_mod ) is modulated .
The value is expressed in samples. The default value is 1 that means that
center_pos_mod is updated at every sample.
For example with a value of 2, the center position position will be
@ -484,6 +499,63 @@ static void set_center_position(fluid_chorus_t *chorus)
chorus->index_rate = chorus->mod_rate;
}
/*-----------------------------------------------------------------------------
Update internal parameters dependent of sample rate.
- mod_depth.
- mod_rate, center_pos_mod, and index rate.
- modulators frequency.
@param chorus, pointer on chorus unit.
-----------------------------------------------------------------------------*/
static void update_parameters_from_sample_rate(fluid_chorus_t *chorus)
{
int i;
/* initialize modulation depth (peak to peak) (in samples) */
/* convert modulation depth in ms to sample number */
chorus->mod_depth = (int)(chorus->depth_ms / 1000.0
* chorus->sample_rate);
/* the delay line is fixed. So we reduce mod_depth (if necessary) */
if(chorus->mod_depth > MAX_SAMPLES)
{
FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).",
MAX_SAMPLES);
chorus->mod_depth = MAX_SAMPLES;
/* set depth_ms to maximum to avoid spamming console with above warning */
chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate;
}
chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */
#ifdef DEBUG_PRINT
printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth);
#endif
/* Initializes the modulated center position:
mod_rate, center_pos_mod, and index rate.
*/
set_center_position(chorus); /* must be called before set_xxxx_frequency() */
#ifdef DEBUG_PRINT
printf("mod_rate:%d\n", chorus->mod_rate);
#endif
/* initialize modulator frequency */
for(i = 0; i < chorus->number_blocks; i++)
{
set_sinus_frequency(&chorus->mod[i].sinus,
chorus->speed_Hz * chorus->mod_rate,
chorus->sample_rate,
/* phase offset between modulators waveform */
(float)((360.0f / (float) chorus->number_blocks) * i));
set_triangle_frequency(&chorus->mod[i].triang,
chorus->speed_Hz * chorus->mod_rate,
chorus->sample_rate,
/* phase offset between modulators waveform */
(float)i / chorus->number_blocks);
}
}
/*-----------------------------------------------------------------------------
Modulated delay line initialization.
@ -491,7 +563,7 @@ static void set_center_position(fluid_chorus_t *chorus)
Remark: the function sets the internal size accordling to the length delay_length.
The size is augmented by INTERP_SAMPLES_NBR to take account of interpolation.
@param chorus, pointer chorus unit.
@param chorus, pointer on chorus unit.
@param delay_length the length of the delay line in samples.
@return FLUID_OK if success , FLUID_FAILED if memory error.
@ -545,8 +617,11 @@ static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length)
API
------------------------------------------------------------------------------*/
/**
* Create the chorus unit.
* @sample_rate audio sample rate in Hz.
* Create the chorus unit. Once created the chorus have no parameters set, so
* fluid_chorus_set() must be called at least one time after calling
* new_fluid_chorus().
*
* @param sample_rate, audio sample rate in Hz.
* @return pointer on chorus unit.
*/
fluid_chorus_t *
@ -577,15 +652,11 @@ new_fluid_chorus(fluid_real_t sample_rate)
if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED)
{
goto error_recovery;
delete_fluid_chorus(chorus);
return NULL;
}
return chorus;
error_recovery:
delete_fluid_chorus(chorus);
return NULL;
}
/**
@ -628,15 +699,16 @@ fluid_chorus_reset(fluid_chorus_t *chorus)
/**
* Set one or more chorus parameters.
* @param chorus Chorus instance
* @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t)
*
* @param chorus Chorus instance.
* @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t).
* @param nr Chorus voice count (0-99, CPU time consumption proportional to
* this value)
* @param level Chorus level (0.0-10.0)
* @param speed Chorus speed in Hz (0.1-5.0)
* this value).
* @param level Chorus level (0.0-10.0).
* @param speed Chorus speed in Hz (0.1-5.0).
* @param depth_ms Chorus depth (max value depends on synth sample rate,
* 0.0-21.0 is safe for sample rate values up to 96KHz)
* @param type Chorus waveform type (#fluid_chorus_mod)
* 0.0-21.0 is safe for sample rate values up to 96KHz).
* @param type Chorus waveform type (#fluid_chorus_mod).
*/
void
fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
@ -713,45 +785,8 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
chorus->level = 0.1;
}
/* initialize modulation depth (peak to peak) (in samples)*/
chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 /* convert modulation depth in ms to s*/
* chorus->sample_rate);
if(chorus->mod_depth > MAX_SAMPLES)
{
FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
chorus->mod_depth = MAX_SAMPLES;
// set depth to maximum to avoid spamming console with above warning
chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate;
}
chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */
#ifdef DEBUG_PRINT
printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth);
#endif
/* Initializes the modulated center position:
mod_rate, center_pos_mod, and index rate.
*/
set_center_position(chorus); /* must be called before set_xxxx_frequency() */
#ifdef DEBUG_PRINT
printf("mod_rate:%d\n", chorus->mod_rate);
#endif
/* initialize modulator frequency */
for(i = 0; i < chorus->number_blocks; i++)
{
set_sinus_frequency(&chorus->mod[i].sinus,
chorus->speed_Hz * chorus->mod_rate,
chorus->sample_rate,
/* phase offset between modulators waveform */
(float)((360.0f / (float) chorus->number_blocks) * i));
set_triangle_frequency(&chorus->mod[i].triang,
chorus->speed_Hz * chorus->mod_rate,
chorus->sample_rate,
/* phase offset between modulators waveform */
(float)i / chorus->number_blocks);
}
/* update parameters dependant of sample rate */
update_parameters_from_sample_rate(chorus);
#ifdef DEBUG_PRINT
printf("lfo type:%d\n", chorus->type);
@ -859,6 +894,32 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
}
}
/*
* Applies a sample rate change on the chorus.
* Note that while the chorus is used by calling any fluid_chorus_processXXX()
* function, calling fluid_chorus_samplerate_change() isn't multi task safe.
* To deal properly with this issue follow the steps:
* 1) Stop chorus processing (i.e disable calling to any fluid_chorus_processXXX().
* chorus functions.
* 2) Change sample rate by calling fluid_chorus_samplerate_change().
* 3) Restart chorus processing (i.e enabling calling any fluid_chorus_processXXX()
* chorus functions.
*
* Another solution is to substitute step (2):
* 2.1) delete the chorus by calling delete_fluid_chorus().
* 2.2) create the chorus by calling new_fluid_chorus().
*
* @param chorus pointer on the chorus.
* @param sample_rate new sample rate value.
*/
void
fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate)
{
chorus->sample_rate = sample_rate;
/* update parameters dependant of sample rate */
update_parameters_from_sample_rate(chorus);
}
/**
* Process chorus by mixing the result in output buffer.

View file

@ -53,6 +53,8 @@ void fluid_chorus_reset(fluid_chorus_t *chorus);
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
fluid_real_t speed, fluid_real_t depth_ms, int type);
void
fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate);
void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in,
fluid_real_t *left_out, fluid_real_t *right_out);

View file

@ -679,11 +679,9 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
{
if(mixer->fx[i].chorus)
{
delete_fluid_chorus(mixer->fx[i].chorus);
fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate);
}
mixer->fx[i].chorus = new_fluid_chorus(samplerate);
if(mixer->fx[i].reverb)
{
fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);