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
...
This commit is contained in:
Robin Gareus 2019-12-02 23:58:15 +01:00
parent c5066dcf38
commit d425f6dcb5
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
23 changed files with 1329 additions and 619 deletions

View file

@ -1,7 +1,7 @@
This is a stripped down version of fluidsynth (library only) This is a stripped down version of fluidsynth (library only)
from git://github.com/FluidSynth/fluidsynth.git 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 fluidsynth is licensed in terms of the LGPL-2+, see individual source
files for (C) holders. files for (C) holders.

View file

@ -95,11 +95,22 @@ enum fluid_gen_type
GEN_EXCLUSIVECLASS, /**< Exclusive class number */ GEN_EXCLUSIVECLASS, /**< Exclusive class number */
GEN_OVERRIDEROOTKEY, /**< Sample root note override */ 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 * @brief Initial Pitch
* is used, however, as the destination for the default pitch wheel *
* modulator. */ * @note This is not "standard" SoundFont generator, because it is not
GEN_PITCH, /**< Pitch @note Not a real SoundFont generator */ * 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 */ GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */
/* non-standard generator for an additional custom high- or low-pass filter */ /* non-standard generator for an additional custom high- or low-pass filter */

View file

@ -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 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 #ifdef __cplusplus

View file

@ -32,13 +32,13 @@ extern "C" {
* @brief Embeddable SoundFont synthesizer * @brief Embeddable SoundFont synthesizer
* *
* You create a new synthesizer with new_fluid_synth() and you destroy * 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. * the synthesizer characteristics.
* *
* You have to load a SoundFont in order to hear any sound. For that * You have to load a SoundFont in order to hear any sound. For that
* you use the fluid_synth_sfload() function. * 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 audio device and create a background audio thread.
* *
* The API for sending MIDI events is probably what you expect: * 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 enum fluid_synth_add_mod
{ {
FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ 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); 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 enum fluid_iir_filter_flags
{ {

File diff suppressed because it is too large Load diff

View file

@ -49,7 +49,6 @@ typedef enum
*/ */
fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate); fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate);
void delete_fluid_chorus(fluid_chorus_t *chorus); 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_reset(fluid_chorus_t *chorus);
void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level,

View file

@ -23,66 +23,56 @@
#include "fluid_conv_tables.c" #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_real_t
fluid_ct2hz_real(fluid_real_t cents) fluid_ct2hz_real(fluid_real_t cents)
{ {
if(cents < 0) if(FLUID_UNLIKELY(cents < 0))
{ {
return (fluid_real_t) 1.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 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];
} }
} }

View file

@ -26,7 +26,7 @@
/* See SFSpec21 $8.1.3 */ /* See SFSpec21 $8.1.3 */
static const fluid_gen_info_t fluid_gen_info[] = 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_STARTADDROFS, 1, 1, 0.0f, 1e10f, 0.0f },
{ GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f }, { GEN_ENDADDROFS, 1, 1, -1e10f, 0.0f, 0.0f },
{ GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f }, { GEN_STARTLOOPADDROFS, 1, 1, -1e10f, 1e10f, 0.0f },

View file

@ -27,7 +27,7 @@
typedef struct _fluid_gen_info_t typedef struct _fluid_gen_info_t
{ {
char num; /* Generator number */ 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()) */ char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */
float min; /* The minimum value */ float min; /* The minimum value */
float max; /* The maximum value */ float max; /* The maximum value */

View file

@ -180,7 +180,7 @@ fluid_file_read_full(fluid_file fp, size_t *length)
return NULL; 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); buffer = FLUID_MALLOC(buflen);
if(buffer == NULL) if(buffer == NULL)
@ -193,7 +193,7 @@ fluid_file_read_full(fluid_file fp, size_t *length)
if(n != buflen) 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); buflen);
FLUID_FREE(buffer); FLUID_FREE(buffer);
return NULL; return NULL;

View file

@ -75,25 +75,26 @@
* the building of a lot of resonances in the reverberation tail even when * the building of a lot of resonances in the reverberation tail even when
* using only 8 delays lines (NBR_DELAYS = 8) (default). * using only 8 delays lines (NBR_DELAYS = 8) (default).
* *
* Although 8 lines give good result, using 12 delays lines brings the overall * The frequency density (often called "modal density" is one property that
* frequency density quality a bit higher. This quality augmentation is noticeable * contributes to sound quality. Although 8 lines give good result, using 12 delays
* particularly when using long reverb time (roomsize = 1) on solo instrument with * lines brings the overall frequency density quality a bit higher.
* long release time. Of course the cpu load augmentation is +50% relatively * This quality augmentation is noticeable particularly when using long reverb time
* to 8 lines. * (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 * As a general rule the reverberation tail quality is easier to perceive by ear
* when using: * when using:
* - percussive instruments (i.e piano and others). * - percussive instruments (i.e piano and others).
* - long reverb time (roomsize = 1). * - long reverb time (roomsize = 1).
* - no damping (damp = 0). * - 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%), * The cpu load for 8 lines is a bit lower than for freeverb (- 3%),
* but higher for 12 lines (+ 41%). * but higher for 12 lines (+ 41%).
* *
* *
* The memory consumption is less than for freeverb. This saves 147480 bytes * The memory consumption is less than for freeverb
* for 8 lines (- 72%) and 110152 bytes for 12 lines (- 54 %).
* (see the results table below). * (see the results table below).
* *
* Two macros are usable at compiler time: * Two macros are usable at compiler time:
@ -113,19 +114,23 @@
* Note: the cpu load in % are relative each to other. These values are * Note: the cpu load in % are relative each to other. These values are
* given by the fluidsynth profile commands. * given by the fluidsynth profile commands.
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* reverb | NBR_DELAYS | Performances | memory size | quality * reverb | NBR_DELAYS | Performances | memory size | quality
* | | (cpu_load: %) | (bytes) | * | | (cpu_load: %) | (bytes)(see note) |
* ========================================================================== * ==========================================================================
* freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing
* | 2 x 4 all-pass | | | * | 2 x 4 all-pass | | |
* ----------|--------------------------------------------------------------- * ----------|---------------------------------------------------------------
* FDN | 8 | 0.650 % | 57136 | far less * FDN | 8 | 0.650 % | 112160 | far less
* modulated | |(feeverb - 3%) |(freeverb - 72%) | ringing * modulated | |(feeverb - 3%) | (55% freeverb) | ringing
* |--------------------------------------------------------------- * |---------------------------------------------------------------
* | 12 | 0.942 % | 94464 | best than * | 12 | 0.942 % | 168240 | best than
* | |(freeverb + 41%) |(freeverb - 54%) | 8 lines * | |(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. * 'Denormalise' method to avoid loss of performance.
@ -173,6 +178,8 @@
roomsize parameter. roomsize parameter.
- DENORMALISING enable denormalising handling. - 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) /* Number of delay lines (must be only 8 or 12)
8 is the default. 8 is the default.
12 produces a better quality but is +50% cpu expensive 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 #define DELAY_L11 1187
#endif #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 /* The FDN late feed back matrix: A
@ -612,6 +609,16 @@ static int set_mod_delay_line(mod_delay_line *mdl,
return FLUID_OK; return FLUID_OK;
} }
/*-----------------------------------------------------------------------------
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. Reads the sample value out of the modulated delay line.
@param mdl, pointer on modulated delay line. @param mdl, pointer on modulated delay line.
@ -738,6 +745,7 @@ static void update_rev_time_damping(fluid_late *late,
{ {
int i; int i;
fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */ 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 dc_rev_time; /* Reverb time at 0 Hz (in seconds) */
fluid_real_t alpha, alpha2; fluid_real_t alpha, alpha2;
@ -756,8 +764,9 @@ static void update_rev_time_damping(fluid_late *late,
Computes dc_rev_time Computes dc_rev_time
------------------------------------------*/ ------------------------------------------*/
dc_rev_time = GET_DC_REV_TIME(roomsize); 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 */ /* 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 */ sample_period / dc_rev_time); /* E2 */
#else #else
/* roomsize parameters have the same response that Freeverb, that is: /* 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 Computes dc_rev_time
------------------------------------------*/ ------------------------------------------*/
fluid_real_t gi_min, gi_max; fluid_real_t gi_min, gi_max;
/* values gi_min et gi_max are computed using E2 for the line with /* values gi_min et gi_max are computed using E2 for the line with
maximum delay */ maximum delay */
gi_max = FLUID_POW(10, (-3 * delay_length[NBR_DELAYS - 1] / MAX_DC_REV_TIME) * delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]);
sample_period); /* E2 */ gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) *
gi_min = FLUID_POW(10, (-3 * delay_length[NBR_DELAYS - 1] / MIN_DC_REV_TIME) * sample_period); /* E2 */
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 = f(roomsize, gi_max, gi_min) */
gi_tmp = gi_min + roomsize * (gi_max - gi_min); gi_tmp = gi_min + roomsize * (gi_max - gi_min);
/* Computes T60DC from gi using inverse of relation E2.*/ /* 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 */ #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 */ /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */
for(i = 0; i < NBR_DELAYS; i++) 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 */ /* iir low pass filter gain */
fluid_real_t gi = FLUID_POW(10, -3 * delay_length[i] * gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time);
sample_period / dc_rev_time);
/* iir low pass filter feedback gain */ /* 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 */ /* b0 = gi * (1 - ai), a1 = - ai */
set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping, 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) static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1)
{ {
int i; int i;
fluid_real_t wet;
for(i = 0; i < NBR_DELAYS; i++) 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| 11|-1 -1|
*/ */
late->out_left_gain[i] = wet1; /* 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)
{
wet = -wet1;
}
late->out_left_gain[i] = wet;
/* Sets Left coefficients first */ /* for right line: 00,01, ,04,05, ,08,09, ,12,13 right_gain = +1 */
if(i % 2) /* Left is 1,-1, 1,-1, 1,-1,.... */ /* for right line: ,02 ,03, ,06,07, ,10,11,... right_gain = -1 */
wet = wet1;
if(i & 2)
{ {
late->out_left_gain[i] *= -1; wet = -wet1;
}
/* 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 */
{
/* Right is same as Left */
late->out_right_gain[i] = late->out_left_gain[i];
} }
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 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. @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 result; /* return value */
int i; int i;
FLUID_MEMSET(late, 0, sizeof(fluid_late)); /*
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.
late->samplerate = 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
First initialize the modulated delay lines 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;
for(i = 0; i < NBR_DELAYS; i++) fluid_real_t mod_depth = MOD_DEPTH;
if(sample_rate > 44100.0f)
{ {
/* sets local delay lines's parameters */ 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];
}
/* 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 */
{
/* allocate delay line and set local delay lines's parameters */
result = set_mod_delay_line(&late->mod_delay_lines[i], 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) 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, set_mod_frequency(&late->mod_delay_lines[i].mod,
MOD_FREQ * MOD_RATE, MOD_FREQ * MOD_RATE,
sample_rate, late->samplerate,
(float)(MOD_PHASE * i)); (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; return FLUID_OK;
} }
@ -982,7 +1065,7 @@ fluid_revmodel_update(fluid_revmodel_t *rev)
/* integrates wet1 in stereo coefficient (this will save one multiply) */ /* integrates wet1 in stereo coefficient (this will save one multiply) */
update_stereo_coefficient(&rev->late, rev->wet1); update_stereo_coefficient(&rev->late, rev->wet1);
if(rev->wet1 > 0.0) if(rev->wet1 > 0.0f)
{ {
rev->wet2 /= rev->wet1; 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. * @param sample_rate sample rate in Hz.
* @return pointer on the new reverb or NULL if memory error. * @return pointer on the new reverb or NULL if memory error.
* Reverb API. * Reverb API.
@ -1024,7 +1110,15 @@ new_fluid_revmodel(fluid_real_t sample_rate)
/* /*
* free the reverb. * 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. * Reverb API.
*/ */
void 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 rev Reverb instance.
* @param set One or more flags from #fluid_revmodel_set_t indicating what * @param set One or more flags from #fluid_revmodel_set_t indicating what
* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters). * 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. * 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 rev the reverb.
* @param sample_rate new sample rate value. * @param sample_rate new sample rate value.
* * @return FLUID_OK if success, FLUID_FAILED otherwise (memory error).
* Reverb API. * Reverb API.
*/ */
void int
fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) 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 */ /* free all delay lines */
for(i = 0; i < NBR_DELAYS; i++) 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, return FLUID_FAILED; /* memory error */
MOD_FREQ * MOD_RATE,
sample_rate,
(float)(MOD_PHASE * i));
} }
/* updates damping filter coefficients according to sample rate change */ /* updates damping filter coefficients according to sample rate change */
rev->late.samplerate = sample_rate;
update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); update_rev_time_damping(&rev->late, rev->roomsize, rev->damp);
/* clears all delay lines */ return FLUID_OK;
fluid_revmodel_init(rev);
} }
/* /*

View file

@ -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, void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize,
fluid_real_t damping, fluid_real_t width, fluid_real_t level); fluid_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 */ #endif /* _FLUID_REV_H */

View file

@ -26,7 +26,7 @@
static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks); 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 static FLUID_INLINE int
fluid_rvoice_calc_amp(fluid_rvoice_t *voice) 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) if(count <= 0)
{ {
return count; return count; /* return -1 if voice is quiet, 0 if voice has finished */
} }
/******************* phase **********************/ /******************* phase **********************/

View file

@ -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 buffers Destination buffer(s)
* @param dsp_buf Mono sample source * @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 sample_count number of samples to mix following \c start_block
* @param dest_bufs Array of buffers to mixdown to * @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 static void
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, 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, int start_block, int sample_count,
fluid_real_t **dest_bufs, int dest_bufcount) fluid_real_t **dest_bufs, int dest_bufcount)
{ {
/* buffers count to mixdown to */
int bufcount = buffers->count; int bufcount = buffers->count;
int i, dsp_i; int i, dsp_i;
/* if there is nothing to mix, return immediatly */
if(sample_count <= 0 || dest_bufcount <= 0) if(sample_count <= 0 || dest_bufcount <= 0)
{ {
return; 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 % FLUID_DEFAULT_ALIGNMENT == 0);
FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % 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++) for(i = 0; i < bufcount; i++)
{ {
fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); 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); 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) #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)
for(dsp_i = 0; dsp_i < sample_count; dsp_i++)
for(dsp_i = (start_block * FLUID_BUFSIZE); 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, fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs,
unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount) 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++) for(i = 0; i < blockcount; i++)
{ {
/* render one block in src_buf */
int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]);
if(s == -1) if(s == -1)
{ {
start_block += s; /* the voice is silent, mix back all the previously rendered sound */
s = FLUID_BUFSIZE; fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed,
total_samples - (last_block_mixed*FLUID_BUFSIZE),
dest_bufs, dest_bufcount);
last_block_mixed = i+1; /* future block start index to mix from */
total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */
} }
else
total_samples += s;
if(s < FLUID_BUFSIZE)
{ {
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) if(total_samples < blockcount * FLUID_BUFSIZE)
{ {
/* voice has finished */
fluid_finish_rvoice(buffers, rvoice); fluid_finish_rvoice(buffers, rvoice);
} }
} }
@ -601,7 +622,7 @@ fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount)
static int static int
fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer) 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->mixer = mixer;
buffers->buf_count = mixer->buffers.buf_count; buffers->buf_count = mixer->buffers.buf_count;

View file

@ -30,6 +30,10 @@
#include <sndfile.h> #include <sndfile.h>
#endif #endif
#if LIBINSTPATCH_SUPPORT
#include <libinstpatch/libinstpatch.h>
#endif
/*=================================sfload.c======================== /*=================================sfload.c========================
Borrowed from Smurf SoundFont Editor by Josh Green 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. * Check if a file is a SoundFont file.
* @param filename Path to the file to check
* @return TRUE if it could be a SoundFont, FALSE otherwise
* *
* @note The current implementation only checks for the "RIFF" and "sfbk" headers in * If fluidsynth was built with DLS support, this function will also identify DLS files.
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files. * @param filename Path to the file to check
* @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise
*
* @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) int fluid_is_soundfont(const char *filename)
{ {
@ -344,7 +350,7 @@ int fluid_is_soundfont(const char *filename)
{ {
return retcode; return retcode;
} }
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{ {
break; break;
@ -366,6 +372,21 @@ int fluid_is_soundfont(const char *filename)
} }
retcode = (fcc == SFBK_FCC); 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); 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)) 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", 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; return FALSE;
} }
@ -909,19 +930,19 @@ static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChu
if(chunk->id != expid) 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; return FALSE;
} }
if(chunk->size % reclen) /* valid chunk size? */ 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; return FALSE;
} }
if((*size -= chunk->size) < 0) 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; return FALSE;
} }
@ -2580,7 +2601,7 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
sfdata.end = end_byte; sfdata.end = end_byte;
sfdata.offset = 0; sfdata.offset = 0;
memset(&sfinfo, 0, sizeof(sfinfo)); FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo));
/* Seek to beginning of Ogg Vorbis data in Soundfont */ /* Seek to beginning of Ogg Vorbis data in Soundfont */
if(sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED) 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) if(!sndfile)
{ {
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile));
return -1; return -1;
} }
// Empty sample // Empty sample
if(!sfinfo.frames || !sfinfo.channels) if(sfinfo.frames <= 0 || sfinfo.channels <= 0)
{ {
FLUID_LOG(FLUID_DBG, "Empty decompressed sample"); FLUID_LOG(FLUID_DBG, "Empty decompressed sample");
*data = NULL; *data = NULL;
@ -2607,7 +2628,12 @@ static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigne
return 0; 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); 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; 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) if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames)
{ {
FLUID_LOG(FLUID_DBG, "Decompression failed!"); FLUID_LOG(FLUID_DBG, "Decompression failed!");
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile)); FLUID_LOG(FLUID_ERR, "%s", sf_strerror(sndfile));
goto error_exit; goto error_exit;
} }

View file

@ -609,7 +609,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
goto error_rec; 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)); FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short));
if(data24 != NULL) if(data24 != NULL)
@ -628,7 +628,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
/* pointers */ /* pointers */
/* all from the start of data */ /* all from the start of data */
sample->start = SAMPLE_LOOP_MARGIN; sample->start = SAMPLE_LOOP_MARGIN;
sample->end = SAMPLE_LOOP_MARGIN + storedNbFrames - 1; sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1;
} }
else else
{ {

View file

@ -117,7 +117,6 @@ static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *
/* Callback handlers for real-time settings */ /* 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_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_polyphony(void *data, const char *name, int value);
static void fluid_synth_handle_device_id(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.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_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.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_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); 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 ... */ /* 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_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */
FLUID_MOD_GC /* CC =0 */ FLUID_MOD_GC /* CC =0 */
| FLUID_MOD_LINEAR /* type=0 */ | FLUID_MOD_LINEAR /* type=0 */
@ -443,7 +446,8 @@ fluid_synth_init(void)
| FLUID_MOD_UNIPOLAR /* P=0 */ | FLUID_MOD_UNIPOLAR /* P=0 */
| FLUID_MOD_POSITIVE /* D=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 */ 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). * @param settings Configuration parameters to use (used directly).
* @return New FluidSynth instance or NULL on error * @return New FluidSynth instance or NULL on error
* *
* @note The settings parameter is used directly and should not be modified * @note The @p settings parameter is used directly and should freed after
* or freed independently. * 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 * fluid_synth_t *
new_fluid_synth(fluid_settings_t *settings) 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); fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important);
/* register the callbacks */ /* 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_settings_callback_num(settings, "synth.gain",
fluid_synth_handle_gain, synth); fluid_synth_handle_gain, synth);
fluid_settings_callback_int(settings, "synth.polyphony", 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. * 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 synth FluidSynth instance
* @param chan MIDI channel number (0 to MIDI channel count - 1) * @param chan MIDI channel number (0 to MIDI channel count - 1)
* @param key MIDI note number (0-127) * @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. * Set up an event to change the sample-rate of the synth during the next rendering call.
* @note This function should only be used when no voices or notes are active. * @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 synth FluidSynth instance
* @param sample_rate New sample rate (Hz) * @param sample_rate New sample-rate (Hz)
* @since 1.1.2 * @since 1.1.2
*/ */
void 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(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(left != 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(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 */ /* First, take what's still available in the buffer */
count = 0; 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(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(nfx % 2 == 0, 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(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; nfxchan = synth->effects_channels;
nfxunits = synth->effects_groups; 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 *lout, int loff, int lincr,
void *rout, int roff, int rincr) void *rout, int roff, int rincr)
{ {
int i, j, k, l; return fluid_synth_write_float_LOCAL(synth, len, lout, loff, lincr, rout, roff, rincr, fluid_synth_render_blocks);
float *left_out = (float *) lout; }
float *right_out = (float *) rout;
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 *left_in;
fluid_real_t *right_in; fluid_real_t *right_in;
double time = fluid_utime(); 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(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(lout != 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(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_set_mix_fx(synth->eventhandler->mixer, 1);
l = synth->cur;
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); 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 */ /* fill up the buffers as needed */
if(l >= synth->curmax) 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); synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
cur = 0;
l = 0;
} }
left_out[j] = (float) left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; /* calculate amount of available samples */
right_out[k] = (float) right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + l]; n = synth->curmax - cur;
}
synth->cur = l; /* keep track of emitted samples */
if(n > size)
{
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 = (float) left_in[n];
*right_out = (float) right_in[n];
left_out += lincr;
right_out += rincr;
}
while(++n < 0);
}
while(size);
synth->cur = cur;
time = fluid_utime() - time; time = fluid_utime() - time;
cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); 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 *lout, int loff, int lincr,
void *rout, int roff, int rincr) void *rout, int roff, int rincr)
{ {
int i, j, k, cur; int di, n, cur, size;
int16_t *left_out = lout; int16_t *left_out = (int16_t *)lout + loff;
int16_t *right_out = rout; int16_t *right_out = (int16_t *)rout + roff;
fluid_real_t *left_in; fluid_real_t *left_in;
fluid_real_t *right_in; fluid_real_t *right_in;
double time = fluid_utime(); double time = fluid_utime();
int di;
float cpu_load; float cpu_load;
fluid_profile_ref_var(prof_ref); 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_set_mix_fx(synth->eventhandler->mixer, 1);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
size = len;
cur = synth->cur; cur = synth->cur;
di = synth->dither_index; 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 */ /* fill up the buffers as needed */
if(cur >= synth->curmax) 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); synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft);
fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in);
cur = 0; 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]); /* calculate amount of available samples */
right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); 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->cur = cur;
synth->dither_index = di; /* keep dither buffer continous */ 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 * @param nr Chorus voice count (0-99, CPU time consumption proportional to
* this value) * this value)
* @param level Chorus level (0.0-10.0) * @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, * @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) * 0.0-21.0 is safe for sample-rate values up to 96KHz)
* @param type Chorus waveform type (#fluid_chorus_mod) * @param type Chorus waveform type (#fluid_chorus_mod)
* @return #FLUID_OK on success, #FLUID_FAILED otherwise * @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); 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 int
fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level, fluid_synth_set_chorus_full(fluid_synth_t *synth, int set, int nr, double level,
double speed, double depth_ms, int type) 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. * Get chorus voice number (delay line count) value.
* @param synth FluidSynth instance * @param synth FluidSynth instance
* @return Chorus voice count (0-99) * @return Chorus voice count
*/ */
int int
fluid_synth_get_chorus_nr(fluid_synth_t *synth) 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. * Get chorus level.
* @param synth FluidSynth instance * @param synth FluidSynth instance
* @return Chorus level value (0.0-10.0) * @return Chorus level value
*/ */
double double
fluid_synth_get_chorus_level(fluid_synth_t *synth) 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. * Get chorus speed in Hz.
* @param synth FluidSynth instance * @param synth FluidSynth instance
* @return Chorus speed in Hz (0.29-5.0) * @return Chorus speed in Hz
*/ */
double double
fluid_synth_get_chorus_speed(fluid_synth_t *synth) fluid_synth_get_chorus_speed(fluid_synth_t *synth)

View file

@ -216,6 +216,11 @@ int fluid_synth_set_gen2(fluid_synth_t *synth, int chan,
int int
fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], 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 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 * misc
*/ */

View file

@ -192,6 +192,29 @@ fluid_log(int level, const char *fmt, ...)
return FLUID_FAILED; 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. * 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(). * 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. * 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) unsigned int fluid_curtime(void)
{ {
static glong initial_seconds = 0; float now;
GTimeVal timeval; static float initial_time = 0;
if(initial_seconds == 0) if(initial_time == 0)
{ {
g_get_current_time(&timeval); initial_time = (float)fluid_utime();
initial_seconds = timeval.tv_sec;
} }
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) 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; 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) 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(); fluid_socket_cleanup();
return NULL; return NULL;
} }
@ -1534,7 +1556,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
if(sock == INVALID_SOCKET) 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(); fluid_socket_cleanup();
return NULL; 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) 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_close(sock);
fluid_socket_cleanup(); fluid_socket_cleanup();
return NULL; 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) 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_close(sock);
fluid_socket_cleanup(); fluid_socket_cleanup();
return NULL; return NULL;

View file

@ -124,6 +124,8 @@ typedef gint32 int32_t;
typedef guint32 uint32_t; typedef guint32 uint32_t;
typedef gint64 int64_t; typedef gint64 int64_t;
typedef guint64 uint64_t; typedef guint64 uint64_t;
typedef guintptr uintptr_t;
typedef gintptr intptr_t;
#endif #endif
@ -168,10 +170,14 @@ typedef guint64 uint64_t;
#define FLUID_INLINE inline #define FLUID_INLINE inline
#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT
#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER /* Integer<->pointer conversion */
#define FLUID_POINTER_TO_INT GPOINTER_TO_INT #define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x))
#define FLUID_INT_TO_POINTER GINT_TO_POINTER #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_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x) #define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
@ -198,6 +204,11 @@ char *fluid_strtok(char **str, const char *delim);
typedef int socklen_t; typedef int socklen_t;
#endif #endif
/**
Time functions
*/
unsigned int fluid_curtime(void); unsigned int fluid_curtime(void);
double fluid_utime(void); double fluid_utime(void);

View file

@ -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; 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 else
{ {
@ -838,7 +838,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
voice->root_pitch = 0; 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 */ /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */

View file

@ -181,12 +181,14 @@ typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t pa
#endif #endif
/* Memory allocation */ /* 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_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_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 */ /* File access */
#define FLUID_FOPEN(_f,_m) fopen(_f,_m) #define FLUID_FOPEN(_f,_m) fopen(_f,_m)
@ -273,7 +275,7 @@ do { strncpy(_dst,_src,_n); \
#define FLUID_LOG fluid_log #define FLUID_LOG fluid_log
#endif #endif
#ifdef DEBUG #if defined(DEBUG) && !defined(NDEBUG)
#define FLUID_ASSERT(a) g_assert(a) #define FLUID_ASSERT(a) g_assert(a)
#else #else
#define FLUID_ASSERT(a) #define FLUID_ASSERT(a)

View file

@ -1,7 +1,16 @@
diff --git b/libs/fluidsynth/fluidsynth/synth.h a/libs/fluidsynth/fluidsynth/synth.h 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 --- b/libs/fluidsynth/fluidsynth/synth.h
+++ a/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 @@ -233,7 +233,7 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p
/* Misc */ /* Misc */
@ -78,7 +87,7 @@ index 946a873bbf..79f83a4583 100644
/* /*
* fluid_hashtable_foreach_remove_or_steal: * fluid_hashtable_foreach_remove_or_steal:
diff --git b/libs/fluidsynth/src/fluid_midi.c a/libs/fluidsynth/src/fluid_midi.c 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 --- b/libs/fluidsynth/src/fluid_midi.c
+++ a/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); @@ -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 #ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod); 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 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 --- b/libs/fluidsynth/src/fluid_rvoice_mixer.c
+++ a/libs/fluidsynth/src/fluid_rvoice_mixer.c +++ a/libs/fluidsynth/src/fluid_rvoice_mixer.c
@@ -23,7 +23,6 @@ @@ -23,7 +23,6 @@
@ -191,7 +200,7 @@ index 78532ad2a3..a825603a44 100644
static int static int
diff --git b/libs/fluidsynth/src/fluid_synth.c a/libs/fluidsynth/src/fluid_synth.c 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 --- b/libs/fluidsynth/src/fluid_synth.c
+++ a/libs/fluidsynth/src/fluid_synth.c +++ a/libs/fluidsynth/src/fluid_synth.c
@@ -25,7 +25,6 @@ @@ -25,7 +25,6 @@
@ -202,7 +211,7 @@ index 3f602b88d1..570a2535cd 100644
#ifdef TRAP_ON_FPE #ifdef TRAP_ON_FPE
#define _GNU_SOURCE #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 * @return FluidSynth version string, which is internal and should not be
* modified or freed. * modified or freed.
*/ */
@ -211,7 +220,7 @@ index 3f602b88d1..570a2535cd 100644
fluid_version_str(void) fluid_version_str(void)
{ {
return FLUIDSYNTH_VERSION; 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); FLUID_API_RETURN(FLUID_OK);
@ -220,7 +229,7 @@ index 3f602b88d1..570a2535cd 100644
/** /**
* Get the total count of MIDI channels. * 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); FLUID_API_RETURN(FLUID_OK);
} }
@ -228,7 +237,7 @@ index 3f602b88d1..570a2535cd 100644
/** /**
* Return the LADSPA effects instance used by FluidSynth * 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; return synth->ladspa_fx;
} }
@ -237,7 +246,7 @@ index 3f602b88d1..570a2535cd 100644
/** /**
* Configure a general-purpose IIR biquad filter. * Configure a general-purpose IIR biquad filter.
diff --git b/libs/fluidsynth/src/fluid_synth.h a/libs/fluidsynth/src/fluid_synth.h 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 --- b/libs/fluidsynth/src/fluid_synth.h
+++ a/libs/fluidsynth/src/fluid_synth.h +++ a/libs/fluidsynth/src/fluid_synth.h
@@ -33,8 +33,6 @@ @@ -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 */ 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 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 --- b/libs/fluidsynth/src/fluid_sys.c
+++ a/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. * @param delim String of delimiter chars.
* @return Pointer to the next token or NULL if no more tokens. * @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) if(str == NULL || delim == NULL || !*delim)
diff --git b/libs/fluidsynth/src/fluid_sys.h a/libs/fluidsynth/src/fluid_sys.h 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 --- b/libs/fluidsynth/src/fluid_sys.h
+++ a/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 #endif
#if defined(WIN32) && HAVE_WINDOWS_H #if defined(WIN32) && HAVE_WINDOWS_H
@ -292,7 +301,7 @@ index 6490359027..3bc0c5a40f 100644
/* WIN32 special defines */ /* WIN32 special defines */
#define STDIN_FILENO 0 #define STDIN_FILENO 0
@@ -187,7 +188,7 @@ typedef guint64 uint64_t; @@ -193,7 +194,7 @@ typedef gintptr intptr_t;
/* /*
* Utility functions * Utility functions
*/ */