Update Fluidsynth to v2.0.3

see https://github.com/FluidSynth/fluidsynth/releases/tag/v2.0.3
This commit is contained in:
Robin Gareus 2019-01-02 15:33:23 +01:00
parent 754591e2ee
commit ac9329f907
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
22 changed files with 2521 additions and 1380 deletions

View file

@ -1,7 +1,14 @@
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.2 (6e9d84f02a7a0f7e436c2adffc4a065608f490ba) rev. v2.0.3-16-g8b18205
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.
---
The source is imported in the repository in order to allow static linking,
with hidden visibility. This is required for a-fluidsynth.lv2 (plugin must
be self-contained and not rely on external shared libs) and to make it
available to to libardour on all platforms that Ardour is.

View file

@ -53,8 +53,12 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *dat
int size, int dynamic); int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt,
void *data, int size, int dynamic); void *data, int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt,
void **data, int *size);
FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt,
void *data, int size, int dynamic); void *data, int size, int dynamic);
FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt,
void **data, int *size);
/** /**
* MIDI router rule type. * MIDI router rule type.

View file

@ -184,12 +184,10 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont);
/** /**
* Virtual SoundFont preset iteration function. * Virtual SoundFont preset iteration function.
* @param sfont Virtual SoundFont * @param sfont Virtual SoundFont
* @param preset Caller supplied uninitialized buffer to fill in with current preset information
* @return NULL when no more presets are available, otherwise the a pointer to the current preset * @return NULL when no more presets are available, otherwise the a pointer to the current preset
* *
* Should store preset information to the caller supplied \a preset structure * Returns preset information to the caller. The returned buffer is only valid until a subsequent
* and advance the internal iteration state to the next preset for subsequent * call to this function.
* calls.
*/ */
typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont);

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,7 @@ static long fluid_getlength(unsigned char *s);
*/ */
static char *fluid_file_read_full(fluid_file fp, size_t *length); static char *fluid_file_read_full(fluid_file fp, size_t *length);
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic);
static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size);
#define READ_FULL_INITIAL_BUFLEN 1024 #define READ_FULL_INITIAL_BUFLEN 1024
static fluid_track_t *new_fluid_track(int num); static fluid_track_t *new_fluid_track(int num);
@ -81,6 +82,41 @@ static int fluid_midi_file_get_division(fluid_midi_file *midifile);
* MIDIFILE * MIDIFILE
*/ */
/**
* Check if a file is a MIDI file.
* @param filename Path to the file to check
* @return TRUE if it could be a MIDI file, FALSE otherwise
*
* The current implementation only checks for the "MThd" header in the file.
* It is useful only to distinguish between SoundFont and MIDI files.
*/
int fluid_is_midifile(const char *filename)
{
FILE *fp = FLUID_FOPEN(filename, "rb");
uint32_t id;
int retcode = FALSE;
do
{
if(fp == NULL)
{
return retcode;
}
if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1)
{
break;
}
retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd'));
}
while(0);
FLUID_FCLOSE(fp);
return retcode;
}
/** /**
* Return a new MIDI file handle for parsing an already-loaded MIDI file. * Return a new MIDI file handle for parsing an already-loaded MIDI file.
* @internal * @internal
@ -1271,8 +1307,6 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
* should be freed when the event is freed (only applies if event gets destroyed * should be freed when the event is freed (only applies if event gets destroyed
* with delete_fluid_midi_event()) * with delete_fluid_midi_event())
* @return Always returns #FLUID_OK * @return Always returns #FLUID_OK
*
* @note Unlike the other event assignment functions, this one sets evt->type.
*/ */
int int
fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1291,7 +1325,6 @@ fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dy
* @return Always returns #FLUID_OK * @return Always returns #FLUID_OK
* *
* @since 2.0.0 * @since 2.0.0
* @note Unlike the other event assignment functions, this one sets evt->type.
*/ */
int int
fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1300,6 +1333,25 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
return FLUID_OK; return FLUID_OK;
} }
/**
* Get the text of a MIDI event structure.
* @param evt MIDI event structure
* @param data Pointer to return text data on.
* @param size Pointer to return text size on.
* @return Returns #FLUID_OK if \p data and \p size previously set by
* fluid_midi_event_set_text() have been successfully retrieved.
* Else #FLUID_FAILED is returned and \p data and \p size are not changed.
* @since 2.0.3
*/
int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size)
{
fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED);
fluid_midi_event_get_sysex_LOCAL(evt, data, size);
return FLUID_OK;
}
/** /**
* Assign lyric data to a MIDI event structure. * Assign lyric data to a MIDI event structure.
* @param evt MIDI event structure * @param evt MIDI event structure
@ -1310,7 +1362,6 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn
* @return Always returns #FLUID_OK * @return Always returns #FLUID_OK
* *
* @since 2.0.0 * @since 2.0.0
* @note Unlike the other event assignment functions, this one sets evt->type.
*/ */
int int
fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic)
@ -1319,6 +1370,25 @@ fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int d
return FLUID_OK; return FLUID_OK;
} }
/**
* Get the lyric of a MIDI event structure.
* @param evt MIDI event structure
* @param data Pointer to return lyric data on.
* @param size Pointer to return lyric size on.
* @return Returns #FLUID_OK if \p data and \p size previously set by
* fluid_midi_event_set_lyrics() have been successfully retrieved.
* Else #FLUID_FAILED is returned and \p data and \p size are not changed.
* @since 2.0.3
*/
int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size)
{
fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);
fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED);
fluid_midi_event_get_sysex_LOCAL(evt, data, size);
return FLUID_OK;
}
static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic)
{ {
evt->type = type; evt->type = type;
@ -1327,6 +1397,19 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type,
evt->param2 = dynamic; evt->param2 = dynamic;
} }
static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size)
{
if(data)
{
*data = evt->paramptr;
}
if(size)
{
*size = evt->param1;
}
}
/****************************************************** /******************************************************
* *
* fluid_track_t * fluid_track_t
@ -1598,7 +1681,7 @@ new_fluid_player(fluid_synth_t *synth)
fluid_settings_getint(synth->settings, "player.reset-synth", &i); fluid_settings_getint(synth->settings, "player.reset-synth", &i);
fluid_player_handle_reset_synth(player, NULL, i); fluid_player_handle_reset_synth(player, NULL, i);
fluid_settings_callback_int(synth->settings, "player.reset-synth", fluid_settings_callback_int(synth->settings, "player.reset-synth",
fluid_player_handle_reset_synth, player); fluid_player_handle_reset_synth, player);

View file

@ -245,10 +245,7 @@ fluid_mod_get_source_value(const unsigned char mod_src,
static fluid_real_t static fluid_real_t
fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range) fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range)
{ {
/* normalized value, i.e. usually in the range [0;1] /* normalized value, i.e. usually in the range [0;1] */
*
* if val was retrieved from pitch_bend then [-0.5;0.5]
*/
const fluid_real_t val_norm = val / range; const fluid_real_t val_norm = val / range;
/* we could also only switch case the lower nibble of mod_flags, however /* we could also only switch case the lower nibble of mod_flags, however
@ -364,7 +361,22 @@ fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, cons
} }
/* /*
* fluid_mod_get_value * fluid_mod_get_value.
* Computes and return modulator output following SF2.01
* (See SoundFont Modulator Controller Model Chapter 9.5).
*
* Output = Transform(Amount * Map(primary source input) * Map(secondary source input))
*
* Notes:
* 1)fluid_mod_get_value, ignores the Transform operator. The result is:
*
* Output = Amount * Map(primary source input) * Map(secondary source input)
*
* 2)When primary source input (src1) is set to General Controller 'No Controller',
* output is forced to 0.
*
* 3)When secondary source input (src2) is set to General Controller 'No Controller',
* output is forced to +1.0
*/ */
fluid_real_t fluid_real_t
fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
@ -418,6 +430,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the input value */ /* transform the input value */
v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1);
} }
/* When primary source input (src1) is set to General Controller 'No Controller',
output is forced to 0.0
*/
else else
{ {
return 0.0; return 0.0;
@ -437,6 +452,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice)
/* transform the second input value */ /* transform the second input value */
v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2);
} }
/* When secondary source input (src2) is set to General Controller 'No Controller',
output is forced to +1.0
*/
else else
{ {
v2 = 1.0f; v2 = 1.0f;
@ -486,6 +504,177 @@ size_t fluid_mod_sizeof()
return sizeof(fluid_mod_t); return sizeof(fluid_mod_t);
} }
/**
* Checks if modulator with source 1 other than CC is FLUID_MOD_NONE.
*
* @param mod, modulator.
* @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise.
*/
static int
fluid_mod_is_src1_none(const fluid_mod_t *mod)
{
return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE));
}
/**
* Checks if modulators source other than CC source is invalid.
* (specs SF 2.01 7.4, 7.8, 8.2.1)
*
* @param mod, modulator.
* @param src1_select, source input selection to check.
* 1 to check src1 source.
* 0 to check src2 source.
* @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */
/* SF2.01 section 8.2.1: Constant value */
|| ((src == FLUID_MOD_NONE)
|| (src == FLUID_MOD_VELOCITY) /* Note-on velocity */
|| (src == FLUID_MOD_KEY) /* Note-on key number */
|| (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */
|| (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
|| (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
|| (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */
));
}
/**
* Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1).
* @param mod, modulator.
* @src1_select, source input selection:
* 1 to check src1 source or
* 0 to check src2 source.
* @return FALSE if selected modulator's source CC is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */
|| ((src != BANK_SELECT_MSB)
&& (src != BANK_SELECT_LSB)
&& (src != DATA_ENTRY_MSB)
&& (src != DATA_ENTRY_LSB)
/* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */
&& ((src < NRPN_LSB) || (RPN_MSB < src))
/* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */
/* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */
&& (src < ALL_SOUND_OFF)
/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1)
However, as long fluidsynth will use only CC 7 bits resolution,
it is safe to ignore these SF recommendations on CC receive.
See explanations in fluid_synth_cc_LOCAL() */
/* uncomment next line to forbid CC lsb */
/* && ((src < 32) || (63 < src)) */
));
}
/**
* Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1)
* @param mod, modulator.
* @param name,if not NULL, pointer on a string displayed as a warning.
* @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.
*/
int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name)
{
static const char *invalid_non_cc_src =
"Invalid modulator, using non-CC source %s.src%d=%d";
static const char *invalid_cc_src =
"Invalid modulator, using CC source %s.src%d=%d";
static const char *src1_is_none =
"Modulator with source 1 none %s.src1=%d";
/* checks valid non cc sources */
if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1);
}
return FALSE;
}
/*
When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but
the output of this modulator will be forced to 0 at synthesis time.
Also this modulator cannot be used to overwrite a default modulator (as
there is no default modulator with src1 source equal to FLUID_MOD_NONE).
Consequently it is useful to return FALSE to indicate this modulator
being useless. It will be removed later with others invalid modulators.
*/
if(fluid_mod_is_src1_none(mod))
{
if(name)
{
FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2);
}
return FALSE;
}
/* checks valid cc sources */
if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2);
}
return FALSE;
}
return TRUE;
}
/** /**
* Checks if two modulators are identical in sources, flags and destination. * Checks if two modulators are identical in sources, flags and destination.
* @param mod1 First modulator * @param mod1 First modulator

View file

@ -44,6 +44,7 @@ struct _fluid_mod_t
}; };
fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice); fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name);
#ifdef DEBUG #ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod); void fluid_dump_modulator(fluid_mod_t *mod);

File diff suppressed because it is too large Load diff

View file

@ -305,6 +305,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
{ {
int ticks = voice->envlfo.ticks; int ticks = voice->envlfo.ticks;
int count, is_looping; int count, is_looping;
fluid_real_t modenv_val;
/******************* sample sanity check **********/ /******************* sample sanity check **********/
@ -361,6 +362,12 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
/******************* phase **********************/ /******************* phase **********************/
/* SF2.04 section 8.1.2 #26:
* attack of modEnv is convex ?!?
*/
modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv))
: fluid_adsr_env_get_val(&voice->envlfo.modenv);
/* Calculate the number of samples, that the DSP loop advances /* Calculate the number of samples, that the DSP loop advances
* through the original waveform with each step in the output * through the original waveform with each step in the output
* buffer. It is the ratio between the frequencies of original * buffer. It is the ratio between the frequencies of original
@ -369,7 +376,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
voice->dsp.pitchoffset + voice->dsp.pitchoffset +
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
+ fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
+ fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) + modenv_val * voice->envlfo.modenv_to_pitch)
/ voice->dsp.root_pitch_hz; / voice->dsp.root_pitch_hz;
/******************* portamento ****************/ /******************* portamento ****************/
@ -455,7 +462,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc); modenv_val * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
@ -585,7 +592,7 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
/* A voice is turned off during the attack section of the volume /* A voice is turned off during the attack section of the volume
* envelope. The attack section ramps up linearly with * envelope. The attack section ramps up linearly with
* amplitude. The other sections use logarithmic scaling. Calculate new * amplitude. The other sections use logarithmic scaling. Calculate new
* volenv_val to achieve equievalent amplitude during the release phase * volenv_val to achieve equivalent amplitude during the release phase
* for seamless volume transition. * for seamless volume transition.
*/ */
if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)
@ -598,6 +605,24 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
} }
} }
if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
{
/* A voice is turned off during the attack section of the modulation
* envelope. The attack section use convex scaling with pitch and filter
* frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)
* The other sections use linear scaling: modenv_val = modenv.val
*
* Calculate new modenv.val to achieve equivalent modenv_val during the release phase
* for seamless pitch and filter frequency cutoff transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)
{
fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));
fluid_clip(env_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);
}
}
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
} }
@ -650,11 +675,12 @@ static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voi
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
{ {
fluid_rvoice_t *voice = obj; fluid_rvoice_t *voice = obj;
int section = fluid_adsr_env_get_section(&voice->envlfo.volenv); int section; /* volume or modulation section */
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
Section skip for volume envelope Section skip for volume envelope
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
if(section >= FLUID_VOICE_ENVHOLD) if(section >= FLUID_VOICE_ENVHOLD)
{ {
/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new
@ -672,16 +698,30 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
/* skips to Attack section from any section */ /* skips to Attack section from any section */
/* Update vol and attack data */ /* Update vol and attack data */
fluid_rvoice_local_retrigger_attack(voice); fluid_rvoice_local_retrigger_attack(voice);
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
Section skip for modulation envelope Section skip for modulation envelope
--------------------------------------------------------------------------*/ --------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.modenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use linear scaling.
Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section
uses convex shape (see fluid_rvoice_write() - fluid_convex()).
Calculate new modenv value (new_value) for seamless attack transition.
Here we need the inverse of fluid_convex() function defined as:
new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)
For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with
val = (1 current_val) . FLUID_PEAK_ATTENUATION / 2.0
*/
fluid_real_t new_value; /* new modenv value */
new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))
* FLUID_PEAK_ATTENUATION / 2.0);
fluid_clip(new_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);
}
/* Skips from any section to ATTACK section */ /* Skips from any section to ATTACK section */
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);
/* Actually (v 1.1.6) all sections are linear, so there is no need to
correct val value. However soundfont 2.01/2.4 spec. says that Attack should
be convex (see issue #153 from Christian Collins). In the case Attack
section would be changed to a non linear shape it will be necessary to do
a correction for seamless val transition. Here is the place to do this */
} }
/** /**

View file

@ -60,8 +60,10 @@ struct _fluid_samplecache_entry_t
static fluid_list_t *samplecache_list = NULL; static fluid_list_t *samplecache_list = NULL;
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT; static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type); static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start,
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type); unsigned int sample_end, int sample_type, time_t mtime);
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start,
unsigned int sample_end, int sample_type, time_t mtime);
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry); static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
static int fluid_get_file_modification_time(char *filename, time_t *modification_time); static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
@ -75,14 +77,20 @@ int fluid_samplecache_load(SFData *sf,
{ {
fluid_samplecache_entry_t *entry; fluid_samplecache_entry_t *entry;
int ret; int ret;
time_t mtime;
fluid_mutex_lock(samplecache_mutex); fluid_mutex_lock(samplecache_mutex);
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type); if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
{
mtime = 0;
}
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL) if(entry == NULL)
{ {
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type); entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
if(entry == NULL) if(entry == NULL)
{ {
@ -180,7 +188,8 @@ unlock_exit:
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
unsigned int sample_start, unsigned int sample_start,
unsigned int sample_end, unsigned int sample_end,
int sample_type) int sample_type,
time_t mtime)
{ {
fluid_samplecache_entry_t *entry; fluid_samplecache_entry_t *entry;
@ -202,12 +211,6 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
goto error_exit; goto error_exit;
} }
if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
entry->modification_time = 0;
}
entry->sf_samplepos = sf->samplepos; entry->sf_samplepos = sf->samplepos;
entry->sf_samplesize = sf->samplesize; entry->sf_samplesize = sf->samplesize;
entry->sf_sample24pos = sf->sample24pos; entry->sf_sample24pos = sf->sample24pos;
@ -215,6 +218,7 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
entry->sample_start = sample_start; entry->sample_start = sample_start;
entry->sample_end = sample_end; entry->sample_end = sample_end;
entry->sample_type = sample_type; entry->sample_type = sample_type;
entry->modification_time = mtime;
entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type, entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
&entry->sample_data, &entry->sample_data24); &entry->sample_data, &entry->sample_data24);
@ -244,18 +248,12 @@ static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
unsigned int sample_start, unsigned int sample_start,
unsigned int sample_end, unsigned int sample_end,
int sample_type) int sample_type,
time_t mtime)
{ {
time_t mtime;
fluid_list_t *entry_list; fluid_list_t *entry_list;
fluid_samplecache_entry_t *entry; fluid_samplecache_entry_t *entry;
if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
mtime = 0;
}
entry_list = samplecache_list; entry_list = samplecache_list;
while(entry_list) while(entry_list)

View file

@ -34,75 +34,79 @@
Borrowed from Smurf SoundFont Editor by Josh Green Borrowed from Smurf SoundFont Editor by Josh Green
=================================================================*/ =================================================================*/
/* /* FOURCC definitions */
functions for loading data from sfont files, with appropriate byte swapping #define RIFF_FCC FLUID_FOURCC('R','I','F','F')
on big endian machines. Sfont IDs are not swapped because the ID read is #define LIST_FCC FLUID_FOURCC('L','I','S','T')
equivalent to the matching ID list in memory regardless of LE/BE machine #define SFBK_FCC FLUID_FOURCC('s','f','b','k')
*/ #define INFO_FCC FLUID_FOURCC('I','N','F','O')
#define SDTA_FCC FLUID_FOURCC('s','d','t','a')
#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */
/* sf file chunk IDs */ #define IFIL_FCC FLUID_FOURCC('i','f','i','l')
enum #define ISNG_FCC FLUID_FOURCC('i','s','n','g')
{ #define INAM_FCC FLUID_FOURCC('I','N','A','M')
UNKN_ID, #define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */
RIFF_ID, #define IVER_FCC FLUID_FOURCC('i','v','e','r')
LIST_ID, #define ICRD_FCC FLUID_FOURCC('I','C','R','D')
SFBK_ID, #define IENG_FCC FLUID_FOURCC('I','E','N','G')
INFO_ID, #define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */
SDTA_ID, #define ICOP_FCC FLUID_FOURCC('I','C','O','P')
PDTA_ID, /* info/sample/preset */ #define ICMT_FCC FLUID_FOURCC('I','C','M','T')
#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */
IFIL_ID, #define SNAM_FCC FLUID_FOURCC('s','n','a','m')
ISNG_ID, #define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */
INAM_ID, #define PHDR_FCC FLUID_FOURCC('p','h','d','r')
IROM_ID, /* info ids (1st byte of info strings) */ #define PBAG_FCC FLUID_FOURCC('p','b','a','g')
IVER_ID, #define PMOD_FCC FLUID_FOURCC('p','m','o','d')
ICRD_ID, #define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */
IENG_ID, #define IHDR_FCC FLUID_FOURCC('i','n','s','t')
IPRD_ID, /* more info ids */ #define IBAG_FCC FLUID_FOURCC('i','b','a','g')
ICOP_ID, #define IMOD_FCC FLUID_FOURCC('i','m','o','d')
ICMT_ID, #define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */
ISFT_ID, /* and yet more info ids */ #define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */
#define SM24_FCC FLUID_FOURCC('s','m','2','4')
SNAM_ID, /* Set when the FCC code is unknown */
SMPL_ID, /* sample ids */ #define UNKN_ID FLUID_N_ELEMENTS(idlist)
PHDR_ID,
PBAG_ID,
PMOD_ID,
PGEN_ID, /* preset ids */
IHDR_ID,
IBAG_ID,
IMOD_ID,
IGEN_ID, /* instrument ids */
SHDR_ID, /* sample info */
SM24_ID
};
/* /*
* This declares a char array containing the SF2 chunk identifiers. This * This declares a uint32_t array containing the SF2 chunk identifiers.
* array is being accessed like an uint32 below to simplify id comparison.
* To make sure it is suitably aligned for uint32 access, we must wrap it
* inside a union along with a uint32 telling the compiler to align it
* for integer access and avoiding undefined behaviour.
* This basically is the C89 equivalent to what is written in C11 as:
* alignas(uint32_t) static const char idlist[] = {};
*
* See: EXP36-C. Do not cast pointers into more strictly aligned pointer
* types - SEI CERT C Coding Standard
*/ */
static const union fluid_idlist static const uint32_t idlist[] =
{ {
/* RIFF_FCC,
* Cannot be char c[ ], because in C89, arrays wraped in unions LIST_FCC,
* must have a fixed size. Otherwise the size of the union would depend SFBK_FCC,
* on the initialization of its first member, which results in INFO_FCC,
* different sizes for different instances of the same union type. SDTA_FCC,
*/ PDTA_FCC,
char c[116];
uint32_t i;
} idlist = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
"ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24"
};
IFIL_FCC,
ISNG_FCC,
INAM_FCC,
IROM_FCC,
IVER_FCC,
ICRD_FCC,
IENG_FCC,
IPRD_FCC,
ICOP_FCC,
ICMT_FCC,
ISFT_FCC,
SNAM_FCC,
SMPL_FCC,
PHDR_FCC,
PBAG_FCC,
PMOD_FCC,
PGEN_FCC,
IHDR_FCC,
IBAG_FCC,
IMOD_FCC,
IGEN_FCC,
SHDR_FCC,
SM24_FCC
};
/* generator types */ /* generator types */
typedef enum typedef enum
@ -205,8 +209,6 @@ static const unsigned short invalid_preset_gen[] =
}; };
#define CHNKIDSTR(id) &idlist.c[(id - 1) * 4]
/* sfont file chunk sizes */ /* sfont file chunk sizes */
#define SF_PHDR_SIZE (38) #define SF_PHDR_SIZE (38)
#define SF_BAG_SIZE (4) #define SF_BAG_SIZE (4)
@ -322,6 +324,56 @@ static void delete_zone(SFZone *zone);
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data);
static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24);
/**
* 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
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
*/
int fluid_is_soundfont(const char *filename)
{
FILE *fp = FLUID_FOPEN(filename, "rb");
uint32_t fcc;
int retcode = FALSE;
do
{
if(fp == NULL)
{
return retcode;
}
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{
break;
}
if(fcc != RIFF_FCC)
{
break;
}
if(FLUID_FSEEK(fp, 4, SEEK_CUR))
{
break;
}
if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1)
{
break;
}
retcode = (fcc == SFBK_FCC);
}
while(0);
FLUID_FCLOSE(fp);
return retcode;
}
/* /*
* Open a SoundFont file and parse it's contents into a SFData structure. * Open a SoundFont file and parse it's contents into a SFData structure.
* *
@ -511,17 +563,17 @@ void fluid_sffile_close(SFData *sf)
static int chunkid(uint32_t id) static int chunkid(uint32_t id)
{ {
unsigned int i; unsigned int i;
const uint32_t *p = &idlist.i;
for(i = 0; i < sizeof(idlist) / sizeof(idlist.i); i++, p += 1) for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++)
{ {
if(*p == id) if(idlist[i] == id)
{ {
return (i + 1); break;
} }
} }
return UNKN_ID; /* Return chunk id or UNKN_ID if not found */
return i;
} }
static int load_header(SFData *sf) static int load_header(SFData *sf)
@ -530,7 +582,7 @@ static int load_header(SFData *sf)
READCHUNK(sf, &chunk); /* load RIFF chunk */ READCHUNK(sf, &chunk); /* load RIFF chunk */
if(chunkid(chunk.id) != RIFF_ID) if(chunk.id != RIFF_FCC)
{ {
/* error if not RIFF */ /* error if not RIFF */
FLUID_LOG(FLUID_ERR, "Not a RIFF file"); FLUID_LOG(FLUID_ERR, "Not a RIFF file");
@ -539,7 +591,7 @@ static int load_header(SFData *sf)
READID(sf, &chunk.id); /* load file ID */ READID(sf, &chunk.id); /* load file ID */
if(chunkid(chunk.id) != SFBK_ID) if(chunk.id != SFBK_FCC)
{ {
/* error if not SFBK_ID */ /* error if not SFBK_ID */
FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); FLUID_LOG(FLUID_ERR, "Not a SoundFont file");
@ -558,7 +610,7 @@ static int load_header(SFData *sf)
return FALSE; return FALSE;
} }
if(chunkid(chunk.id) != INFO_ID) if(chunk.id != INFO_FCC)
{ {
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk");
return FALSE; return FALSE;
@ -575,7 +627,7 @@ static int load_header(SFData *sf)
return FALSE; return FALSE;
} }
if(chunkid(chunk.id) != SDTA_ID) if(chunk.id != SDTA_FCC)
{ {
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk");
return FALSE; return FALSE;
@ -592,7 +644,7 @@ static int load_header(SFData *sf)
return FALSE; return FALSE;
} }
if(chunkid(chunk.id) != PDTA_ID) if(chunk.id != PDTA_FCC)
{ {
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
return FALSE; return FALSE;
@ -637,7 +689,7 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
{ {
READCHUNK(sf, chunk); /* read list chunk */ READCHUNK(sf, chunk); /* read list chunk */
if(chunkid(chunk->id) != LIST_ID) /* error if ! list chunk */ if(chunk->id != LIST_FCC) /* error if ! list chunk */
{ {
FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse");
return FALSE; return FALSE;
@ -651,8 +703,11 @@ static int read_listchunk(SFData *sf, SFChunk *chunk)
static int process_info(SFData *sf, int size) static int process_info(SFData *sf, int size)
{ {
SFChunk chunk; SFChunk chunk;
unsigned char id; union
char *item; {
char *chr;
uint32_t *fcc;
} item;
unsigned short ver; unsigned short ver;
while(size > 0) while(size > 0)
@ -660,9 +715,7 @@ static int process_info(SFData *sf, int size)
READCHUNK(sf, &chunk); READCHUNK(sf, &chunk);
size -= 8; size -= 8;
id = chunkid(chunk.id); if(chunk.id == IFIL_FCC)
if(id == IFIL_ID)
{ {
/* sound font version chunk? */ /* sound font version chunk? */
if(chunk.size != 4) if(chunk.size != 4)
@ -703,7 +756,7 @@ static int process_info(SFData *sf, int size)
return FALSE; return FALSE;
} }
} }
else if(id == IVER_ID) else if(chunk.id == IVER_FCC)
{ {
/* ROM version chunk? */ /* ROM version chunk? */
if(chunk.size != 4) if(chunk.size != 4)
@ -717,34 +770,35 @@ static int process_info(SFData *sf, int size)
READW(sf, ver); READW(sf, ver);
sf->romver.minor = ver; sf->romver.minor = ver;
} }
else if(id != UNKN_ID) else if(chunkid(chunk.id) != UNKN_ID)
{ {
if((id != ICMT_ID && 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); &chunk.id, chunk.size);
return FALSE; return FALSE;
} }
/* alloc for chunk id and da chunk */ /* alloc for chunk fcc and da chunk */
if(!(item = FLUID_MALLOC(chunk.size + 1))) if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1)))
{ {
FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_LOG(FLUID_ERR, "Out of memory");
return FALSE; return FALSE;
} }
/* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */
sf->info = fluid_list_append(sf->info, item); sf->info = fluid_list_append(sf->info, item.fcc);
*(unsigned char *)item = id; /* save chunk fcc and update pointer to data value */
*item.fcc++ = chunk.id;
if(sf->fcbs->fread(&item[1], chunk.size, sf->sffd) == FLUID_FAILED) if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED)
{ {
return FALSE; return FALSE;
} }
/* force terminate info item (don't forget uint8 info ID) */ /* force terminate info item */
*(item + chunk.size) = '\0'; item.chr[chunk.size] = '\0';
} }
else else
{ {
@ -777,7 +831,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk); READCHUNK(sf, &chunk);
size -= 8; size -= 8;
if(chunkid(chunk.id) != SMPL_ID) if(chunk.id != SMPL_FCC)
{ {
FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead");
return FALSE; return FALSE;
@ -810,7 +864,7 @@ static int process_sdta(SFData *sf, unsigned int size)
READCHUNK(sf, &chunk); READCHUNK(sf, &chunk);
size -= 8; size -= 8;
if(chunkid(chunk.id) == SM24_ID) if(chunk.id == SM24_FCC)
{ {
int sm24size, sdtahalfsize; int sm24size, sdtahalfsize;
@ -850,29 +904,24 @@ ret:
static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size)
{ {
unsigned int id;
const char *expstr;
expstr = CHNKIDSTR(expid); /* in case we need it */
READCHUNK(sf, chunk); READCHUNK(sf, chunk);
*size -= 8; *size -= 8;
if((id = chunkid(chunk->id)) != expid) if(chunk->id != expid)
{ {
FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", expstr); FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", &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", expstr, reclen); FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", &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", expstr); FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", &expid);
return FALSE; return FALSE;
} }
@ -883,7 +932,7 @@ static int process_pdta(SFData *sf, int size)
{ {
SFChunk chunk; SFChunk chunk;
if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size)) if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -893,7 +942,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size)) if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -903,7 +952,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size)) if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -913,7 +962,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size)) if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -923,7 +972,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size)) if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -933,7 +982,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size)) if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -943,7 +992,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size)) if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -953,7 +1002,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size)) if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }
@ -963,7 +1012,7 @@ static int process_pdta(SFData *sf, int size)
return FALSE; return FALSE;
} }
if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size)) if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size))
{ {
return FALSE; return FALSE;
} }

View file

@ -512,9 +512,13 @@ delete_fluid_sample(fluid_sample_t *sample)
/** /**
* Returns the size of the fluid_sample_t structure. * Returns the size of the fluid_sample_t structure.
* *
* Useful in low latency scenarios e.g. to allocate a sample on the stack. * Useful in low latency scenarios e.g. to allocate a pool of samples.
* *
* @return Size of fluid_sample_t in bytes * @return Size of fluid_sample_t in bytes
*
* @note It is recommend to zero initialize the memory before using the object.
*
* @warning Do NOT allocate samples on the stack and assign them to a voice!
*/ */
size_t fluid_sample_sizeof() size_t fluid_sample_sizeof()
{ {
@ -563,16 +567,17 @@ fluid_sample_set_sound_data(fluid_sample_t *sample,
fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); fluid_return_val_if_fail(sample != NULL, FLUID_FAILED);
fluid_return_val_if_fail(data != NULL, FLUID_FAILED); fluid_return_val_if_fail(data != NULL, FLUID_FAILED);
fluid_return_val_if_fail(nbframes == 0, FLUID_FAILED); fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED);
/* in case we already have some data */ /* in case we already have some data */
if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free)
{ {
FLUID_FREE(sample->data); FLUID_FREE(sample->data);
FLUID_FREE(sample->data24); FLUID_FREE(sample->data24);
sample->data = NULL;
sample->data24 = NULL;
} }
sample->data = NULL;
sample->data24 = NULL;
if(copy_data) if(copy_data)
{ {
@ -635,6 +640,8 @@ error_rec:
FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_LOG(FLUID_ERR, "Out of memory");
FLUID_FREE(sample->data); FLUID_FREE(sample->data);
FLUID_FREE(sample->data24); FLUID_FREE(sample->data24);
sample->data = NULL;
sample->data24 = NULL;
return FLUID_FAILED; return FLUID_FAILED;
#undef SAMPLE_LOOP_MARGIN #undef SAMPLE_LOOP_MARGIN

View file

@ -183,8 +183,6 @@ struct _fluid_sample_t
* @return Should return #FLUID_OK * @return Should return #FLUID_OK
*/ */
int (*notify)(fluid_sample_t *sample, int reason); int (*notify)(fluid_sample_t *sample, int reason);
void *userdata; /**< User defined data */
}; };

View file

@ -227,7 +227,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
#else #else
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0);
#endif #endif
fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0); fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);
@ -1331,6 +1331,13 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
/* Checks if modulators sources are valid */
if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod"))
{
return FLUID_FAILED;
}
fluid_synth_api_enter(synth); fluid_synth_api_enter(synth);
default_mod = synth->default_mod; default_mod = synth->default_mod;
@ -1902,7 +1909,7 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
int bank = 0, prog, channels; int bank = 0, prog, channels;
double tunedata[128]; double tunedata[128];
int keys[128]; int keys[128];
char name[17]; char name[17]={0};
int note, frac, frac2; int note, frac, frac2;
uint8_t chksum; uint8_t chksum;
int i, count, index; int i, count, index;
@ -1973,7 +1980,8 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len,
} }
*resptr++ = prog; *resptr++ = prog;
FLUID_STRNCPY(resptr, name, 16); /* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */
FLUID_MEMCPY(resptr, name, 16);
resptr += 16; resptr += 16;
for(i = 0; i < 128; i++) for(i = 0; i < 128; i++)
@ -3368,7 +3376,7 @@ 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);
/* First, take what's still available in the buffer */ /* First, take what's still available in the buffer */
count = 0; count = 0;
num = synth->cur; num = synth->cur;
@ -3521,7 +3529,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
/** /**
* mixes the samples of \p in to \p out * mixes the samples of \p in to \p out
* *
* @param out the output sample buffer to mix to * @param out the output sample buffer to mix to
* @param ooff sample offset in \p out * @param ooff sample offset in \p out
* @param in the rvoice_mixer input sample buffer to mix from * @param in the rvoice_mixer input sample buffer to mix from
@ -3539,6 +3547,7 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out
if(out != NULL) if(out != NULL)
{ {
int j; int j;
for(j = 0; j < num; j++) for(j = 0; j < num; j++)
{ {
out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];
@ -3687,7 +3696,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++) for(i = 0; i < nfxchan; i++)
{ {
int buf_idx = f * nfxchan + i; int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx]; float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);
@ -3730,10 +3739,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++) for(i = 0; i < nfxchan; i++)
{ {
int buf_idx = f * nfxchan + i; int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx]; float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);
out_buf = fx[(buf_idx * 2 + 1) % nfx]; out_buf = fx[(buf_idx * 2 + 1) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);
} }
@ -3784,7 +3793,7 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
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(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);
@ -3853,10 +3862,12 @@ static FLUID_INLINE int16_t
round_clip_to_i16(float x) round_clip_to_i16(float x)
{ {
long i; long i;
if(x >= 0.0f) if(x >= 0.0f)
{ {
i = (long)(x + 0.5f); i = (long)(x + 0.5f);
if (FLUID_UNLIKELY(i > 32767))
if(FLUID_UNLIKELY(i > 32767))
{ {
i = 32767; i = 32767;
} }
@ -3864,12 +3875,13 @@ round_clip_to_i16(float x)
else else
{ {
i = (long)(x - 0.5f); i = (long)(x - 0.5f);
if (FLUID_UNLIKELY(i < -32768))
if(FLUID_UNLIKELY(i < -32768))
{ {
i = -32768; i = -32768;
} }
} }
return (int16_t)i; return (int16_t)i;
} }
@ -4347,11 +4359,11 @@ fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int
) )
{ {
// Replacement of default_vel2att modulator by custom_breath2att_modulator // Replacement of default_vel2att modulator by custom_breath2att_modulator
fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT); fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);
} }
else else
{ {
fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT); fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);
} }
// Next default modulator to add to the voice // Next default modulator to add to the voice

View file

@ -275,71 +275,6 @@ fluid_error()
return fluid_errbuf; return fluid_errbuf;
} }
/**
* Check if a file is a MIDI file.
* @param filename Path to the file to check
* @return TRUE if it could be a MIDI file, FALSE otherwise
*
* The current implementation only checks for the "MThd" header in the file.
* It is useful only to distinguish between SoundFont and MIDI files.
*/
int
fluid_is_midifile(const char *filename)
{
FILE *fp = fopen(filename, "rb");
char id[4];
if(fp == NULL)
{
return 0;
}
if(fread((void *) id, 1, 4, fp) != 4)
{
fclose(fp);
return 0;
}
fclose(fp);
return FLUID_STRNCMP(id, "MThd", 4) == 0;
}
/**
* 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
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
*/
int
fluid_is_soundfont(const char *filename)
{
FILE *fp = fopen(filename, "rb");
char riff_id[4], sfbk_id[4];
if(fp == NULL)
{
return 0;
}
if((fread((void *) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) ||
(fseek(fp, 4, SEEK_CUR) != 0) ||
(fread((void *) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id)))
{
goto error_rec;
}
fclose(fp);
return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) &&
(FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0);
error_rec:
fclose(fp);
return 0;
}
/** /**
* Suspend the execution of the current thread for the specified amount of time. * Suspend the execution of the current thread for the specified amount of time.
* @param milliseconds to wait. * @param milliseconds to wait.
@ -1280,7 +1215,11 @@ fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt,
FLUID_SNPRINTF(buf, len, "%s", line); FLUID_SNPRINTF(buf, len, "%s", line);
buf[len - 1] = 0; buf[len - 1] = 0;
add_history(buf);
if(buf[0] != '\0')
{
add_history(buf);
}
free(line); free(line);
return 1; return 1;

View file

@ -77,6 +77,14 @@
#define FLUID_LE32TOH(x) GINT32_FROM_LE(x) #define FLUID_LE32TOH(x) GINT32_FROM_LE(x)
#define FLUID_LE16TOH(x) GINT16_FROM_LE(x) #define FLUID_LE16TOH(x) GINT16_FROM_LE(x)
#if FLUID_IS_BIG_ENDIAN
#define FLUID_FOURCC(_a, _b, _c, _d) \
(uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d))
#else
#define FLUID_FOURCC(_a, _b, _c, _d) \
(uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a))
#endif
#define fluid_return_if_fail(cond) \ #define fluid_return_if_fail(cond) \
if(cond) \ if(cond) \

View file

@ -516,7 +516,6 @@ fluid_voice_calculate_gen_pitch(fluid_voice_t *voice)
voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice));
} }
/* /*
* fluid_voice_calculate_runtime_synthesis_parameters * fluid_voice_calculate_runtime_synthesis_parameters
* *
@ -1153,7 +1152,9 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* Recalculate voice parameters for a given control. * Recalculate voice parameters for a given control.
* @param voice the synthesis voice * @param voice the synthesis voice
* @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...) * @param cc flag to distinguish between a continous control and a channel control (pitch bend, ...)
* @param ctrl the control number * @param ctrl the control number:
* when >=0, only modulators's destination having ctrl as source are updated.
* when -1, all modulators's destination are updated (regardless of ctrl).
* *
* In this implementation, I want to make sure that all controllers * In this implementation, I want to make sure that all controllers
* are event based: the parameter values of the DSP algorithm should * are event based: the parameter values of the DSP algorithm should
@ -1161,57 +1162,83 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
* iteration of the audio cycle (which would probably be feasible if * iteration of the audio cycle (which would probably be feasible if
* the synth was made in silicon). * the synth was made in silicon).
* *
* The update is done in three steps: * The update is done in two steps:
* *
* - first, we look for all the modulators that have the changed * - step 1: first, we look for all the modulators that have the changed
* controller as a source. This will yield a list of generators that * controller as a source. This will yield a generator that will be changed
* will be changed because of the controller event. * because of the controller event.
* *
* - For every changed generator, calculate its new value. This is the * - step 2: For this generator, calculate its new value. This is the
* sum of its original value plus the values of al the attached * sum of its original value plus the values of all the attached modulators.
* modulators. * The generator flag is set to indicate the parameters must be updated.
* * This avoid the risk to call 'fluid_voice_update_param' several
* - For every changed generator, convert its value to the correct * times for the same generator if several modulators have that generator as
* unit of the corresponding DSP parameter * destination. So every changed generators are updated only once.
*/ */
/* bit table for each generator being updated. The bits are packed in variables
Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2.
The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT.
Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu)
but this could be set to 6 for 64 bits cpu.
*/
#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */
#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2)
#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1)
#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR)
#define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
#define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK)))
int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
{ {
int i, k; int i, k;
fluid_mod_t *mod; fluid_mod_t *mod;
int gen; uint32_t gen;
fluid_real_t modval; fluid_real_t modval;
/* Clears registered bits table of updated generators */
uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0};
/* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ /* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */
for(i = 0; i < voice->mod_count; i++) for(i = 0; i < voice->mod_count; i++)
{ {
mod = &voice->mod[i]; mod = &voice->mod[i];
/* step 1: find all the modulators that have the changed controller /* step 1: find all the modulators that have the changed controller
* as input source. */ as input source. When ctrl is -1 all modulators destination
if(fluid_mod_has_source(mod, cc, ctrl)) are updated */
if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl))
{ {
gen = fluid_mod_get_dest(mod); gen = fluid_mod_get_dest(mod);
modval = 0.0;
/* step 2: for every changed modulator, calculate the modulation /* Skip if this generator has already been updated */
* value of its associated generator */ if(!is_gen_updated(updated_gen_bit, gen))
for(k = 0; k < voice->mod_count; k++)
{ {
if(fluid_mod_has_dest(&voice->mod[k], gen)) modval = 0.0;
/* step 2: for every attached modulator, calculate the modulation
* value for the generator gen */
for(k = 0; k < voice->mod_count; k++)
{ {
modval += fluid_mod_get_value(&voice->mod[k], voice); if(fluid_mod_has_dest(&voice->mod[k], gen))
{
modval += fluid_mod_get_value(&voice->mod[k], voice);
}
} }
fluid_gen_set_mod(&voice->gen[gen], modval);
/* now recalculate the parameter values that are derived from the
generator */
fluid_voice_update_param(voice, gen);
/* set the bit that indicates this generator is updated */
set_gen_updated(updated_gen_bit, gen);
} }
fluid_gen_set_mod(&voice->gen[gen], modval);
/* step 3: now that we have the new value of the generator,
* recalculate the parameter values that are derived from the
* generator */
fluid_voice_update_param(voice, gen);
} }
} }
@ -1222,47 +1249,11 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
* Update all the modulators. This function is called after a * Update all the modulators. This function is called after a
* ALL_CTRL_OFF MIDI message has been received (CC 121). * ALL_CTRL_OFF MIDI message has been received (CC 121).
* *
* All destination of all modulators must be updated.
*/ */
int fluid_voice_modulate_all(fluid_voice_t *voice) int fluid_voice_modulate_all(fluid_voice_t *voice)
{ {
fluid_mod_t *mod; return fluid_voice_modulate(voice, 0, -1);
int i, k, gen;
fluid_real_t modval;
/* Loop through all the modulators.
FIXME: we should loop through the set of generators instead of
the set of modulators. We risk to call 'fluid_voice_update_param'
several times for the same generator if several modulators have
that generator as destination. It's not an error, just a wast of
energy (think polution, global warming, unhappy musicians,
...) */
for(i = 0; i < voice->mod_count; i++)
{
mod = &voice->mod[i];
gen = fluid_mod_get_dest(mod);
modval = 0.0;
/* Accumulate the modulation values of all the modulators with
* destination generator 'gen' */
for(k = 0; k < voice->mod_count; k++)
{
if(fluid_mod_has_dest(&voice->mod[k], gen))
{
modval += fluid_mod_get_value(&voice->mod[k], voice);
}
}
fluid_gen_set_mod(&voice->gen[gen], modval);
/* Update the parameter values that are depend on the generator
* 'gen' */
fluid_voice_update_param(voice, gen);
}
return FLUID_OK;
} }
/** legato update functions --------------------------------------------------*/ /** legato update functions --------------------------------------------------*/
@ -1474,10 +1465,10 @@ fluid_voice_stop(fluid_voice_t *voice)
} }
/** /**
* Adds a modulator to the voice. * Adds a modulator to the voice if the modulator has valid sources.
* @param voice Voice instance * @param voice Voice instance.
* @param mod Modulator info (copied) * @param mod Modulator info (copied).
* @param mode Determines how to handle an existing identical modulator * @param mode Determines how to handle an existing identical modulator.
* #FLUID_VOICE_ADD to add (offset) the modulator amounts, * #FLUID_VOICE_ADD to add (offset) the modulator amounts,
* #FLUID_VOICE_OVERWRITE to replace the modulator, * #FLUID_VOICE_OVERWRITE to replace the modulator,
* #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should
@ -1485,33 +1476,43 @@ fluid_voice_stop(fluid_voice_t *voice)
*/ */
void void
fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode) fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* Ignore the modulator if its sources inputs are invalid */
if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod"))
{
fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD);
}
}
/**
* Adds a modulator to the voice.
* local version of fluid_voice_add_mod function. Called at noteon time.
* @param voice, mod, mode, same as for fluid_voice_add_mod() (see above).
* @param check_limit_count is the modulator number limit to handle with existing
* identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD).
* - When FLUID_NUM_MOD, all the voices modulators (since the previous call)
* are checked for identity.
* - When check_count_limit is below the actual number of voices modulators
* (voice->mod_count), this will restrict identity check to this number,
* This is usefull when we know by advance that there is no duplicate with
* modulators at index above this limit. This avoid wasting cpu cycles at noteon.
*/
void
fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count)
{ {
int i; int i;
/* /* check_limit_count cannot be above voice->mod_count */
* Some soundfonts come with a huge number of non-standard if(check_limit_count > voice->mod_count)
* controllers, because they have been designed for one particular
* sound card. Discard them, maybe print a warning.
*/
if(((mod->flags1 & FLUID_MOD_CC) == 0)
&& ((mod->src1 != FLUID_MOD_NONE) /* SF2.01 section 8.2.1: Constant value */
&& (mod->src1 != FLUID_MOD_VELOCITY) /* Note-on velocity */
&& (mod->src1 != FLUID_MOD_KEY) /* Note-on key number */
&& (mod->src1 != FLUID_MOD_KEYPRESSURE) /* Poly pressure */
&& (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
&& (mod->src1 != FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
&& (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
{ {
FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); check_limit_count = voice->mod_count;
return;
} }
if(mode == FLUID_VOICE_ADD) if(mode == FLUID_VOICE_ADD)
{ {
/* if identical modulator exists, add them */ /* if identical modulator exists, add them */
for(i = 0; i < voice->mod_count; i++) for(i = 0; i < check_limit_count; i++)
{ {
if(fluid_mod_test_identity(&voice->mod[i], mod)) if(fluid_mod_test_identity(&voice->mod[i], mod))
{ {
@ -1526,7 +1527,7 @@ fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{ {
/* if identical modulator exists, replace it (only the amount has to be changed) */ /* if identical modulator exists, replace it (only the amount has to be changed) */
for(i = 0; i < voice->mod_count; i++) for(i = 0; i < check_limit_count; i++)
{ {
if(fluid_mod_test_identity(&voice->mod[i], mod)) if(fluid_mod_test_identity(&voice->mod[i], mod))
{ {
@ -1704,9 +1705,10 @@ int fluid_voice_get_velocity(const fluid_voice_t *voice)
* A lower boundary for the attenuation (as in 'the minimum * A lower boundary for the attenuation (as in 'the minimum
* attenuation of this voice, with volume pedals, modulators * attenuation of this voice, with volume pedals, modulators
* etc. resulting in minimum attenuation, cannot fall below x cB) is * etc. resulting in minimum attenuation, cannot fall below x cB) is
* calculated. This has to be called during fluid_voice_init, after * calculated. This has to be called during fluid_voice_start, after
* all modulators have been run on the voice once. Also, * all modulators have been run on the voice once. Also,
* voice->attenuation has to be initialized. * voice->attenuation has to be initialized.
* (see fluid_voice_calculate_runtime_synthesis_parameters())
*/ */
static fluid_real_t static fluid_real_t
fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
@ -1733,30 +1735,42 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice)
{ {
fluid_real_t current_val = fluid_mod_get_value(mod, voice); fluid_real_t current_val = fluid_mod_get_value(mod, voice);
fluid_real_t v = fabs(mod->amount); /* min_val is the possible minimum value for this modulator.
it depends of 3 things :
1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar
or 0 if mapping is unipolar).
2)the sign of amount.
3)absolute value of amount.
if((mod->src1 == FLUID_MOD_PITCHWHEEL) When at least one source mapping is bipolar:
|| (mod->flags1 & FLUID_MOD_BIPOLAR) min_val is -|amount| regardless the sign of amount.
When both sources mapping are unipolar:
min_val is -|amount|, if amount is negative.
min_val is 0, if amount is positive
*/
fluid_real_t min_val = fabs(mod->amount);
/* Can this modulator produce a negative contribution? */
if((mod->flags1 & FLUID_MOD_BIPOLAR)
|| (mod->flags2 & FLUID_MOD_BIPOLAR) || (mod->flags2 & FLUID_MOD_BIPOLAR)
|| (mod->amount < 0)) || (mod->amount < 0))
{ {
/* Can this modulator produce a negative contribution? */ min_val *= -1.0; /* min_val = - |amount|*/
v *= -1.0;
} }
else else
{ {
/* No negative value possible. But still, the minimum contribution is 0. */ /* No negative value possible. But still, the minimum contribution is 0. */
v = 0; min_val = 0;
} }
/* For example: /* For example:
* - current_val=100 * - current_val=100
* - min_val=-4000 * - min_val=-4000
* - possible_att_reduction_cB += 4100 * - possible reduction contribution of this modulator = current_val - min_val = 4100
*/ */
if(current_val > v) if(current_val > min_val)
{ {
possible_att_reduction_cB += (current_val - v); possible_att_reduction_cB += (current_val - min_val);
} }
} }
} }

View file

@ -157,6 +157,7 @@ void fluid_voice_release(fluid_voice_t *voice);
void fluid_voice_noteoff(fluid_voice_t *voice); void fluid_voice_noteoff(fluid_voice_t *voice);
void fluid_voice_off(fluid_voice_t *voice); void fluid_voice_off(fluid_voice_t *voice);
void fluid_voice_stop(fluid_voice_t *voice); void fluid_voice_stop(fluid_voice_t *voice);
void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice); void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice);
int fluid_voice_kill_excl(fluid_voice_t *voice); int fluid_voice_kill_excl(fluid_voice_t *voice);

View file

@ -103,7 +103,7 @@
#endif #endif
#if HAVE_IO_H #if HAVE_IO_H
#include <io.h> #include <io.h> // _open(), _close(), read(), write() on windows
#endif #endif
#if HAVE_SIGNAL_H #if HAVE_SIGNAL_H
@ -262,7 +262,7 @@ typedef FILE *fluid_file;
#define FLUID_STRNCPY(_dst,_src,_n) \ #define FLUID_STRNCPY(_dst,_src,_n) \
do { strncpy(_dst,_src,_n); \ do { strncpy(_dst,_src,_n); \
(_dst)[(_n)-1]=0; \ (_dst)[(_n)-1]='\0'; \
}while(0) }while(0)
#define FLUID_STRCHR(_s,_c) strchr(_s,_c) #define FLUID_STRCHR(_s,_c) strchr(_s,_c)

View file

@ -65,7 +65,7 @@ def build(bld):
'src/fluid_synth_monopoly.c', 'src/fluid_synth_monopoly.c',
'src/fluid_sys.c' 'src/fluid_sys.c'
], ],
cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden', '-std=gnu99' ], cflags = [ bld.env['compiler_flags_dict']['pic'], '-fvisibility=hidden', '-std=gnu99', '-Wno-unused-function' ],
includes = ['.', 'src/' ], includes = ['.', 'src/' ],
target = 'libfluidsynth', target = 'libfluidsynth',
use = 'libfluidsynth_includes', use = 'libfluidsynth_includes',

View file

@ -78,10 +78,10 @@ index 37b0a06a4..b6586895b 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 4795fceb3..b82bfdf61 100644 index 1a394f811..8deb0b0d1 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
@@ -75,7 +75,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); @@ -76,7 +76,7 @@ static int fluid_midi_file_read_tracklen(fluid_midi_file *mf);
static int fluid_midi_file_eot(fluid_midi_file *mf); static int fluid_midi_file_eot(fluid_midi_file *mf);
static int fluid_midi_file_get_division(fluid_midi_file *midifile); static int fluid_midi_file_get_division(fluid_midi_file *midifile);
@ -90,7 +90,7 @@ index 4795fceb3..b82bfdf61 100644
/*************************************************************** /***************************************************************
* *
* MIDIFILE * MIDIFILE
@@ -1011,6 +1011,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile) @@ -1047,6 +1047,7 @@ fluid_midi_file_get_division(fluid_midi_file *midifile)
{ {
return midifile->division; return midifile->division;
} }
@ -98,7 +98,7 @@ index 4795fceb3..b82bfdf61 100644
/****************************************************** /******************************************************
* *
@@ -1330,7 +1331,7 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, @@ -1413,7 +1414,7 @@ static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **dat
* *
* fluid_track_t * fluid_track_t
*/ */
@ -107,35 +107,39 @@ index 4795fceb3..b82bfdf61 100644
/* /*
* new_fluid_track * new_fluid_track
*/ */
@@ -2434,3 +2435,4 @@ fluid_midi_event_length(unsigned char event) @@ -2517,3 +2518,4 @@ fluid_midi_event_length(unsigned char event)
return 1; return 1;
} }
+#endif +#endif
diff --git b/libs/fluidsynth/src/fluid_rev.c a/libs/fluidsynth/src/fluid_rev.c diff --git b/libs/fluidsynth/src/fluid_mod.c a/libs/fluidsynth/src/fluid_mod.c
index 198a06e58..894afc5a0 100644 index 84e97731e..5e57455d4 100644
--- b/libs/fluidsynth/src/fluid_rev.c --- b/libs/fluidsynth/src/fluid_mod.c
+++ a/libs/fluidsynth/src/fluid_rev.c +++ a/libs/fluidsynth/src/fluid_mod.c
@@ -51,7 +51,7 @@ void fluid_allpass_init(fluid_allpass *allpass); @@ -603,7 +603,7 @@ fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
void fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val); * @param name,if not NULL, pointer on a string displayed as a warning.
fluid_real_t fluid_allpass_getfeedback(fluid_allpass *allpass); * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.
*/
-void -int fluid_mod_check_sources(const fluid_mod_t *mod, char *name)
+static void +int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name)
fluid_allpass_setbuffer(fluid_allpass *allpass, int size)
{ {
allpass->bufidx = 0; static const char *invalid_non_cc_src =
@@ -59,7 +59,7 @@ fluid_allpass_setbuffer(fluid_allpass *allpass, int size) "Invalid modulator, using non-CC source %s.src%d=%d";
allpass->bufsize = size; diff --git b/libs/fluidsynth/src/fluid_mod.h a/libs/fluidsynth/src/fluid_mod.h
} index 3e7661741..ec8e967a3 100644
--- b/libs/fluidsynth/src/fluid_mod.h
+++ a/libs/fluidsynth/src/fluid_mod.h
@@ -44,7 +44,7 @@ struct _fluid_mod_t
};
-void fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
+static void -int fluid_mod_check_sources(const fluid_mod_t *mod, char *name);
fluid_allpass_release(fluid_allpass *allpass) +int fluid_mod_check_sources(const fluid_mod_t *mod, const char *name);
{
FLUID_FREE(allpass->buffer); #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 diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.c a/libs/fluidsynth/src/fluid_rvoice_mixer.c
index af0ef75d1..9e7b164bb 100644 index af0ef75d1..9acc5f830 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
@@ -24,7 +24,6 @@ @@ -24,7 +24,6 @@
@ -146,7 +150,6 @@ index af0ef75d1..9e7b164bb 100644
#include "fluid_synth.h" #include "fluid_synth.h"
// If less than x voices, the thread overhead is larger than the gain,
diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.h a/libs/fluidsynth/src/fluid_rvoice_mixer.h diff --git b/libs/fluidsynth/src/fluid_rvoice_mixer.h a/libs/fluidsynth/src/fluid_rvoice_mixer.h
index 4ee072e4b..1b3fceb34 100644 index 4ee072e4b..1b3fceb34 100644
--- b/libs/fluidsynth/src/fluid_rvoice_mixer.h --- b/libs/fluidsynth/src/fluid_rvoice_mixer.h
@ -188,7 +191,7 @@ index 02be9a033..9207ab063 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 faadefb27..79c5a9c87 100644 index 07bfc0427..b4524a2ac 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
@@ -267,7 +267,7 @@ void fluid_version(int *major, int *minor, int *micro) @@ -267,7 +267,7 @@ void fluid_version(int *major, int *minor, int *micro)
@ -200,7 +203,7 @@ index faadefb27..79c5a9c87 100644
fluid_version_str(void) fluid_version_str(void)
{ {
return FLUIDSYNTH_VERSION; return FLUIDSYNTH_VERSION;
@@ -6430,6 +6430,7 @@ int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) @@ -6442,6 +6442,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);
} }
@ -208,7 +211,7 @@ index faadefb27..79c5a9c87 100644
/** /**
* Return the LADSPA effects instance used by FluidSynth * Return the LADSPA effects instance used by FluidSynth
* *
@@ -6442,6 +6443,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) @@ -6454,6 +6455,7 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
return synth->ladspa_fx; return synth->ladspa_fx;
} }
@ -240,7 +243,7 @@ index 156424af1..58869730c 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 3df88fc23..cce778b3c 100644 index d686737f6..28911ee7f 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
@@ -202,9 +202,10 @@ fluid_log(int level, const char *fmt, ...) @@ -202,9 +202,10 @@ fluid_log(int level, const char *fmt, ...)
@ -257,10 +260,10 @@ index 3df88fc23..cce778b3c 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 47cc95c11..d95f6159f 100644 index 72b323029..122938655 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
@@ -91,7 +91,7 @@ else \ @@ -99,7 +99,7 @@ else \
/* /*
* Utility functions * Utility functions
*/ */
@ -270,7 +273,7 @@ index 47cc95c11..d95f6159f 100644
#if defined(__OS2__) #if defined(__OS2__)
diff --git b/libs/fluidsynth/src/fluidsynth_priv.h a/libs/fluidsynth/src/fluidsynth_priv.h diff --git b/libs/fluidsynth/src/fluidsynth_priv.h a/libs/fluidsynth/src/fluidsynth_priv.h
index d5dbdf7e9..5de758dc0 100644 index 4df590e21..384785fcc 100644
--- b/libs/fluidsynth/src/fluidsynth_priv.h --- b/libs/fluidsynth/src/fluidsynth_priv.h
+++ a/libs/fluidsynth/src/fluidsynth_priv.h +++ a/libs/fluidsynth/src/fluidsynth_priv.h
@@ -26,10 +26,6 @@ @@ -26,10 +26,6 @@

View file

@ -23,6 +23,11 @@ cd $TMP
#git clone git://git.code.sf.net/p/fluidsynth/code-git fs-git #git clone git://git.code.sf.net/p/fluidsynth/code-git fs-git
git clone git://github.com/FluidSynth/fluidsynth.git fs-git git clone git://github.com/FluidSynth/fluidsynth.git fs-git
cd fs-git;
git describe --tags
git log | head
cd $TMP
FSR=fs-git/ FSR=fs-git/
rsync -auc --info=progress2 \ rsync -auc --info=progress2 \