mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-04-19 06:02:03 +00:00
Allow the reverb to pre allocate delay lines. (#638)
This PR allows the reverb to pre-allocate the memory needed for the maximum sample-rate of the synth. That means that on sample rate change there are no memory allocation and the function fluid_revmodel_samplerate_change() should always return FLUID_OK. The PR addresses discussion in #608.
This commit is contained in:
parent
791dac6736
commit
6aea18bef4
7 changed files with 279 additions and 213 deletions
|
@ -120,17 +120,17 @@
|
|||
* freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing
|
||||
* | 2 x 4 all-pass | | |
|
||||
* ----------|---------------------------------------------------------------
|
||||
* FDN | 8 | 0.650 % | 112160 | far less
|
||||
* modulated | |(feeverb - 3%) | (55% freeverb) | ringing
|
||||
* FDN | 8 | 0.650 % | 112480 | far less
|
||||
* modulated | |(feeverb - 3%) | (56% freeverb) | ringing
|
||||
* |---------------------------------------------------------------
|
||||
* | 12 | 0.942 % | 168240 | best than
|
||||
* | 12 | 0.942 % | 168720 | best than
|
||||
* | |(freeverb + 41%) | (82 %freeverb) | 8 lines
|
||||
*---------------------------------------------------------------------------
|
||||
*
|
||||
* Note:
|
||||
* Values in this column is the memory consumption for sample rate <= 44100Hz.
|
||||
* For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz).
|
||||
*
|
||||
* For example: for sample rate 96000Hz, the memory consumed is 244760 bytes
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* 'Denormalise' method to avoid loss of performance.
|
||||
|
@ -182,7 +182,7 @@
|
|||
|
||||
/* Number of delay lines (must be only 8 or 12)
|
||||
8 is the default.
|
||||
12 produces a better quality but is +50% cpu expensive
|
||||
12 produces a better quality but is +50% cpu expensive.
|
||||
*/
|
||||
#define NBR_DELAYS 8 /* default*/
|
||||
|
||||
|
@ -274,7 +274,7 @@ a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. *
|
|||
/*
|
||||
Number of samples to add to the desired length of a delay line. This
|
||||
allow to take account of modulation interpolation.
|
||||
1 is sufficient with MOD_DEPTH equal to 6.
|
||||
1 is sufficient with MOD_DEPTH equal to 4.
|
||||
*/
|
||||
#define INTERP_SAMPLES_NBR 1
|
||||
|
||||
|
@ -368,7 +368,7 @@ static void set_fdn_delay_lpf(fdn_delay_lpf *lpf,
|
|||
typedef struct
|
||||
{
|
||||
fluid_real_t *line; /* buffer line */
|
||||
int size; /* effective internal size (in samples) */
|
||||
int size; /* effective internal size (in samples) */
|
||||
/*-------------*/
|
||||
int line_in; /* line in position */
|
||||
int line_out; /* line out position */
|
||||
|
@ -500,115 +500,6 @@ typedef struct
|
|||
fluid_real_t buffer;
|
||||
} mod_delay_line;
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Modulated delay line initialization.
|
||||
|
||||
Sets the length line ( alloc delay samples).
|
||||
Remark: the function sets the internal size accordling to the length delay_length.
|
||||
As the delay line is a modulated line, its internal size is augmented by mod_depth.
|
||||
The size is also augmented by INTERP_SAMPLES_NBR to take account of interpolation.
|
||||
|
||||
@param mdl, pointer on modulated delay line.
|
||||
@param delay_length the length of the delay line in samples.
|
||||
@param mod_depth depth of the modulation in samples (amplitude of the sine wave).
|
||||
@param mod_rate the rate of the modulation in samples.
|
||||
@return FLUID_OK if success , FLUID_FAILED if memory error.
|
||||
|
||||
Return FLUID_OK if success, FLUID_FAILED if memory error.
|
||||
-----------------------------------------------------------------------------*/
|
||||
static int set_mod_delay_line(mod_delay_line *mdl,
|
||||
int delay_length,
|
||||
int mod_depth,
|
||||
int mod_rate
|
||||
)
|
||||
{
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* checks parameter */
|
||||
if(delay_length < 1)
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
/* limits mod_depth to the requested delay length */
|
||||
if(mod_depth >= delay_length)
|
||||
{
|
||||
FLUID_LOG(FLUID_INFO,
|
||||
"fdn reverb: modulation depth has been limited");
|
||||
mod_depth = delay_length - 1;
|
||||
}
|
||||
|
||||
mdl->mod_depth = mod_depth;
|
||||
/*-----------------------------------------------------------------------
|
||||
allocates delay_line and initialize members:
|
||||
- line, size, line_in, line_out...
|
||||
*/
|
||||
{
|
||||
/* total size of the line:
|
||||
size = INTERP_SAMPLES_NBR + mod_depth + delay_length */
|
||||
mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR;
|
||||
mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size);
|
||||
|
||||
if(! mdl->dl.line)
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
clear_delay_line(&mdl->dl); /* clears the buffer */
|
||||
|
||||
/* Initializes line_in to the start of the buffer */
|
||||
mdl->dl.line_in = 0;
|
||||
/* Initializes line_out index INTERP_SAMPLES_NBR samples after line_in */
|
||||
/* so that the delay between line_out and line_in is:
|
||||
mod_depth + delay_length */
|
||||
mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR;
|
||||
}
|
||||
|
||||
/* Damping low pass filter -------------------*/
|
||||
mdl->dl.damping.buffer = 0;
|
||||
/*------------------------------------------------------------------------
|
||||
Initializes modulation members:
|
||||
- modulated center position: center_pos_mod
|
||||
- index rate to know when to update center_pos_mod:index_rate
|
||||
- modulation rate (the speed at which center_pos_mod is modulated: mod_rate
|
||||
- interpolator member: buffer, frac_pos_mod
|
||||
-------------------------------------------------------------------------*/
|
||||
/* Sets the modulation rate. This rate defines how often
|
||||
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
|
||||
updated only one time every 2 samples only.
|
||||
*/
|
||||
mdl->mod_rate = 1; /* default modulation rate: every one sample */
|
||||
|
||||
if(mod_rate > mdl->dl.size)
|
||||
{
|
||||
FLUID_LOG(FLUID_INFO,
|
||||
"fdn reverb: modulation rate is out of range");
|
||||
}
|
||||
else
|
||||
{
|
||||
mdl->mod_rate = mod_rate;
|
||||
}
|
||||
|
||||
/* Initializes the modulated center position (center_pos_mod) so that:
|
||||
- the delay between line_out and center_pos_mod is mod_depth.
|
||||
- the delay between center_pos_mod and line_in is delay_length.
|
||||
*/
|
||||
mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth;
|
||||
|
||||
/* index rate to control when to update center_pos_mod */
|
||||
/* Important: must be set to get center_pos_mod immediately used for the
|
||||
reading of first sample (see get_mod_delay()) */
|
||||
mdl->index_rate = mdl->mod_rate;
|
||||
|
||||
/* initializes 1st order All-Pass interpolator members */
|
||||
mdl->buffer = 0; /* previous delay sample value */
|
||||
mdl->frac_pos_mod = 0; /* fractional position (between consecutives sample) */
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Return norminal delay length
|
||||
|
||||
|
@ -702,6 +593,7 @@ static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl)
|
|||
struct _fluid_late
|
||||
{
|
||||
fluid_real_t samplerate; /* sample rate */
|
||||
fluid_real_t sample_rate_max; /* sample rate maximum */
|
||||
/*----- High pass tone corrector -------------------------------------*/
|
||||
fluid_real_t tone_buffer;
|
||||
fluid_real_t b1, b2;
|
||||
|
@ -904,118 +796,220 @@ static void delete_fluid_rev_late(fluid_late *late)
|
|||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Creates all modulated lines.
|
||||
@param late, pointer on the fnd late reverb to initialize.
|
||||
@param sample_rate, the audio sample rate.
|
||||
@return FLUID_OK if success, FLUID_FAILED otherwise.
|
||||
-----------------------------------------------------------------------------*/
|
||||
static int create_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate)
|
||||
|
||||
/* Nominal delay lines length table (in samples) */
|
||||
static const int nom_delay_length[NBR_DELAYS] =
|
||||
{
|
||||
/* Delay lines length table (in samples) */
|
||||
static const int delay_length[NBR_DELAYS] =
|
||||
{
|
||||
DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3,
|
||||
DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7,
|
||||
#if (NBR_DELAYS == 12)
|
||||
DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11
|
||||
#endif
|
||||
};
|
||||
DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3,
|
||||
DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7,
|
||||
#if (NBR_DELAYS == 12)
|
||||
DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11
|
||||
#endif
|
||||
};
|
||||
|
||||
int result; /* return value */
|
||||
int i;
|
||||
/*
|
||||
1)"modal density" is one property that contributes to the quality of the reverb tail.
|
||||
The more is the modal density, the less are unwanted resonant frequencies
|
||||
build during the decay time: modal density = total delay / sample rate.
|
||||
|
||||
/*
|
||||
1)"modal density" is one property that contributes to the quality of the reverb tail.
|
||||
The more is the modal density, the less are unwanted resonant frequencies
|
||||
build during the decay time: modal density = total delay / sample rate.
|
||||
Delay line's length given by static table delay_length[] are nominal
|
||||
to get minimum modal density of 0.15 at sample rate 44100Hz.
|
||||
Here we set length_factor to 2 to multiply this nominal modal
|
||||
density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for
|
||||
sample rate <= 44100.
|
||||
|
||||
Delay line's length given by static table delay_length[] is nominal
|
||||
to get minimum modal density of 0.15 at sample rate 44100Hz.
|
||||
Here we set length_factor to 2 to multiply this nominal modal
|
||||
density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for
|
||||
sample rate <= 44100.
|
||||
For sample rate > 44100, length_factor is multiplied by
|
||||
sample_rate / 44100. This ensures that the default modal density keeps inchanged.
|
||||
(Without this compensation, the default modal density would be diminished for
|
||||
new sample rate change above 44100Hz).
|
||||
|
||||
For sample rate > 44100, length_factor is multiplied by
|
||||
sample_rate / 44100. This ensures that the default modal density keeps inchanged.
|
||||
(Without this compensation, the default modal density would be diminished for
|
||||
new sample rate change above 44100Hz).
|
||||
|
||||
2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing").
|
||||
Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz.
|
||||
For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures
|
||||
that the effect of modulated delay line keeps inchanged.
|
||||
*/
|
||||
fluid_real_t length_factor = 2.0f;
|
||||
fluid_real_t mod_depth = MOD_DEPTH;
|
||||
2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing").
|
||||
Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz.
|
||||
For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures
|
||||
that the effect of modulated delay line remains inchanged.
|
||||
*/
|
||||
static void compensate_from_sample_rate(fluid_real_t sample_rate,
|
||||
fluid_real_t *mod_depth,
|
||||
fluid_real_t *length_factor)
|
||||
{
|
||||
*mod_depth = MOD_DEPTH;
|
||||
*length_factor = 2.0f;
|
||||
if(sample_rate > 44100.0f)
|
||||
{
|
||||
fluid_real_t sample_rate_factor = sample_rate/44100.0f;
|
||||
length_factor *= sample_rate_factor;
|
||||
mod_depth *= sample_rate_factor;
|
||||
*length_factor *= sample_rate_factor;
|
||||
*mod_depth *= sample_rate_factor;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Creates all modulated lines.
|
||||
@param late, pointer on the fnd late reverb to initialize.
|
||||
@param sample_rate_max, the maximum audio sample rate expected.
|
||||
@return FLUID_OK if success, FLUID_FAILED otherwise.
|
||||
-----------------------------------------------------------------------------*/
|
||||
static int create_mod_delay_lines(fluid_late *late,
|
||||
fluid_real_t sample_rate_max)
|
||||
{
|
||||
int i;
|
||||
|
||||
fluid_real_t mod_depth, length_factor;
|
||||
|
||||
/* compute mod_depth, length factor */
|
||||
compensate_from_sample_rate(sample_rate_max, &mod_depth, &length_factor);
|
||||
|
||||
late->sample_rate_max = sample_rate_max;
|
||||
|
||||
#ifdef INFOS_PRINT // allows message to be printed on the console.
|
||||
printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth);
|
||||
/* Print: modal density and total memory bytes */
|
||||
{
|
||||
int i;
|
||||
int total_delay; /* total delay in samples */
|
||||
for (i = 0, total_delay = 0; i < NBR_DELAYS; i++)
|
||||
int total_delay = 0; /* total delay in samples */
|
||||
for (i = 0; i < NBR_DELAYS; i++)
|
||||
{
|
||||
total_delay += length_factor * delay_length[i];
|
||||
int length = (length_factor * nom_delay_length[i])
|
||||
+ mod_depth + INTERP_SAMPLES_NBR;
|
||||
total_delay += length;
|
||||
}
|
||||
|
||||
/* modal density and total memory bytes */
|
||||
printf("modal density:%f, total memory:%d bytes\n",
|
||||
total_delay / sample_rate , total_delay * sizeof(fluid_real_t));
|
||||
printf("modal density:%f, total delay:%d, total memory:%d bytes\n",
|
||||
total_delay / sample_rate_max ,total_delay ,
|
||||
total_delay * sizeof(fluid_real_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */
|
||||
{
|
||||
/* allocate delay line and set local delay lines's parameters */
|
||||
result = set_mod_delay_line(&late->mod_delay_lines[i],
|
||||
delay_length[i] * length_factor,
|
||||
mod_depth, MOD_RATE);
|
||||
int delay_length = nom_delay_length[i] * length_factor;
|
||||
mod_delay_line *mdl = &late->mod_delay_lines[i];
|
||||
|
||||
if(result == FLUID_FAILED)
|
||||
/*-------------------------------------------------------------------*/
|
||||
/* checks parameter */
|
||||
if(delay_length < 1)
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
/* Sets local Modulators parameters: frequency and phase
|
||||
Each modulateur are shifted of MOD_PHASE degree
|
||||
/* limits mod_depth to the requested delay length */
|
||||
if(mod_depth >= delay_length)
|
||||
{
|
||||
FLUID_LOG(FLUID_INFO,
|
||||
"fdn reverb: modulation depth has been limited");
|
||||
mod_depth = delay_length - 1;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------
|
||||
allocates delay lines
|
||||
*/
|
||||
set_mod_frequency(&late->mod_delay_lines[i].mod,
|
||||
MOD_FREQ * MOD_RATE,
|
||||
late->samplerate,
|
||||
(float)(MOD_PHASE * i));
|
||||
|
||||
/* real size of the line in use (in samples):
|
||||
size = INTERP_SAMPLES_NBR + mod_depth + delay_length */
|
||||
mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR;
|
||||
mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size);
|
||||
|
||||
if(! mdl->dl.line)
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
}
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
Creates the fdn reverb.
|
||||
Initialize all modulated lines.
|
||||
@param late, pointer on the fnd late reverb to initialize.
|
||||
@param sample_rate the sample rate.
|
||||
@param sample_rate, the audio sample rate.
|
||||
@return FLUID_OK if success, FLUID_FAILED otherwise.
|
||||
-----------------------------------------------------------------------------*/
|
||||
static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate)
|
||||
static void initialize_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate)
|
||||
{
|
||||
FLUID_MEMSET(late, 0, sizeof(fluid_late));
|
||||
int i;
|
||||
fluid_real_t mod_depth, length_factor;
|
||||
|
||||
/* update delay line parameter dependant of sample rate */
|
||||
late->samplerate = sample_rate;
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
First initialize the modulated delay lines
|
||||
*/
|
||||
/* compute mod_depth, length factor */
|
||||
compensate_from_sample_rate(sample_rate, &mod_depth, &length_factor);
|
||||
|
||||
if(create_mod_delay_lines(late, sample_rate) == FLUID_FAILED)
|
||||
for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
mod_delay_line *mdl = &late->mod_delay_lines[i];
|
||||
int delay_length = nom_delay_length[i] * length_factor;
|
||||
|
||||
return FLUID_OK;
|
||||
/* limits mod_depth to the requested delay length */
|
||||
if(mod_depth >= delay_length)
|
||||
{
|
||||
mod_depth = delay_length - 1;
|
||||
}
|
||||
|
||||
mdl->mod_depth = mod_depth;
|
||||
|
||||
clear_delay_line(&mdl->dl); /* clears the buffer */
|
||||
|
||||
/* Initializes line_in to the start of the buffer */
|
||||
mdl->dl.line_in = 0;
|
||||
|
||||
/* Initializes line_out index INTERP_SAMPLES_NBR samples after
|
||||
line_in so that the delay between line_out and line_in is:
|
||||
mod_depth + delay_length
|
||||
*/
|
||||
mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR;
|
||||
|
||||
/* Damping low pass filter ------------------------------------------*/
|
||||
mdl->dl.damping.buffer = 0;
|
||||
|
||||
/*---------------------------------------------------------------------
|
||||
Initializes modulation members:
|
||||
- modulated center position: center_pos_mod
|
||||
- modulation rate (the speed at which center_pos_mod is modulated: mod_rate
|
||||
- index rate to know when to update center_pos_mod:index_rate
|
||||
- interpolator member: buffer, frac_pos_mod
|
||||
---------------------------------------------------------------------*/
|
||||
/* Initializes the modulated center position (center_pos_mod) so that:
|
||||
- the delay between line_out and center_pos_mod is mod_depth.
|
||||
- the delay between center_pos_mod and line_in is delay_length.
|
||||
*/
|
||||
mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth;
|
||||
|
||||
/* Sets the modulation rate. This rate defines how often
|
||||
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
|
||||
updated only one time every 2 samples only.
|
||||
*/
|
||||
if(MOD_RATE < 1 || MOD_RATE > mdl->dl.size)
|
||||
{
|
||||
FLUID_LOG(FLUID_INFO, "fdn reverb: modulation rate is out of range");
|
||||
mdl->mod_rate = 1; /* default modulation rate: every one sample */
|
||||
}
|
||||
else
|
||||
{
|
||||
mdl->mod_rate = MOD_RATE;
|
||||
}
|
||||
|
||||
/* index rate to control when to update center_pos_mod.
|
||||
Important: must be set to get center_pos_mod immediately used for
|
||||
the reading of first sample (see get_mod_delay())
|
||||
*/
|
||||
mdl->index_rate = mdl->mod_rate;
|
||||
|
||||
/* initializes first order All-Pass interpolator members */
|
||||
mdl->buffer = 0; /* previous delay sample value */
|
||||
mdl->frac_pos_mod = 0; /* frac. position (between consecutives sample) */
|
||||
|
||||
|
||||
/* Sets local Modulators parameters: frequency and phase.
|
||||
Each modulateur are shifted of MOD_PHASE degree
|
||||
*/
|
||||
set_mod_frequency(&mdl->mod,
|
||||
MOD_FREQ * MOD_RATE,
|
||||
sample_rate,
|
||||
(float)(MOD_PHASE * i));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1077,20 +1071,27 @@ fluid_revmodel_update(fluid_revmodel_t *rev)
|
|||
/*----------------------------------------------------------------------------
|
||||
Reverb API
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Creates a reverb. One created the reverb have no parameters set, so
|
||||
* Creates a reverb. Once created the reverb have no parameters set, so
|
||||
* fluid_revmodel_set() must be called at least one time after calling
|
||||
* new_fluid_revmodel().
|
||||
*
|
||||
* @param sample_rate sample rate in Hz.
|
||||
* @param sample_rate_max maximum sample rate expected in Hz.
|
||||
*
|
||||
* @param sample_rate actual sample rate needed in Hz.
|
||||
* @return pointer on the new reverb or NULL if memory error.
|
||||
* Reverb API.
|
||||
*/
|
||||
fluid_revmodel_t *
|
||||
new_fluid_revmodel(fluid_real_t sample_rate)
|
||||
new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate)
|
||||
{
|
||||
fluid_revmodel_t *rev;
|
||||
|
||||
if(sample_rate <= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rev = FLUID_NEW(fluid_revmodel_t);
|
||||
|
||||
if(rev == NULL)
|
||||
|
@ -1098,13 +1099,33 @@ new_fluid_revmodel(fluid_real_t sample_rate)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* create fdn reverb */
|
||||
if(create_fluid_rev_late(&rev->late, sample_rate) != FLUID_OK)
|
||||
FLUID_MEMSET(&rev->late, 0, sizeof(fluid_late));
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Create fdn late reverb.
|
||||
*/
|
||||
|
||||
/* update minimum value for sample_rate_max */
|
||||
if(sample_rate > sample_rate_max)
|
||||
{
|
||||
sample_rate_max = sample_rate;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Allocate the modulated delay lines
|
||||
*/
|
||||
if(create_mod_delay_lines(&rev->late, sample_rate_max) == FLUID_FAILED)
|
||||
{
|
||||
delete_fluid_revmodel(rev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Initialize the fdn reverb
|
||||
*/
|
||||
/* Initialize all modulated lines. */
|
||||
initialize_mod_delay_lines(&rev->late, sample_rate);
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
|
@ -1131,7 +1152,8 @@ delete_fluid_revmodel(fluid_revmodel_t *rev)
|
|||
|
||||
/*
|
||||
* Sets one or more reverb parameters. Note this must be called at least one
|
||||
* time after calling new_fluid_revmodel().
|
||||
* time after calling new_fluid_revmodel() and before any call to
|
||||
* fluid_revmodel_processXXX() and fluid_revmodel_samplerate_change().
|
||||
*
|
||||
* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
|
||||
* function, calling fluid_revmodel_set() could produce audible clics.
|
||||
|
@ -1152,6 +1174,8 @@ void
|
|||
fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
|
||||
fluid_real_t damping, fluid_real_t width, fluid_real_t level)
|
||||
{
|
||||
fluid_return_if_fail(rev != NULL);
|
||||
|
||||
/*-----------------------------------*/
|
||||
if(set & FLUID_REVMODEL_SET_ROOMSIZE)
|
||||
{
|
||||
|
@ -1185,12 +1209,15 @@ fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
|
|||
|
||||
/*
|
||||
* Applies a sample rate change on the reverb.
|
||||
* fluid_revmodel_set() must be called at least one time before calling
|
||||
* this function.
|
||||
*
|
||||
* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
|
||||
* function, calling fluid_revmodel_samplerate_change() isn't multi task safe because
|
||||
* delay line are memory reallocated. To deal properly with this issue follow
|
||||
* the steps:
|
||||
* function, calling fluid_revmodel_samplerate_change() isn't multi task safe.
|
||||
* To deal properly with this issue follow the steps:
|
||||
* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX().
|
||||
* reverb functions.
|
||||
* Optionally, call fluid_revmodel_reset() to damp the reverb.
|
||||
* 2) Change sample rate by calling fluid_revmodel_samplerate_change().
|
||||
* 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX()
|
||||
* reverb functions.
|
||||
|
@ -1199,29 +1226,44 @@ fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
|
|||
* 2.1) delete the reverb by calling delete_fluid_revmodel().
|
||||
* 2.2) create the reverb by calling new_fluid_revmodel().
|
||||
*
|
||||
* The best solution would be that this function be called only by the same task
|
||||
* calling fluid_revmodel_processXXX().
|
||||
*
|
||||
* @param rev the reverb.
|
||||
* @param sample_rate new sample rate value.
|
||||
* @return FLUID_OK if success, FLUID_FAILED otherwise (memory error).
|
||||
* @param sample_rate new sample rate value. Must be <= sample_rate_max
|
||||
* @return FLUID_OK if success, FLUID_FAILED if new sample rate is greater
|
||||
* then the maximumum sample rate set at creation time. The reverb will
|
||||
* continue to work but with possible lost of quality.
|
||||
* If this is a problem, the caller should follow steps 2.1 and 2.2.
|
||||
* Reverb API.
|
||||
*/
|
||||
int
|
||||
fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
|
||||
{
|
||||
rev->late.samplerate = sample_rate; /* new sample rate value */
|
||||
int status = FLUID_OK;
|
||||
|
||||
/* free all delay lines */
|
||||
delete_fluid_rev_late(&rev->late);
|
||||
fluid_return_val_if_fail(rev != NULL, FLUID_FAILED);
|
||||
|
||||
/* create all delay lines */
|
||||
if(create_mod_delay_lines(&rev->late, sample_rate) == FLUID_FAILED)
|
||||
if(sample_rate > rev->late.sample_rate_max)
|
||||
{
|
||||
return FLUID_FAILED; /* memory error */
|
||||
FLUID_LOG(FLUID_WARN,
|
||||
"fdn reverb: sample rate %.0f Hz is deduced to %.0f Hz\n",
|
||||
sample_rate, rev->late.sample_rate_max);
|
||||
|
||||
/* Reduce sample rate to the maximum value set at creation time.
|
||||
The reverb will continue to work with possible lost of quality.
|
||||
*/
|
||||
sample_rate = rev->late.sample_rate_max;
|
||||
status = FLUID_FAILED;
|
||||
}
|
||||
|
||||
/* Initialize all modulated lines according to sample rate change. */
|
||||
initialize_mod_delay_lines(&rev->late, sample_rate);
|
||||
|
||||
/* updates damping filter coefficients according to sample rate change */
|
||||
update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
|
||||
|
||||
return FLUID_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1233,6 +1275,8 @@ fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate
|
|||
void
|
||||
fluid_revmodel_reset(fluid_revmodel_t *rev)
|
||||
{
|
||||
fluid_return_if_fail(rev != NULL);
|
||||
|
||||
fluid_revmodel_init(rev);
|
||||
}
|
||||
|
||||
|
@ -1347,7 +1391,7 @@ fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in,
|
|||
right_out[k] = out_right * rev->wet1 + out_left * rev->wet2;
|
||||
|
||||
As wet1 is integrated in stereo coefficient wet 1 is now
|
||||
integrated in out_left and out_right we simplify previous
|
||||
integrated in out_left and out_right, so we simplify previous
|
||||
relation by suppression of one multiply as this:
|
||||
|
||||
left_out[k] = out_left + out_right * rev->wet2;
|
||||
|
@ -1467,7 +1511,7 @@ void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
|
|||
right_out[k] += out_right * rev->wet1 + out_left * rev->wet2;
|
||||
|
||||
As wet1 is integrated in stereo coefficient wet 1 is now
|
||||
integrated in out_left and out_right we simplify previous
|
||||
integrated in out_left and out_right, so we simplify previous
|
||||
relation by suppression of one multiply as this:
|
||||
|
||||
left_out[k] += out_left + out_right * rev->wet2;
|
||||
|
|
|
@ -58,7 +58,9 @@ typedef struct _fluid_revmodel_presets_t
|
|||
/*
|
||||
* reverb
|
||||
*/
|
||||
fluid_revmodel_t *new_fluid_revmodel(fluid_real_t sample_rate);
|
||||
fluid_revmodel_t *
|
||||
new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate);
|
||||
|
||||
void delete_fluid_revmodel(fluid_revmodel_t *rev);
|
||||
|
||||
void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in,
|
||||
|
|
|
@ -114,7 +114,9 @@ fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *e
|
|||
|
||||
fluid_rvoice_eventhandler_t *
|
||||
new_fluid_rvoice_eventhandler(int queuesize,
|
||||
int finished_voices_size, int bufs, int fx_bufs, int fx_units, fluid_real_t sample_rate, int extra_threads, int prio)
|
||||
int finished_voices_size, int bufs, int fx_bufs, int fx_units,
|
||||
fluid_real_t sample_rate_max, fluid_real_t sample_rate,
|
||||
int extra_threads, int prio)
|
||||
{
|
||||
fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
|
||||
|
||||
|
@ -145,7 +147,8 @@ new_fluid_rvoice_eventhandler(int queuesize,
|
|||
goto error_recovery;
|
||||
}
|
||||
|
||||
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, sample_rate, eventhandler, extra_threads, prio);
|
||||
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units,
|
||||
sample_rate_max, sample_rate, eventhandler, extra_threads, prio);
|
||||
|
||||
if(eventhandler->mixer == NULL)
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ struct _fluid_rvoice_eventhandler_t
|
|||
|
||||
fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler(
|
||||
int queuesize, int finished_voices_size, int bufs,
|
||||
int fx_bufs, int fx_units, fluid_real_t sample_rate, int, int);
|
||||
int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int, int);
|
||||
|
||||
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *);
|
||||
|
||||
|
|
|
@ -685,6 +685,13 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
|
|||
if(mixer->fx[i].reverb)
|
||||
{
|
||||
fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);
|
||||
|
||||
/*
|
||||
fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created
|
||||
with sample_rate_max set to the maximum sample rate indicated in the settings.
|
||||
If this condition isn't respected, the reverb will continue to work but with
|
||||
lost of quality.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -704,7 +711,11 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)
|
|||
* @param fx_buf_count number of stereo effect buffers
|
||||
*/
|
||||
fluid_rvoice_mixer_t *
|
||||
new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *evthandler, int extra_threads, int prio)
|
||||
new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units,
|
||||
fluid_real_t sample_rate_max,
|
||||
fluid_real_t sample_rate,
|
||||
fluid_rvoice_eventhandler_t *evthandler,
|
||||
int extra_threads, int prio)
|
||||
{
|
||||
int i;
|
||||
fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t);
|
||||
|
@ -733,7 +744,8 @@ new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, fluid_real
|
|||
|
||||
for(i = 0; i < fx_units; i++)
|
||||
{
|
||||
mixer->fx[i].reverb = new_fluid_revmodel(sample_rate);
|
||||
/* create reverb and chorus units */
|
||||
mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate);
|
||||
mixer->fx[i].chorus = new_fluid_chorus(sample_rate);
|
||||
|
||||
if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL)
|
||||
|
|
|
@ -38,7 +38,8 @@ int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer);
|
|||
int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer);
|
||||
#endif
|
||||
fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units,
|
||||
fluid_real_t sample_rate, fluid_rvoice_eventhandler_t *, int, int);
|
||||
fluid_real_t sample_rate_max, fluid_real_t sample_rate,
|
||||
fluid_rvoice_eventhandler_t *, int, int);
|
||||
|
||||
void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *);
|
||||
|
||||
|
|
|
@ -607,6 +607,7 @@ new_fluid_synth(fluid_settings_t *settings)
|
|||
char *important_channels;
|
||||
int i, nbuf, prio_level = 0;
|
||||
int with_ladspa = 0;
|
||||
fluid_real_t sample_rate_min, sample_rate_max;
|
||||
|
||||
/* initialize all the conversion tables and other stuff */
|
||||
if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1))
|
||||
|
@ -637,6 +638,7 @@ new_fluid_synth(fluid_settings_t *settings)
|
|||
|
||||
fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony);
|
||||
fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate);
|
||||
fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max);
|
||||
fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels);
|
||||
fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels);
|
||||
fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups);
|
||||
|
@ -778,7 +780,9 @@ new_fluid_synth(fluid_settings_t *settings)
|
|||
/* Allocate event queue for rvoice mixer */
|
||||
/* In an overflow situation, a new voice takes about 50 spaces in the queue! */
|
||||
synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64,
|
||||
synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups, synth->sample_rate, synth->cores - 1, prio_level);
|
||||
synth->polyphony, nbuf, synth->effects_channels, synth->effects_groups,
|
||||
sample_rate_max, synth->sample_rate,
|
||||
synth->cores - 1, prio_level);
|
||||
|
||||
if(synth->eventhandler == NULL)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue