summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2019-12-02 23:58:15 +0100
committerRobin Gareus <robin@gareus.org>2019-12-03 00:01:10 +0100
commitd425f6dcb597a1bcc01267490627a08bfe48bddd (patch)
tree3d40c38e53a96d8298e0b5eccd4008b47ab577f8
parentc5066dcf38298e18445f2407bd5f92f2c8b4a16d (diff)
Update to fluidsynth-2.1
see https://github.com/FluidSynth/fluidsynth/releases/tag/v2.1.0 - new, less "ringing" reverb engine - new, stereophonic chorus engine - improved integrity checking of SoundFont modulators ...
-rw-r--r--libs/fluidsynth/README2
-rw-r--r--libs/fluidsynth/fluidsynth/gen.h21
-rw-r--r--libs/fluidsynth/fluidsynth/log.h6
-rw-r--r--libs/fluidsynth/fluidsynth/synth.h8
-rw-r--r--libs/fluidsynth/src/fluid_chorus.c1053
-rw-r--r--libs/fluidsynth/src/fluid_chorus.h1
-rw-r--r--libs/fluidsynth/src/fluid_conv.c92
-rw-r--r--libs/fluidsynth/src/fluid_gen.c2
-rw-r--r--libs/fluidsynth/src/fluid_gen.h2
-rw-r--r--libs/fluidsynth/src/fluid_midi.c4
-rw-r--r--libs/fluidsynth/src/fluid_rev.c275
-rw-r--r--libs/fluidsynth/src/fluid_rev.h2
-rw-r--r--libs/fluidsynth/src/fluid_rvoice.c4
-rw-r--r--libs/fluidsynth/src/fluid_rvoice_mixer.c55
-rw-r--r--libs/fluidsynth/src/fluid_sffile.c54
-rw-r--r--libs/fluidsynth/src/fluid_sfont.c4
-rw-r--r--libs/fluidsynth/src/fluid_synth.c208
-rw-r--r--libs/fluidsynth/src/fluid_synth.h5
-rw-r--r--libs/fluidsynth/src/fluid_sys.c48
-rw-r--r--libs/fluidsynth/src/fluid_sys.h19
-rw-r--r--libs/fluidsynth/src/fluid_voice.c4
-rw-r--r--libs/fluidsynth/src/fluidsynth_priv.h12
-rw-r--r--tools/fluid-patches/ardour_fluidsynth.diff37
23 files changed, 1314 insertions, 604 deletions
diff --git a/libs/fluidsynth/README b/libs/fluidsynth/README
index 376a96dfc9..153bab67a4 100644
--- a/libs/fluidsynth/README
+++ b/libs/fluidsynth/README
@@ -1,7 +1,7 @@
This is a stripped down version of fluidsynth (library only)
from git://github.com/FluidSynth/fluidsynth.git
-rev. v2.0.7-98-g9ab3e3ab
+rev. v2.1.0-1-gb266cf2ab
fluidsynth is licensed in terms of the LGPL-2+, see individual source
files for (C) holders.
diff --git a/libs/fluidsynth/fluidsynth/gen.h b/libs/fluidsynth/fluidsynth/gen.h
index 4b625831b0..1f46fe2a99 100644
--- a/libs/fluidsynth/fluidsynth/gen.h
+++ b/libs/fluidsynth/fluidsynth/gen.h
@@ -95,11 +95,22 @@ enum fluid_gen_type
GEN_EXCLUSIVECLASS, /**< Exclusive class number */
GEN_OVERRIDEROOTKEY, /**< Sample root note override */
- /* the initial pitch is not a "standard" generator. It is not
- * mentioned in the list of generator in the SF2 specifications. It
- * is used, however, as the destination for the default pitch wheel
- * modulator. */
- GEN_PITCH, /**< Pitch @note Not a real SoundFont generator */
+ /**
+ * @brief Initial Pitch
+ *
+ * @note This is not "standard" SoundFont generator, because it is not
+ * mentioned in the list of generators in the SF2 specifications.
+ * It is used by FluidSynth internally to compute the nominal pitch of
+ * a note on note-on event. By nature it shouldn't be allowed to be modulated,
+ * however the specification defines a default modulator having "Initial Pitch"
+ * as destination (cf. SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch).
+ * Thus it is impossible to cancel this default modulator, which would be required
+ * to let the MIDI Pitch Wheel controller modulate a different generator.
+ * In order to provide this flexibility, FluidSynth >= 2.1.0 uses a default modulator
+ * "Pitch Wheel to Fine Tune", rather than Initial Pitch. The same "compromise" can
+ * be found on the Audigy 2 ZS for instance.
+ */
+ GEN_PITCH,
GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */
/* non-standard generator for an additional custom high- or low-pass filter */
diff --git a/libs/fluidsynth/fluidsynth/log.h b/libs/fluidsynth/fluidsynth/log.h
index 00d802f50e..3ea74b2614 100644
--- a/libs/fluidsynth/fluidsynth/log.h
+++ b/libs/fluidsynth/fluidsynth/log.h
@@ -77,7 +77,11 @@ fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun,
FLUIDSYNTH_API void fluid_default_log_function(int level, const char *message, void *data);
-FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...);
+FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...)
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+__attribute__ ((format (printf, 2, 3)))
+#endif
+;
#ifdef __cplusplus
diff --git a/libs/fluidsynth/fluidsynth/synth.h b/libs/fluidsynth/fluidsynth/synth.h
index 87826809fd..3003972542 100644
--- a/libs/fluidsynth/fluidsynth/synth.h
+++ b/libs/fluidsynth/fluidsynth/synth.h
@@ -32,13 +32,13 @@ extern "C" {
* @brief Embeddable SoundFont synthesizer
*
* You create a new synthesizer with new_fluid_synth() and you destroy
- * if with delete_fluid_synth(). Use the settings structure to specify
+ * it with delete_fluid_synth(). Use the fluid_settings_t structure to specify
* the synthesizer characteristics.
*
* You have to load a SoundFont in order to hear any sound. For that
* you use the fluid_synth_sfload() function.
*
- * You can use the audio driver functions described below to open
+ * You can use the audio driver functions to open
* the audio device and create a background audio thread.
*
* The API for sending MIDI events is probably what you expect:
@@ -244,7 +244,7 @@ const char *fluid_synth_error(fluid_synth_t *synth);
enum fluid_synth_add_mod
{
FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */
- FLUID_SYNTH_ADD, /**< Add (sum) modulator amounts */
+ FLUID_SYNTH_ADD, /**< Sum up modulator amounts */
};
FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode);
@@ -296,7 +296,7 @@ enum fluid_iir_filter_type
};
/**
- * Specifies optional settings to use for the custom IIR filter
+ * Specifies optional settings to use for the custom IIR filter. Can be bitwise ORed.
*/
enum fluid_iir_filter_flags
{
diff --git a/libs/fluidsynth/src/fluid_chorus.c b/libs/fluidsynth/src/fluid_chorus.c
index abcd4bc0c4..7092d5e7d6 100644
--- a/libs/fluidsynth/src/fluid_chorus.c
+++ b/libs/fluidsynth/src/fluid_chorus.c
@@ -19,7 +19,7 @@
*/
/*
- based on a chrous implementation made by Juergen Mueller And Sundry Contributors in 1998
+ based on a chorus implementation made by Juergen Mueller And Sundry Contributors in 1998
CHANGES
@@ -28,6 +28,8 @@
- Variable delay line implementation using bandlimited
interpolation, code reorganization: Markus Nentwig May 2002
+ - Complete rewrite using lfo computed on the fly, first order all-pass
+ interpolator and adding stereo unit: Jean-Jacques Ceresa, Jul 2019
*/
@@ -36,83 +38,169 @@
*
* Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ):
*
- * * gain-in ___
- * ibuff -----+--------------------------------------------->| |
- * | _________ | |
- * | | | * level 1 | |
- * +---->| delay 1 |----------------------------->| |
- * | |_________| | |
- * | /|\ | |
- * : | | |
- * : +-----------------+ +--------------+ | + |
- * : | Delay control 1 |<--| mod. speed 1 | | |
- * : +-----------------+ +--------------+ | |
- * | _________ | |
- * | | | * level n | |
- * +---->| delay n |----------------------------->| |
- * |_________| | |
- * /|\ |___|
- * | |
- * +-----------------+ +--------------+ | * gain-out
- * | Delay control n |<--| mod. speed n | |
- * +-----------------+ +--------------+ +----->obuff
+ * ________
+ * direct signal (not implemented) >-->| |
+ * _________ | |
+ * mono | | | |
+ * input ---+---->| delay 1 |-------------------------->| Stereo |---> right
+ * | |_________| | | output
+ * | /|\ | Unit |
+ * : | | |
+ * : +-----------------+ |(width) |
+ * : | Delay control 1 |<-+ | |
+ * : +-----------------+ | | |---> left
+ * | _________ | | | output
+ * | | | | | |
+ * +---->| delay n |-------------------------->| |
+ * |_________| | | |
+ * /|\ | |________|
+ * | | +--------------+ /|\
+ * +-----------------+ | |mod depth (ms)| |
+ * | Delay control n |<-*--|lfo speed (Hz)| gain-out
+ * +-----------------+ +--------------+
*
*
* The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n).
*
- * The delay of each block is modulated between 0..depth ms
+ * The chorus unit process a monophonic input signal and produces stereo output
+ * controled by WIDTH macro.
+ * Actually WIDTH is fixed to maximum value. But in the future, we could add a
+ * setting (e.g "synth.chorus.width") allowing the user to get a gradually stereo
+ * effect from minimum (monophonic) to maximum stereo effect.
*
- */
-
-
-/* Variable delay line implementation
- * ==================================
- *
- * The modulated delay needs the value of the delayed signal between
- * samples. A lowpass filter is used to obtain intermediate values
- * between samples (bandlimited interpolation). The sample pulse
- * train is convoluted with the impulse response of the low pass
- * filter (sinc function). To make it work with a small number of
- * samples, the sinc function is windowed (Hamming window).
+ * Delays lines are implemented using only one line for all chorus blocks.
+ * Each chorus block has it own lfo (sinus/triangle). Each lfo are out of phase
+ * to produce uncorrelated signal at the output of the delay line (this simulates
+ * the presence of individual line for each block). Each lfo modulates the length
+ * of the line using a depth modulation value and lfo frequency value common to
+ * all lfos.
*
+ * LFO modulators are computed on the fly, instead of using lfo lookup table.
+ * The advantages are:
+ * - Avoiding a lost of 608272 memory bytes when lfo speed is low (0.3Hz).
+ * - Allows to diminish the lfo speed lower limit to 0.1Hz instead of 0.3Hz.
+ * A speed of 0.1 is interresting for chorus. Using a lookuptable for 0.1Hz
+ * would require too much memory (1824816 bytes).
+ * - Interpolation make use of first order all-pass interpolator instead of
+ * bandlimited interpolation.
+ * - Although lfo modulator is computed on the fly, cpu load is lower than
+ * using lfo lookup table with bandlimited interpolator.
*/
#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
-
-/* Length of one delay line in samples:
- * Set through MAX_SAMPLES_LN2.
- * For example:
- * MAX_SAMPLES_LN2=12
- * => MAX_SAMPLES=pow(2,12-1)=2048
- * => MAX_SAMPLES_ANDMASK=2047
- */
-#define MAX_SAMPLES_LN2 12
-#define MAX_SAMPLES (1 << (MAX_SAMPLES_LN2-1))
-#define MAX_SAMPLES_ANDMASK (MAX_SAMPLES-1)
+/*-------------------------------------------------------------------------------------
+ Private
+--------------------------------------------------------------------------------------*/
+// #define DEBUG_PRINT // allows message to be printed on the console.
+#define MAX_CHORUS 99 /* number maximum of block */
+#define MAX_LEVEL 10 /* max output level */
+#define MIN_SPEED_HZ 0.1 /* min lfo frequency (Hz) */
+#define MAX_SPEED_HZ 5 /* max lfo frequency (Hz) */
-/* Interpolate how many steps between samples? Must be power of two
- For example: 8 => use a resolution of 256 steps between any two
- samples
+/* WIDTH [0..10] value define a stereo separation between left and right.
+ When 0, the output is monophonic. When > 0 , the output is stereophonic.
+ Actually WIDTH is fixed to maximum value. But in the future we could add a setting to
+ allow a gradually stereo effect from minimum (monophonic) to maximum stereo effect.
+*/
+#define WIDTH 10
+
+/* SCALE_WET_WIDTH is a compensation weight factor to get an output
+ amplitude (wet) rather independent of the width setting.
+ 0: the output amplitude is fully dependant on the width setting.
+ >0: the output amplitude is less dependant on the width setting.
+ With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather
+ independent of width setting (see fluid_chorus_set()).
+ */
+#define SCALE_WET_WIDTH 0.2f
+#define SCALE_WET 1.0f
+
+#define MAX_SAMPLES 2048 /* delay length in sample (46.4 ms at sample rate: 44100Hz).*/
+#define LOW_MOD_DEPTH 176 /* low mod_depth/2 in samples */
+#define HIGH_MOD_DEPTH MAX_SAMPLES/2 /* high mod_depth in sample */
+#define RANGE_MOD_DEPTH (HIGH_MOD_DEPTH - LOW_MOD_DEPTH)
+
+/* Important min max values for MOD_RATE */
+/* mod rate define the rate at which the modulator is updated. Examples
+ 50: the modulator is updated every 50 samples (less cpu cycles expensive).
+ 1: the modulator is updated every sample (more cpu cycles expensive).
*/
-#define INTERPOLATION_SUBSAMPLES_LN2 8
-#define INTERPOLATION_SUBSAMPLES (1 << (INTERPOLATION_SUBSAMPLES_LN2-1))
-#define INTERPOLATION_SUBSAMPLES_ANDMASK (INTERPOLATION_SUBSAMPLES-1)
-
-/* Use how many samples for interpolation? Must be odd. '7' sounds
- relatively clean, when listening to the modulated delay signal
- alone. For a demo on aliasing try '1' With '3', the aliasing is
- still quite pronounced for some input frequencies
+/* MOD_RATE acceptable for max lfo speed (5Hz) and max modulation depth (46.6 ms) */
+#define LOW_MOD_RATE 5 /* MOD_RATE acceptable for low modulation depth (8 ms) */
+#define HIGH_MOD_RATE 4 /* MOD_RATE acceptable for max modulation depth (46.6 ms) */
+ /* and max lfo speed (5 Hz) */
+#define RANGE_MOD_RATE (HIGH_MOD_RATE - LOW_MOD_RATE)
+
+/* some chorus cpu_load measurement dependant of modulation rate: mod_rate
+ (number of chorus blocks: 2)
+
+ No stero unit:
+ mod_rate | chorus cpu load(%) | one voice cpu load (%)
+ ----------------------------------------------------
+ 50 | 0.204 |
+ 5 | 0.256 | 0.169
+ 1 | 0.417 |
+
+ With stero unit:
+ mod_rate | chorus cpu load(%) | one voice cpu load (%)
+ ----------------------------------------------------
+ 50 | 0.220 |
+ 5 | 0.274 | 0.169
+ 1 | 0.465 |
+
*/
-#define INTERPOLATION_SAMPLES 5
+
+/*
+ Number of samples to add to the desired length of the delay line. This
+ allows to take account of rounding error interpolation when using large
+ modulation depth.
+ 1 is sufficient for max modulation depth (46.6 ms) and max lfo speed (5 Hz).
+*/
+//#define INTERP_SAMPLES_NBR 0
+#define INTERP_SAMPLES_NBR 1
+
+
+/*-----------------------------------------------------------------------------
+ Sinusoidal modulator
+-----------------------------------------------------------------------------*/
+/* modulator */
+typedef struct
+{
+ fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */
+ fluid_real_t buffer1; /* buffer1 */
+ fluid_real_t buffer2; /* buffer2 */
+ fluid_real_t reset_buffer2;/* reset value of buffer2 */
+} sinus_modulator;
+
+/*-----------------------------------------------------------------------------
+ Triangle modulator
+-----------------------------------------------------------------------------*/
+typedef struct
+{
+ fluid_real_t freq; /* Osc. Frequency (in Hertz) */
+ fluid_real_t val; /* internal current value */
+ fluid_real_t inc; /* increment value */
+} triang_modulator;
+
+/*-----------------------------------------------------------------------------
+ modulator
+-----------------------------------------------------------------------------*/
+typedef struct
+{
+ /*-------------*/
+ int line_out; /* current line out position for this modulator */
+ /*-------------*/
+ sinus_modulator sinus; /* sinus lfo */
+ triang_modulator triang; /* triangle lfo */
+ /*-------------------------*/
+ /* first order All-Pass interpolator members */
+ fluid_real_t frac_pos_mod; /* fractional position part between samples */
+ /* previous value used when interpolating using fractional */
+ fluid_real_t buffer;
+} modulator;
/* Private data for SKEL file */
struct _fluid_chorus_t
@@ -122,93 +210,375 @@ struct _fluid_chorus_t
fluid_real_t level;
fluid_real_t speed_Hz;
int number_blocks;
-
- fluid_real_t *chorusbuf;
- int counter;
- long phase[MAX_CHORUS];
- long modulation_period_samples;
- int *lookup_tab;
fluid_real_t sample_rate;
- /* sinc lookup table */
- fluid_real_t sinc_table[INTERPOLATION_SAMPLES][INTERPOLATION_SUBSAMPLES];
+ /* width control: 0 monophonic, > 0 more stereophonic */
+ fluid_real_t width;
+ fluid_real_t wet1, wet2;
+
+ fluid_real_t *line; /* buffer line */
+ int size; /* effective internal size (in samples) */
+
+ int line_in; /* line in position */
+
+ /* center output position members */
+ fluid_real_t center_pos_mod; /* center output position modulated by modulator */
+ int mod_depth; /* modulation depth (in samples) */
+
+ /* variable rate control of center output position */
+ int index_rate; /* index rate to know when to update center_pos_mod */
+ int mod_rate; /* rate at which center_pos_mod is updated */
+
+ /* modulator member */
+ modulator mod[MAX_CHORUS]; /* sinus/triangle modulator */
};
-static void fluid_chorus_triangle(int *buf, int len, int depth);
-static void fluid_chorus_sine(int *buf, int len, int depth);
+/*-----------------------------------------------------------------------------
+ Sets the frequency of sinus oscillator.
+ @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 phase initial phase of the oscillator in degree (0 to 360).
+-----------------------------------------------------------------------------*/
+static void set_sinus_frequency(sinus_modulator *mod,
+ float freq, float sample_rate, float phase)
+{
+ fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* intial angle */
+ fluid_real_t a;
-fluid_chorus_t *
-new_fluid_chorus(fluid_real_t sample_rate)
+ mod->a1 = 2 * FLUID_COS(w);
+
+ a = (2 * FLUID_M_PI / 360) * phase;
+
+ mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */
+ mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */
+ mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */
+}
+
+/*-----------------------------------------------------------------------------
+ Gets current value of sinus modulator:
+ y(n) = a1 . y(n-1) - y(n-2)
+ out = a1 . buffer1 - buffer2
+
+ @param pointer on modulator structure.
+ @return current value of the modulator sine wave.
+-----------------------------------------------------------------------------*/
+static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod)
{
- int i;
- int ii;
- fluid_chorus_t *chorus;
+ fluid_real_t out;
+ out = mod->a1 * mod->buffer1 - mod->buffer2;
+ mod->buffer2 = mod->buffer1;
- chorus = FLUID_NEW(fluid_chorus_t);
+ if(out >= 1.0f) /* reset in case of instability near PI/2 */
+ {
+ out = 1.0f; /* forces output to the right value */
+ mod->buffer2 = mod->reset_buffer2;
+ }
- if(chorus == NULL)
+ if(out <= -1.0f) /* reset in case of instability near -PI/2 */
{
- FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
- return NULL;
+ out = -1.0f; /* forces output to the right value */
+ mod->buffer2 = - mod->reset_buffer2;
}
- FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
+ mod->buffer1 = out;
+ return out;
+}
- chorus->sample_rate = sample_rate;
+/*-----------------------------------------------------------------------------
+ Set the frequency of triangular oscillator
+ The frequency is converted in a slope value.
+ The initial value is set according to frac_phase which is a position
+ 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.
+-----------------------------------------------------------------------------*/
+static void set_triangle_frequency(triang_modulator *mod, float freq,
+ float sample_rate, float frac_phase)
+{
+ fluid_real_t ns_period; /* period in numbers of sample */
+
+ if(freq <= 0.0)
+ {
+ freq = 0.5f;
+ }
+
+ mod->freq = freq;
+
+ ns_period = sample_rate / freq;
+
+ /* the slope of a triangular osc (0 up to +1 down to -1 up to 0....) is equivalent
+ to the slope of a saw osc (0 -> +4) */
+ mod->inc = 4 / ns_period; /* positive slope */
+
+ /* The initial value and the sign of the slope depend of initial phase:
+ intial value = = (ns_period * frac_phase) * slope
+ */
+ mod->val = ns_period * frac_phase * mod->inc;
+
+ if(1.0 <= mod->val && mod->val < 3.0)
+ {
+ mod->val = 2.0 - mod->val; /* 1.0 down to -1.0 */
+ mod->inc = -mod->inc; /* negative slope */
+ }
+ else if(3.0 <= mod->val)
+ {
+ mod->val = mod->val - 4.0; /* -1.0 up to +1.0. */
+ }
+
+ /* else val < 1.0 */
+}
+
+/*-----------------------------------------------------------------------------
+ Get current value of triangular oscillator
+ y(n) = y(n-1) + dy
+-----------------------------------------------------------------------------*/
+static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod)
+{
+ mod->val = mod->val + mod->inc ;
- /* Lookup table for the SI function (impulse response of an ideal low pass) */
+ if(mod->val >= 1.0)
+ {
+ mod->inc = -mod->inc;
+ return 1.0;
+ }
- /* i: Offset in terms of whole samples */
- for(i = 0; i < INTERPOLATION_SAMPLES; i++)
+ if(mod->val <= -1.0)
{
+ mod->inc = -mod->inc;
+ return -1.0;
+ }
+
+ return mod->val;
+}
+/*-----------------------------------------------------------------------------
+ Reads the sample value out of the modulated delay line.
+ @param mdl, pointer on modulated delay line.
+ @return the sample value.
+-----------------------------------------------------------------------------*/
+static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus,
+ modulator *mod)
+{
+ fluid_real_t out_index; /* new modulated index position */
+ int int_out_index; /* integer part of out_index */
+ fluid_real_t out; /* value to return */
- /* ii: Offset in terms of fractional samples ('subsamples') */
- for(ii = 0; ii < INTERPOLATION_SUBSAMPLES; ii++)
+ /* Checks if the modulator must be updated (every mod_rate samples). */
+ /* Important: center_pos_mod must be used immediatly for the
+ first sample. So, mdl->index_rate must be initialized
+ to mdl->mod_rate (new_mod_delay_line()) */
+
+ if(chorus->index_rate >= chorus->mod_rate)
+ {
+ /* out_index = center position (center_pos_mod) + sinus waweform */
+ if(chorus->type == FLUID_CHORUS_MOD_SINE)
{
- /* Move the origin into the center of the table */
- double i_shifted = ((double) i - ((double) INTERPOLATION_SAMPLES) / 2.
- + (double) ii / (double) INTERPOLATION_SUBSAMPLES);
+ out_index = chorus->center_pos_mod +
+ get_mod_sinus(&mod->sinus) * chorus->mod_depth;
+ }
+ else
+ {
+ out_index = chorus->center_pos_mod +
+ get_mod_triang(&mod->triang) * chorus->mod_depth;
+ }
- if(fabs(i_shifted) < 0.000001)
- {
- /* sinc(0) cannot be calculated straightforward (limit needed
- for 0/0) */
- chorus->sinc_table[i][ii] = (fluid_real_t)1.;
+ /* extracts integer part in int_out_index */
+ if(out_index >= 0.0f)
+ {
+ int_out_index = (int)out_index; /* current integer part */
- }
- else
+ /* forces read index (line_out) with integer modulation value */
+ /* Boundary check and circular motion as needed */
+ if((mod->line_out = int_out_index) >= chorus->size)
{
- chorus->sinc_table[i][ii] = (fluid_real_t)sin(i_shifted * M_PI) / (M_PI * i_shifted);
- /* Hamming window */
- chorus->sinc_table[i][ii] *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * M_PI * i_shifted / (fluid_real_t)INTERPOLATION_SAMPLES));
- };
- };
- };
+ mod->line_out -= chorus->size;
+ }
+ }
+ else /* negative */
+ {
+ int_out_index = (int)(out_index - 1); /* previous integer part */
+ /* forces read index (line_out) with integer modulation value */
+ /* circular motion as needed */
+ mod->line_out = int_out_index + chorus->size;
+ }
+
+ /* extracts fractionnal part. (it will be used when interpolating
+ between line_out and line_out +1) and memorize it.
+ Memorizing is necessary for modulation rate above 1 */
+ mod->frac_pos_mod = out_index - int_out_index;
+ }
- /* allocate lookup tables */
- chorus->lookup_tab = FLUID_ARRAY(int, (int)(chorus->sample_rate / MIN_SPEED_HZ));
+ /* First order all-pass interpolation ----------------------------------*/
+ /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */
+ /* begins interpolation: read current sample */
+ out = chorus->line[mod->line_out];
- if(chorus->lookup_tab == NULL)
+ /* updates line_out to the next sample.
+ Boundary check and circular motion as needed */
+ if(++mod->line_out >= chorus->size)
{
- FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
- goto error_recovery;
+ mod->line_out -= chorus->size;
+ }
+
+ /* Fractional interpolation beetween next sample (at next position) and
+ previous output added to current sample.
+ */
+ out += mod->frac_pos_mod * (chorus->line[mod->line_out] - mod->buffer);
+ mod->buffer = out; /* memorizes current output */
+ return out;
+}
+
+/*-----------------------------------------------------------------------------
+ Push a sample val into the delay line
+-----------------------------------------------------------------------------*/
+#define push_in_delay_line(dl, val) \
+{\
+ dl->line[dl->line_in] = val;\
+ /* Incrementation and circular motion if necessary */\
+ if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\
+}\
+
+/*-----------------------------------------------------------------------------
+ Initialize : mod_rate, center_pos_mod, and index rate
+
+ center_pos_mod is initialized so that the delay between center_pos_mod and
+ line_in is: mod_depth + INTERP_SAMPLES_NBR.
+-----------------------------------------------------------------------------*/
+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 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.
+ */
+ chorus->mod_rate = LOW_MOD_RATE; /* default modulation rate */
+
+ /* compensate mod rate for high modulation depth */
+ if(chorus->mod_depth > LOW_MOD_DEPTH)
+ {
+ int delta_mod_depth = (chorus->mod_depth - LOW_MOD_DEPTH);
+ chorus->mod_rate += (delta_mod_depth * RANGE_MOD_RATE) / RANGE_MOD_DEPTH;
+ }
+
+ /* Initializes the modulated center position (center_pos_mod) so that:
+ - the delay between center_pos_mod and line_in is:
+ mod_depth + INTERP_SAMPLES_NBR.
+ */
+ center = chorus->line_in - (INTERP_SAMPLES_NBR + chorus->mod_depth);
+
+ if(center < 0)
+ {
+ center += chorus->size;
+ }
+
+ chorus->center_pos_mod = (fluid_real_t)center;
+
+ /* index rate to control when to update center_pos_mod */
+ /* Important: must be set to get center_pos_mod immediatly used for the
+ reading of first sample (see get_mod_delay()) */
+ chorus->index_rate = chorus->mod_rate;
+}
+
+/*-----------------------------------------------------------------------------
+ Modulated delay line initialization.
+
+ Sets the length line ( alloc delay samples).
+ 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 delay_length the length of the delay line in samples.
+ @return FLUID_OK if success , FLUID_FAILED if memory error.
+
+ Return FLUID_OK if success, FLUID_FAILED if memory error.
+-----------------------------------------------------------------------------*/
+static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length)
+{
+ /*-----------------------------------------------------------------------*/
+ /* checks parameter */
+ if(delay_length < 1)
+ {
+ return FLUID_FAILED;
}
- /* allocate sample buffer */
+ chorus->mod_depth = 0;
+ /*-----------------------------------------------------------------------
+ allocates delay_line and initialize members: - line, size, line_in...
+ */
+ /* total size of the line: size = INTERP_SAMPLES_NBR + delay_length */
+ chorus->size = delay_length + INTERP_SAMPLES_NBR;
+ chorus->line = FLUID_ARRAY(fluid_real_t, chorus->size);
+
+ if(! chorus->line)
+ {
+ return FLUID_FAILED;
+ }
- chorus->chorusbuf = FLUID_ARRAY(fluid_real_t, MAX_SAMPLES);
+ /* clears the buffer:
+ - delay line
+ - interpolator member: buffer, frac_pos_mod
+ */
+ fluid_chorus_reset(chorus);
+
+ /* Initializes line_in to the start of the buffer */
+ chorus->line_in = 0;
+ /*------------------------------------------------------------------------
+ Initializes modulation members:
+ - modulation rate (the speed at which center_pos_mod is modulated: mod_rate
+ - modulated center position: center_pos_mod
+ - index rate to know when to update center_pos_mod:index_rate
+ -------------------------------------------------------------------------*/
+ /* Initializes the modulated center position:
+ mod_rate, center_pos_mod, and index rate
+ */
+ set_center_position(chorus);
+
+ return FLUID_OK;
+}
- if(chorus->chorusbuf == NULL)
+/*-----------------------------------------------------------------------------
+ API
+------------------------------------------------------------------------------*/
+/**
+ * Create the chorus unit.
+ * @sample_rate audio sample rate in Hz.
+ * @return pointer on chorus unit.
+ */
+fluid_chorus_t *
+new_fluid_chorus(fluid_real_t sample_rate)
+{
+ fluid_chorus_t *chorus;
+
+ chorus = FLUID_NEW(fluid_chorus_t);
+
+ if(chorus == NULL)
{
FLUID_LOG(FLUID_PANIC, "chorus: Out of memory");
- goto error_recovery;
+ return NULL;
}
- if(fluid_chorus_init(chorus) != FLUID_OK)
+ FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t));
+
+ chorus->sample_rate = sample_rate;
+
+#ifdef DEBUG_PRINT
+ printf("fluid_chorus_t:%d bytes\n", sizeof(fluid_chorus_t));
+ printf("fluid_real_t:%d bytes\n", sizeof(fluid_real_t));
+#endif
+
+#ifdef DEBUG_PRINT
+ printf("NEW_MOD\n");
+#endif
+
+ if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED)
{
goto error_recovery;
- };
+ }
return chorus;
@@ -218,33 +588,42 @@ error_recovery:
return NULL;
}
+/**
+ * Delete the chorus unit.
+ * @param chorus pointer on chorus unit returned by new_fluid_chorus().
+ */
void
delete_fluid_chorus(fluid_chorus_t *chorus)
{
fluid_return_if_fail(chorus != NULL);
- FLUID_FREE(chorus->chorusbuf);
- FLUID_FREE(chorus->lookup_tab);
+ FLUID_FREE(chorus->line);
FLUID_FREE(chorus);
}
-int
-fluid_chorus_init(fluid_chorus_t *chorus)
+/**
+ * Clear the internal delay line and associate filter.
+ * @param chorus pointer on chorus unit returned by new_fluid_chorus().
+ */
+void
+fluid_chorus_reset(fluid_chorus_t *chorus)
{
int i;
+ unsigned int u;
- for(i = 0; i < MAX_SAMPLES; i++)
+ /* reset delay line */
+ for(i = 0; i < chorus->size; i++)
{
- chorus->chorusbuf[i] = 0.0;
+ chorus->line[i] = 0;
}
- return FLUID_OK;
-}
-
-void
-fluid_chorus_reset(fluid_chorus_t *chorus)
-{
- fluid_chorus_init(chorus);
+ /* reset modulators's allpass filter */
+ for(u = 0; u < FLUID_N_ELEMENTS(chorus->mod); u++)
+ {
+ /* initializes 1st order All-Pass interpolator members */
+ chorus->mod[u].buffer = 0; /* previous delay sample value */
+ chorus->mod[u].frac_pos_mod = 0; /* fractional position (between consecutives sample) */
+ }
}
/**
@@ -254,7 +633,7 @@ fluid_chorus_reset(fluid_chorus_t *chorus)
* @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.29-5.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)
@@ -263,34 +642,34 @@ 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)
{
- int modulation_depth_samples;
int i;
- if(set & FLUID_CHORUS_SET_NR)
+ if(set & FLUID_CHORUS_SET_NR) /* number of block */
{
chorus->number_blocks = nr;
}
- if(set & FLUID_CHORUS_SET_LEVEL)
+ if(set & FLUID_CHORUS_SET_LEVEL) /* output level */
{
chorus->level = level;
}
- if(set & FLUID_CHORUS_SET_SPEED)
+ if(set & FLUID_CHORUS_SET_SPEED) /* lfo frequency (in Hz) */
{
chorus->speed_Hz = speed;
}
- if(set & FLUID_CHORUS_SET_DEPTH)
+ if(set & FLUID_CHORUS_SET_DEPTH) /* modulation depth (in ms) */
{
chorus->depth_ms = depth_ms;
}
- if(set & FLUID_CHORUS_SET_TYPE)
+ if(set & FLUID_CHORUS_SET_TYPE) /* lfo shape (sinus, triangle) */
{
chorus->type = type;
}
+ /* check min , max parameters */
if(chorus->number_blocks < 0)
{
FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0.");
@@ -322,272 +701,304 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
chorus->depth_ms = 0.0;
}
- /* Depth: Check for too high value through modulation_depth_samples. */
-
if(chorus->level < 0.0)
{
FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0.");
chorus->level = 0.0;
}
- else if(chorus->level > 10)
+ else if(chorus->level > MAX_LEVEL)
{
FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! "
"Setting it to 0.1.");
chorus->level = 0.1;
}
- /* The modulating LFO goes through a full period every x samples: */
- chorus->modulation_period_samples = chorus->sample_rate / chorus->speed_Hz;
+ /* 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);
- /* The variation in delay time is x: */
- modulation_depth_samples = (int)
- (chorus->depth_ms / 1000.0 /* convert modulation depth in ms to s*/
- * chorus->sample_rate);
-
- if(modulation_depth_samples > MAX_SAMPLES)
+ if(chorus->mod_depth > MAX_SAMPLES)
{
FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES);
- modulation_depth_samples = MAX_SAMPLES;
+ chorus->mod_depth = MAX_SAMPLES;
// set depth to maximum to avoid spamming console with above warning
- chorus->depth_ms = (modulation_depth_samples * 1000) / chorus->sample_rate;
+ 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);
}
- /* initialize LFO table */
- switch(chorus->type)
+#ifdef DEBUG_PRINT
+ printf("lfo type:%d\n", chorus->type);
+ printf("speed_Hz:%f\n", chorus->speed_Hz);
+#endif
+
+ /* Initialize the lfo waveform */
+ if((chorus->type != FLUID_CHORUS_MOD_SINE) &&
+ (chorus->type != FLUID_CHORUS_MOD_TRIANGLE))
{
- default:
FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave.");
chorus->type = FLUID_CHORUS_MOD_SINE;
- /* fall-through */
-
- case FLUID_CHORUS_MOD_SINE:
- fluid_chorus_sine(chorus->lookup_tab, chorus->modulation_period_samples,
- modulation_depth_samples);
- break;
-
- case FLUID_CHORUS_MOD_TRIANGLE:
- fluid_chorus_triangle(chorus->lookup_tab, chorus->modulation_period_samples,
- modulation_depth_samples);
- break;
}
- for(i = 0; i < chorus->number_blocks; i++)
+#ifdef DEBUG_PRINT
+
+ if(chorus->type == FLUID_CHORUS_MOD_SINE)
+ {
+ printf("lfo: sinus\n");
+ }
+ else
{
- /* Set the phase of the chorus blocks equally spaced */
- chorus->phase[i] = (int)((double) chorus->modulation_period_samples
- * (double) i / (double) chorus->number_blocks);
+ printf("lfo: triangle\n");
}
- /* Start of the circular buffer */
- chorus->counter = 0;
+ printf("nr:%d\n", chorus->number_blocks);
+#endif
+
+ /* Recalculate internal values after parameters change */
+
+ /*
+ Note:
+ Actually WIDTH is fixed to maximum value. But in the future we could add a setting
+ "synth.chorus.width" to allow a gradually stereo effect from minimum (monophonic) to
+ maximum stereo effect.
+ If this setting will be added, remove the following instruction.
+ */
+ chorus->width = WIDTH;
+ {
+ /* The stereo amplitude equation (wet1 and wet2 below) have a
+ tendency to produce high amplitude with high width values ( 1 < width < 10).
+ This results in an unwanted noisy output clipped by the audio card.
+ To avoid this dependency, we divide by (1 + chorus->width * SCALE_WET_WIDTH)
+ Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting),
+ the output amplitude (wet) seems rather independent of width setting */
+
+ fluid_real_t wet = chorus->level * SCALE_WET ;
+
+ /* wet1 and wet2 are used by the stereo effect controled by the width setting
+ for producing a stereo ouptput from a monophonic chorus signal.
+ Please see the note above about a side effect tendency */
+
+ if(chorus->number_blocks > 1)
+ {
+ wet = wet / (1.0f + chorus->width * SCALE_WET_WIDTH);
+ chorus->wet1 = wet * (chorus->width / 2.0f + 0.5f);
+ chorus->wet2 = wet * ((1.0f - chorus->width) / 2.0f);
+#ifdef DEBUG_PRINT
+ printf("width:%f\n", chorus->width);
+
+ if(chorus->width > 0)
+ {
+ printf("nr > 1, width > 0 => out stereo\n");
+ }
+ else
+ {
+ printf("nr > 1, width:0 =>out mono\n");
+ }
+
+#endif
+ }
+ else
+ {
+ /* only one chorus block */
+ if(chorus->width == 0.0)
+ {
+ /* wet1 and wet2 should make stereo output monomophic */
+ chorus->wet1 = chorus->wet2 = wet;
+ }
+ else
+ {
+ /* for width > 0, wet1 and wet2 should make stereo output stereo
+ with only one block. This will only possible by inverting
+ the unique signal on each left and right output.
+ Note however that with only one block, it isn't possible to
+ have a graduate width effect */
+ chorus->wet1 = wet;
+ chorus->wet2 = -wet; /* inversion */
+ }
+
+#ifdef DEBUG_PRINT
+ printf("width:%f\n", chorus->width);
+
+ if(chorus->width != 0)
+ {
+ printf("one block, width > 0 => out stereo\n");
+ }
+ else
+ {
+ printf("one block, width:0 => out mono\n");
+ }
+
+#endif
+ }
+ }
}
+/**
+ * Process chorus by mixing the result in output buffer.
+ * @param chorus pointer on chorus unit returned by new_fluid_chorus().
+ * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples.
+ * @param left_out, right_out, pointers on stereo output buffers of
+ * FLUID_BUFSIZE samples.
+ */
void fluid_chorus_processmix(fluid_chorus_t *chorus, const 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;
+ fluid_real_t d_out[2]; /* output stereo Left and Right */
+ /* foreach sample, process output sample then input sample */
for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++)
{
+ fluid_real_t out; /* block output */
- d_in = in[sample_index];
- d_out = 0.0f;
+ d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */
-# if 0
+#if 0
/* Debug: Listen to the chorus signal only */
left_out[sample_index] = 0;
right_out[sample_index] = 0;
#endif
- /* Write the current sample into the circular buffer */
- chorus->chorusbuf[chorus->counter] = d_in;
+ ++chorus->index_rate; /* modulator rate */
+ /* foreach chorus block, process output sample */
for(i = 0; i < chorus->number_blocks; i++)
{
- int ii;
- /* Calculate the delay in subsamples for the delay line of chorus block nr. */
-
- /* The value in the lookup table is so, that this expression
- * will always be positive. It will always include a number of
- * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
- * remain positive at all times. */
- int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- - chorus->lookup_tab[chorus->phase[i]]);
+ /* get sample from the output of modulated delay line */
+ out = get_mod_delay(chorus, &chorus->mod[i]);
- int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
+ /* accumulate out into stereo unit input */
+ d_out[i & 1] += out;
+ }
- /* modulo divide by INTERPOLATION_SUBSAMPLES */
- pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+ /* update modulator index rate and output center position */
+ if(chorus->index_rate >= chorus->mod_rate)
+ {
+ chorus->index_rate = 0; /* clear modulator index rate */
- for(ii = 0; ii < INTERPOLATION_SAMPLES; ii++)
+ /* updates center position (center_pos_mod) to the next position
+ specified by modulation rate */
+ if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size)
{
- /* Add the delayed signal to the chorus sum d_out Note: The
- * delay in the delay line moves backwards for increasing
- * delay!*/
-
- /* The & in chorusbuf[...] is equivalent to a division modulo
- MAX_SAMPLES, only faster. */
- d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
- * chorus->sinc_table[ii][pos_subsamples];
-
- pos_samples--;
- };
-
- /* Cycle the phase of the modulating LFO */
- chorus->phase[i]++;
-
- chorus->phase[i] %= (chorus->modulation_period_samples);
- } /* foreach chorus block */
-
- d_out *= chorus->level;
+ chorus->center_pos_mod -= chorus->size;
+ }
+ }
- /* Add the chorus sum d_out to output */
- left_out[sample_index] += d_out;
- right_out[sample_index] += d_out;
+ /* Adjust stereo input level in case of number_blocks odd:
+ In those case, d_out[1] level is lower than d_out[0], so we need to
+ add out value to d_out[1] to have d_out[0] and d_out[1] balanced.
+ */
+ if((i & 1) && i > 2) // i = 3,5,7...
+ {
+ d_out[1] += out ;
+ }
- /* Move forward in circular buffer */
- chorus->counter++;
- chorus->counter %= MAX_SAMPLES;
+ /* process stereo unit */
+ /* Add the chorus stereo unit d_out to left and right output */
+ left_out[sample_index] += d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2;
+ right_out[sample_index] += d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2;
- } /* foreach sample */
+ /* Write the current input sample into the circular buffer */
+ push_in_delay_line(chorus, in[sample_index]);
+ }
}
+/**
+ * Process chorus by putting the result in output buffer (no mixing).
+ * @param chorus pointer on chorus unit returned by new_fluid_chorus().
+ * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples.
+ * @param left_out, right_out, pointers on stereo output buffers of
+ * FLUID_BUFSIZE samples.
+ */
/* Duplication of code ... (replaces sample data instead of mixing) */
void fluid_chorus_processreplace(fluid_chorus_t *chorus, const 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;
+ fluid_real_t d_out[2]; /* output stereo Left and Right */
+ /* foreach sample, process output sample then input sample */
for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++)
{
+ fluid_real_t out; /* block output */
- d_in = in[sample_index];
- d_out = 0.0f;
+ d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */
-# if 0
+#if 0
/* Debug: Listen to the chorus signal only */
left_out[sample_index] = 0;
right_out[sample_index] = 0;
#endif
- /* Write the current sample into the circular buffer */
- chorus->chorusbuf[chorus->counter] = d_in;
+ ++chorus->index_rate; /* modulator rate */
+ /* foreach chorus block, process output sample */
for(i = 0; i < chorus->number_blocks; i++)
{
- int ii;
- /* Calculate the delay in subsamples for the delay line of chorus block nr. */
-
- /* The value in the lookup table is so, that this expression
- * will always be positive. It will always include a number of
- * full periods of MAX_SAMPLES*INTERPOLATION_SUBSAMPLES to
- * remain positive at all times. */
- int pos_subsamples = (INTERPOLATION_SUBSAMPLES * chorus->counter
- - chorus->lookup_tab[chorus->phase[i]]);
+ /* get sample from the output of modulated delay line */
+ out = get_mod_delay(chorus, &chorus->mod[i]);
- int pos_samples = pos_subsamples / INTERPOLATION_SUBSAMPLES;
+ /* accumulate out into stereo unit input */
+ d_out[i & 1] += out;
+ }
- /* modulo divide by INTERPOLATION_SUBSAMPLES */
- pos_subsamples &= INTERPOLATION_SUBSAMPLES_ANDMASK;
+ /* update modulator index rate and output center position */
+ if(chorus->index_rate >= chorus->mod_rate)
+ {
+ chorus->index_rate = 0; /* clear modulator index rate */
- for(ii = 0; ii < INTERPOLATION_SAMPLES; ii++)
+ /* updates center position (center_pos_mod) to the next position
+ specified by modulation rate */
+ if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size)
{
- /* Add the delayed signal to the chorus sum d_out Note: The
- * delay in the delay line moves backwards for increasing
- * delay!*/
-
- /* The & in chorusbuf[...] is equivalent to a division modulo
- MAX_SAMPLES, only faster. */
- d_out += chorus->chorusbuf[pos_samples & MAX_SAMPLES_ANDMASK]
- * chorus->sinc_table[ii][pos_subsamples];
-
- pos_samples--;
- };
-
- /* Cycle the phase of the modulating LFO */
- chorus->phase[i]++;
-
- chorus->phase[i] %= (chorus->modulation_period_samples);
- } /* foreach chorus block */
-
- d_out *= chorus->level;
-
- /* Store the chorus sum d_out to output */
- left_out[sample_index] = d_out;
- right_out[sample_index] = d_out;
-
- /* Move forward in circular buffer */
- chorus->counter++;
- chorus->counter %= MAX_SAMPLES;
-
- } /* foreach sample */
-}
-
-/* Purpose:
- *
- * Calculates a modulation waveform (sine) Its value ( modulo
- * MAXSAMPLES) varies between 0 and depth*INTERPOLATION_SUBSAMPLES.
- * Its period length is len. The waveform data will be used modulo
- * MAXSAMPLES only. Since MAXSAMPLES is substracted from the waveform
- * a couple of times here, the resulting (current position in
- * buffer)-(waveform sample) will always be positive.
- */
-static void
-fluid_chorus_sine(int *buf, int len, int depth)
-{
- int i;
- double angle, incr, mult;
-
- /* Pre-calculate increment between angles. */
- incr = (2. * M_PI) / (double)len;
-
- /* Pre-calculate 'depth' multiplier. */
- mult = (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES;
-
- /* Initialize to zero degrees. */
- angle = 0.;
-
- /* Build sine modulation waveform */
- for(i = 0; i < len; i++)
- {
- buf[i] = (int)((1. + sin(angle)) * mult) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
-
- angle += incr;
- }
-}
-
-/* Purpose:
- * Calculates a modulation waveform (triangle)
- * See fluid_chorus_sine for comments.
- */
-static void
-fluid_chorus_triangle(int *buf, int len, int depth)
-{
- int *il = buf;
- int *ir = buf + len - 1;
- int ival;
- double val, incr;
-
- /* Pre-calculate increment for the ramp. */
- incr = 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
-
- /* Initialize first value */
- val = 0. - 3. * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
+ chorus->center_pos_mod -= chorus->size;
+ }
+ }
- /* Build triangular modulation waveform */
- while(il <= ir)
- {
- /* Assume 'val' to be always negative for rounding mode */
- ival = (int)(val - 0.5);
+ /* Adjust stereo input level in case of number_blocks odd:
+ In those case, d_out[1] level is lower than d_out[0], so we need to
+ add out value to d_out[1] to have d_out[0] and d_out[1] balanced.
+ */
+ if((i & 1) && i > 2) // i = 3,5,7...
+ {
+ d_out[1] += out ;
+ }
- *il++ = ival;
- *ir-- = ival;
+ /* process stereo unit */
+ /* store the chorus stereo unit d_out to left and right output */
+ left_out[sample_index] = d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2;
+ right_out[sample_index] = d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2;
- val += incr;
+ /* Write the current input sample into the circular buffer */
+ push_in_delay_line(chorus, in[sample_index]);
}
}
diff --git a/libs/fluidsynth/src/fluid_chorus.h b/libs/fluidsynth/src/fluid_chorus.h
index 94130957a2..f815ac42d4 100644
--- a/libs/fluidsynth/src/fluid_chorus.h
+++ b/libs/fluidsynth/src/fluid_chorus.h
@@ -49,7 +49,6 @@ typedef enum
*/
fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate);
void delete_fluid_chorus(fluid_chorus_t *chorus);
-int fluid_chorus_init(fluid_chorus_t *chorus);
void fluid_chorus_reset(fluid_chorus_t *chorus);
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,
diff --git a/libs/fluidsynth/src/fluid_conv.c b/libs/fluidsynth/src/fluid_conv.c
index a2ba770b35..5c055d2be9 100644
--- a/libs/fluidsynth/src/fluid_conv.c
+++ b/libs/fluidsynth/src/fluid_conv.c
@@ -23,66 +23,56 @@
#include "fluid_conv_tables.c"
/*
- * fluid_ct2hz
+ * Converts absolute cents to Hertz
+ *
+ * As per sfspec section 9.3:
+ *
+ * ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a
+ * reference of MIDI key number scaled by 100.
+ * A cent is 1/1200 of an octave [which is the twelve hundredth root of two],
+ * and value 6900 is 440 Hz (A-440).
+ *
+ * Implemented below basically is the following:
+ * 440 * 2^((cents-6900)/1200)
+ * = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200))
+ * = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200)))
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * This second factor is stored in the lookup table.
+ *
+ * The first factor can be implemented with a fast shift when the exponent
+ * is always an int. This is the case when using 440/2^6 Hz rather than 440Hz
+ * reference.
*/
fluid_real_t
fluid_ct2hz_real(fluid_real_t cents)
{
- if(cents < 0)
+ if(FLUID_UNLIKELY(cents < 0))
{
return (fluid_real_t) 1.0;
}
- else if(cents < 900)
- {
- return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int)(cents + 300)];
- }
- else if(cents < 2100)
- {
- return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int)(cents - 900)];
- }
- else if(cents < 3300)
- {
- return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int)(cents - 2100)];
- }
- else if(cents < 4500)
- {
- return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int)(cents - 3300)];
- }
- else if(cents < 5700)
- {
- return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int)(cents - 4500)];
- }
- else if(cents < 6900)
- {
- return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int)(cents - 5700)];
- }
- else if(cents < 8100)
- {
- return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int)(cents - 6900)];
- }
- else if(cents < 9300)
- {
- return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int)(cents - 8100)];
- }
- else if(cents < 10500)
- {
- return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int)(cents - 9300)];
- }
- else if(cents < 11700)
- {
- return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int)(cents - 10500)];
- }
- else if(cents < 12900)
- {
- return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int)(cents - 11700)];
- }
- else if(cents < 14100)
- {
- return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int)(cents - 12900)];
- }
else
{
- return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
+ unsigned int mult, fac, rem;
+ unsigned int icents = (unsigned int)cents;
+ icents += 300u;
+
+ // don't use stdlib div() here, it turned out have poor performance
+ fac = icents / 1200u;
+ rem = icents % 1200u;
+
+ // Think of "mult" as the factor that we multiply (440/2^6)Hz with,
+ // or in other words mult is the "first factor" of the above
+ // functions comment.
+ //
+ // Assuming sizeof(uint)==4 this will give us a maximum range of
+ // 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz
+ // which is much more than ever needed. For bigger values, just
+ // safely wrap around (the & is just a replacement for the quick
+ // modulo operation % 32).
+ mult = 1u << (fac & (sizeof(mult)*8u - 1u));
+
+ // don't use ldexp() either (poor performance)
+ return mult * fluid_ct2hz_tab[rem];
}
}
diff --git a/libs/fluidsynth/src/fluid_gen.c b/libs/fluidsynth/src/fluid_gen.c
index cf3cc48b46..6472dd3258 100644
--- a/libs/fluidsynth/src/fluid_gen.c
+++ b/libs/fluidsynth/src/fluid_gen.c
@@ -26,7 +26,7 @@
/* See SFSpec21 $8.1.3 */
static const fluid_gen_info_t fluid_gen_info[] =
{
- /* number/name init scale min max def */
+ /* number/name init nrpn-scale min max def */
{ GEN_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f },
{ GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f },
{ GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },
diff --git a/libs/fluidsynth/src/fluid_gen.h b/libs/fluidsynth/src/fluid_gen.h
index 76d168c25f..3f8f14b1c5 100644
--- a/libs/fluidsynth/src/fluid_gen.h
+++ b/libs/fluidsynth/src/fluid_gen.h
@@ -27,7 +27,7 @@
typedef struct _fluid_gen_info_t
{
char num; /* Generator number */
- char init; /* Does the generator need to be initialized (cfr. fluid_voice_init()) */
+ char init; /* Does the generator need to be initialized (not used) */
char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
float min; /* The minimum value */
float max; /* The maximum value */
diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c
index c92fb2fefa..844de01c8f 100644
--- a/libs/fluidsynth/src/fluid_midi.c
+++ b/libs/fluidsynth/src/fluid_midi.c
@@ -180,7 +180,7 @@ fluid_file_read_full(fluid_file fp, size_t *length)
return NULL;
}
- FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen);
+ FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", buflen);
buffer = FLUID_MALLOC(buflen);
if(buffer == NULL)
@@ -193,7 +193,7 @@ fluid_file_read_full(fluid_file fp, size_t *length)
if(n != buflen)
{
- FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n,
+ FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", n,
buflen);
FLUID_FREE(buffer);
return NULL;
diff --git a/libs/fluidsynth/src/fluid_rev.c b/libs/fluidsynth/src/fluid_rev.c
index 014562e807..f3ee0e47f9 100644
--- a/libs/fluidsynth/src/fluid_rev.c
+++ b/libs/fluidsynth/src/fluid_rev.c
@@ -75,25 +75,26 @@
* the building of a lot of resonances in the reverberation tail even when
* using only 8 delays lines (NBR_DELAYS = 8) (default).
*
- * Although 8 lines give good result, using 12 delays lines brings the overall
- * frequency density quality a bit higher. This quality augmentation is noticeable
- * particularly when using long reverb time (roomsize = 1) on solo instrument with
- * long release time. Of course the cpu load augmentation is +50% relatively
- * to 8 lines.
+ * The frequency density (often called "modal density" is one property that
+ * contributes to sound quality. Although 8 lines give good result, using 12 delays
+ * lines brings the overall frequency density quality a bit higher.
+ * This quality augmentation is noticeable particularly when using long reverb time
+ * (roomsize = 1) on solo instrument with long release time. Of course the cpu load
+ * augmentation is +50% relatively to 8 lines.
*
* As a general rule the reverberation tail quality is easier to perceive by ear
* when using:
* - percussive instruments (i.e piano and others).
* - long reverb time (roomsize = 1).
* - no damping (damp = 0).
- *
+ * - Using headphone. Avoid using loud speaker, you will be quickly misguided by the
+ * natural reverberation of the room in which you are.
*
* The cpu load for 8 lines is a bit lower than for freeverb (- 3%),
* but higher for 12 lines (+ 41%).
*
*
- * The memory consumption is less than for freeverb. This saves 147480 bytes
- * for 8 lines (- 72%) and 110152 bytes for 12 lines (- 54 %).
+ * The memory consumption is less than for freeverb
* (see the results table below).
*
* Two macros are usable at compiler time:
@@ -113,19 +114,23 @@
* Note: the cpu load in % are relative each to other. These values are
* given by the fluidsynth profile commands.
* --------------------------------------------------------------------------
- * reverb | NBR_DELAYS | Performances | memory size | quality
- * | | (cpu_load: %) | (bytes) |
+ * reverb | NBR_DELAYS | Performances | memory size | quality
+ * | | (cpu_load: %) | (bytes)(see note) |
* ==========================================================================
- * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing
- * | 2 x 4 all-pass | | |
+ * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing
+ * | 2 x 4 all-pass | | |
* ----------|---------------------------------------------------------------
- * FDN | 8 | 0.650 % | 57136 | far less
- * modulated | |(feeverb - 3%) |(freeverb - 72%) | ringing
+ * FDN | 8 | 0.650 % | 112160 | far less
+ * modulated | |(feeverb - 3%) | (55% freeverb) | ringing
* |---------------------------------------------------------------
- * | 12 | 0.942 % | 94464 | best than
- * | |(freeverb + 41%) |(freeverb - 54%) | 8 lines
+ * | 12 | 0.942 % | 168240 | 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).
+ *
*
*----------------------------------------------------------------------------
* 'Denormalise' method to avoid loss of performance.
@@ -173,6 +178,8 @@
roomsize parameter.
- DENORMALISING enable denormalising handling.
-----------------------------------------------------------------------------*/
+//#define INFOS_PRINT /* allows message to be printed on the console. */
+
/* Number of delay lines (must be only 8 or 12)
8 is the default.
12 produces a better quality but is +50% cpu expensive
@@ -298,16 +305,6 @@ a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. *
#define DELAY_L11 1187
#endif
-/* 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
-};
-
/*---------------------------------------------------------------------------*/
/* The FDN late feed back matrix: A
@@ -613,6 +610,16 @@ static int set_mod_delay_line(mod_delay_line *mdl,
}
/*-----------------------------------------------------------------------------
+ Return norminal delay length
+
+ @param mdl, pointer on modulated delay line.
+-----------------------------------------------------------------------------*/
+static int get_mod_delay_line_length(mod_delay_line *mdl)
+{
+ return (mdl->dl.size - mdl->mod_depth - INTERP_SAMPLES_NBR);
+}
+
+/*-----------------------------------------------------------------------------
Reads the sample value out of the modulated delay line.
@param mdl, pointer on modulated delay line.
@return the sample value.
@@ -738,6 +745,7 @@ static void update_rev_time_damping(fluid_late *late,
{
int i;
fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */
+ int delay_length; /* delay length */
fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */
fluid_real_t alpha, alpha2;
@@ -756,8 +764,9 @@ static void update_rev_time_damping(fluid_late *late,
Computes dc_rev_time
------------------------------------------*/
dc_rev_time = GET_DC_REV_TIME(roomsize);
+ delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]);
/* computes gi_tmp from dc_rev_time using relation E2 */
- gi_tmp = FLUID_POW(10, -3 * delay_length[NBR_DELAYS - 1] *
+ gi_tmp = FLUID_POW(10, -3 * delay_length *
sample_period / dc_rev_time); /* E2 */
#else
/* roomsize parameters have the same response that Freeverb, that is:
@@ -768,16 +777,18 @@ static void update_rev_time_damping(fluid_late *late,
Computes dc_rev_time
------------------------------------------*/
fluid_real_t gi_min, gi_max;
+
/* values gi_min et gi_max are computed using E2 for the line with
maximum delay */
- gi_max = FLUID_POW(10, (-3 * delay_length[NBR_DELAYS - 1] / MAX_DC_REV_TIME) *
- sample_period); /* E2 */
- gi_min = FLUID_POW(10, (-3 * delay_length[NBR_DELAYS - 1] / MIN_DC_REV_TIME) *
- sample_period); /* E2 */
+ delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]);
+ gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) *
+ sample_period); /* E2 */
+ gi_min = FLUID_POW(10, (-3 * delay_length / MIN_DC_REV_TIME) *
+ sample_period); /* E2 */
/* gi = f(roomsize, gi_max, gi_min) */
gi_tmp = gi_min + roomsize * (gi_max - gi_min);
/* Computes T60DC from gi using inverse of relation E2.*/
- dc_rev_time = -3 * FLUID_M_LN10 * delay_length[NBR_DELAYS - 1] * sample_period / FLUID_LOGF(gi_tmp);
+ dc_rev_time = -3 * FLUID_M_LN10 * delay_length * sample_period / FLUID_LOGF(gi_tmp);
}
#endif /* ROOMSIZE_RESPONSE_LINEAR */
/*--------------------------------------------
@@ -809,12 +820,16 @@ static void update_rev_time_damping(fluid_late *late,
/* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */
for(i = 0; i < NBR_DELAYS; i++)
{
+ fluid_real_t gi, ai;
+
+ /* delay length */
+ delay_length = get_mod_delay_line_length(&late->mod_delay_lines[i]);
+
/* iir low pass filter gain */
- fluid_real_t gi = FLUID_POW(10, -3 * delay_length[i] *
- sample_period / dc_rev_time);
+ gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time);
/* iir low pass filter feedback gain */
- fluid_real_t ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2);
+ ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2);
/* b0 = gi * (1 - ai), a1 = - ai */
set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping,
@@ -830,6 +845,7 @@ static void update_rev_time_damping(fluid_late *late,
static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1)
{
int i;
+ fluid_real_t wet;
for(i = 0; i < NBR_DELAYS; i++)
{
@@ -852,26 +868,23 @@ static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1)
11|-1 -1|
*/
- late->out_left_gain[i] = wet1;
-
- /* Sets Left coefficients first */
- if(i % 2) /* Left is 1,-1, 1,-1, 1,-1,.... */
+ /* for left line: 00, ,02, ,04, ,06, ,08, ,10, ,12,... left_gain = +1 */
+ /* for left line: ,01, ,03, ,05, ,07, ,09, ,11,... left_gain = -1 */
+ wet = wet1;
+ if(i & 1)
{
- late->out_left_gain[i] *= -1;
+ wet = -wet1;
}
+ late->out_left_gain[i] = wet;
- /* Now sets right gain as function of Left */
- /* for right line 1,2,5,6,9,10,13,14, right = - left */
- if((i == 1) || (i == 2) || (i == 5) || (i == 6) || (i == 9) || (i == 10) || (i == 13) || (i == 14))
- {
- /* Right is reverse of Left */
- late->out_right_gain[i] = -1 * late->out_left_gain[i];
- }
- else /* for Right : line 0,3,4,7,8,11,12,15 */
+ /* for right line: 00,01, ,04,05, ,08,09, ,12,13 right_gain = +1 */
+ /* for right line: ,02 ,03, ,06,07, ,10,11,... right_gain = -1 */
+ wet = wet1;
+ if(i & 2)
{
- /* Right is same as Left */
- late->out_right_gain[i] = late->out_left_gain[i];
+ wet = -wet1;
}
+ late->out_right_gain[i] = wet;
}
}
@@ -892,29 +905,78 @@ static void delete_fluid_rev_late(fluid_late *late)
}
/*-----------------------------------------------------------------------------
- Creates the fdn reverb.
+ Creates 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 int create_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate)
{
+ /* 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
+ };
+
int result; /* return value */
int i;
- FLUID_MEMSET(late, 0, sizeof(fluid_late));
-
- late->samplerate = sample_rate;
-
- /*--------------------------------------------------------------------------
- First initialize the modulated delay lines
+ /*
+ 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[] is nominal
+ to get minimum modal density of 0.15 at sample rate 44100Hz.
+ Here we set length_factor to 2 to mutiply 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).
+
+ 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;
+ 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;
+ }
+#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++)
+ {
+ total_delay += length_factor * delay_length[i];
+ }
- for(i = 0; i < NBR_DELAYS; i++)
+ /* 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));
+ }
+#endif
+
+ for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */
{
- /* sets local delay lines's parameters */
+ /* allocate delay line and set local delay lines's parameters */
result = set_mod_delay_line(&late->mod_delay_lines[i],
- delay_length[i], MOD_DEPTH, MOD_RATE);
+ delay_length[i] * length_factor,
+ mod_depth, MOD_RATE);
if(result == FLUID_FAILED)
{
@@ -926,12 +988,33 @@ static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate)
*/
set_mod_frequency(&late->mod_delay_lines[i].mod,
MOD_FREQ * MOD_RATE,
- sample_rate,
+ late->samplerate,
(float)(MOD_PHASE * i));
}
+ return FLUID_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ Creates the fdn reverb.
+ @param late, pointer on the fnd late reverb to initialize.
+ @param sample_rate the sample rate.
+ @return FLUID_OK if success, FLUID_FAILED otherwise.
+-----------------------------------------------------------------------------*/
+static int create_fluid_rev_late(fluid_late *late, fluid_real_t sample_rate)
+{
+ FLUID_MEMSET(late, 0, sizeof(fluid_late));
+
+ late->samplerate = sample_rate;
+
+ /*--------------------------------------------------------------------------
+ First initialize the modulated delay lines
+ */
+
+ if(create_mod_delay_lines(late, sample_rate) == FLUID_FAILED)
+ {
+ return FLUID_FAILED;
+ }
- /*-----------------------------------------------------------------------*/
- update_stereo_coefficient(late, 1.0f);
return FLUID_OK;
}
@@ -982,7 +1065,7 @@ fluid_revmodel_update(fluid_revmodel_t *rev)
/* integrates wet1 in stereo coefficient (this will save one multiply) */
update_stereo_coefficient(&rev->late, rev->wet1);
- if(rev->wet1 > 0.0)
+ if(rev->wet1 > 0.0f)
{
rev->wet2 /= rev->wet1;
}
@@ -996,7 +1079,10 @@ fluid_revmodel_update(fluid_revmodel_t *rev)
-----------------------------------------------------------------------------*/
/*
-* Creates a reverb.
+* Creates a reverb. One 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.
* @return pointer on the new reverb or NULL if memory error.
* Reverb API.
@@ -1024,7 +1110,15 @@ new_fluid_revmodel(fluid_real_t sample_rate)
/*
* free the reverb.
-* @param pointer on rev to free.
+* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
+* function, calling delete_fluid_revmodel() isn't multi task safe because
+* delay line are freed. To deal properly with this issue follow the steps:
+*
+* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX().
+* reverb functions.
+* 2) Delete the reverb by calling delete_fluid_revmodel().
+*
+* @param rev pointer on reverb to free.
* Reverb API.
*/
void
@@ -1036,7 +1130,14 @@ delete_fluid_revmodel(fluid_revmodel_t *rev)
}
/*
-* Sets one or more reverb parameters.
+* Sets one or more reverb parameters. Note this must be called at least one
+* time after calling new_fluid_revmodel().
+*
+* Note that while the reverb is used by calling any fluid_revmodel_processXXX()
+* function, calling fluid_revmodel_set() could produce audible clics.
+* If this is a problem, optionnaly call fluid_revmodel_reset() before calling
+* fluid_revmodel_set().
+*
* @param rev Reverb instance.
* @param set One or more flags from #fluid_revmodel_set_t indicating what
* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters).
@@ -1084,31 +1185,43 @@ fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
/*
* Applies a sample rate change on the reverb.
+* 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:
+* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX().
+* reverb functions.
+* 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.
+*
+* Another solution is to substitute step (2):
+* 2.1) delete the reverb by calling delete_fluid_revmodel().
+* 2.2) create the reverb by calling new_fluid_revmodel().
+*
* @param rev the reverb.
* @param sample_rate new sample rate value.
-*
+* @return FLUID_OK if success, FLUID_FAILED otherwise (memory error).
* Reverb API.
*/
-void
+int
fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate)
{
- int i;
+ rev->late.samplerate = sample_rate; /* new sample rate value */
- /* updates modulator frequency according to sample rate change */
- for(i = 0; i < NBR_DELAYS; i++)
+ /* free all delay lines */
+ delete_fluid_rev_late(&rev->late);
+
+ /* create all delay lines */
+ if(create_mod_delay_lines(&rev->late, sample_rate) == FLUID_FAILED)
{
- set_mod_frequency(&rev->late.mod_delay_lines[i].mod,
- MOD_FREQ * MOD_RATE,
- sample_rate,
- (float)(MOD_PHASE * i));
+ return FLUID_FAILED; /* memory error */
}
/* updates damping filter coefficients according to sample rate change */
- rev->late.samplerate = sample_rate;
update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
- /* clears all delay lines */
- fluid_revmodel_init(rev);
+ return FLUID_OK;
}
/*
diff --git a/libs/fluidsynth/src/fluid_rev.h b/libs/fluidsynth/src/fluid_rev.h
index 75ab5d23c4..b24fd5fd78 100644
--- a/libs/fluidsynth/src/fluid_rev.h
+++ b/libs/fluidsynth/src/fluid_rev.h
@@ -72,6 +72,6 @@ void fluid_revmodel_reset(fluid_revmodel_t *rev);
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);
-void fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate);
+int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate);
#endif /* _FLUID_REV_H */
diff --git a/libs/fluidsynth/src/fluid_rvoice.c b/libs/fluidsynth/src/fluid_rvoice.c
index 76e5579257..8837e4415d 100644
--- a/libs/fluidsynth/src/fluid_rvoice.c
+++ b/libs/fluidsynth/src/fluid_rvoice.c
@@ -26,7 +26,7 @@
static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks);
/**
- * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
+ * @return -1 if voice is quiet, 0 if voice has finished, 1 otherwise
*/
static FLUID_INLINE int
fluid_rvoice_calc_amp(fluid_rvoice_t *voice)
@@ -357,7 +357,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
if(count <= 0)
{
- return count;
+ return count; /* return -1 if voice is quiet, 0 if voice has finished */
}
/******************* phase **********************/
diff --git a/libs/fluidsynth/src/fluid_rvoice_mixer.c b/libs/fluidsynth/src/fluid_rvoice_mixer.c
index 5ea08ff6fa..d7fd2f541f 100644
--- a/libs/fluidsynth/src/fluid_rvoice_mixer.c
+++ b/libs/fluidsynth/src/fluid_rvoice_mixer.c
@@ -359,14 +359,14 @@ get_dest_buf(fluid_rvoice_buffers_t *buffers, int index,
}
/**
- * Mix data down to buffers
+ * Mix samples down from internal dsp_buf to output buffers
*
* @param buffers Destination buffer(s)
* @param dsp_buf Mono sample source
- * @param start_block Block to start mixing at
+ * @param start_block starting sample in dsp_buf
* @param sample_count number of samples to mix following \c start_block
* @param dest_bufs Array of buffers to mixdown to
- * @param dest_bufcount Length of dest_bufs
+ * @param dest_bufcount Length of dest_bufs (i.e count of buffers)
*/
static void
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
@@ -374,9 +374,11 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
int start_block, int sample_count,
fluid_real_t **dest_bufs, int dest_bufcount)
{
+ /* buffers count to mixdown to */
int bufcount = buffers->count;
int i, dsp_i;
+ /* if there is nothing to mix, return immediatly */
if(sample_count <= 0 || dest_bufcount <= 0)
{
return;
@@ -385,6 +387,7 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0);
FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0);
+ /* mixdown for each buffer */
for(i = 0; i < bufcount; i++)
{
fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
@@ -397,11 +400,17 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,
FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0);
+ /* mixdown sample_count samples in the current buffer buf
+ Note, that this loop could be unrolled by FLUID_BUFSIZE elements */
#pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)
-
- for(dsp_i = (start_block * FLUID_BUFSIZE); dsp_i < sample_count; dsp_i++)
+ for(dsp_i = 0; dsp_i < sample_count; dsp_i++)
{
- buf[dsp_i] += amp * dsp_buf[dsp_i];
+ // Index by blocks (not by samples) to let the compiler know that we always start accessing
+ // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere
+ // in between.
+ // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing
+ // this loop. Great.
+ buf[start_block * FLUID_BUFSIZE + dsp_i] += amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i];
}
}
}
@@ -416,30 +425,42 @@ fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers,
fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs,
unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount)
{
- int i, total_samples = 0, start_block = 0;
+ int i, total_samples = 0, last_block_mixed = 0;
for(i = 0; i < blockcount; i++)
{
+ /* render one block in src_buf */
int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]);
-
if(s == -1)
{
- start_block += s;
- s = FLUID_BUFSIZE;
- }
+ /* the voice is silent, mix back all the previously rendered sound */
+ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed,
+ total_samples - (last_block_mixed*FLUID_BUFSIZE),
+ dest_bufs, dest_bufcount);
- total_samples += s;
-
- if(s < FLUID_BUFSIZE)
+ last_block_mixed = i+1; /* future block start index to mix from */
+ total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */
+ }
+ else
{
- break;
+ /* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */
+ total_samples += s;
+ if(s < FLUID_BUFSIZE)
+ {
+ /* voice has finished */
+ break;
+ }
}
}
- fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, -start_block, total_samples - ((-start_block)*FLUID_BUFSIZE), dest_bufs, dest_bufcount);
+ /* Now mix the remaining blocks from last_block_mixed to total_sample */
+ fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed,
+ total_samples - (last_block_mixed*FLUID_BUFSIZE),
+ dest_bufs, dest_bufcount);
if(total_samples < blockcount * FLUID_BUFSIZE)
{
+ /* voice has finished */
fluid_finish_rvoice(buffers, rvoice);
}
}
@@ -601,7 +622,7 @@ fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount)
static int
fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer)
{
- const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT;
+ static const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT;
buffers->mixer = mixer;
buffers->buf_count = mixer->buffers.buf_count;
diff --git a/libs/fluidsynth/src/fluid_sffile.c b/libs/fluidsynth/src/fluid_sffile.c
index 5fa34297aa..131b56b0c2 100644
--- a/libs/fluidsynth/src/fluid_sffile.c
+++ b/libs/fluidsynth/src/fluid_sffile.c
@@ -30,6 +30,10 @@
#include <sndfile.h>
#endif
+#if LIBINSTPATCH_SUPPORT
+#include <libinstpatch/libinstpatch.h>
+#endif
+
/*=================================sfload.c========================
Borrowed from Smurf SoundFont Editor by Josh Green
=================================================================*/
@@ -326,11 +330,13 @@ static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int en
/**
* Check if a file is a SoundFont file.
+ *
+ * If fluidsynth was built with DLS support, this function will also identify DLS files.
* @param filename Path to the file to check
- * @return TRUE if it could be a SoundFont, FALSE otherwise
+ * @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise
*
- * @note The current implementation only checks for the "RIFF" and "sfbk" headers in
- * the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
+ * @note This function only checks whether header(s) in the RIFF chunk are present.
+ * A call to fluid_synth_sfload() might still fail.
*/
int fluid_is_soundfont(const char *filename)
{
@@ -344,7 +350,7 @@ int fluid_is_soundfont(const char *filename)
{
return retcode;
}
-
+
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{
break;
@@ -366,6 +372,21 @@ int fluid_is_soundfont(const char *filename)
}
retcode = (fcc == SFBK_FCC);
+ if(retcode)
+ {
+ break; // seems to be SF2, stop here
+ }
+#ifdef LIBINSTPATCH_SUPPORT
+ else
+ {
+ IpatchFileHandle *fhandle = ipatch_file_identify_open(filename, NULL);
+ if(fhandle != NULL)
+ {
+ retcode = (ipatch_file_identify(fhandle->file, NULL) == IPATCH_TYPE_DLS_FILE);
+ ipatch_file_close(fhandle);
+ }
+ }
+#endif
}
while(0);
@@ -775,7 +796,7 @@ static int process_info(SFData *sf, int size)
if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
{
FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes",
- &chunk.id, chunk.size);
+ (char*)&chunk.id, chunk.size);
return FALSE;
}
@@ -909,19 +930,19 @@ static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChu
if(chunk->id != expid)
{
- FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", &expid);
+ FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", (char*)&expid);
return FALSE;
}
if(chunk->size % reclen) /* valid chunk size? */
{
- FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", &expid, reclen);
+ FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", (char*)&expid, reclen);
return FALSE;
}
if((*size -= chunk->size) < 0)
{
- FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", &expid);
+ FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", (char*)&expid);
return FALSE;
}
@@ -2580,7 +2601,7 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
sfdata.end = end_byte;
sfdata.offset = 0;
- memset(&sfinfo, 0, sizeof(sfinfo));
+ FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo));
/* Seek to beginning of Ogg Vorbis data in Soundfont */
if(sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED)
@@ -2594,12 +2615,12 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
if(!sndfile)
{
- FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
+ FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile));
return -1;
}
// Empty sample
- if(!sfinfo.frames || !sfinfo.channels)
+ if(sfinfo.frames <= 0 || sfinfo.channels <= 0)
{
FLUID_LOG(FLUID_DBG, "Empty decompressed sample");
*data = NULL;
@@ -2607,7 +2628,12 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
return 0;
}
- /* FIXME: ensure that the decompressed WAV data is 16-bit mono? */
+ // Mono sample
+ if(sfinfo.channels != 1)
+ {
+ FLUID_LOG(FLUID_DBG, "Unsupported channel count %d in ogg sample", sfinfo.channels);
+ goto error_exit;
+ }
wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels);
@@ -2617,11 +2643,11 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
goto error_exit;
}
- /* Automatically decompresses the Ogg Vorbis data to 16-bit WAV */
+ /* Automatically decompresses the Ogg Vorbis data to 16-bit PCM */
if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames)
{
FLUID_LOG(FLUID_DBG, "Decompression failed!");
- FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
+ FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile));
goto error_exit;
}
diff --git a/libs/fluidsynth/src/fluid_sfont.c b/libs/fluidsynth/src/fluid_sfont.c
index 4780c7833f..35c0f36356 100644
--- a/libs/fluidsynth/src/fluid_sfont.c
+++ b/libs/fluidsynth/src/fluid_sfont.c
@@ -609,7 +609,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
goto error_rec;
}
- FLUID_MEMSET(sample->data, 0, storedNbFrames);
+ FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short));
FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
if(data24 != NULL)
@@ -628,7 +628,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
/* pointers */
/* all from the start of data */
sample->start = SAMPLE_LOOP_MARGIN;
- sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1;
+ sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1;
}
else
{
diff --git a/libs/fluidsynth/src/fluid_synth.c b/libs/fluidsynth/src/fluid_synth.c
index 570a2535cd..382979f7f5 100644
--- a/libs/fluidsynth/src/fluid_synth.c
+++ b/libs/fluidsynth/src/fluid_synth.c
@@ -117,7 +117,6 @@ static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *
/* Callback handlers for real-time settings */
-static void fluid_synth_handle_sample_rate(void *data, const char *name, double value);
static void fluid_synth_handle_gain(void *data, const char *name, double value);
static void fluid_synth_handle_polyphony(void *data, const char *name, int value);
static void fluid_synth_handle_device_id(void *data, const char *name, int value);
@@ -197,7 +196,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED);
fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0);
fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0);
- fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.0f, 0);
+ fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1f, 5.0f, 0);
fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0);
fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED);
@@ -431,6 +430,10 @@ fluid_synth_init(void)
/* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */
+ /* Initial Pitch is not a "standard" generator, because it isn't mentioned in the
+ list of generators in the SF2 specifications. That's why destination Initial Pitch
+ is replaced here by fine tune generator.
+ */
fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */
FLUID_MOD_GC /* CC =0 */
| FLUID_MOD_LINEAR /* type=0 */
@@ -443,7 +446,8 @@ fluid_synth_init(void)
| FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=0 */
);
- fluid_mod_set_dest(&default_pitch_bend_mod, GEN_PITCH); /* Destination: Initial pitch */
+ /* Also see the comment in gen.h about GEN_PITCH */
+ fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */
fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */
@@ -589,8 +593,10 @@ static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_syn
* @param settings Configuration parameters to use (used directly).
* @return New FluidSynth instance or NULL on error
*
- * @note The settings parameter is used directly and should not be modified
- * or freed independently.
+ * @note The @p settings parameter is used directly and should freed after
+ * the synth has been deleted. Further note that you may modify FluidSettings of the
+ * @p settings instance. However, only those FluidSettings marked as 'realtime' will
+ * affect the synth immediately.
*/
fluid_synth_t *
new_fluid_synth(fluid_settings_t *settings)
@@ -647,8 +653,6 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important);
/* register the callbacks */
- fluid_settings_callback_num(settings, "synth.sample-rate",
- fluid_synth_handle_sample_rate, synth);
fluid_settings_callback_num(settings, "synth.gain",
fluid_synth_handle_gain, synth);
fluid_settings_callback_int(settings, "synth.polyphony",
@@ -1126,6 +1130,10 @@ fluid_synth_error(fluid_synth_t *synth)
/**
* Send a note-on event to a FluidSynth object.
+ *
+ * This function will take care of proper legato playing. If a note on channel @p chan is
+ * already playing at the given key @p key, it will be released (even if it is sustained).
+ * In other words, overlapping notes are not allowed.
* @param synth FluidSynth instance
* @param chan MIDI channel number (0 to MIDI channel count - 1)
* @param key MIDI note number (0-127)
@@ -3046,20 +3054,31 @@ fluid_synth_update_presets(fluid_synth_t *synth)
}
}
-/* Handler for synth.sample-rate setting. */
-static void
-fluid_synth_handle_sample_rate(void *data, const char *name, double value)
-{
- fluid_synth_t *synth = (fluid_synth_t *)data;
- fluid_synth_set_sample_rate(synth, (float) value);
-}
-
-
/**
- * Set sample rate of the synth.
- * @note This function should only be used when no voices or notes are active.
+ * Set up an event to change the sample-rate of the synth during the next rendering call.
+ * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth.
+ * @deprecated As of fluidsynth 2.1.0 this function has been deprecated.
+ * Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized).
+ * The sample-rate change may also require memory allocation deep down in the effect units.
+ * However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering.
+ * This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block.
+ * Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change.
+ * Long story short: don't use it.
+ * @code{.cpp}
+ fluid_synth_t* synth; // assume initialized
+ // [...]
+ // sample-rate change needed? Delete the audio driver, if any.
+ delete_fluid_audio_driver(adriver);
+ // then delete the synth
+ delete_fluid_synth(synth);
+ // update the sample-rate
+ fluid_settings_setnum(settings, "synth.sample-rate", 22050.0);
+ // and re-create objects
+ synth = new_fluid_synth(settings);
+ adriver = new_fluid_audio_driver(settings, synth);
+ * @endcode
* @param synth FluidSynth instance
- * @param sample_rate New sample rate (Hz)
+ * @param sample_rate New sample-rate (Hz)
* @since 1.1.2
*/
void
@@ -3382,6 +3401,8 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(left != NULL, FLUID_FAILED);
fluid_return_val_if_fail(right != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
+ fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
/* First, take what's still available in the buffer */
count = 0;
@@ -3666,6 +3687,8 @@ fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED);
fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED);
+ fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
+ fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
nfxchan = synth->effects_channels;
nfxunits = synth->effects_groups;
@@ -3798,9 +3821,19 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
void *lout, int loff, int lincr,
void *rout, int roff, int rincr)
{
- int i, j, k, l;
- float *left_out = (float *) lout;
- float *right_out = (float *) rout;
+ return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks);
+}
+
+int
+fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
+ void *lout, int loff, int lincr,
+ void *rout, int roff, int rincr,
+ int (*block_render_func)(fluid_synth_t *, int)
+ )
+{
+ int n, cur, size;
+ float *left_out = (float *) lout + loff;
+ float *right_out = (float *) rout + roff;
fluid_real_t *left_in;
fluid_real_t *right_in;
double time = fluid_utime();
@@ -3811,28 +3844,60 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
+ fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
- l = synth->cur;
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
- for(i = 0, j = loff, k = roff; i < len; i++, l++, j += lincr, k += rincr)
+ size = len;
+ cur = synth->cur;
+
+ do
{
/* fill up the buffers as needed */
- if(l >= synth->curmax)
+ if(cur >= synth->curmax)
{
- int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
- synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
+ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
+ synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+ cur = 0;
+ }
+
+ /* calculate amount of available samples */
+ n = synth->curmax - cur;
- l = 0;
+ /* keep track of emitted samples */
+ if(n > size)
+ {
+ n = size;
}
- left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
- right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l];
+ size -= n;
+
+ /* update pointers to current position */
+ left_in += cur + n;
+ right_in += cur + n;
+
+ /* set final cursor position */
+ cur += n;
+
+ /* reverse index */
+ n = 0 - n;
+
+ do
+ {
+ *left_out = (float) left_in[n];
+ *right_out = (float) right_in[n];
+
+ left_out += lincr;
+ right_out += rincr;
+ }
+ while(++n < 0);
}
+ while(size);
- synth->cur = l;
+ synth->cur = cur;
time = fluid_utime() - time;
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0);
@@ -3924,43 +3989,77 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len,
void *lout, int loff, int lincr,
void *rout, int roff, int rincr)
{
- int i, j, k, cur;
- int16_t *left_out = lout;
- int16_t *right_out = rout;
+ int di, n, cur, size;
+ int16_t *left_out = (int16_t *)lout + loff;
+ int16_t *right_out = (int16_t *)rout + roff;
fluid_real_t *left_in;
fluid_real_t *right_in;
double time = fluid_utime();
- int di;
float cpu_load;
fluid_profile_ref_var(prof_ref);
+ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(len >= 0, FLUID_FAILED);
+ fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below
+
fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 1);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
+ size = len;
cur = synth->cur;
di = synth->dither_index;
- for(i = 0, j = loff, k = roff; i < len; i++, cur++, j += lincr, k += rincr)
+ do
{
-
/* fill up the buffers as needed */
if(cur >= synth->curmax)
{
- int blocksleft = (len - i + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
+ int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE;
synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
cur = 0;
}
- left_out[j] = round_clip_to_i16(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]);
- right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]);
+ /* calculate amount of available samples */
+ n = synth->curmax - cur;
- if(++di >= DITHER_SIZE)
+ /* keep track of emitted samples */
+ if(n > size)
{
- di = 0;
+ n = size;
}
+
+ size -= n;
+
+ /* update pointers to current position */
+ left_in += cur + n;
+ right_in += cur + n;
+
+ /* set final cursor position */
+ cur += n;
+
+ /* reverse index */
+ n = 0 - n;
+
+ do
+ {
+ *left_out = round_clip_to_i16(left_in[n] * 32766.0f + rand_table[0][di]);
+ *right_out = round_clip_to_i16(right_in[n] * 32766.0f + rand_table[1][di]);
+
+ left_out += lincr;
+ right_out += rincr;
+
+ if(++di >= DITHER_SIZE)
+ {
+ di = 0;
+ }
+ }
+ while(++n < 0);
}
+ while(size);
synth->cur = cur;
synth->dither_index = di; /* keep dither buffer continous */
@@ -5185,9 +5284,9 @@ fluid_synth_set_chorus_on(fluid_synth_t *synth, int on)
* @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.29-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 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)
* @return #FLUID_OK on success, #FLUID_FAILED otherwise
*/
@@ -5243,19 +5342,6 @@ int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type)
return fluid_synth_set_chorus_full(synth, FLUID_CHORUS_SET_TYPE, 0, 0, 0, 0, type);
}
-/**
- * Set one or more chorus parameters.
- * @param synth FluidSynth 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.29-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)
- * @return #FLUID_OK on success, #FLUID_FAILED otherwise
- */
int
fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
double speed, double depth_ms, int type)
@@ -5312,7 +5398,7 @@ fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
/**
* Get chorus voice number (delay line count) value.
* @param synth FluidSynth instance
- * @return Chorus voice count (0-99)
+ * @return Chorus voice count
*/
int
fluid_synth_get_chorus_nr(fluid_synth_t *synth)
@@ -5328,7 +5414,7 @@ fluid_synth_get_chorus_nr(fluid_synth_t *synth)
/**
* Get chorus level.
* @param synth FluidSynth instance
- * @return Chorus level value (0.0-10.0)
+ * @return Chorus level value
*/
double
fluid_synth_get_chorus_level(fluid_synth_t *synth)
@@ -5344,7 +5430,7 @@ fluid_synth_get_chorus_level(fluid_synth_t *synth)
/**
* Get chorus speed in Hz.
* @param synth FluidSynth instance
- * @return Chorus speed in Hz (0.29-5.0)
+ * @return Chorus speed in Hz
*/
double
fluid_synth_get_chorus_speed(fluid_synth_t *synth)
diff --git a/libs/fluidsynth/src/fluid_synth.h b/libs/fluidsynth/src/fluid_synth.h
index fe222f7b6f..955b3fa12e 100644
--- a/libs/fluidsynth/src/fluid_synth.h
+++ b/libs/fluidsynth/src/fluid_synth.h
@@ -216,6 +216,11 @@ int fluid_synth_set_gen2(fluid_synth_t *synth, int chan,
int
fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[],
int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int));
+int
+fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len,
+ void *lout, int loff, int lincr,
+ void *rout, int roff, int rincr,
+ int (*block_render_func)(fluid_synth_t *, int));
/*
* misc
*/
diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c
index f6c56d3995..c6300b90c2 100644
--- a/libs/fluidsynth/src/fluid_sys.c
+++ b/libs/fluidsynth/src/fluid_sys.c
@@ -192,6 +192,29 @@ fluid_log(int level, const char *fmt, ...)
return FLUID_FAILED;
}
+void* fluid_alloc(size_t len)
+{
+ void* ptr = malloc(len);
+
+#if defined(DEBUG) && !defined(_MSC_VER)
+ // garbage initialize allocated memory for debug builds to ease reproducing
+ // bugs like 44453ff23281b3318abbe432fda90888c373022b .
+ //
+ // MSVC++ already garbage initializes allocated memory by itself (debug-heap).
+ //
+ // 0xCC because
+ // * it makes pointers reliably crash when dereferencing them,
+ // * floating points are still some valid but insanely huge negative number, and
+ // * if for whatever reason this allocated memory is executed, it'll trigger
+ // INT3 (...at least on x86)
+ if(ptr != NULL)
+ {
+ memset(ptr, 0xCC, len);
+ }
+#endif
+ return ptr;
+}
+
/**
* Convenience wrapper for free() that satisfies at least C90 requirements.
* Especially useful when using fluidsynth with programming languages that do not provide malloc() and free().
@@ -291,22 +314,21 @@ void fluid_msleep(unsigned int msecs)
/**
* Get time in milliseconds to be used in relative timing operations.
- * @return Unix time in milliseconds.
+ * @return Monotonic time in milliseconds.
*/
unsigned int fluid_curtime(void)
{
- static glong initial_seconds = 0;
- GTimeVal timeval;
+ float now;
+ static float initial_time = 0;
- if(initial_seconds == 0)
+ if(initial_time == 0)
{
- g_get_current_time(&timeval);
- initial_seconds = timeval.tv_sec;
+ initial_time = (float)fluid_utime();
}
- g_get_current_time(&timeval);
+ now = (float)fluid_utime();
- return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
+ return (unsigned int)((now - initial_time) / 1000.0f);
}
/**
@@ -1460,7 +1482,7 @@ static fluid_thread_return_t fluid_server_socket_run(void *data)
{
if(server_socket->cont)
{
- FLUID_LOG(FLUID_ERR, "Failed to accept connection: %ld", fluid_socket_get_error());
+ FLUID_LOG(FLUID_ERR, "Failed to accept connection: %d", fluid_socket_get_error());
}
server_socket->cont = 0;
@@ -1519,7 +1541,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
if(sock == INVALID_SOCKET)
{
- FLUID_LOG(FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error());
fluid_socket_cleanup();
return NULL;
}
@@ -1534,7 +1556,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
if(sock == INVALID_SOCKET)
{
- FLUID_LOG(FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error());
fluid_socket_cleanup();
return NULL;
}
@@ -1547,7 +1569,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR)
{
- FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %ld", fluid_socket_get_error());
+ FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error());
fluid_socket_close(sock);
fluid_socket_cleanup();
return NULL;
@@ -1555,7 +1577,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
if(listen(sock, SOMAXCONN) == SOCKET_ERROR)
{
- FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %ld", fluid_socket_get_error());
+ FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error());
fluid_socket_close(sock);
fluid_socket_cleanup();
return NULL;
diff --git a/libs/fluidsynth/src/fluid_sys.h b/libs/fluidsynth/src/fluid_sys.h
index 3bc0c5a40f..b747e5676a 100644
--- a/libs/fluidsynth/src/fluid_sys.h
+++ b/libs/fluidsynth/src/fluid_sys.h
@@ -124,6 +124,8 @@ typedef gint32 int32_t;
typedef guint32 uint32_t;
typedef gint64 int64_t;
typedef guint64 uint64_t;
+typedef guintptr uintptr_t;
+typedef gintptr intptr_t;
#endif
@@ -168,10 +170,14 @@ typedef guint64 uint64_t;
#define FLUID_INLINE inline
-#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT
-#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER
-#define FLUID_POINTER_TO_INT GPOINTER_TO_INT
-#define FLUID_INT_TO_POINTER GINT_TO_POINTER
+
+/* Integer<->pointer conversion */
+#define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x))
+#define FLUID_UINT_TO_POINTER(x) ((void *)(uintptr_t)(x))
+#define FLUID_POINTER_TO_INT(x) ((signed int)(intptr_t)(x))
+#define FLUID_INT_TO_POINTER(x) ((void *)(intptr_t)(x))
+
+/* Endian detection */
#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
@@ -198,6 +204,11 @@ char *fluid_strtok(char **str, const char *delim);
typedef int socklen_t;
#endif
+/**
+ Time functions
+
+ */
+
unsigned int fluid_curtime(void);
double fluid_utime(void);
diff --git a/libs/fluidsynth/src/fluid_voice.c b/libs/fluidsynth/src/fluid_voice.c
index 85a2d43e69..d961ae4a7c 100644
--- a/libs/fluidsynth/src/fluid_voice.c
+++ b/libs/fluidsynth/src/fluid_voice.c
@@ -825,7 +825,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj;
}
- x = (fluid_ct2hz(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
+ x = (fluid_ct2hz_real(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate));
}
else
{
@@ -838,7 +838,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
voice->root_pitch = 0;
}
- x = fluid_ct2hz(voice->root_pitch);
+ x = fluid_ct2hz_real(voice->root_pitch);
}
/* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
diff --git a/libs/fluidsynth/src/fluidsynth_priv.h b/libs/fluidsynth/src/fluidsynth_priv.h
index 1567a10a8c..71569bd045 100644
--- a/libs/fluidsynth/src/fluidsynth_priv.h
+++ b/libs/fluidsynth/src/fluidsynth_priv.h
@@ -181,12 +181,14 @@ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t pa
#endif
/* Memory allocation */
-#define FLUID_MALLOC(_n) malloc(_n)
+#define FLUID_MALLOC(_n) fluid_alloc(_n)
#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
-#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
-#define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)malloc((_n)*sizeof(_t) + ((unsigned int)_a - 1u))
-#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u)
#define FLUID_FREE(_p) fluid_free(_p)
+#define FLUID_NEW(_t) (_t*)FLUID_MALLOC(sizeof(_t))
+#define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)FLUID_MALLOC((_n)*sizeof(_t) + ((unsigned int)_a - 1u))
+#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u)
+
+void* fluid_alloc(size_t len);
/* File access */
#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
@@ -273,7 +275,7 @@ do { strncpy(_dst,_src,_n); \
#define FLUID_LOG fluid_log
#endif
-#ifdef DEBUG
+#if defined(DEBUG) && !defined(NDEBUG)
#define FLUID_ASSERT(a) g_assert(a)
#else
#define FLUID_ASSERT(a)
diff --git a/tools/fluid-patches/ardour_fluidsynth.diff b/tools/fluid-patches/ardour_fluidsynth.diff
index 68251eb522..8aaf597687 100644
--- a/tools/fluid-patches/ardour_fluidsynth.diff
+++ b/tools/fluid-patches/ardour_fluidsynth.diff
@@ -1,7 +1,16 @@
diff --git b/libs/fluidsynth/fluidsynth/synth.h a/libs/fluidsynth/fluidsynth/synth.h
-index 369a2c2615..87826809fd 100644
+index f4802ee5b9..3003972542 100644
--- b/libs/fluidsynth/fluidsynth/synth.h
+++ a/libs/fluidsynth/fluidsynth/synth.h
+@@ -176,7 +176,7 @@ FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth);
+
+ /* Synthesis parameters */
+
+-FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate);
++FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate);
+ FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain);
+ FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth);
+ FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony);
@@ -233,7 +233,7 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p
/* Misc */
@@ -78,7 +87,7 @@ index 946a873bbf..79f83a4583 100644
/*
* fluid_hashtable_foreach_remove_or_steal:
diff --git b/libs/fluidsynth/src/fluid_midi.c a/libs/fluidsynth/src/fluid_midi.c
-index cd0d23ced3..c92fb2fefa 100644
+index ea1aff5202..844de01c8f 100644
--- b/libs/fluidsynth/src/fluid_midi.c
+++ a/libs/fluidsynth/src/fluid_midi.c
@@ -77,7 +77,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf);
@@ -139,7 +148,7 @@ index 3e7661741f..ec8e967a35 100644
#ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod);
diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.c a/libs/fluidsynth/src/fluid_rvoice_mixer.c
-index 85c3fb9142..5ea08ff6fa 100644
+index 257f0fbdec..d7fd2f541f 100644
--- b/libs/fluidsynth/src/fluid_rvoice_mixer.c
+++ a/libs/fluidsynth/src/fluid_rvoice_mixer.c
@@ -23,7 +23,6 @@
@@ -191,7 +200,7 @@ index 78532ad2a3..a825603a44 100644
static int
diff --git b/libs/fluidsynth/src/fluid_synth.c a/libs/fluidsynth/src/fluid_synth.c
-index 3f602b88d1..570a2535cd 100644
+index e03c64089f..382979f7f5 100644
--- b/libs/fluidsynth/src/fluid_synth.c
+++ a/libs/fluidsynth/src/fluid_synth.c
@@ -25,7 +25,6 @@
@@ -202,7 +211,7 @@ index 3f602b88d1..570a2535cd 100644
#ifdef TRAP_ON_FPE
#define _GNU_SOURCE
-@@ -263,7 +262,7 @@ void fluid_version(int *major, int *minor, int *micro)
+@@ -262,7 +261,7 @@ void fluid_version(int *major, int *minor, int *micro)
* @return FluidSynth version string, which is internal and should not be
* modified or freed.
*/
@@ -211,7 +220,7 @@ index 3f602b88d1..570a2535cd 100644
fluid_version_str(void)
{
return FLUIDSYNTH_VERSION;
-@@ -5472,7 +5471,7 @@ fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method)
+@@ -5558,7 +5557,7 @@ fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method)
}
FLUID_API_RETURN(FLUID_OK);
@@ -220,7 +229,7 @@ index 3f602b88d1..570a2535cd 100644
/**
* Get the total count of MIDI channels.
-@@ -6410,6 +6409,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)
+@@ -6496,6 +6495,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type)
FLUID_API_RETURN(FLUID_OK);
}
@@ -228,7 +237,7 @@ index 3f602b88d1..570a2535cd 100644
/**
* Return the LADSPA effects instance used by FluidSynth
*
-@@ -6422,6 +6422,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
+@@ -6508,6 +6508,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
return synth->ladspa_fx;
}
@@ -237,7 +246,7 @@ index 3f602b88d1..570a2535cd 100644
/**
* Configure a general-purpose IIR biquad filter.
diff --git b/libs/fluidsynth/src/fluid_synth.h a/libs/fluidsynth/src/fluid_synth.h
-index 0b9758bad5..fe222f7b6f 100644
+index b649bcf340..955b3fa12e 100644
--- b/libs/fluidsynth/src/fluid_synth.h
+++ a/libs/fluidsynth/src/fluid_synth.h
@@ -33,8 +33,6 @@
@@ -260,10 +269,10 @@ index 0b9758bad5..fe222f7b6f 100644
enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */
};
diff --git b/libs/fluidsynth/src/fluid_sys.c a/libs/fluidsynth/src/fluid_sys.c
-index 8128fbba91..f6c56d3995 100644
+index 7454217dd2..c6300b90c2 100644
--- b/libs/fluidsynth/src/fluid_sys.c
+++ a/libs/fluidsynth/src/fluid_sys.c
-@@ -216,9 +216,10 @@ void fluid_free(void* ptr)
+@@ -239,9 +239,10 @@ void fluid_free(void* ptr)
* @param delim String of delimiter chars.
* @return Pointer to the next token or NULL if no more tokens.
*/
@@ -277,10 +286,10 @@ index 8128fbba91..f6c56d3995 100644
if(str == NULL || delim == NULL || !*delim)
diff --git b/libs/fluidsynth/src/fluid_sys.h a/libs/fluidsynth/src/fluid_sys.h
-index 6490359027..3bc0c5a40f 100644
+index 24df6edb5b..b747e5676a 100644
--- b/libs/fluidsynth/src/fluid_sys.h
+++ a/libs/fluidsynth/src/fluid_sys.h
-@@ -128,8 +128,9 @@ typedef guint64 uint64_t;
+@@ -130,8 +130,9 @@ typedef gintptr intptr_t;
#endif
#if defined(WIN32) && HAVE_WINDOWS_H
@@ -292,7 +301,7 @@ index 6490359027..3bc0c5a40f 100644
/* WIN32 special defines */
#define STDIN_FILENO 0
-@@ -187,7 +188,7 @@ typedef guint64 uint64_t;
+@@ -193,7 +194,7 @@ typedef gintptr intptr_t;
/*
* Utility functions
*/