From cee3d4e985d633da9299921176f99673988c6f26 Mon Sep 17 00:00:00 2001 From: derselbst Date: Mon, 25 Jun 2018 17:59:57 +0200 Subject: [PATCH 001/101] force attack section of modEnv to be convex --- src/rvoice/fluid_rvoice.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rvoice/fluid_rvoice.c b/src/rvoice/fluid_rvoice.c index e7687116..4fb13e16 100644 --- a/src/rvoice/fluid_rvoice.c +++ b/src/rvoice/fluid_rvoice.c @@ -305,6 +305,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) { int ticks = voice->envlfo.ticks; int count, is_looping; + fluid_real_t modenv_val; /******************* sample sanity check **********/ @@ -361,6 +362,12 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) /******************* 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 * through the original waveform with each step in the output * 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 + 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_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) + + modenv_val * voice->envlfo.modenv_to_pitch) / voice->dsp.root_pitch_hz; /******************* 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_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); From 8d8e7ecd20864a98e4dc4c516b8cac2c402fa7c5 Mon Sep 17 00:00:00 2001 From: jjceresa Date: Sun, 1 Jul 2018 23:00:54 +0200 Subject: [PATCH 002/101] noteoff during the attack section of the modulation envelope. - this insures seamless pitch and filter frequency cutoff transition. --- src/rvoice/fluid_rvoice.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/rvoice/fluid_rvoice.c b/src/rvoice/fluid_rvoice.c index 4fb13e16..3bc421ab 100644 --- a/src/rvoice/fluid_rvoice.c +++ b/src/rvoice/fluid_rvoice.c @@ -592,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 * envelope. The attack section ramps up linearly with * 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. */ if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) @@ -605,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.modenv, FLUID_VOICE_ENVRELEASE); } From 3db36117a1f7d24e4536e25e4ee510cebcda999c Mon Sep 17 00:00:00 2001 From: jjceresa Date: Sat, 14 Jul 2018 23:33:16 +0200 Subject: [PATCH 003/101] Adding seamless attack transition for legato notes. Since v 2.1 ,as recommended by soundfont 2.01/2.4 spec ATTACK section is convex. So, when skiping from a section above ATTACK to ATTACK, it is necessary to correct current modulation envelope value to get seamless transition. --- src/rvoice/fluid_rvoice.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/rvoice/fluid_rvoice.c b/src/rvoice/fluid_rvoice.c index 3bc421ab..600f18f8 100644 --- a/src/rvoice/fluid_rvoice.c +++ b/src/rvoice/fluid_rvoice.c @@ -675,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) { 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 = fluid_adsr_env_get_section(&voice->envlfo.volenv); if(section >= FLUID_VOICE_ENVHOLD) { /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new @@ -697,16 +698,30 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) /* skips to Attack section from any section */ /* Update vol and attack data */ fluid_rvoice_local_retrigger_attack(voice); + /*------------------------------------------------------------------------- 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 */ 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 */ } /** From f0312c13d351c06f38a5d8e575e3af9bd18c2b2a Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 9 Oct 2018 17:52:33 +0200 Subject: [PATCH 004/101] deprecate fluid_synth_error() --- doc/fluidsynth-v20-devdoc.txt | 6 ++++++ include/fluidsynth/synth.h | 2 +- src/synth/fluid_synth.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index c19b4ce6..8c9cdcbd 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -21,6 +21,7 @@ All the source code examples in this document are in the public domain; you can - \ref Disclaimer - \ref Introduction +- \ref NewIn2_0_2 - \ref NewIn2_0_0 - \ref CreatingSettings - \ref CreatingSynth @@ -60,6 +61,11 @@ What is FluidSynth? - FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org + +\section NewIn2_0_2 Whats new in 2.0.2? + +- fluid_synth_error() has been deprecated, use fluid_set_log_function() to interfere log messages + \section NewIn2_0_0 Whats new in 2.0.0? FluidSynths major version was bumped. The API was reworked, deprecated functions were removed. diff --git a/include/fluidsynth/synth.h b/include/fluidsynth/synth.h index 1a0046fe..369a2c26 100644 --- a/include/fluidsynth/synth.h +++ b/include/fluidsynth/synth.h @@ -233,7 +233,7 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p /* Misc */ FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); -FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); /* Default modulators */ diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index c7ce3dde..65318a77 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1110,6 +1110,8 @@ delete_fluid_synth(fluid_synth_t *synth) * @return Pointer to string of last error message. Valid until the same * calling thread calls another FluidSynth function which fails. String is * internal and should not be modified or freed. + * @deprecated This function is not thread-safe and does not work with multiple synths. + * It has been deprecated. It may return "" in a future release and will eventually be removed. */ /* FIXME - The error messages are not thread-safe, yet. They are still stored * in a global message buffer (see fluid_sys.c). */ From d8890038b6eac678ba0539fd79cfe5b753037b2e Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 13 Oct 2018 14:37:41 +0200 Subject: [PATCH 005/101] Rounded samples do not need to be float. Actually, the function roundi() already returns an "int" type value, so in my opinion there is no need to use a floating point value for saturating the values in the range -32768/+32767. The generated assembly code looks more efficient after that. --- src/synth/fluid_synth.c | 48 +++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index a40ba2ea..50028c5f 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -3851,8 +3851,8 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, signed short *right_out = (signed short *) rout; fluid_real_t *left_in; fluid_real_t *right_in; - fluid_real_t left_sample; - fluid_real_t right_sample; + int left_sample; + int right_sample; double time = fluid_utime(); int di; float cpu_load; @@ -3880,32 +3880,30 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, left_sample = roundi(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]); right_sample = roundi(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); - di++; - - if(di >= DITHER_SIZE) + if(++di >= DITHER_SIZE) { di = 0; } /* digital clipping */ - if(left_sample > 32767.0f) + if(left_sample > 32767) { - left_sample = 32767.0f; + left_sample = 32767; } - if(left_sample < -32768.0f) + if(left_sample < -32768) { - left_sample = -32768.0f; + left_sample = -32768; } - if(right_sample > 32767.0f) + if(right_sample > 32767) { - right_sample = 32767.0f; + right_sample = 32767; } - if(right_sample < -32768.0f) + if(right_sample < -32768) { - right_sample = -32768.0f; + right_sample = -32768; } left_out[j] = (signed short) left_sample; @@ -3950,8 +3948,8 @@ fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, int i, j, k; signed short *left_out = (signed short *) lout; signed short *right_out = (signed short *) rout; - fluid_real_t left_sample; - fluid_real_t right_sample; + int left_sample; + int right_sample; int di = *dither_index; fluid_profile_ref_var(prof_ref); @@ -3961,32 +3959,30 @@ fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, left_sample = roundi(lin[i] * 32766.0f + rand_table[0][di]); right_sample = roundi(rin[i] * 32766.0f + rand_table[1][di]); - di++; - - if(di >= DITHER_SIZE) + if(++di >= DITHER_SIZE) { di = 0; } /* digital clipping */ - if(left_sample > 32767.0f) + if(left_sample > 32767) { - left_sample = 32767.0f; + left_sample = 32767; } - if(left_sample < -32768.0f) + if(left_sample < -32768) { - left_sample = -32768.0f; + left_sample = -32768; } - if(right_sample > 32767.0f) + if(right_sample > 32767) { - right_sample = 32767.0f; + right_sample = 32767; } - if(right_sample < -32768.0f) + if(right_sample < -32768) { - right_sample = -32768.0f; + right_sample = -32768; } left_out[j] = (signed short) left_sample; From ebc177f48f2197e1c5d1e7371deebb1ffca88ffe Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Tue, 16 Oct 2018 18:02:27 +0200 Subject: [PATCH 006/101] Consistently use FLUID_LOG macro (#443) --- src/midi/fluid_seq.c | 18 +++++++++--------- src/midi/fluid_seqbind.c | 6 ++---- src/rvoice/fluid_chorus.c | 24 ++++++++++++------------ src/synth/fluid_event.c | 6 +++--- src/utils/fluidsynth_priv.h | 9 +++++++++ 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/midi/fluid_seq.c b/src/midi/fluid_seq.c index f3cd6a05..a6780670 100644 --- a/src/midi/fluid_seq.c +++ b/src/midi/fluid_seq.c @@ -117,7 +117,7 @@ new_fluid_sequencer2(int use_system_timer) if(seq == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } @@ -132,7 +132,7 @@ new_fluid_sequencer2(int use_system_timer) if(-1 == _fluid_seq_queue_init(seq, FLUID_SEQUENCER_EVENTS_MAX)) { FLUID_FREE(seq); - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } @@ -144,7 +144,7 @@ new_fluid_sequencer2(int use_system_timer) { _fluid_seq_queue_end(seq); FLUID_FREE(seq); - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } @@ -297,7 +297,7 @@ fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name, if(client == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } @@ -305,7 +305,7 @@ fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name, if(nameCopy == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); FLUID_FREE(client); return FLUID_FAILED; } @@ -575,7 +575,7 @@ fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale) { if(scale <= 0) { - fluid_log(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); + FLUID_LOG(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); return; } @@ -722,7 +722,7 @@ _fluid_seq_queue_init(fluid_sequencer_t *seq, int maxEvents) if(seq->heap == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return -1; } @@ -801,7 +801,7 @@ _fluid_seq_queue_pre_insert(fluid_sequencer_t *seq, fluid_event_t *evt) if(evtentry == NULL) { /* should not happen */ - fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: no more free events\n"); return -1; } @@ -839,7 +839,7 @@ _fluid_seq_queue_pre_remove(fluid_sequencer_t *seq, fluid_seq_id_t src, fluid_se if(evtentry == NULL) { /* should not happen */ - fluid_log(FLUID_PANIC, "sequencer: no more free events\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: no more free events\n"); return; } diff --git a/src/midi/fluid_seqbind.c b/src/midi/fluid_seqbind.c index 88f2542e..28f0f356 100644 --- a/src/midi/fluid_seqbind.c +++ b/src/midi/fluid_seqbind.c @@ -108,7 +108,7 @@ fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth if(seqbind == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return FLUID_FAILED; } @@ -125,7 +125,7 @@ fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth if(seqbind->sample_timer == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); delete_fluid_seqbind(seqbind); return FLUID_FAILED; } @@ -359,5 +359,3 @@ fluid_sequencer_add_midi_event_to_buffer(void *data, fluid_midi_event_t *event) /* Schedule for sending at next call to fluid_sequencer_process */ return fluid_sequencer_send_at(seq, &evt, 0, 0); } - - diff --git a/src/rvoice/fluid_chorus.c b/src/rvoice/fluid_chorus.c index 1ffcec26..83a88d38 100644 --- a/src/rvoice/fluid_chorus.c +++ b/src/rvoice/fluid_chorus.c @@ -149,7 +149,7 @@ new_fluid_chorus(fluid_real_t sample_rate) if(chorus == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); return NULL; } @@ -191,7 +191,7 @@ new_fluid_chorus(fluid_real_t sample_rate) if(chorus->lookup_tab == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); goto error_recovery; } @@ -201,7 +201,7 @@ new_fluid_chorus(fluid_real_t sample_rate) if(chorus->chorusbuf == NULL) { - fluid_log(FLUID_PANIC, "chorus: Out of memory"); + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); goto error_recovery; } @@ -293,32 +293,32 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, if(chorus->number_blocks < 0) { - fluid_log(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); + FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); chorus->number_blocks = 0; } else if(chorus->number_blocks > MAX_CHORUS) { - fluid_log(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", + FLUID_LOG(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", MAX_CHORUS); chorus->number_blocks = MAX_CHORUS; } if(chorus->speed_Hz < MIN_SPEED_HZ) { - fluid_log(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", + FLUID_LOG(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", (double) MIN_SPEED_HZ); chorus->speed_Hz = MIN_SPEED_HZ; } else if(chorus->speed_Hz > MAX_SPEED_HZ) { - fluid_log(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", + FLUID_LOG(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", (double) MAX_SPEED_HZ); chorus->speed_Hz = MAX_SPEED_HZ; } if(chorus->depth_ms < 0.0) { - fluid_log(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); + FLUID_LOG(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); chorus->depth_ms = 0.0; } @@ -326,12 +326,12 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, if(chorus->level < 0.0) { - fluid_log(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); + FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); chorus->level = 0.0; } else if(chorus->level > 10) { - fluid_log(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " + FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " "Setting it to 0.1."); chorus->level = 0.1; } @@ -346,7 +346,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, if(modulation_depth_samples > MAX_SAMPLES) { - fluid_log(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); + FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", MAX_SAMPLES); modulation_depth_samples = MAX_SAMPLES; // set depth to maximum to avoid spamming console with above warning chorus->depth_ms = (modulation_depth_samples * 1000) / chorus->sample_rate; @@ -356,7 +356,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, switch(chorus->type) { default: - fluid_log(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); + FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); chorus->type = FLUID_CHORUS_MOD_SINE; /* fall-through */ diff --git a/src/synth/fluid_event.c b/src/synth/fluid_event.c index 6baec6fa..d7962eac 100644 --- a/src/synth/fluid_event.c +++ b/src/synth/fluid_event.c @@ -62,7 +62,7 @@ new_fluid_event() if(evt == NULL) { - fluid_log(FLUID_PANIC, "event: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "event: Out of memory\n"); return NULL; } @@ -751,7 +751,7 @@ _fluid_evt_heap_init(int nbEvents) if(heap == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } @@ -782,7 +782,7 @@ _fluid_evt_heap_init(int nbEvents) if(heap == NULL) { - fluid_log(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); return NULL; } diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index 5d216b10..41e39839 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -322,7 +322,16 @@ do { strncpy(_dst,_src,_n); \ #define FLUID_FLUSH() fflush(stdout) #endif +/* People who want to reduce the size of the may do this by entirely + * removing the logging system. This will cause all log messages to + * be discarded at compile time, allowing to save about 80 KiB for + * the compiled binary. + */ +#if 0 +#define FLUID_LOG (void)sizeof +#else #define FLUID_LOG fluid_log +#endif #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 From 5bd07c120c670d4e03cfd954c7cc5ba9a61223ef Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 20 Oct 2018 14:54:42 +0200 Subject: [PATCH 007/101] fix build when no audio drivers are supported --- src/drivers/fluid_adriver.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index efec3de6..405d44a7 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -138,6 +138,8 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = NULL }, #endif + /* NULL terminator to avoid zero size array if no driver available */ + { NULL, NULL, NULL, NULL, NULL } }; #define ENABLE_AUDIO_DRIVER(_drv, _idx) \ @@ -227,7 +229,7 @@ void fluid_audio_driver_settings(fluid_settings_t *settings) fluid_settings_add_option(settings, "audio.driver", "file"); #endif - for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { if(fluid_audio_drivers[i].settings != NULL && IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) @@ -244,7 +246,7 @@ find_fluid_audio_driver(fluid_settings_t *settings) char *name; char *allnames; - for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { /* If this driver is de-activated, just ignore it */ if(!IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) @@ -363,7 +365,7 @@ delete_fluid_audio_driver(fluid_audio_driver_t *driver) fluid_return_if_fail(driver != NULL); /* iterate over fluid_audio_drivers_template to ensure deleting even drivers currently not registered */ - for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { if(fluid_audio_drivers[i].name == driver->name) { @@ -417,8 +419,8 @@ int fluid_audio_driver_register(const char **adrivers) { unsigned int j; - /* search the requested audio driver in the template and copy it over if found */ - for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers); j++) + /* search the requested audio driver in the template and enable it if found */ + for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; j++) { if(FLUID_STRCMP(adrivers[i], fluid_audio_drivers[j].name) == 0) { @@ -427,19 +429,13 @@ int fluid_audio_driver_register(const char **adrivers) } } - if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers)) + if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers) - 1) { /* requested driver not found, failure */ return FLUID_FAILED; } } - if(i >= FLUID_N_ELEMENTS(fluid_audio_drivers)) - { - /* user requested more drivers than this build of fluidsynth supports, failure */ - return FLUID_FAILED; - } - /* Update list of activated drivers */ FLUID_MEMCPY(fluid_adriver_disable_mask, disable_mask, sizeof(disable_mask)); From 08b53333989c0894240df1fbf80407b1c9408325 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 20 Oct 2018 14:56:38 +0200 Subject: [PATCH 008/101] remove macro workaround when no midi drivers are supported --- src/drivers/fluid_mdriver.c | 42 +++++++++---------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/src/drivers/fluid_mdriver.c b/src/drivers/fluid_mdriver.c index d82c72aa..25a956e5 100644 --- a/src/drivers/fluid_mdriver.c +++ b/src/drivers/fluid_mdriver.c @@ -21,16 +21,6 @@ #include "fluid_mdriver.h" #include "fluid_settings.h" -#undef FLUID_MIDI_SUPPORT - -#if ALSA_SUPPORT || JACK_SUPPORT || OSS_SUPPORT || \ - WINMIDI_SUPPORT || MIDISHARE_SUPPORT || COREMIDI_SUPPORT -/* At least an input driver exits */ -#define FLUID_MIDI_SUPPORT 1 -#endif - - -#ifdef FLUID_MIDI_SUPPORT /* * fluid_mdriver_definition @@ -102,15 +92,14 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = fluid_coremidi_driver_settings }, #endif + /* NULL terminator to avoid zero size array if no driver available */ + { NULL, NULL, NULL, NULL } }; -#endif /* FLUID_MIDI_SUPPORT */ void fluid_midi_driver_settings(fluid_settings_t *settings) { -#ifdef FLUID_MIDI_SUPPORT unsigned int i; -#endif fluid_settings_register_int(settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED); @@ -155,17 +144,13 @@ void fluid_midi_driver_settings(fluid_settings_t *settings) fluid_settings_add_option(settings, "midi.driver", "coremidi"); #endif -#ifdef FLUID_MIDI_SUPPORT - - for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { if(fluid_midi_drivers[i].settings != NULL) { fluid_midi_drivers[i].settings(settings); } } - -#endif } /** @@ -178,21 +163,21 @@ void fluid_midi_driver_settings(fluid_settings_t *settings) */ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data) { -#ifdef FLUID_MIDI_SUPPORT fluid_midi_driver_t *driver = NULL; char *allnames; unsigned int i; - for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { - if(fluid_settings_str_equal(settings, "midi.driver", fluid_midi_drivers[i].name)) + const char* name = fluid_midi_drivers[i].name; + if(fluid_settings_str_equal(settings, "midi.driver", name)) { - FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", fluid_midi_drivers[i].name); + FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", name); driver = fluid_midi_drivers[i].new(settings, handler, event_handler_data); if(driver) { - driver->name = fluid_midi_drivers[i].name; + driver->name = name; } return driver; @@ -203,12 +188,8 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver. Valid drivers are: %s.", allnames ? allnames : "ERROR"); - if(allnames) - { - FLUID_FREE(allnames); - } + FLUID_FREE(allnames); -#endif return NULL; } @@ -218,11 +199,10 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi */ void delete_fluid_midi_driver(fluid_midi_driver_t *driver) { -#ifdef FLUID_MIDI_SUPPORT unsigned int i; fluid_return_if_fail(driver != NULL); - for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers); i++) + for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { if(fluid_midi_drivers[i].name == driver->name) { @@ -230,6 +210,4 @@ void delete_fluid_midi_driver(fluid_midi_driver_t *driver) return; } } - -#endif } From 670cdf1e8fb121ea9152ed3a6ffa7d013fc06b26 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 20 Oct 2018 15:15:43 +0200 Subject: [PATCH 009/101] conditionally compile fluid_aufile.c --- src/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b90af05d..43228ab5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,6 +105,10 @@ if ( MIDISHARE_SUPPORT ) include_directories ( ${MidiShare_INCLUDE_DIRS} ) endif ( MIDISHARE_SUPPORT ) +if ( AUFILE_SUPPORT ) + set ( fluid_aufile_SOURCES drivers/fluid_aufile.c ) +endif ( AUFILE_SUPPORT ) + set ( config_SOURCES ${CMAKE_BINARY_DIR}/config.h ) set ( libfluidsynth_SOURCES @@ -172,7 +176,6 @@ set ( libfluidsynth_SOURCES drivers/fluid_adriver.h drivers/fluid_mdriver.c drivers/fluid_mdriver.h - drivers/fluid_aufile.c bindings/fluid_cmd.c bindings/fluid_cmd.h bindings/fluid_filerenderer.c @@ -229,6 +232,7 @@ endif ( WIN32 AND NOT MINGW ) add_library ( libfluidsynth-OBJ OBJECT ${config_SOURCES} ${fluid_alsa_SOURCES} + ${fluid_aufile_SOURCES} ${fluid_coreaudio_SOURCES} ${fluid_coremidi_SOURCES} ${fluid_dart_SOURCES} From 326c20c0e33402d87f89b6eac632615e870e0bfb Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 20 Oct 2018 20:06:38 +0200 Subject: [PATCH 010/101] Remove duplicated init_dither() declaration (#446) --- src/synth/fluid_synth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index a40ba2ea..c10a0106 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -148,7 +148,6 @@ static int fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int /* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ static fluid_atomic_int_t fluid_synth_initialized = {0}; static void fluid_synth_init(void); -static void init_dither(void); /* default modulators * SF2.01 page 52 ff: From 70f4551e9093029bae7650bfdb4276fb44385fee Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 20 Oct 2018 20:59:58 +0200 Subject: [PATCH 011/101] Simplify audio driver installation In my opionion, it should be possible to simplify the code by installing the drivers with a for() cycle instead of doing tons of #ifdef...#endif. The size of the binary code is basically the same as before, but the source lines are much less. I think that it could be done also for MIDI input drivers. --- src/drivers/fluid_adriver.c | 110 +++++++++++------------------------- 1 file changed, 33 insertions(+), 77 deletions(-) diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index 405d44a7..e54d8509 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -59,16 +59,6 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif -#if OSS_SUPPORT - { - "oss", - new_fluid_oss_audio_driver, - new_fluid_oss_audio_driver2, - delete_fluid_oss_audio_driver, - fluid_oss_audio_driver_settings - }, -#endif - #if PULSE_SUPPORT { "pulseaudio", @@ -79,6 +69,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif +#if OSS_SUPPORT + { + "oss", + new_fluid_oss_audio_driver, + new_fluid_oss_audio_driver2, + delete_fluid_oss_audio_driver, + fluid_oss_audio_driver_settings + }, +#endif + #if COREAUDIO_SUPPORT { "coreaudio", @@ -99,16 +99,6 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif -#if PORTAUDIO_SUPPORT - { - "portaudio", - new_fluid_portaudio_driver, - NULL, - delete_fluid_portaudio_driver, - fluid_portaudio_driver_settings - }, -#endif - #if SNDMAN_SUPPORT { "sndman", @@ -119,6 +109,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif +#if PORTAUDIO_SUPPORT + { + "portaudio", + new_fluid_portaudio_driver, + NULL, + delete_fluid_portaudio_driver, + fluid_portaudio_driver_settings + }, +#endif + #if DART_SUPPORT { "dart", @@ -153,6 +153,7 @@ static uint8_t fluid_adriver_disable_mask[(FLUID_N_ELEMENTS(fluid_audio_drivers) void fluid_audio_driver_settings(fluid_settings_t *settings) { unsigned int i; + const char *def_name = NULL; fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0); fluid_settings_add_option(settings, "audio.sample-format", "16bits"); @@ -172,71 +173,26 @@ void fluid_audio_driver_settings(fluid_settings_t *settings) fluid_settings_register_int(settings, "audio.realtime-prio", FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0); - /* Set the default driver */ -#if JACK_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "jack", 0); -#elif ALSA_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "alsa", 0); -#elif PULSE_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "pulseaudio", 0); -#elif OSS_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "oss", 0); -#elif COREAUDIO_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "coreaudio", 0); -#elif DSOUND_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "dsound", 0); -#elif SNDMAN_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "sndman", 0); -#elif PORTAUDIO_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "portaudio", 0); -#elif DART_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "dart", 0); -#elif AUFILE_SUPPORT - fluid_settings_register_str(settings, "audio.driver", "file", 0); -#else - fluid_settings_register_str(settings, "audio.driver", "", 0); -#endif - - /* Add all drivers to the list of options */ -#if PULSE_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "pulseaudio"); -#endif -#if ALSA_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "alsa"); -#endif -#if OSS_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "oss"); -#endif -#if COREAUDIO_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "coreaudio"); -#endif -#if DSOUND_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "dsound"); -#endif -#if SNDMAN_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "sndman"); -#endif -#if PORTAUDIO_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "portaudio"); -#endif -#if JACK_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "jack"); -#endif -#if DART_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "dart"); -#endif -#if AUFILE_SUPPORT - fluid_settings_add_option(settings, "audio.driver", "file"); -#endif - for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { + /* Select the default driver */ + if (def_name == NULL) + { + def_name = fluid_audio_drivers[i].name; + } + + /* Add the driver to the list of options */ + fluid_settings_add_option(settings, "audio.driver", fluid_audio_drivers[i].name); + if(fluid_audio_drivers[i].settings != NULL && IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) { fluid_audio_drivers[i].settings(settings); } } + + /* Set the default driver */ + fluid_settings_register_str(settings, "audio.driver", def_name ? def_name : "", 0); } static const fluid_audriver_definition_t * From a7c5b66872ebc80e4b023a2476b66093fa1665d0 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 21 Oct 2018 12:18:57 +0200 Subject: [PATCH 012/101] Simplify MIDI driver installation --- src/drivers/fluid_mdriver.c | 78 +++++++++++++------------------------ 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/src/drivers/fluid_mdriver.c b/src/drivers/fluid_mdriver.c index 25a956e5..4062bd21 100644 --- a/src/drivers/fluid_mdriver.c +++ b/src/drivers/fluid_mdriver.c @@ -38,6 +38,20 @@ struct fluid_mdriver_definition_t static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = { +#if ALSA_SUPPORT + { + "alsa_seq", + new_fluid_alsa_seq_driver, + delete_fluid_alsa_seq_driver, + fluid_alsa_seq_driver_settings + }, + { + "alsa_raw", + new_fluid_alsa_rawmidi_driver, + delete_fluid_alsa_rawmidi_driver, + fluid_alsa_rawmidi_driver_settings + }, +#endif #if JACK_SUPPORT { "jack", @@ -54,20 +68,6 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = fluid_oss_midi_driver_settings }, #endif -#if ALSA_SUPPORT - { - "alsa_raw", - new_fluid_alsa_rawmidi_driver, - delete_fluid_alsa_rawmidi_driver, - fluid_alsa_rawmidi_driver_settings - }, - { - "alsa_seq", - new_fluid_alsa_seq_driver, - delete_fluid_alsa_seq_driver, - fluid_alsa_seq_driver_settings - }, -#endif #if WINMIDI_SUPPORT { "winmidi", @@ -100,57 +100,33 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = void fluid_midi_driver_settings(fluid_settings_t *settings) { unsigned int i; + const char *def_name = NULL; fluid_settings_register_int(settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED); fluid_settings_register_int(settings, "midi.realtime-prio", FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0); - /* Set the default driver */ -#if ALSA_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "alsa_seq", 0); -#elif JACK_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "jack", 0); -#elif OSS_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "oss", 0); -#elif WINMIDI_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "winmidi", 0); -#elif MIDISHARE_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "midishare", 0); -#elif COREMIDI_SUPPORT - fluid_settings_register_str(settings, "midi.driver", "coremidi", 0); -#else - fluid_settings_register_str(settings, "midi.driver", "", 0); -#endif - - /* Add all drivers to the list of options */ -#if ALSA_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "alsa_seq"); - fluid_settings_add_option(settings, "midi.driver", "alsa_raw"); -#endif -#if JACK_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "jack"); -#endif -#if OSS_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "oss"); -#endif -#if WINMIDI_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "winmidi"); -#endif -#if MIDISHARE_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "midishare"); -#endif -#if COREMIDI_SUPPORT - fluid_settings_add_option(settings, "midi.driver", "coremidi"); -#endif for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { + /* Select the default driver */ + if (def_name == NULL) + { + def_name = fluid_midi_drivers[i].name; + } + + /* Add the driver to the list of options */ + fluid_settings_add_option(settings, "midi.driver", fluid_midi_drivers[i].name); + if(fluid_midi_drivers[i].settings != NULL) { fluid_midi_drivers[i].settings(settings); } } + + /* Set the default driver */ + fluid_settings_register_str(settings, "midi.driver", def_name ? def_name : "", 0); } /** From 327b3fb45b78da3f7a40c57209c89c552e582473 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 21 Oct 2018 20:21:21 +0200 Subject: [PATCH 013/101] clarify log message when no audio drivers are available --- src/drivers/fluid_adriver.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index e54d8509..17cde0aa 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -217,21 +217,25 @@ find_fluid_audio_driver(fluid_settings_t *settings) } } - allnames = fluid_settings_option_concat(settings, "audio.driver", NULL); fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ - FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver %s. Valid drivers are: %s.", - name ? name : "NULL", allnames ? allnames : "ERROR"); - - if(name) + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver '%s'.", name ? name : "NULL"); + + allnames = fluid_settings_option_concat(settings, "audio.driver", NULL); + if(allnames != NULL) { - FLUID_FREE(name); + if(allnames[0] != '\0') + { + FLUID_LOG(FLUID_INFO, "Valid drivers are: %s", allnames); + } + else + { + FLUID_LOG(FLUID_INFO, "No audio drivers available."); + } } - - if(allnames) - { - FLUID_FREE(allnames); - } - + + FLUID_FREE(name); + FLUID_FREE(allnames); + return NULL; } From f6e70b38c749c9ef6e78d62ef5febf98621e59a7 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 21 Oct 2018 20:26:46 +0200 Subject: [PATCH 014/101] clarify log message when no MIDI drivers are available --- src/drivers/fluid_mdriver.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/drivers/fluid_mdriver.c b/src/drivers/fluid_mdriver.c index 4062bd21..9953cefe 100644 --- a/src/drivers/fluid_mdriver.c +++ b/src/drivers/fluid_mdriver.c @@ -160,9 +160,19 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi } } + FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver."); allnames = fluid_settings_option_concat(settings, "midi.driver", NULL); - FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver. Valid drivers are: %s.", - allnames ? allnames : "ERROR"); + if(allnames != NULL) + { + if(allnames[0] != '\0') + { + FLUID_LOG(FLUID_INFO, "Valid drivers are: %s", allnames); + } + else + { + FLUID_LOG(FLUID_INFO, "No MIDI drivers available."); + } + } FLUID_FREE(allnames); From e03e2edf8d21a2677aa7771284bc1b40d54b5ea9 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 23 Oct 2018 17:07:34 +0200 Subject: [PATCH 015/101] register settings before adding options --- src/drivers/fluid_adriver.c | 9 +++++++-- src/drivers/fluid_mdriver.c | 10 +++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index 17cde0aa..7b7a28ce 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -172,6 +172,8 @@ void fluid_audio_driver_settings(fluid_settings_t *settings) fluid_settings_register_int(settings, "audio.realtime-prio", FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0); + + fluid_settings_register_str(settings, "audio.driver", "", 0); for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) { @@ -191,8 +193,11 @@ void fluid_audio_driver_settings(fluid_settings_t *settings) } } - /* Set the default driver */ - fluid_settings_register_str(settings, "audio.driver", def_name ? def_name : "", 0); + /* Set the default driver, if any */ + if(def_name != NULL) + { + fluid_settings_setstr(settings, "audio.driver", def_name); + } } static const fluid_audriver_definition_t * diff --git a/src/drivers/fluid_mdriver.c b/src/drivers/fluid_mdriver.c index 9953cefe..6c23ab93 100644 --- a/src/drivers/fluid_mdriver.c +++ b/src/drivers/fluid_mdriver.c @@ -106,7 +106,8 @@ void fluid_midi_driver_settings(fluid_settings_t *settings) fluid_settings_register_int(settings, "midi.realtime-prio", FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0); - + + fluid_settings_register_str(settings, "midi.driver", "", 0); for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) { @@ -125,8 +126,11 @@ void fluid_midi_driver_settings(fluid_settings_t *settings) } } - /* Set the default driver */ - fluid_settings_register_str(settings, "midi.driver", def_name ? def_name : "", 0); + /* Set the default driver, if any */ + if(def_name != NULL) + { + fluid_settings_setstr(settings, "midi.driver", def_name); + } } /** From 4f75973f88b89d9b9dacd947ec980f8780a88192 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:35:50 +0200 Subject: [PATCH 016/101] Print status of Windows drivers --- cmake_admin/report.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake_admin/report.cmake b/cmake_admin/report.cmake index de10487d..f9150e83 100644 --- a/cmake_admin/report.cmake +++ b/cmake_admin/report.cmake @@ -74,6 +74,18 @@ else ( WINDOWS_SUPPORT ) message ( "Windows: no" ) endif ( WINDOWS_SUPPORT ) +if ( DSOUND_SUPPORT ) + message ( "DSound: yes" ) +else ( DSOUND_SUPPORT ) + message ( "DSound: no" ) +endif ( DSOUND_SUPPORT ) + +if ( WINMIDI_SUPPORT ) + message ( "WinMidi support: yes" ) +else ( WINMIDI_SUPPORT ) + message ( "WinMidi support: no" ) +endif ( WINMIDI_SUPPORT ) + if ( LADSPA_SUPPORT ) message ( "LADSPA support: yes" ) else ( LADSPA_SUPPORT ) From f8c470d3f73ccec1c635ca610910dd3b4f8cedee Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:38:04 +0200 Subject: [PATCH 017/101] Don't use an MFC include. --- cmake_admin/VersionResource.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake_admin/VersionResource.rc b/cmake_admin/VersionResource.rc index 3b01c1de..27bc7b30 100644 --- a/cmake_admin/VersionResource.rc +++ b/cmake_admin/VersionResource.rc @@ -1,5 +1,5 @@ #include "VersionInfo.h" -#include "winres.h" +#include "winver.h" VS_VERSION_INFO VERSIONINFO FILEVERSION FILE_VERSION_RESOURCE From 7b6ebbea5b4bd16a46ff018b7dc0e403f7b6d49a Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:41:34 +0200 Subject: [PATCH 018/101] Add Windows driver selection --- src/CMakeLists.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 43228ab5..70b7131a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,9 +78,13 @@ if ( PORTAUDIO_SUPPORT ) include_directories ( ${PORTAUDIO_INCLUDE_DIRS} ) endif ( PORTAUDIO_SUPPORT ) -if ( WINDOWS_SUPPORT ) - set ( fluid_windows_SOURCES drivers/fluid_dsound.c drivers/fluid_winmidi.c ) -endif ( WINDOWS_SUPPORT ) +if ( DSOUND_SUPPORT ) + set ( fluid_dsound_SOURCES drivers/fluid_dsound.c ) +endif ( DSOUND_SUPPORT ) + +if ( WINMIDI_SUPPORT ) + set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c ) +endif ( WINMIDI_SUPPORT ) if ( OSS_SUPPORT ) set ( fluid_oss_SOURCES drivers/fluid_oss.c ) @@ -243,7 +247,8 @@ add_library ( libfluidsynth-OBJ OBJECT ${fluid_oss_SOURCES} ${fluid_portaudio_SOURCES} ${fluid_pulse_SOURCES} - ${fluid_windows_SOURCES} + ${fluid_dsound_SOURCES} + ${fluid_winmidi_SOURCES} ${libfluidsynth_SOURCES} ${public_HEADERS} ${public_main_HEADER} From 8ef6ac7dcc3c2dc83b6853d158ac1259d22522e9 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:42:15 +0200 Subject: [PATCH 019/101] Enable VersionInfo also for MINGW --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70b7131a..ae729f70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -216,7 +216,7 @@ configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth/version.h.in configure_file ( ${CMAKE_SOURCE_DIR}/include/fluidsynth.cmake ${public_main_HEADER} ) -if ( WIN32 AND NOT MINGW ) +if ( WIN32 ) include(generate_product_version) generate_product_version( VersionFilesOutputVariable @@ -231,7 +231,7 @@ generate_product_version( ORIGINAL_FILENAME "libfluidsynth.dll" FILE_DESCRIPTION "Fluidsynth" ) -endif ( WIN32 AND NOT MINGW ) +endif ( WIN32 ) add_library ( libfluidsynth-OBJ OBJECT ${config_SOURCES} From 413bcbeb8471e95e93fcf5d6d43921a33304c8f1 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:43:27 +0200 Subject: [PATCH 020/101] Add Windows driver support macro. --- src/config.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config.cmake b/src/config.cmake index aa222676..f2d5256f 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -190,6 +190,12 @@ /* Define to enable PulseAudio driver */ #cmakedefine PULSE_SUPPORT @PULSE_SUPPORT@ +/* Define to enable DirectSound driver */ +#cmakedefine DSOUND_SUPPORT @DSOUND_SUPPORT@ + +/* Define to enable Windows MIDI driver */ +#cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@ + /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ From 30886a3d7851e870b516c90bce7a634205a2ec95 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:44:27 +0200 Subject: [PATCH 021/101] Remove handwritten macros --- src/utils/fluidsynth_priv.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index 41e39839..d5dbdf7e 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -137,8 +137,6 @@ typedef guint64 uint64_t; #include /* Provides also socklen_t */ /* WIN32 special defines */ -#define DSOUND_SUPPORT 1 -#define WINMIDI_SUPPORT 1 #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 From 2ffad4dd2cd80335eac23693adc26c09c140e09b Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:48:01 +0200 Subject: [PATCH 022/101] Add options for Windows drivers --- CMakeLists.txt | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c05c84b..18b52dc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,8 @@ option ( enable-libsndfile "compile libsndfile support (if it is available)" on option ( enable-midishare "compile MidiShare support (if it is available)" on ) option ( enable-network "enable network support (requires BSD sockets)" on ) option ( enable-oss "compile OSS support (if it is available)" on ) +option ( enable-dsound "compile DirectSound support (if it is available)" on ) +option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) @@ -190,15 +192,34 @@ endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE # Windows unset ( WINDOWS_SUPPORT CACHE ) unset ( WINDOWS_LIBS CACHE ) +unset ( DSOUND_SUPPORT CACHE ) +unset ( WINMIDI_SUPPORT CACHE ) unset ( MINGW32 CACHE ) if ( WIN32 ) include ( CheckIncludeFiles ) + + # Check presence of MS include files check_include_file ( windows.h HAVE_WINDOWS_H ) check_include_file ( io.h HAVE_IO_H ) check_include_file ( dsound.h HAVE_DSOUND_H ) check_include_files ( "windows.h;mmsystem.h" HAVE_MMSYSTEM_H ) + set ( WINDOWS_SUPPORT ${HAVE_WINDOWS_H} ) - set ( WINDOWS_LIBS "dsound;winmm;ws2_32" ) + + if ( NETWORK_SUPPORT ) + set ( WINDOWS_LIBS "${WINDOWS_LIBS};ws2_32" ) + endif ( NETWORK_SUPPORT ) + + if ( enable-dsound AND HAVE_DSOUND_H ) + set ( WINDOWS_LIBS "${WINDOWS_LIBS};dsound" ) + set ( DSOUND_SUPPORT 1 ) + endif () + + if ( enable-winmidi AND HAVE_MMSYSTEM_H ) + set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" ) + set ( WINMIDI_SUPPORT 1 ) + endif () + set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" ) set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" ) if ( MSVC ) From 02d7ab6939bcde016598d54b86ac6b6a2dff2a5c Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:49:27 +0200 Subject: [PATCH 023/101] Fix "-mms-bitfields" option. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18b52dc5..312554f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,7 +250,7 @@ if ( WIN32 ) # MinGW compiler (a Windows GCC port) if ( MINGW ) set ( MINGW32 1 ) - add_definitions ( -mms-bitfields ) + add_compile_options ( -mms-bitfields ) endif ( MINGW ) else ( WIN32 ) # Check PThreads, but not in Windows From a1affca2ca17cc52a2b665fffb6cf0aba9639b96 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 27 Oct 2018 10:11:28 +0200 Subject: [PATCH 024/101] Use "enable-network" instead of NETWORK_SUPPORT --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 312554f3..a5c740fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,9 +206,9 @@ if ( WIN32 ) set ( WINDOWS_SUPPORT ${HAVE_WINDOWS_H} ) - if ( NETWORK_SUPPORT ) + if ( enable-network ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};ws2_32" ) - endif ( NETWORK_SUPPORT ) + endif ( enable-network ) if ( enable-dsound AND HAVE_DSOUND_H ) set ( WINDOWS_LIBS "${WINDOWS_LIBS};dsound" ) From e298fefd7fde2311acc10037331ee15a496ea842 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 27 Oct 2018 16:22:44 +0200 Subject: [PATCH 025/101] cleanup audio and midi driver instantiation (#448) Instead of saving the name of the driver, it would be worth to save to pointer to the selected definition instead. In this way, the function for deleting the driver does not need to search its name by parsing all the list, but it just needs to call the pointer into the saved definition (less code). This fix can be applied to MIDI drivers too. I also moved the FLUID_FREE(allnames) inside the "if(allnames != NULL)" block, nothing bad should happen even by keeping that instruction outside, but actually there is no need to call the free if allnames is NULL. --- src/drivers/fluid_adriver.c | 23 +++++++---------------- src/drivers/fluid_adriver.h | 4 +++- src/drivers/fluid_mdriver.c | 32 +++++++++++--------------------- src/drivers/fluid_mdriver.h | 4 +++- 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index 7b7a28ce..f1ab772a 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -25,7 +25,7 @@ * fluid_adriver_definition_t */ -typedef struct _fluid_audriver_definition_t +struct _fluid_audriver_definition_t { const char *name; fluid_audio_driver_t *(*new)(fluid_settings_t *settings, fluid_synth_t *synth); @@ -34,7 +34,7 @@ typedef struct _fluid_audriver_definition_t void *data); void (*free)(fluid_audio_driver_t *driver); void (*settings)(fluid_settings_t *settings); -} fluid_audriver_definition_t; +}; /* Available audio drivers, listed in order of preference */ static const fluid_audriver_definition_t fluid_audio_drivers[] = @@ -236,10 +236,11 @@ find_fluid_audio_driver(fluid_settings_t *settings) { FLUID_LOG(FLUID_INFO, "No audio drivers available."); } + + FLUID_FREE(allnames); } FLUID_FREE(name); - FLUID_FREE(allnames); return NULL; } @@ -265,7 +266,7 @@ new_fluid_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) if(driver) { - driver->name = def->name; + driver->define = def; } return driver; @@ -307,7 +308,7 @@ new_fluid_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, voi if(driver) { - driver->name = def->name; + driver->define = def; } } @@ -326,18 +327,8 @@ new_fluid_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, voi void delete_fluid_audio_driver(fluid_audio_driver_t *driver) { - unsigned int i; fluid_return_if_fail(driver != NULL); - - /* iterate over fluid_audio_drivers_template to ensure deleting even drivers currently not registered */ - for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) - { - if(fluid_audio_drivers[i].name == driver->name) - { - fluid_audio_drivers[i].free(driver); - return; - } - } + driver->define->free(driver); } diff --git a/src/drivers/fluid_adriver.h b/src/drivers/fluid_adriver.h index 430c4748..1564168f 100644 --- a/src/drivers/fluid_adriver.h +++ b/src/drivers/fluid_adriver.h @@ -27,9 +27,11 @@ * fluid_audio_driver_t */ +typedef struct _fluid_audriver_definition_t fluid_audriver_definition_t; + struct _fluid_audio_driver_t { - const char *name; + const fluid_audriver_definition_t *define; }; void fluid_audio_driver_settings(fluid_settings_t *settings); diff --git a/src/drivers/fluid_mdriver.c b/src/drivers/fluid_mdriver.c index 6c23ab93..79b20970 100644 --- a/src/drivers/fluid_mdriver.c +++ b/src/drivers/fluid_mdriver.c @@ -25,7 +25,7 @@ /* * fluid_mdriver_definition */ -struct fluid_mdriver_definition_t +struct _fluid_mdriver_definition_t { const char *name; fluid_midi_driver_t *(*new)(fluid_settings_t *settings, @@ -36,7 +36,7 @@ struct fluid_mdriver_definition_t }; -static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = +static const fluid_mdriver_definition_t fluid_midi_drivers[] = { #if ALSA_SUPPORT { @@ -145,19 +145,18 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi { fluid_midi_driver_t *driver = NULL; char *allnames; - unsigned int i; + const fluid_mdriver_definition_t *def; - for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) + for(def = fluid_midi_drivers; def->name != NULL; def++) { - const char* name = fluid_midi_drivers[i].name; - if(fluid_settings_str_equal(settings, "midi.driver", name)) + if(fluid_settings_str_equal(settings, "midi.driver", def->name)) { - FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", name); - driver = fluid_midi_drivers[i].new(settings, handler, event_handler_data); + FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", def->name); + driver = def->new(settings, handler, event_handler_data); if(driver) { - driver->name = name; + driver->define = def; } return driver; @@ -176,9 +175,9 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi { FLUID_LOG(FLUID_INFO, "No MIDI drivers available."); } - } - FLUID_FREE(allnames); + FLUID_FREE(allnames); + } return NULL; } @@ -189,15 +188,6 @@ fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_mi */ void delete_fluid_midi_driver(fluid_midi_driver_t *driver) { - unsigned int i; fluid_return_if_fail(driver != NULL); - - for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) - { - if(fluid_midi_drivers[i].name == driver->name) - { - fluid_midi_drivers[i].free(driver); - return; - } - } + driver->define->free(driver); } diff --git a/src/drivers/fluid_mdriver.h b/src/drivers/fluid_mdriver.h index eb212ccb..76b8b7b0 100644 --- a/src/drivers/fluid_mdriver.h +++ b/src/drivers/fluid_mdriver.h @@ -27,9 +27,11 @@ * fluid_midi_driver_t */ +typedef struct _fluid_mdriver_definition_t fluid_mdriver_definition_t; + struct _fluid_midi_driver_t { - const char *name; + const fluid_mdriver_definition_t *define; handle_midi_event_func_t handler; void *data; }; From e4ab5067f128dcefb87ef9fb3318c0ee75ee32bf Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 27 Oct 2018 16:32:54 +0200 Subject: [PATCH 026/101] fix linker error on win32 without network support by adding missing #ifdefs --- src/utils/fluid_sys.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index 5a4a2dd9..e644ea5f 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -1330,9 +1330,10 @@ fluid_istream_gets(fluid_istream_t in, char *buf, int len) } else { +#ifdef NETWORK_SUPPORT n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0); - if(n == SOCKET_ERROR) +#endif { return -1; } @@ -1405,10 +1406,13 @@ fluid_ostream_printf(fluid_ostream_t out, const char *format, ...) return write(out, buf, FLUID_STRLEN(buf)); } +#ifdef NETWORK_SUPPORT /* Socket */ retval = send(out & ~FLUID_SOCKET_FLAG, buf, FLUID_STRLEN(buf), 0); - return retval != SOCKET_ERROR ? retval : -1; +#else + return -1; +#endif } #endif } From 3d94d012255be3747633f4d816c02ad070cd5ede Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 28 Oct 2018 14:21:36 +0100 Subject: [PATCH 027/101] clang-tidy: remove annoying readability-inconsistent-declaration-parameter-name --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 72bb0cc7..687d6885 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cert-*,clang-analyzer-*,performance-*,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-delete-null-pointer,readability-implicit-bool-conversion,readability-inconsistent-declaration-parameter-name,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-simplify-boolean-expr' +Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cert-*,clang-analyzer-*,performance-*,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-delete-null-pointer,readability-implicit-bool-conversion,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-simplify-boolean-expr' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false From c0ff5a0af1d699834d080d3aee929c6c43b2e29a Mon Sep 17 00:00:00 2001 From: Tom M Date: Sun, 28 Oct 2018 16:29:15 +0100 Subject: [PATCH 028/101] Optimize Travis and AppVeyor CI builds (#451) * cleanup unneeded compiler variations (now builds in 7 min rather than 13 min) * Windows: * use VS2017 for vcpkg, VS2015 for manual build * Linux: * install alsa, jack, pulse, portaudio, ladspa, libsndfile for all builds * MacOSX: * use AppleClang * reorder macosx build in between linux build, as macosx usually takes longer to build, allowing to make better use of build time (via pipelining) * switch to XCode10, enabling TravisCI to support CoreAudio and CoreMidi --- .appveyor-vcpkg.yml | 1 - .appveyor.yml | 1 - .travis.yml | 238 ++++++++------------------------------------ 3 files changed, 40 insertions(+), 200 deletions(-) diff --git a/.appveyor-vcpkg.yml b/.appveyor-vcpkg.yml index 91202dd9..1025f579 100644 --- a/.appveyor-vcpkg.yml +++ b/.appveyor-vcpkg.yml @@ -1,5 +1,4 @@ image: - - Visual Studio 2015 - Visual Studio 2017 build: diff --git a/.appveyor.yml b/.appveyor.yml index 445f1f16..2cb89770 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,6 +1,5 @@ image: - Visual Studio 2015 - - Visual Studio 2017 build: parallel: true diff --git a/.travis.yml b/.travis.yml index c55e5f2d..f2fe152f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,221 +1,63 @@ language: c -#sudo: required +os: linux dist: trusty +addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - llvm-toolchain-trusty-7 + packages: + - cmake-data + - cmake + - libglib2.0-0 + - libsndfile-dev + - libasound2-dev + - libjack-dev + - portaudio19-dev + - libpulse-dev + - libdbus-glib-1-dev + - ladspa-sdk env: - CMAKE_FLAGS="-Denable-profiling=1" - CMAKE_FLAGS="-Denable-floats=1 -Denable-profiling=1" + - CMAKE_FLAGS="-Denable-floats=0" - CMAKE_FLAGS="-Denable-trap-on-fpe=1" - CMAKE_FLAGS="-Denable-fpe-check=1" - CMAKE_FLAGS="-Denable-ipv6=0" - CMAKE_FLAGS="-Denable-network=0" - CMAKE_FLAGS="-Denable-aufile=0" - CMAKE_FLAGS="-DBUILD_SHARED_LIBS=0" + matrix: include: - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - - cmake-data - - cmake - - libglib2.0-0 - - libsndfile-dev - - libasound2-dev - - libjack-dev - - portaudio19-dev - - libpulse-dev - - libdbus-glib-1-dev - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8" - - CMAKE_FLAGS="-Denable-floats=1" - - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - - cmake-data - - cmake - - libglib2.0-0 - - libsndfile-dev - - libasound2-dev - - libjack-dev - - portaudio19-dev - - libpulse-dev - - libdbus-glib-1-dev - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8" - - CMAKE_FLAGS="-Denable-floats=0" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - - CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-8 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - - cmake-data - - cmake - - libglib2.0-0 - - libsndfile-dev - - libasound2-dev - - libjack-dev - - portaudio19-dev - - libpulse-dev - - libdbus-glib-1-dev - - ladspa-sdk - env: - - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" - - # works on Trusty - - os: linux - addons: - apt: - sources: - - llvm-toolchain-trusty-4.0 - packages: - - clang-4.0 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" - - # works on Trusty - - os: linux - addons: - apt: - sources: - - llvm-toolchain-trusty-5.0 - packages: - - clang-5.0 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" - - # works on Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-6.0 - packages: - - clang-6.0 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" - - # works on Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-7 - packages: - - clang-7 - - cmake-data - - cmake - - libglib2.0-0 - - ladspa-sdk - env: - - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - os: osx - osx_image: xcode8 - env: - - MATRIX_EVAL="brew install gcc5 glib gettext && brew link gettext && CC=gcc-5 && CXX=g++-5" # gettext should provide libintl, but doesnt work + osx_image: xcode10 - - os: osx - osx_image: xcode8 + - os: linux env: - - MATRIX_EVAL="brew install gcc6 glib gettext && brew link gettext && CC=gcc-6 && CXX=g++-6" + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && sudo apt-get install gcc-7" - - os: osx - osx_image: xcode8 + - os: linux env: - - MATRIX_EVAL="brew install gcc glib gettext libsndfile jack dbus-glib pulseaudio portaudio && brew link gettext && CC=gcc-8 && CXX=g++-8" + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && sudo apt-get install gcc-8" + - CMAKE_FLAGS="-Denable-debug=1 -DCMAKE_C_FLAGS_DEBUG=-fuse-ld=gold" + + - os: linux + env: + - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8 && sudo apt-get install clang-3.8" + + - os: linux + env: + - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0 && sudo apt-get install clang-6.0" + + - os: linux + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && sudo apt-get install clang-7" before_install: - if [ $TRAVIS_OS_NAME = linux ]; then sudo apt-get update; else brew update; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install glib gettext libsndfile jack dbus-glib pulseaudio portaudio; fi # && brew link gettext - eval "${MATRIX_EVAL}" before_script: @@ -223,7 +65,7 @@ before_script: - mkdir build && cd build script: - - cmake -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install ${CMAKE_FLAGS} "-Denable-portaudio=1" "-Denable-ladspa=1" "-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=1" .. + - cmake -DCMAKE_INSTALL_PREFIX=$HOME/fluidsynth_install ${CMAKE_FLAGS} -Denable-portaudio=1 -Denable-ladspa=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 .. - make -j4 - make check - if [ $TRAVIS_OS_NAME = linux ]; then make install; fi # install only on linux, as CMAKE_INSTALL_PREFIX is ignored for frameworks on macosx and I cant tell whether that's correct or a bug. From a4ddc9396c8cea6bdfbed8cd41c002ef3698ecbb Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 28 Oct 2018 16:37:44 +0100 Subject: [PATCH 029/101] avoid leaking memory in pulse driver --- src/drivers/fluid_pulse.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/drivers/fluid_pulse.c b/src/drivers/fluid_pulse.c index 26317a03..ded534a5 100644 --- a/src/drivers/fluid_pulse.c +++ b/src/drivers/fluid_pulse.c @@ -176,7 +176,6 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings, } buf = FLUID_ARRAY(float, period_size * 2); - if(buf == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory."); @@ -196,29 +195,17 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings, goto error_recovery; } - if(server) - { - FLUID_FREE(server); /* -- free server string */ - } - - if(device) - { - FLUID_FREE(device); /* -- free device string */ - } + FLUID_FREE(server); /* -- free server string */ + FLUID_FREE(device); /* -- free device string */ return (fluid_audio_driver_t *) dev; error_recovery: - - if(server) - { - FLUID_FREE(server); /* -- free server string */ - } - - if(device) - { - FLUID_FREE(device); /* -- free device string */ - } + FLUID_FREE(server); /* -- free server string */ + FLUID_FREE(device); /* -- free device string */ + FLUID_FREE(left); + FLUID_FREE(right); + FLUID_FREE(buf); delete_fluid_pulse_audio_driver((fluid_audio_driver_t *) dev); return NULL; From ee5b0ea1c702ee60b852dccf5e7149883ab8ef42 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 28 Oct 2018 16:42:20 +0100 Subject: [PATCH 030/101] remove redundant declarations --- src/synth/fluid_synth.c | 3 --- src/synth/fluid_synth_monopoly.c | 5 ----- 2 files changed, 8 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index c10a0106..0a76257d 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -95,8 +95,6 @@ static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth) static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, fluid_voice_t *new_voice); static int fluid_synth_sfunload_callback(void *data, unsigned int msec); -void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, - int chan, int key); static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog); static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, @@ -147,7 +145,6 @@ static int fluid_synth_set_chorus_full_LOCAL(fluid_synth_t *synth, int set, int /* has the synth module been initialized? */ /* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ static fluid_atomic_int_t fluid_synth_initialized = {0}; -static void fluid_synth_init(void); /* default modulators * SF2.01 page 52 ff: diff --git a/src/synth/fluid_synth_monopoly.c b/src/synth/fluid_synth_monopoly.c index b7828af5..23e63bac 100644 --- a/src/synth/fluid_synth_monopoly.c +++ b/src/synth/fluid_synth_monopoly.c @@ -290,11 +290,6 @@ static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, * Sust.on/off >------------------------->|_______________| * Sost.on/off ------------------------------------------------------------------------------*/ -int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, - char Mono); - -int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, - int fromkey, int tokey, int vel); /** * Plays a noteon event for a Synth instance in "monophonic playing" state. From b628115092dabafc1c02a066a8c513cb4a7d6050 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 31 Oct 2018 09:23:39 +0100 Subject: [PATCH 031/101] optimize rounding and clipping to int16 samples --- src/synth/fluid_synth.c | 90 +++++++++++------------------------------ 1 file changed, 24 insertions(+), 66 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 50028c5f..172abdaf 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -88,7 +88,7 @@ static void fluid_synth_update_presets(fluid_synth_t *synth); static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth); static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony); static void init_dither(void); -static FLUID_INLINE int roundi(float x); +static FLUID_INLINE int16_t round_clip_to_i16(float x); static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount); static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth); @@ -3808,17 +3808,28 @@ init_dither(void) } /* A portable replacement for roundf(), seems it may actually be faster too! */ -static FLUID_INLINE int -roundi(float x) +static FLUID_INLINE int16_t +round_clip_to_i16(float x) { + long i; if(x >= 0.0f) { - return (int)(x + 0.5f); + i = (long)(x + 0.5f); + if (FLUID_UNLIKELY(i > 32767)) + { + i = 32767; + } } else { - return (int)(x - 0.5f); + i = (long)(x - 0.5f); + if (FLUID_UNLIKELY(i < -32768)) + { + i = -32768; + } } + + return (int16_t)i; } /** @@ -3847,12 +3858,10 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, void *rout, int roff, int rincr) { int i, j, k, cur; - signed short *left_out = (signed short *) lout; - signed short *right_out = (signed short *) rout; + int16_t *left_out = lout; + int16_t *right_out = rout; fluid_real_t *left_in; fluid_real_t *right_in; - int left_sample; - int right_sample; double time = fluid_utime(); int di; float cpu_load; @@ -3877,37 +3886,13 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, cur = 0; } - left_sample = roundi(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]); - right_sample = roundi(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); + left_out[j] = round_clip_to_i16(left_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[0][di]); + right_out[k] = round_clip_to_i16(right_in[0 * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + cur] * 32766.0f + rand_table[1][di]); if(++di >= DITHER_SIZE) { di = 0; } - - /* digital clipping */ - if(left_sample > 32767) - { - left_sample = 32767; - } - - if(left_sample < -32768) - { - left_sample = -32768; - } - - if(right_sample > 32767) - { - right_sample = 32767; - } - - if(right_sample < -32768) - { - right_sample = -32768; - } - - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; } synth->cur = cur; @@ -3946,47 +3931,20 @@ fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, void *rout, int roff, int rincr) { int i, j, k; - signed short *left_out = (signed short *) lout; - signed short *right_out = (signed short *) rout; - int left_sample; - int right_sample; + int16_t *left_out = lout; + int16_t *right_out = rout; int di = *dither_index; fluid_profile_ref_var(prof_ref); for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) { - - left_sample = roundi(lin[i] * 32766.0f + rand_table[0][di]); - right_sample = roundi(rin[i] * 32766.0f + rand_table[1][di]); + left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]); + right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]); if(++di >= DITHER_SIZE) { di = 0; } - - /* digital clipping */ - if(left_sample > 32767) - { - left_sample = 32767; - } - - if(left_sample < -32768) - { - left_sample = -32768; - } - - if(right_sample > 32767) - { - right_sample = 32767; - } - - if(right_sample < -32768) - { - right_sample = -32768; - } - - left_out[j] = (signed short) left_sample; - right_out[k] = (signed short) right_sample; } *dither_index = di; /* keep dither buffer continous */ From cf09b654ab685c88c591edba7a6b94be7894c844 Mon Sep 17 00:00:00 2001 From: Colin Kinloch Date: Wed, 31 Oct 2018 12:10:29 +0000 Subject: [PATCH 032/101] jack midi autoconnect (#450) --- src/drivers/fluid_jack.c | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/drivers/fluid_jack.c b/src/drivers/fluid_jack.c index 14c5e0c8..d86daa26 100644 --- a/src/drivers/fluid_jack.c +++ b/src/drivers/fluid_jack.c @@ -82,6 +82,8 @@ struct _fluid_jack_midi_driver_t int midi_port_count; jack_port_t **midi_port; // array of midi port handles fluid_midi_parser_t *parser; + int autoconnect_inputs; + int autoconnect_is_outdated; }; static fluid_jack_client_t *new_fluid_jack_client(fluid_settings_t *settings, @@ -94,7 +96,7 @@ void fluid_jack_driver_shutdown(void *arg); int fluid_jack_driver_srate(jack_nframes_t nframes, void *arg); int fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg); int fluid_jack_driver_process(jack_nframes_t nframes, void *arg); - +void fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg); static fluid_mutex_t last_client_mutex = FLUID_MUTEX_INIT; /* Probably not necessary, but just in case drivers are created by multiple threads */ static fluid_jack_client_t *last_client = NULL; /* Last unpaired client. For audio/MIDI driver pairing. */ @@ -109,6 +111,31 @@ fluid_jack_audio_driver_settings(fluid_settings_t *settings) fluid_settings_register_str(settings, "audio.jack.server", "", 0); } +/* + * Connect all midi input ports to all terminal midi output ports + */ +void +fluid_jack_midi_autoconnect(jack_client_t *client, fluid_jack_midi_driver_t *midi_driver) { + int i, j; + const char ** midi_source_ports; + + midi_source_ports = jack_get_ports(client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput | JackPortIsTerminal); + if(midi_source_ports != NULL) + { + for(j = 0; midi_source_ports[j] != NULL; j++) + { + for(i = 0; i < midi_driver->midi_port_count; i++) + { + FLUID_LOG(FLUID_INFO, "jack midi autoconnect \"%s\" to \"%s\"", midi_source_ports[j], jack_port_name(midi_driver->midi_port[i])); + jack_connect(client, midi_source_ports[j], jack_port_name(midi_driver->midi_port[i])); + } + } + jack_free(midi_source_ports); + } + + midi_driver->autoconnect_is_outdated = FALSE; +} + /* * Create Jack client as necessary, share clients of the same server. * @param settings Settings object @@ -213,6 +240,7 @@ new_fluid_jack_client(fluid_settings_t *settings, int isaudio, void *driver) goto error_recovery; } + jack_set_port_registration_callback(client_ref->client, fluid_jack_port_registration, client_ref); jack_set_process_callback(client_ref->client, fluid_jack_driver_process, client_ref); jack_set_buffer_size_callback(client_ref->client, fluid_jack_driver_bufsize, client_ref); jack_set_sample_rate_callback(client_ref->client, fluid_jack_driver_srate, client_ref); @@ -629,6 +657,11 @@ fluid_jack_driver_process(jack_nframes_t nframes, void *arg) if(midi_driver) { + if(midi_driver->autoconnect_is_outdated) + { + fluid_jack_midi_autoconnect(client->client, midi_driver); + } + for(i = 0; i < midi_driver->midi_port_count; i++) { midi_buffer = jack_port_get_buffer(midi_driver->midi_port[i], 0); @@ -728,6 +761,15 @@ fluid_jack_driver_shutdown(void *arg) /* exit (1); */ } +void +fluid_jack_port_registration(jack_port_id_t port, int is_registering, void *arg) +{ + fluid_jack_client_t *client_ref = (fluid_jack_client_t *)arg; + if(client_ref->midi_driver != NULL) + { + client_ref->midi_driver->autoconnect_is_outdated = client_ref->midi_driver->autoconnect_inputs && is_registering != 0; + } +} void fluid_jack_midi_driver_settings(fluid_settings_t *settings) { @@ -770,6 +812,9 @@ new_fluid_jack_midi_driver(fluid_settings_t *settings, return NULL; } + fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconnect_inputs); + dev->autoconnect_is_outdated = dev->autoconnect_inputs; + dev->client_ref = new_fluid_jack_client(settings, FALSE, dev); if(!dev->client_ref) From ac356a408ba5d5c0176c9be868cd5cd6471b45a4 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 31 Oct 2018 13:14:59 +0100 Subject: [PATCH 033/101] update fluidsettings.xml on jack midi autoconnect --- doc/fluidsettings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index 1be1823e..7bc3adcc 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -584,7 +584,7 @@ Developers: Settings can be deprecated by adding: SOME TEXTbool 0 (FALSE) - If 1 (TRUE), automatically connects FluidSynth to available MIDI input ports. alsa_seq and coremidi are currently the only drivers making use of this. + If 1 (TRUE), automatically connects FluidSynth to available MIDI input ports. alsa_seq, coremidi and jack are currently the only drivers making use of this. From da6a2e7a91820bf97f89d3bcdb15b76e94e90bc2 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 30 Oct 2018 17:30:48 +0100 Subject: [PATCH 034/101] travis: disable sudo --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f2fe152f..e32b0a85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: c +sudo: false os: linux dist: trusty addons: From a948f7c5b26347a12cd05edd14bf45917ff29b60 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Thu, 1 Nov 2018 11:43:03 +0100 Subject: [PATCH 035/101] Use integer math Actually, using an integer division produces the same result. Addresses #455. --- src/midi/fluid_midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index c05f994c..194eb4bc 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -2225,7 +2225,7 @@ int fluid_player_get_total_ticks(fluid_player_t *player) */ int fluid_player_get_bpm(fluid_player_t *player) { - return (int)(60e6 / player->miditempo); + return 60000000L / player->miditempo; } /** From 2205e4a8fccc51f3b358bd6c17feb316a3bbc3fa Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Thu, 1 Nov 2018 11:51:04 +0100 Subject: [PATCH 036/101] Use integer math Since 'data' is an integer value clipped between +/-8192 and 'nrpn_scale' is a char value, this calculation could be done with integers and then return the fluid_real_t value. Addresses #455. --- src/synth/fluid_gen.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/synth/fluid_gen.c b/src/synth/fluid_gen.c index f01f9419..0d07d786 100644 --- a/src/synth/fluid_gen.c +++ b/src/synth/fluid_gen.c @@ -150,7 +150,7 @@ fluid_real_t fluid_gen_scale(int gen, float value) fluid_real_t fluid_gen_scale_nrpn(int gen, int data) { - fluid_real_t value = (float) data - 8192.0f; - fluid_clip(value, -8192, 8192); - return value * (float) fluid_gen_info[gen].nrpn_scale; + data = data - 8192; + fluid_clip(data, -8192, 8192); + return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale); } From 16d81d50ea9cc35df3ee46d2f81a4e71eca10f97 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 3 Nov 2018 09:13:08 +0100 Subject: [PATCH 037/101] remove unused fluid_hz2ct() Closes #455. --- src/utils/fluid_conv.c | 18 +++++++----------- src/utils/fluid_conv.h | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/utils/fluid_conv.c b/src/utils/fluid_conv.c index 555dd613..2c46062d 100644 --- a/src/utils/fluid_conv.c +++ b/src/utils/fluid_conv.c @@ -299,6 +299,13 @@ fluid_tc2sec_release(fluid_real_t tc) * fluid_act2hz * * Convert from absolute cents to Hertz + * + * The inverse operation, converting from Hertz to cents, was unused and implemented as + * +fluid_hz2ct(fluid_real_t f) +{ + return (fluid_real_t)(6900 + (1200 / M_LN2) * log(f / 440.0)); +} */ fluid_real_t fluid_act2hz(fluid_real_t c) @@ -306,17 +313,6 @@ fluid_act2hz(fluid_real_t c) return (fluid_real_t)(8.176 * pow(2.0, (double) c / 1200.0)); } -/* - * fluid_hz2ct - * - * Convert from Hertz to cents - */ -fluid_real_t -fluid_hz2ct(fluid_real_t f) -{ - return (fluid_real_t)(6900 + 1200 * log(f / 440.0) / M_LN2); -} - /* * fluid_pan */ diff --git a/src/utils/fluid_conv.h b/src/utils/fluid_conv.h index d84a321c..aa3ea895 100644 --- a/src/utils/fluid_conv.h +++ b/src/utils/fluid_conv.h @@ -64,7 +64,6 @@ fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); fluid_real_t fluid_tc2sec_release(fluid_real_t tc); fluid_real_t fluid_act2hz(fluid_real_t c); -fluid_real_t fluid_hz2ct(fluid_real_t c); fluid_real_t fluid_pan(fluid_real_t c, int left); fluid_real_t fluid_balance(fluid_real_t balance, int left); fluid_real_t fluid_concave(fluid_real_t val); From ffdf5b89d751aefaaa8c4ac1b0f4ff9f84475b15 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 30 Oct 2018 17:39:50 +0100 Subject: [PATCH 038/101] constify function parameters if possible --- src/rvoice/fluid_chorus.c | 4 ++-- src/rvoice/fluid_chorus.h | 4 ++-- src/rvoice/fluid_rev.c | 4 ++-- src/rvoice/fluid_rev.h | 4 ++-- src/rvoice/fluid_rvoice_mixer.c | 6 +++--- src/synth/fluid_synth.c | 2 +- src/synth/fluid_synth.h | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/rvoice/fluid_chorus.c b/src/rvoice/fluid_chorus.c index 83a88d38..abcd4bc0 100644 --- a/src/rvoice/fluid_chorus.c +++ b/src/rvoice/fluid_chorus.c @@ -383,7 +383,7 @@ fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, } -void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in, +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; @@ -456,7 +456,7 @@ void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in, } /* Duplication of code ... (replaces sample data instead of mixing) */ -void fluid_chorus_processreplace(fluid_chorus_t *chorus, fluid_real_t *in, +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int sample_index; diff --git a/src/rvoice/fluid_chorus.h b/src/rvoice/fluid_chorus.h index 8a6734aa..94130957 100644 --- a/src/rvoice/fluid_chorus.h +++ b/src/rvoice/fluid_chorus.h @@ -55,9 +55,9 @@ void fluid_chorus_reset(fluid_chorus_t *chorus); void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, fluid_real_t speed, fluid_real_t depth_ms, int type); -void fluid_chorus_processmix(fluid_chorus_t *chorus, fluid_real_t *in, +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); -void fluid_chorus_processreplace(fluid_chorus_t *chorus, fluid_real_t *in, +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); diff --git a/src/rvoice/fluid_rev.c b/src/rvoice/fluid_rev.c index 8a58d1e8..c025e105 100644 --- a/src/rvoice/fluid_rev.c +++ b/src/rvoice/fluid_rev.c @@ -368,7 +368,7 @@ fluid_revmodel_reset(fluid_revmodel_t *rev) } void -fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in, +fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k = 0; @@ -410,7 +410,7 @@ fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in, } void -fluid_revmodel_processmix(fluid_revmodel_t *rev, fluid_real_t *in, +fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out) { int i, k = 0; diff --git a/src/rvoice/fluid_rev.h b/src/rvoice/fluid_rev.h index 69c00ea7..75ab5d23 100644 --- a/src/rvoice/fluid_rev.h +++ b/src/rvoice/fluid_rev.h @@ -61,10 +61,10 @@ typedef struct _fluid_revmodel_presets_t fluid_revmodel_t *new_fluid_revmodel(fluid_real_t sample_rate); void delete_fluid_revmodel(fluid_revmodel_t *rev); -void fluid_revmodel_processmix(fluid_revmodel_t *rev, fluid_real_t *in, +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); -void fluid_revmodel_processreplace(fluid_revmodel_t *rev, fluid_real_t *in, +void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); void fluid_revmodel_reset(fluid_revmodel_t *rev); diff --git a/src/rvoice/fluid_rvoice_mixer.c b/src/rvoice/fluid_rvoice_mixer.c index 3b264e4d..fc717306 100644 --- a/src/rvoice/fluid_rvoice_mixer.c +++ b/src/rvoice/fluid_rvoice_mixer.c @@ -130,8 +130,8 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcoun const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; int i, f; - void (*reverb_process_func)(fluid_revmodel_t *rev, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); - void (*chorus_process_func)(fluid_chorus_t *chorus, fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r; @@ -375,7 +375,7 @@ get_dest_buf(fluid_rvoice_buffers_t *buffers, int index, */ static void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, - fluid_real_t *FLUID_RESTRICT dsp_buf, + const fluid_real_t *FLUID_RESTRICT dsp_buf, int start_block, int sample_count, fluid_real_t **dest_bufs, int dest_bufcount) { diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 0a76257d..2e29428f 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -3939,7 +3939,7 @@ fluid_synth_write_s16(fluid_synth_t *synth, int len, * @note Currently private to libfluidsynth. */ void -fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, +fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, void *lout, int loff, int lincr, void *rout, int roff, int rincr) { diff --git a/src/synth/fluid_synth.h b/src/synth/fluid_synth.h index 95e2c2e5..156424af 100644 --- a/src/synth/fluid_synth.h +++ b/src/synth/fluid_synth.h @@ -190,7 +190,7 @@ fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, int prognum); void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont); -void fluid_synth_dither_s16(int *dither_index, int len, float *lin, float *rin, +void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, void *lout, int loff, int lincr, void *rout, int roff, int rincr); From 31ccc6b6a0668bf8fa99b2eeba42c78df7f7bc89 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 30 Oct 2018 17:58:45 +0100 Subject: [PATCH 039/101] fix a NULL deref in coreaudio driver --- src/drivers/fluid_coreaudio.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/drivers/fluid_coreaudio.c b/src/drivers/fluid_coreaudio.c index 4acb0ebf..7a3f4fee 100644 --- a/src/drivers/fluid_coreaudio.c +++ b/src/drivers/fluid_coreaudio.c @@ -266,10 +266,7 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func } } - if(devname) - { - FLUID_FREE(devname); /* free device name */ - } + FLUID_FREE(devname); /* free device name */ dev->buffer_size = period_size * periods; @@ -322,6 +319,12 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size); dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size); + + if(dev->buffers[0] == NULL || dev->buffers[1] == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory."); + goto error_recovery; + } // Initialize the audio unit status = AudioUnitInitialize(dev->outputUnit); From 4d8954ee30239b4380e8c078f8fbd01ded981133 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 30 Oct 2018 18:07:07 +0100 Subject: [PATCH 040/101] fix mistaken usage of fluid_synth_process in coreaudio driver --- src/drivers/fluid_coreaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/fluid_coreaudio.c b/src/drivers/fluid_coreaudio.c index 7a3f4fee..56860701 100644 --- a/src/drivers/fluid_coreaudio.c +++ b/src/drivers/fluid_coreaudio.c @@ -144,7 +144,7 @@ fluid_audio_driver_t * new_fluid_core_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) { return new_fluid_core_audio_driver2(settings, - fluid_synth_process, + NULL, synth); } From 28edbbfe838083d0f7530b506deb8b28843f3bee Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 3 Nov 2018 14:17:40 +0100 Subject: [PATCH 041/101] fluid_settings_option_concat: dont count NULL options should never happen though --- src/utils/fluid_settings.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/utils/fluid_settings.c b/src/utils/fluid_settings.c index d77d5ed7..02be9a03 100644 --- a/src/utils/fluid_settings.c +++ b/src/utils/fluid_settings.c @@ -1733,7 +1733,6 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name, const char *separator) { fluid_setting_node_t *node; - fluid_str_setting_t *setting; fluid_list_t *p, *newlist = NULL; size_t count, len; char *str, *option; @@ -1756,10 +1755,8 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name, return (NULL); } - setting = &node->str; - /* Duplicate option list, count options and get total string length */ - for(p = setting->options, count = 0, len = 0; p; p = p->next, count++) + for(p = node->str.options, count = 0, len = 0; p; p = p->next) { option = fluid_list_get(p); @@ -1767,6 +1764,7 @@ fluid_settings_option_concat(fluid_settings_t *settings, const char *name, { newlist = fluid_list_append(newlist, option); len += FLUID_STRLEN(option); + count++; } } From 0160543cdde0af8e147be36d47a29d6bdc16d907 Mon Sep 17 00:00:00 2001 From: Tom M Date: Sat, 3 Nov 2018 14:38:54 +0100 Subject: [PATCH 042/101] Compile time constant lookup tables with cmake (#438) Autogenerate lookup tables with a C helper tool, allowing them to be compile time constant and reduce memory requirements esp. on embedded systems. --- src/CMakeLists.txt | 10 ++++ src/gentables/CMakeLists.txt | 27 +++++++++ src/gentables/gen_conv.c | 81 +++++++++++++++++++++++++++ src/gentables/gen_rvoice_dsp.c | 81 +++++++++++++++++++++++++++ src/gentables/make_tables.c | 84 ++++++++++++++++++++++++++++ src/gentables/make_tables.h | 21 +++++++ src/rvoice/fluid_phase.h | 4 +- src/rvoice/fluid_rvoice_dsp.c | 80 +------------------------- src/rvoice/fluid_rvoice_dsp_tables.h | 8 +++ src/synth/fluid_synth.c | 4 -- src/utils/fluid_conv.c | 70 +---------------------- src/utils/fluid_conv.h | 34 +---------- src/utils/fluid_conv_tables.h | 41 ++++++++++++++ 13 files changed, 358 insertions(+), 187 deletions(-) create mode 100644 src/gentables/CMakeLists.txt create mode 100644 src/gentables/gen_conv.c create mode 100644 src/gentables/gen_rvoice_dsp.c create mode 100644 src/gentables/make_tables.c create mode 100644 src/gentables/make_tables.h create mode 100644 src/rvoice/fluid_rvoice_dsp_tables.h create mode 100644 src/utils/fluid_conv_tables.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ae729f70..cf8f3369 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -360,3 +360,13 @@ else ( MACOSX_FRAMEWORK ) install ( FILES ${public_main_HEADER} DESTINATION ${INCLUDE_INSTALL_DIR} ) endif ( MACOSX_FRAMEWORK ) +# ******* Auto Generated Lookup Tables ****** + +include(ExternalProject) +ExternalProject_Add(gentables + DOWNLOAD_COMMAND "" + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gentables + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gentables + INSTALL_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gentables/make_tables${CMAKE_EXECUTABLE_SUFFIX} "${CMAKE_BINARY_DIR}/" +) +add_dependencies(libfluidsynth-OBJ gentables) diff --git a/src/gentables/CMakeLists.txt b/src/gentables/CMakeLists.txt new file mode 100644 index 00000000..437027ae --- /dev/null +++ b/src/gentables/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.1) + +# remove $CC from the current environment and by that force cmake to look for a (working) C compiler, +# which hopefully will be the host compiler +unset(ENV{CC}) + +project (gentables C) + +set ( CMAKE_BUILD_TYPE Debug ) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) + +# Add the executable that generates the table +add_executable( make_tables + make_tables.c + gen_conv.c + gen_rvoice_dsp.c) + +if ( WIN32 ) + add_definitions ( -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS ) +else ( WIN32 ) + target_link_libraries (make_tables "m") +endif () diff --git a/src/gentables/gen_conv.c b/src/gentables/gen_conv.c new file mode 100644 index 00000000..86b119d5 --- /dev/null +++ b/src/gentables/gen_conv.c @@ -0,0 +1,81 @@ + +#include "utils/fluid_conv_tables.h" +#include "make_tables.h" + + +/* conversion tables */ +static double fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; +static double fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; +static double fluid_concave_tab[FLUID_VEL_CB_SIZE]; +static double fluid_convex_tab[FLUID_VEL_CB_SIZE]; +static double fluid_pan_tab[FLUID_PAN_SIZE]; + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +static void fluid_conversion_config(void) +{ + int i; + double x; + + for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++) + { + fluid_ct2hz_tab[i] = pow(2.0, (double) i / 1200.0); + } + + /* centibels to amplitude conversion + * Note: SF2.01 section 8.1.3: Initial attenuation range is + * between 0 and 144 dB. Therefore a negative attenuation is + * not allowed. + */ + for(i = 0; i < FLUID_CB_AMP_SIZE; i++) + { + fluid_cb2amp_tab[i] = pow(10.0, (double) i / -200.0); + } + + /* initialize the conversion tables (see fluid_mod.c + fluid_mod_get_value cases 4 and 8) */ + + /* concave unipolar positive transform curve */ + fluid_concave_tab[0] = 0.0; + fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; + + /* convex unipolar positive transform curve */ + fluid_convex_tab[0] = 0; + fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; + + /* There seems to be an error in the specs. The equations are + implemented according to the pictures on SF2.01 page 73. */ + + for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++) + { + x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((double)(i * i) / ((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10; + fluid_convex_tab[i] = (1.0 - x); + fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = x; + } + + /* initialize the pan conversion table */ + x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0); + + for(i = 0; i < FLUID_PAN_SIZE; i++) + { + fluid_pan_tab[i] = sin(i * x); + } +} + + +void gen_conv_table(FILE *fp) +{ + /* Calculate the values */ + fluid_conversion_config(); + + /* fluid_ct2hz_tab */ + EMIT_ARRAY(fp, fluid_ct2hz_tab); + EMIT_ARRAY(fp, fluid_cb2amp_tab); + EMIT_ARRAY(fp, fluid_concave_tab); + EMIT_ARRAY(fp, fluid_convex_tab); + EMIT_ARRAY(fp, fluid_pan_tab); +} + diff --git a/src/gentables/gen_rvoice_dsp.c b/src/gentables/gen_rvoice_dsp.c new file mode 100644 index 00000000..0d326236 --- /dev/null +++ b/src/gentables/gen_rvoice_dsp.c @@ -0,0 +1,81 @@ + +#include "rvoice/fluid_rvoice_dsp_tables.h" +#include "make_tables.h" + +/* Linear interpolation table (2 coefficients centered on 1st) */ +static double interp_coeff_linear[FLUID_INTERP_MAX][2]; + +/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ +static double interp_coeff[FLUID_INTERP_MAX][4]; + +/* 7th order interpolation (7 coefficients centered on 3rd) */ +static double sinc_table7[FLUID_INTERP_MAX][SINC_INTERP_ORDER]; + +static double cb_interp_coeff_linear(int y, int x) { return interp_coeff_linear[y][x]; } +static double cb_interp_coeff (int y, int x) { return interp_coeff[y][x]; } +static double cb_sinc_table7 (int y, int x) { return sinc_table7[y][x]; } + +/* Initializes interpolation tables */ +void fluid_rvoice_dsp_config(void) +{ + int i, i2; + double x, v; + double i_shifted; + + /* Initialize the coefficients for the interpolation. The math comes + * from a mail, posted by Olli Niemitalo to the music-dsp mailing + * list (I found it in the music-dsp archives + * http://www.smartelectronix.com/musicdsp/). */ + + for(i = 0; i < FLUID_INTERP_MAX; i++) + { + x = (double) i / (double) FLUID_INTERP_MAX; + + interp_coeff[i][0] = (x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i][1] = (1.0 + x * x * (1.5 * x - 2.5)); + interp_coeff[i][2] = (x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i][3] = (0.5 * x * x * (x - 1.0)); + + interp_coeff_linear[i][0] = (1.0 - x); + interp_coeff_linear[i][1] = x; + } + + /* i: Offset in terms of whole samples */ + for(i = 0; i < SINC_INTERP_ORDER; i++) + { + /* i2: Offset in terms of fractional samples ('subsamples') */ + for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++) + { + /* center on middle of table */ + i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) + + (double)i2 / (double)FLUID_INTERP_MAX; + + /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ + if(fabs(i_shifted) > 0.000001) + { + double arg = M_PI * i_shifted; + v = sin(arg) / (arg); + /* Hanning window */ + v *= 0.5 * (1.0 + cos(2.0 * arg / (double)SINC_INTERP_ORDER)); + } + else + { + v = 1.0; + } + + sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; + } + } +} + + +void gen_rvoice_table_dsp (FILE *fp) +{ + /* Calculate the values */ + fluid_rvoice_dsp_config(); + + /* Emit the matrices */ + emit_matrix(fp, "interp_coeff_linear", cb_interp_coeff_linear, FLUID_INTERP_MAX, 2); + emit_matrix(fp, "interp_coeff", cb_interp_coeff, FLUID_INTERP_MAX, 4); + emit_matrix(fp, "sinc_table7", cb_sinc_table7, FLUID_INTERP_MAX, 7); +} diff --git a/src/gentables/make_tables.c b/src/gentables/make_tables.c new file mode 100644 index 00000000..b1e4dbb2 --- /dev/null +++ b/src/gentables/make_tables.c @@ -0,0 +1,84 @@ + +#include "make_tables.h" + +static void write_value(FILE *fp, double val, int i) +{ + fprintf(fp, " %.15e%c /* %d */\n", + val, + ',', + i + ); +} + +/* Emit an array of real numbers */ +void emit_array(FILE *fp, const char *tblname, const double *tbl, int size) +{ + int i; + + fprintf(fp, "static const fluid_real_t %s[%d] = {\n", tblname, size); + + for (i = 0; i < size; i++) + { + write_value(fp, tbl[i], i); + } + fprintf(fp, "};\n\n"); +} + +/* Emit a matrix of real numbers */ +void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel) +{ + int i, j; + + fprintf(fp, "static const fluid_real_t %s[%d][%d] = {\n {\n", tblname, sizeh, sizel); + + for (i = 0; i < sizeh; i++) + { + for (j = 0; j < sizel; j++) + { + write_value(fp, tbl_cb(i, j), i*sizel+j); + } + + + if (i < (sizeh-1)) + fprintf(fp, " }, {\n"); + else + fprintf(fp, " }\n};\n\n"); + } +} + +static void open_table(FILE**fp, const char* dir, const char* file) +{ + char buf[2048] = {0}; + + strcat(buf, dir); + strcat(buf, file); + + /* open the output file */ + *fp = fopen(buf, "w"); + if (*fp == NULL) + { + exit(-2); + } + + /* Emit warning header */ + fprintf(*fp, "/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */\n\n"); +} + +int main (int argc, char *argv[]) +{ + FILE *fp; + + // make sure we have enough arguments + if (argc < 2) + return -1; + + open_table(&fp, argv[1], "fluid_conv_tables.c"); + gen_conv_table(fp); + fclose(fp); + + open_table(&fp, argv[1], "fluid_rvoice_dsp_tables.c"); + gen_rvoice_table_dsp(fp); + fclose(fp); + + return 0; +} diff --git a/src/gentables/make_tables.h b/src/gentables/make_tables.h new file mode 100644 index 00000000..95972e06 --- /dev/null +++ b/src/gentables/make_tables.h @@ -0,0 +1,21 @@ +#include +#include +#include +#include + + +#define EMIT_ARRAY(__fp__, __arr__) emit_array(__fp__, #__arr__, __arr__, sizeof(__arr__)/sizeof(*__arr__)) + +/* callback for general access to matrices */ +typedef double (*emit_matrix_cb)(int y, int x); + +/* Generators */ +void gen_rvoice_table_dsp(FILE *fp); +void gen_conv_table(FILE *fp); + +/* Emit an array of real numbers */ +void emit_array(FILE *fp, const char *tblname, const double *tbl, int size); + +/* Emit a matrix of real numbers */ +void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel); + diff --git a/src/rvoice/fluid_phase.h b/src/rvoice/fluid_phase.h index 08975cbb..44df6b24 100644 --- a/src/rvoice/fluid_phase.h +++ b/src/rvoice/fluid_phase.h @@ -22,8 +22,6 @@ #ifndef _FLUID_PHASE_H #define _FLUID_PHASE_H -#include "config.h" - /* * phase */ @@ -31,7 +29,7 @@ #define FLUID_INTERP_BITS 8 #define FLUID_INTERP_BITS_MASK 0xff000000 #define FLUID_INTERP_BITS_SHIFT 24 -#define FLUID_INTERP_MAX 256 + #define FLUID_FRACT_MAX ((double)4294967296.0) diff --git a/src/rvoice/fluid_rvoice_dsp.c b/src/rvoice/fluid_rvoice_dsp.c index cc162829..25f4a4b8 100644 --- a/src/rvoice/fluid_rvoice_dsp.c +++ b/src/rvoice/fluid_rvoice_dsp.c @@ -22,6 +22,7 @@ #include "fluid_phase.h" #include "fluid_rvoice.h" #include "fluid_sys.h" +#include "fluid_rvoice_dsp_tables.c" /* Purpose: * @@ -47,85 +48,6 @@ /* Interpolation (find a value between two samples of the original waveform) */ -/* Linear interpolation table (2 coefficients centered on 1st) */ -static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2]; - -/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ -static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4]; - -/* 7th order interpolation (7 coefficients centered on 3rd) */ -static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7]; - - -#define SINC_INTERP_ORDER 7 /* 7th order constant */ - - -/* Initializes interpolation tables */ -void fluid_rvoice_dsp_config(void) -{ - int i, i2; - double x, v; - double i_shifted; - - /* Initialize the coefficients for the interpolation. The math comes - * from a mail, posted by Olli Niemitalo to the music-dsp mailing - * list (I found it in the music-dsp archives - * http://www.smartelectronix.com/musicdsp/). */ - - for(i = 0; i < FLUID_INTERP_MAX; i++) - { - x = (double) i / (double) FLUID_INTERP_MAX; - - interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x))); - interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5)); - interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x))); - interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0)); - - interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x); - interp_coeff_linear[i][1] = (fluid_real_t)x; - } - - /* i: Offset in terms of whole samples */ - for(i = 0; i < SINC_INTERP_ORDER; i++) - { - /* i2: Offset in terms of fractional samples ('subsamples') */ - for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++) - { - /* center on middle of table */ - i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) - + (double)i2 / (double)FLUID_INTERP_MAX; - - /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ - if(fabs(i_shifted) > 0.000001) - { - double arg = M_PI * i_shifted; - v = (fluid_real_t)sin(arg) / (arg); - /* Hanning window */ - v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * arg / (fluid_real_t)SINC_INTERP_ORDER)); - } - else - { - v = 1.0; - } - - sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; - } - } - -#if 0 - - for(i = 0; i < FLUID_INTERP_MAX; i++) - { - printf("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n", - i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i], - sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]); - } - -#endif - - fluid_check_fpe("interpolation table calculation"); -} - static FLUID_INLINE fluid_real_t fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) { diff --git a/src/rvoice/fluid_rvoice_dsp_tables.h b/src/rvoice/fluid_rvoice_dsp_tables.h new file mode 100644 index 00000000..befc9faf --- /dev/null +++ b/src/rvoice/fluid_rvoice_dsp_tables.h @@ -0,0 +1,8 @@ + +#ifndef _FLUID_RVOICE_DSP_TABLES_H +#define _FLUID_RVOICE_DSP_TABLES_H + +#define FLUID_INTERP_MAX 256 +#define SINC_INTERP_ORDER 7 /* 7th order constant */ + +#endif diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 078911f1..0f0cf7a8 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -285,10 +285,6 @@ fluid_synth_init(void) feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); #endif - fluid_conversion_config(); - - fluid_rvoice_dsp_config(); - init_dither(); /* custom_breath2att_mod is not a default modulator specified in SF2.01. diff --git a/src/utils/fluid_conv.c b/src/utils/fluid_conv.c index 2c46062d..483b371d 100644 --- a/src/utils/fluid_conv.c +++ b/src/utils/fluid_conv.c @@ -19,74 +19,7 @@ */ #include "fluid_conv.h" - -#define FLUID_CENTS_HZ_SIZE 1200 -#define FLUID_VEL_CB_SIZE 128 -#define FLUID_CB_AMP_SIZE 1441 -#define FLUID_PAN_SIZE 1002 - -/* conversion tables */ -static fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; -static fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; -static fluid_real_t fluid_concave_tab[FLUID_VEL_CB_SIZE]; -static fluid_real_t fluid_convex_tab[FLUID_VEL_CB_SIZE]; -static fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE]; - -/* - * void fluid_synth_init - * - * Does all the initialization for this module. - */ -void -fluid_conversion_config(void) -{ - int i; - double x; - - for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++) - { - fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0); - } - - /* centibels to amplitude conversion - * Note: SF2.01 section 8.1.3: Initial attenuation range is - * between 0 and 144 dB. Therefore a negative attenuation is - * not allowed. - */ - for(i = 0; i < FLUID_CB_AMP_SIZE; i++) - { - fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0); - } - - /* initialize the conversion tables (see fluid_mod.c - fluid_mod_get_value cases 4 and 8) */ - - /* concave unipolar positive transform curve */ - fluid_concave_tab[0] = 0.0; - fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; - - /* convex unipolar positive transform curve */ - fluid_convex_tab[0] = 0; - fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; - - /* There seems to be an error in the specs. The equations are - implemented according to the pictures on SF2.01 page 73. */ - - for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++) - { - x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((i * i) / (fluid_real_t)((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10; - fluid_convex_tab[i] = (fluid_real_t)(1.0 - x); - fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = (fluid_real_t) x; - } - - /* initialize the pan conversion table */ - x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0); - - for(i = 0; i < FLUID_PAN_SIZE; i++) - { - fluid_pan_tab[i] = (fluid_real_t) sin(i * x); - } -} +#include "fluid_conv_tables.c" /* * fluid_ct2hz @@ -403,3 +336,4 @@ fluid_convex(fluid_real_t val) return fluid_convex_tab[(int) val]; } + diff --git a/src/utils/fluid_conv.h b/src/utils/fluid_conv.h index aa3ea895..60f441c4 100644 --- a/src/utils/fluid_conv.h +++ b/src/utils/fluid_conv.h @@ -22,39 +22,7 @@ #define _FLUID_CONV_H #include "fluidsynth_priv.h" - -/* - Attenuation range in centibels. - Attenuation range is the dynamic range of the volume envelope generator - from 0 to the end of attack segment. - fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. - However the spec makes no distinction between 16 or 24 bit synths, so use - 96 dB here. - - Note about usefulness of 24 bits: - 1)Even fluidsynth is a 24 bit synth, this format is only relevant if - the sample format coming from the soundfont is 24 bits and the audio sample format - choosen by the application (audio.sample.format) is not 16 bits. - - 2)When the sample soundfont is 16 bits, the internal 24 bits number have - 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of - this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) - even if this sample is produced by the audio driver using an audio sample format - compatible for a 24 bit DAC. - - 3)When the audio sample format settings is 16 bits (audio.sample.format), the - audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB - even if the initial sample comes from a 24 bits soundfont. - - In both cases (2) or (3), the real dynamic range is only 96 dB. - - Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): - - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). - - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). - */ -#define FLUID_PEAK_ATTENUATION 960.0f - -void fluid_conversion_config(void); +#include "utils/fluid_conv_tables.h" fluid_real_t fluid_ct2hz_real(fluid_real_t cents); fluid_real_t fluid_ct2hz(fluid_real_t cents); diff --git a/src/utils/fluid_conv_tables.h b/src/utils/fluid_conv_tables.h new file mode 100644 index 00000000..744733b4 --- /dev/null +++ b/src/utils/fluid_conv_tables.h @@ -0,0 +1,41 @@ + +#ifndef _FLUID_CONV_TABLES_H +#define _FLUID_CONV_TABLES_H + +/* + Attenuation range in centibels. + Attenuation range is the dynamic range of the volume envelope generator + from 0 to the end of attack segment. + fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. + However the spec makes no distinction between 16 or 24 bit synths, so use + 96 dB here. + + Note about usefulness of 24 bits: + 1)Even fluidsynth is a 24 bit synth, this format is only relevant if + the sample format coming from the soundfont is 24 bits and the audio sample format + choosen by the application (audio.sample.format) is not 16 bits. + + 2)When the sample soundfont is 16 bits, the internal 24 bits number have + 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of + this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) + even if this sample is produced by the audio driver using an audio sample format + compatible for a 24 bit DAC. + + 3)When the audio sample format settings is 16 bits (audio.sample.format), the + audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB + even if the initial sample comes from a 24 bits soundfont. + + In both cases (2) or (3), the real dynamic range is only 96 dB. + + Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): + - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). + - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). + */ +#define FLUID_PEAK_ATTENUATION 960.0f + +#define FLUID_CENTS_HZ_SIZE 1200 +#define FLUID_VEL_CB_SIZE 128 +#define FLUID_CB_AMP_SIZE 1441 +#define FLUID_PAN_SIZE 1002 + +#endif From afbd818f8a03aa4cd9a8f2406e38c9cc862f1791 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 3 Nov 2018 17:07:20 +0100 Subject: [PATCH 043/101] Use M_LN10 macro instead of log(10.) --- src/rvoice/fluid_rvoice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rvoice/fluid_rvoice.c b/src/rvoice/fluid_rvoice.c index e7687116..f6565bf0 100644 --- a/src/rvoice/fluid_rvoice.c +++ b/src/rvoice/fluid_rvoice.c @@ -592,7 +592,7 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks) { fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo); - fluid_real_t env_value = - ((-200 * log(amp) / log(10.0) - lfo) / FLUID_PEAK_ATTENUATION - 1); + fluid_real_t env_value = - (((-200 / M_LN10) * log(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1); fluid_clip(env_value, 0.0, 1.0); fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); } From d20f6cff6674fc71261b17b4d1147930c57d3164 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 3 Nov 2018 21:07:37 +0100 Subject: [PATCH 044/101] Use integer math. --- src/midi/fluid_midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index 194eb4bc..4795fceb 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -2152,7 +2152,7 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) */ int fluid_player_set_bpm(fluid_player_t *player, int bpm) { - return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm)); + return fluid_player_set_midi_tempo(player, 60000000L / bpm); } /** From 06bcf8db9fd22ebfea5745238b034691e18e8a17 Mon Sep 17 00:00:00 2001 From: Tom M Date: Sat, 10 Nov 2018 08:07:30 +0100 Subject: [PATCH 045/101] Fix an incorrectly aligned result when converting between pointer types (#457) fix alignment issue of idlist --- src/sfloader/fluid_sffile.c | 53 ++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index b3e64cc3..40055e81 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -77,9 +77,31 @@ enum SM24_ID }; -static const char idlist[] = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" - "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24" - }; +/* + * This declares a char array containing the SF2 chunk identifiers. This + * 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 +{ + /* + * Cannot be char c[ ], because in C89, arrays wraped in unions + * must have a fixed size. Otherwise the size of the union would depend + * on the initialization of its first member, which results in + * different sizes for different instances of the same union type. + */ + char c[116]; + uint32_t i; +} idlist = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" + "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24" + }; /* generator types */ @@ -183,7 +205,7 @@ static const unsigned short invalid_preset_gen[] = }; -#define CHNKIDSTR(id) &idlist[(id - 1) * 4] +#define CHNKIDSTR(id) &idlist.c[(id - 1) * 4] /* sfont file chunk sizes */ #define SF_PHDR_SIZE (38) @@ -284,7 +306,7 @@ static int load_shdr(SFData *sf, unsigned int size); static int fixup_pgen(SFData *sf); static int fixup_igen(SFData *sf); -static int chunkid(unsigned int id); +static int chunkid(uint32_t id); static int read_listchunk(SFData *sf, SFChunk *chunk); static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size); static int preset_compare_func(void *a, void *b); @@ -486,14 +508,12 @@ void fluid_sffile_close(SFData *sf) */ /* sound font file load functions */ -static int chunkid(unsigned int id) +static int chunkid(uint32_t id) { unsigned int i; - const unsigned int *p; + const uint32_t *p = &idlist.i; - p = (const unsigned int *)&idlist; - - for(i = 0; i < sizeof(idlist) / sizeof(int); i++, p += 1) + for(i = 0; i < sizeof(idlist) / sizeof(idlist.i); i++, p += 1) { if(*p == id) { @@ -987,6 +1007,7 @@ static int load_phdr(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + sf->preset = fluid_list_append(sf->preset, preset); preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */ READSTR(sf, &preset->name); /* possible read failure ^ */ @@ -1078,6 +1099,7 @@ static int load_pbag(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p2->data = z; z->gen = NULL; /* Init gen and mod before possible failure, */ z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ @@ -1211,6 +1233,7 @@ static int load_pmod(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p3->data = m; READW(sf, m->src); READW(sf, m->dest); @@ -1367,6 +1390,7 @@ static int load_pgen(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p3->data = g; g->id = genid; } @@ -1508,6 +1532,7 @@ static int load_ihdr(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + sf->inst = fluid_list_append(sf->inst, p); p->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ p->idx = i; @@ -1593,6 +1618,7 @@ static int load_ibag(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p2->data = z; z->gen = NULL; /* In case of failure, */ z->mod = NULL; /* fluid_sffile_close can clean up */ @@ -1727,6 +1753,7 @@ static int load_imod(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p3->data = m; READW(sf, m->src); READW(sf, m->dest); @@ -1872,6 +1899,7 @@ static int load_igen(SFData *sf, int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + p3->data = g; g->id = genid; } @@ -2011,6 +2039,7 @@ static int load_shdr(SFData *sf, unsigned int size) FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } + sf->sample = fluid_list_append(sf->sample, p); READSTR(sf, &p->name); READD(sf, p->start); @@ -2138,7 +2167,7 @@ static void delete_preset(SFPreset *preset) } delete_fluid_list(preset->zone); - + FLUID_FREE(preset); } @@ -2162,7 +2191,7 @@ static void delete_inst(SFInst *inst) } delete_fluid_list(inst->zone); - + FLUID_FREE(inst); } From 0bf5c2827536a351eb06e9f11b2ab8272bed98a5 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 9 Nov 2018 00:58:39 +0100 Subject: [PATCH 046/101] "ready" is unused if ENABLE_MIXER_THREADS=0 --- src/rvoice/fluid_rvoice_mixer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rvoice/fluid_rvoice_mixer.c b/src/rvoice/fluid_rvoice_mixer.c index fc717306..062f6acf 100644 --- a/src/rvoice/fluid_rvoice_mixer.c +++ b/src/rvoice/fluid_rvoice_mixer.c @@ -41,13 +41,12 @@ struct _fluid_mixer_buffers_t fluid_rvoice_mixer_t *mixer; /**< Owner of object */ #if ENABLE_MIXER_THREADS fluid_thread_t *thread; /**< Thread object */ + fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ #endif fluid_rvoice_t **finished_voices; /* List of voices who have finished */ int finished_voice_count; - fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ - fluid_real_t *local_buf; int buf_count; From bd41795263e8c4326d5311497fc6f110f8314548 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Wed, 7 Nov 2018 19:49:13 +0100 Subject: [PATCH 047/101] Remove redundant dependency to readline in fluid_cmd.c Libreadline is already used into fluid_istream_readline(). In my opinion, there is no need to have this duplicated code, so I would suggest to move the call to add_history() into fluid_sys.h Closes #460. --- src/bindings/fluid_cmd.c | 14 -------------- src/utils/fluid_sys.c | 1 + 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/bindings/fluid_cmd.c b/src/bindings/fluid_cmd.c index 156c4da2..a45eba97 100644 --- a/src/bindings/fluid_cmd.c +++ b/src/bindings/fluid_cmd.c @@ -28,11 +28,6 @@ #include "fluid_sfont.h" #include "fluid_chan.h" -#if WITH_READLINE -#include -#include -#endif - /* FIXME: LADSPA used to need a lot of parameters on a single line. This is not * necessary anymore, so the limits below could probably be reduced */ #define MAX_TOKENS 100 @@ -522,15 +517,6 @@ fluid_shell_run(void *data) break; } -#if WITH_READLINE - - if(shell->in == fluid_get_stdin()) - { - add_history(workline); - } - -#endif - /* handle the command */ switch(fluid_command(shell->handler, workline, shell->out)) { diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index e644ea5f..3df88fc2 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -1279,6 +1279,7 @@ fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, FLUID_SNPRINTF(buf, len, "%s", line); buf[len - 1] = 0; + add_history(buf); free(line); return 1; From 29c668683f43e3b8b4aab0b8aa73cb02aacd6fcb Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 16 Nov 2018 16:48:25 +0100 Subject: [PATCH 048/101] Improve float calculations (#462) I tried to check the generated ASM code when WITH_FLOAT macro is defined and when it is undefined. In both cases I noticed that there are some points where the math number are converted from float to double and viceversa. Actually this happens whether the FPU is present or not, perhaps for granting the right precision in digits. I also noticed few points that could be simplified a bit by using integer math, so I also included them. * Since fluid_voice_get_actual_key() returns an integer value, the value 60 could be subtracted directly as integer, the code generated looks a bit better. * FLUID_NOISE_FLOOR needs to be cast to fluid_real_t to generate a bit more efficient code. * DC_OFFSET needs a cast to fluid_real_t to generate a bit more efficient code. * "last_fres" and "output_rate" are already fluid_real_t, so they do not need type cast. * "chan_add", "par1_add" and "par2_add" are integer types, so they can be added without FPU. "chan_mul", "par1_mul" and "par2_mul" are already fluid_real_t, so they do not need type cast. Instead, the constant 0.5 needs cast to fluid_real_t to be more efficient. --- src/midi/fluid_midi_router.c | 12 ++++++------ src/rvoice/fluid_iir_filter.c | 4 ++-- src/rvoice/fluid_rev.c | 2 +- src/rvoice/fluid_rvoice.h | 2 +- src/synth/fluid_voice.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/midi/fluid_midi_router.c b/src/midi/fluid_midi_router.c index c3bc8916..dfc32d75 100644 --- a/src/midi/fluid_midi_router.c +++ b/src/midi/fluid_midi_router.c @@ -678,18 +678,18 @@ fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event) * Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all * input channels is mapped to the same synth channel. */ - chan = (int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul - + (fluid_real_t)rule->chan_add + 0.5); + chan = rule->chan_add + (int)((fluid_real_t)event->channel * rule->chan_mul + + (fluid_real_t)0.5); /* Par 1 scaling / offset */ - par1 = (int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul - + (fluid_real_t)rule->par1_add + 0.5); + par1 = rule->par1_add + (int)((fluid_real_t)event_par1 * rule->par1_mul + + (fluid_real_t)0.5); /* Par 2 scaling / offset, if applicable */ if(event_has_par2) { - par2 = (int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul - + (fluid_real_t)rule->par2_add + 0.5); + par2 = rule->par2_add + (int)((fluid_real_t)event_par2 * rule->par2_mul + + (fluid_real_t)0.5); } else { diff --git a/src/rvoice/fluid_iir_filter.c b/src/rvoice/fluid_iir_filter.c index 0770ef62..d20c9a8e 100644 --- a/src/rvoice/fluid_iir_filter.c +++ b/src/rvoice/fluid_iir_filter.c @@ -275,8 +275,8 @@ fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter, * into account for both significant frequency relocation and for * bandwidth readjustment'. */ - fluid_real_t omega = (fluid_real_t)(2.0 * M_PI * - (iir_filter->last_fres / ((float) output_rate))); + fluid_real_t omega = (fluid_real_t)(2.0 * M_PI) * + (iir_filter->last_fres / output_rate); fluid_real_t sin_coeff = (fluid_real_t) sin(omega); fluid_real_t cos_coeff = (fluid_real_t) cos(omega); fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin); diff --git a/src/rvoice/fluid_rev.c b/src/rvoice/fluid_rev.c index c025e105..198a06e5 100644 --- a/src/rvoice/fluid_rev.c +++ b/src/rvoice/fluid_rev.c @@ -34,7 +34,7 @@ * output. There is a very small turn-on transient response, which should not * cause problems. */ -#define DC_OFFSET 1e-8 +#define DC_OFFSET ((fluid_real_t)1e-8) typedef struct _fluid_allpass fluid_allpass; typedef struct _fluid_comb fluid_comb; diff --git a/src/rvoice/fluid_rvoice.h b/src/rvoice/fluid_rvoice.h index bae3ac93..56cd53f4 100644 --- a/src/rvoice/fluid_rvoice.h +++ b/src/rvoice/fluid_rvoice.h @@ -39,7 +39,7 @@ typedef struct _fluid_rvoice_t fluid_rvoice_t; * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7 * 1.e-7 * 2 == 2.e-7 :) */ -#define FLUID_NOISE_FLOOR 2.e-7 +#define FLUID_NOISE_FLOOR ((fluid_real_t)2.e-7) enum fluid_loop { diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 51c1ebf6..2f146ba0 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -685,7 +685,7 @@ calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, * will cause (60-72)*100=-1200 timecents of time variation. * The time is cut in half. */ - timecents = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (60.0 - fluid_voice_get_actual_key(voice))); + timecents = (fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * (fluid_real_t)(60 - fluid_voice_get_actual_key(voice))); /* Range checking */ if(is_decay) From fe92a0a6553ed714e173d5a568ef9fdabb059b77 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 18 Nov 2018 08:55:52 +0100 Subject: [PATCH 049/101] Introduce enable-threads option to cmake (#463) --- CMakeLists.txt | 6 ++++++ cmake_admin/report.cmake | 6 ++++++ src/config.cmake | 3 +++ src/rvoice/fluid_rvoice_mixer.c | 2 -- src/synth/fluid_synth.c | 6 +++++- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5c740fe..4ce00a37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) +option ( enable-threads "enable threads support on multi-processor systems" on ) # Platform specific options if ( CMAKE_SYSTEM MATCHES "Linux" ) @@ -576,6 +577,11 @@ else ( enable-readline ) unset ( READLINE_LIBS CACHE ) endif ( enable-readline ) +unset ( ENABLE_MIXER_THREADS CACHE ) +if ( enable-threads ) + set ( ENABLE_MIXER_THREADS 1 ) +endif ( enable-threads ) + unset ( HAVE_OPENMP CACHE ) find_package ( OpenMP QUIET ) if ( OpenMP_FOUND OR OpenMP_C_FOUND ) diff --git a/cmake_admin/report.cmake b/cmake_admin/report.cmake index f9150e83..e25a989d 100644 --- a/cmake_admin/report.cmake +++ b/cmake_admin/report.cmake @@ -146,6 +146,12 @@ else ( HAVE_OPENMP ) message ( "OpenMP 4.0: no" ) endif ( HAVE_OPENMP ) +if ( ENABLE_MIXER_THREADS ) + message ( "Multi-thread support yes" ) +else ( ENABLE_MIXER_THREADS ) + message ( "Multi-thread support no" ) +endif ( ENABLE_MIXER_THREADS ) + if ( ENABLE_DEBUG ) message ( "Debug: yes" ) else ( ENABLE_DEBUG ) diff --git a/src/config.cmake b/src/config.cmake index f2d5256f..2d1d786b 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -76,6 +76,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_TCP_H @HAVE_NETINET_TCP_H@ +/* Define if compiling the mixer with multi-thread support */ +#cmakedefine ENABLE_MIXER_THREADS @ENABLE_MIXER_THREADS@ + /* Define if compiling with openMP to enable parallel audio rendering */ #cmakedefine HAVE_OPENMP @HAVE_OPENMP@ diff --git a/src/rvoice/fluid_rvoice_mixer.c b/src/rvoice/fluid_rvoice_mixer.c index 062f6acf..af0ef75d 100644 --- a/src/rvoice/fluid_rvoice_mixer.c +++ b/src/rvoice/fluid_rvoice_mixer.c @@ -28,8 +28,6 @@ #include "fluid_synth.h" -#define ENABLE_MIXER_THREADS 1 - // If less than x voices, the thread overhead is larger than the gain, // so don't activate the thread(s). #define VOICES_PER_THREAD 8 diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 9a061cbf..359d2e45 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -222,8 +222,12 @@ void fluid_synth_settings(fluid_settings_t *settings) fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0); fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0); fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0); +#ifdef ENABLE_MIXER_THREADS fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0); - +#else + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); +#endif + 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); From eb22cfbdbb193a0816a6384ed18f325b9299c494 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 18 Nov 2018 09:39:09 +0100 Subject: [PATCH 050/101] bump to version 2.0.2 --- CMakeLists.txt | 4 ++-- doc/Doxyfile | 2 +- doc/fluidsynth-v20-devdoc.txt | 4 ++-- doc/fluidsynth.1 | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ce00a37..e2363319 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ set ( PACKAGE "fluidsynth" ) # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 2 ) set ( FLUIDSYNTH_VERSION_MINOR 0 ) -set ( FLUIDSYNTH_VERSION_MICRO 1 ) +set ( FLUIDSYNTH_VERSION_MICRO 2 ) set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" ) set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) @@ -44,7 +44,7 @@ set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) # This is not exactly the same algorithm as the libtool one, but the results are the same. set ( LIB_VERSION_CURRENT 2 ) set ( LIB_VERSION_AGE 0 ) -set ( LIB_VERSION_REVISION 1 ) +set ( LIB_VERSION_REVISION 2 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) diff --git a/doc/Doxyfile b/doc/Doxyfile index 5719824a..7f3b2f46 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth -PROJECT_NUMBER = 2.0.1 +PROJECT_NUMBER = 2.0.2 OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index 8c9cdcbd..b0e4630d 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -8,8 +8,8 @@ \author David Henningsson \author Tom Moebert \author Copyright © 2003-2018 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert -\version Revision 2.0.1 -\date 2018-10-07 +\version Revision 2.0.2 +\date 2018-11-18 All the source code examples in this document are in the public domain; you can use them as you please. This document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ . The FluidSynth library is distributed under the GNU Lesser General Public License. A copy of the GNU Lesser General Public License is contained in the FluidSynth package; if not, visit http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/doc/fluidsynth.1 b/doc/fluidsynth.1 index 9ced5a25..76a7e287 100644 --- a/doc/fluidsynth.1 +++ b/doc/fluidsynth.1 @@ -13,7 +13,7 @@ .\" along with this program; see the file LICENSE. If not, write to .\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. .\" -.TH FluidSynth 1 "Oct 6, 2018" +.TH FluidSynth 1 "Nov 18, 2018" .\" Please update the above date whenever this man page is modified. .\" .\" Some roff macros, for reference: From f52e4fdf39b7444baa8d93b6794148fd7193bd30 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 18 Nov 2018 09:45:28 +0100 Subject: [PATCH 051/101] update documentation for enable-threads --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2363319..298199dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) -option ( enable-threads "enable threads support on multi-processor systems" on ) +option ( enable-threads "enable multi-threading support (such as parallel voice synthesis)" on ) # Platform specific options if ( CMAKE_SYSTEM MATCHES "Linux" ) From 6e9d84f02a7a0f7e436c2adffc4a065608f490ba Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun, 18 Nov 2018 15:43:03 +0100 Subject: [PATCH 052/101] Forbid DATA_ENTRY_LSB to modulate (#465) and document illegal CC modulation --- src/synth/fluid_synth.c | 79 +++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index cc771ccb..faadefb2 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1318,7 +1318,7 @@ fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan) * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards) * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) * @return #FLUID_OK on success, #FLUID_FAILED otherwise - * + * * @note Not realtime safe (due to internal memory allocation) and therefore should not be called * from synthesis context at the risk of stalling audio output. */ @@ -1388,7 +1388,7 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo * @param synth synth instance * @param mod The modulator to remove * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise - * + * * @note Not realtime safe (due to internal memory allocation) and therefore should not be called * from synthesis context at the risk of stalling audio output. */ @@ -1513,7 +1513,37 @@ fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) FLUID_API_RETURN(result); } -/* Local synthesis thread variant of MIDI CC set function. */ +/* Local synthesis thread variant of MIDI CC set function. + Most of CC are allowed to modulate but not all. A comment describes if CC num + isn't allowed to modulate. + Following explanations should help to understand both MIDI specifications and + Soundfont specifications in regard to MIDI specs. + + MIDI specs: + CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31). + It's up to the synthesizer to decide to take LSB values into account or not. + Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param() + (once fluid_voice_modulate() has been triggered). This is because actually + fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs. + So fluidsynth is using only 7 bit MSB (except for portamento time). + In regard to MIDI specs Fluidsynth behaves correctly. + + Soundfont specs 2.01 - 8.2.1: + To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit) + or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of + CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators + with CC LSB connected to sources inputs should be ignored). + These specifics are particularly suited for synths that use 14 bits CCs. In this case, + the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives + both CC LSB and CC MSB but only CC MSB will trigger the modulation. + This will produce correct synthesis parameters update from a correct 14 bits CC. + If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and + CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters + update followed by correct synthesis parameters update. + + However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore + these SF recommendations on CC receive. +*/ static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) { @@ -1525,8 +1555,11 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) switch(num) { + case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; /* CC omnioff, omnion, mono, poly */ + /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ case POLY_OFF: case POLY_ON: case OMNI_OFF: @@ -1581,18 +1614,18 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) return FLUID_FAILED; - case LEGATO_SWITCH: + case LEGATO_SWITCH: /* not allowed to modulate */ /* handles Poly/mono commutation on Legato pedal On/Off.*/ fluid_channel_cc_legato(chan, value); break; - case PORTAMENTO_SWITCH: + case PORTAMENTO_SWITCH: /* not allowed to modulate */ /* Special handling of the monophonic list */ /* Invalids the most recent note played in a staccato manner */ fluid_channel_invalid_prev_note_staccato(chan); break; - case SUSTAIN_SWITCH: + case SUSTAIN_SWITCH: /* not allowed to modulate */ /* Release voices if Sustain switch is released */ if(value < 64) /* Sustain is released */ @@ -1602,7 +1635,7 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) break; - case SOSTENUTO_SWITCH: + case SOSTENUTO_SWITCH: /* not allowed to modulate */ /* Release voices if Sostetuno switch is released */ if(value < 64) /* Sostenuto is released */ @@ -1617,28 +1650,31 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) break; - case BANK_SELECT_MSB: + case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_bank_msb(chan, value & 0x7F); break; - case BANK_SELECT_LSB: + case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_bank_lsb(chan, value & 0x7F); break; - case ALL_NOTES_OFF: + case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_synth_all_notes_off_LOCAL(synth, channum); break; - case ALL_SOUND_OFF: + case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_synth_all_sounds_off_LOCAL(synth, channum); break; - case ALL_CTRL_OFF: + case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_init_ctrl(chan, 1); fluid_synth_modulate_voices_all_LOCAL(synth, channum); break; - case DATA_ENTRY_MSB: + case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; + + case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ { int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); @@ -1698,13 +1734,13 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) break; } - case NRPN_MSB: + case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ fluid_channel_set_cc(chan, NRPN_LSB, 0); chan->nrpn_select = 0; chan->nrpn_active = 1; break; - case NRPN_LSB: + case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ if(fluid_channel_get_cc(chan, NRPN_MSB) == 120) @@ -1730,8 +1766,8 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) chan->nrpn_active = 1; break; - case RPN_MSB: - case RPN_LSB: + case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ chan->nrpn_active = 0; break; @@ -1741,7 +1777,14 @@ fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) /* fall-through */ default: - return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); + /* 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 above */ + /* if (! (32 <= num && num <= 63)) */ + { + return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); + } } return FLUID_OK; From 98e404609634622a91f79c799c012102dad210f0 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 23 Nov 2018 19:49:20 +0100 Subject: [PATCH 053/101] Fix handle leak in winmidi driver (#469) --- src/drivers/fluid_winmidi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 7fb1bed3..c910a241 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -302,6 +302,7 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t *p) PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0); WaitForSingleObject(dev->hThread, INFINITE); + CloseHandle(dev->hThread); dev->hThread = NULL; } From 36da0111916866856bb8dc0b12158b55c9651b3c Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun, 25 Nov 2018 08:21:51 +0100 Subject: [PATCH 054/101] Avoid redundant dsp parameter updates upon modulation. (#461) --- src/synth/fluid_voice.c | 102 ++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 2f146ba0..38b4bfe6 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -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)); } - /* * 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. * @param voice the synthesis voice * @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 * are event based: the parameter values of the DSP algorithm should @@ -1163,36 +1164,56 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen) * * The update is done in three 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 * will be changed because of the controller event. * - * - For every changed generator, calculate its new value. This is the - * sum of its original value plus the values of al the attached - * modulators. + * - step 2: For every changed generator, calculate its new value. This is the + * sum of its original value plus the values of all the attached modulators. + * The generator flag is set to indicate the parameters must be updated. * - * - For every changed generator, convert its value to the correct - * unit of the corresponding DSP parameter + * - step 3: We need to avoid the risk to call 'fluid_voice_update_param' several + * times for the same generator if several modulators have that generator as + * 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. + + 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 i, k; fluid_mod_t *mod; - int gen; + uint32_t gen; fluid_real_t modval; + /* 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); */ for(i = 0; i < voice->mod_count; i++) { - mod = &voice->mod[i]; /* step 1: find all the modulators that have the changed controller - * as input source. */ - if(fluid_mod_has_source(mod, cc, ctrl)) + as input source. When ctrl is -1 all modulators's destination + are updated */ + if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) { - gen = fluid_mod_get_dest(mod); modval = 0.0; @@ -1207,10 +1228,17 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) } 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 */ + /* set the bit that indicates this generator is updated */ + set_gen_updated(updated_gen_bit, gen); + } + } + + /* step 3: now recalculate the parameter values that are derived from the + generator */ + for(gen = 0; gen < GEN_LAST; gen++) + { + if (is_gen_updated(updated_gen_bit, gen)) + { fluid_voice_update_param(voice, gen); } } @@ -1222,47 +1250,11 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) * Update all the modulators. This function is called after a * 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) { - fluid_mod_t *mod; - 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; + return fluid_voice_modulate(voice, 0, -1); } /** legato update functions --------------------------------------------------*/ From b8b41f781fb988b49d5229b5e247684a5b289a2f Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 10:02:33 +0100 Subject: [PATCH 055/101] Improve chunk id search (#471) If we move UNKN_ID to the bottom of the enum, in the for() cycle inside chunkid() if the value is not found then the "i" variable will be already UNKN_ID. --- src/sfloader/fluid_sffile.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index 40055e81..f9382b10 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -43,7 +43,6 @@ /* sf file chunk IDs */ enum { - UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, @@ -74,7 +73,9 @@ enum IMOD_ID, IGEN_ID, /* instrument ids */ SHDR_ID, /* sample info */ - SM24_ID + SM24_ID, + + UNKN_ID }; /* @@ -517,11 +518,12 @@ static int chunkid(uint32_t id) { if(*p == 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) From cea26455302358c08d485ab0248941dd0e2ce0f5 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 20:03:12 +0100 Subject: [PATCH 056/101] Makes fluid_winmidi_input_error() thread-safe The changes are: 1) made the function static 2) implemented UNICODE support 3) made thread-safe 4) delete unused static buffer --- src/drivers/fluid_winmidi.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index c910a241..fbfbefe4 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -57,8 +57,6 @@ typedef struct } fluid_winmidi_driver_t; -static char fluid_winmidi_error_buffer[256]; - #define msg_type(_m) ((unsigned char)(_m & 0xf0)) #define msg_chan(_m) ((unsigned char)(_m & 0x0f)) #define msg_p1(_m) ((_m >> 8) & 0x7f) @@ -66,8 +64,21 @@ static char fluid_winmidi_error_buffer[256]; void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR msg, DWORD_PTR extra); -static char *fluid_winmidi_input_error(MMRESULT no); +static char * +fluid_winmidi_input_error(char *strError, MMRESULT no) +{ +#ifdef _UNICODE + WCHAR wStr[MAXERRORLENGTH]; + + midiInGetErrorText(no, wStr, MAXERRORLENGTH); + WideCharToMultiByte(CP_UTF8, 0, wStr, -1, strError, MAXERRORLENGTH, 0, 0); +#else + midiInGetErrorText(no, strError, MAXERRORLENGTH); +#endif + + return strError; +} void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings) { @@ -139,6 +150,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, UINT i, num, midi_num = 0; MIDIINCAPS in_caps; char *devname = NULL; + char strError[MAXERRORLENGTH]; /* not much use doing anything */ if(handler == NULL) @@ -216,7 +228,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if(res != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)", - fluid_winmidi_input_error(res), res); + fluid_winmidi_input_error(strError, res), res); goto error_recovery; } @@ -238,13 +250,13 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if(res != MMSYSERR_NOERROR) { FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", - fluid_winmidi_input_error(res), res); + fluid_winmidi_input_error(strError, res), res); midiInUnprepareHeader(dev->hmidiin, hdr, sizeof(MIDIHDR)); } } else FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", - fluid_winmidi_input_error(res), res); + fluid_winmidi_input_error(strError, res), res); } /* Create thread which processes re-adding SYSEX buffers */ From cfa8019a53652809aceb27e3a37b111fff79ed66 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 20:05:40 +0100 Subject: [PATCH 057/101] Remove old fluid_winmidi_input_error() --- src/drivers/fluid_winmidi.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index fbfbefe4..331df449 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -396,11 +396,4 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, } } -static char * -fluid_winmidi_input_error(MMRESULT no) -{ - midiInGetErrorText(no, fluid_winmidi_error_buffer, 256); - return fluid_winmidi_error_buffer; -} - #endif /* WINMIDI_SUPPORT */ From c57d7fffe803ddc8d655ddfc0f9e5aaaee9031c1 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 20:06:56 +0100 Subject: [PATCH 058/101] move fluid_winmidi_callback() Function fluid_winmidi_callback() has been moved on top of the source and made static. --- src/drivers/fluid_winmidi.c | 139 ++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 71 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 331df449..c9a1057b 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -62,9 +62,6 @@ typedef struct #define msg_p1(_m) ((_m >> 8) & 0x7f) #define msg_p2(_m) ((_m >> 16) & 0x7f) -void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, - DWORD_PTR msg, DWORD_PTR extra); - static char * fluid_winmidi_input_error(char *strError, MMRESULT no) { @@ -80,6 +77,74 @@ fluid_winmidi_input_error(char *strError, MMRESULT no) return strError; } +static void CALLBACK +fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) dwInstance; + fluid_midi_event_t event; + LPMIDIHDR pMidiHdr; + unsigned char *data; + unsigned int msg_param = (unsigned int) dwParam1; + + switch(wMsg) + { + case MIM_OPEN: + break; + + case MIM_CLOSE: + break; + + case MIM_DATA: + event.type = msg_type(msg_param); + event.channel = msg_chan(msg_param); + + if(event.type != PITCH_BEND) + { + event.param1 = msg_p1(msg_param); + event.param2 = msg_p2(msg_param); + } + else /* Pitch bend is a 14 bit value */ + { + event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param); + event.param2 = 0; + } + + (*dev->driver.handler)(dev->driver.data, &event); + break; + + case MIM_LONGDATA: /* SYSEX data */ + if(dev->hThread == NULL) + { + break; + } + + pMidiHdr = (LPMIDIHDR)dwParam1; + data = (unsigned char *)(pMidiHdr->lpData); + + /* We only process complete SYSEX messages (discard those that are too small or too large) */ + if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0 + && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7) + { + fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1, + pMidiHdr->dwBytesRecorded - 2, FALSE); + (*dev->driver.handler)(dev->driver.data, &event); + } + + PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, 0, dwParam1); + break; + + case MIM_ERROR: + break; + + case MIM_LONGERROR: + break; + + case MIM_MOREDATA: + break; + } +} + void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings) { MMRESULT res; @@ -328,72 +393,4 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t *p) FLUID_FREE(dev); } -void CALLBACK -fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, - DWORD_PTR dwParam1, DWORD_PTR dwParam2) -{ - fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) dwInstance; - fluid_midi_event_t event; - LPMIDIHDR pMidiHdr; - unsigned char *data; - unsigned int msg_param = (unsigned int) dwParam1; - - switch(wMsg) - { - case MIM_OPEN: - break; - - case MIM_CLOSE: - break; - - case MIM_DATA: - event.type = msg_type(msg_param); - event.channel = msg_chan(msg_param); - - if(event.type != PITCH_BEND) - { - event.param1 = msg_p1(msg_param); - event.param2 = msg_p2(msg_param); - } - else /* Pitch bend is a 14 bit value */ - { - event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param); - event.param2 = 0; - } - - (*dev->driver.handler)(dev->driver.data, &event); - break; - - case MIM_LONGDATA: /* SYSEX data */ - if(dev->hThread == NULL) - { - break; - } - - pMidiHdr = (LPMIDIHDR)dwParam1; - data = (unsigned char *)(pMidiHdr->lpData); - - /* We only process complete SYSEX messages (discard those that are too small or too large) */ - if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0 - && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7) - { - fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1, - pMidiHdr->dwBytesRecorded - 2, FALSE); - (*dev->driver.handler)(dev->driver.data, &event); - } - - PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, 0, dwParam1); - break; - - case MIM_ERROR: - break; - - case MIM_LONGERROR: - break; - - case MIM_MOREDATA: - break; - } -} - #endif /* WINMIDI_SUPPORT */ From 9aeac6ca6e70706183b9c400e67a21d9b720ae9a Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 20:14:18 +0100 Subject: [PATCH 059/101] Unprepare MIDI buffers This was missing. --- src/drivers/fluid_winmidi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index c9a1057b..94f047ac 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -371,6 +371,8 @@ error_recovery: void delete_fluid_winmidi_driver(fluid_midi_driver_t *p) { + int i; + fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) p; fluid_return_if_fail(dev != NULL); @@ -387,6 +389,17 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t *p) { midiInStop(dev->hmidiin); midiInReset(dev->hmidiin); + + for(i = 0; i < MIDI_SYSEX_BUF_COUNT; i++) + { + MIDIHDR *hdr = &dev->sysExHdrs[i]; + + if ((hdr->dwFlags & MHDR_PREPARED)) + { + midiInUnprepareHeader(dev->hmidiin, hdr, sizeof(MIDIHDR)); + } + } + midiInClose(dev->hmidiin); } From fa924516f03b2d85994be2e16e2dd6f2d34f3553 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 21:10:16 +0100 Subject: [PATCH 060/101] Max length of device name is MAXPNAMELEN --- src/drivers/fluid_winmidi.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 94f047ac..ab630639 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -214,8 +214,8 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, MMRESULT res; UINT i, num, midi_num = 0; MIDIINCAPS in_caps; - char *devname = NULL; char strError[MAXERRORLENGTH]; + char dev_name[MAXPNAMELEN]; /* not much use doing anything */ if(handler == NULL) @@ -238,15 +238,10 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, dev->driver.data = data; /* get the device name. if none is specified, use the default device. */ - if(fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) != FLUID_OK || !devname) + if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK) { - devname = FLUID_STRDUP("default"); - - if(!devname) - { - FLUID_LOG(FLUID_ERR, "Out of memory"); - goto error_recovery; - } + FLUID_LOG(FLUID_ERR, "Error getting MIDI device name"); + goto error_recovery; } /* check if there any midi devices installed */ @@ -259,7 +254,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, } /* find the device */ - if(FLUID_STRCASECMP("default", devname) != 0) + if(FLUID_STRCASECMP("default", dev_name) != 0) { for(i = 0; i < num; i++) { @@ -269,7 +264,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, { FLUID_LOG(FLUID_DBG, "Testing midi device: %s\n", in_caps.szPname); - if(FLUID_STRCASECMP(devname, in_caps.szPname) == 0) + if(FLUID_STRCASECMP(dev_name, in_caps.szPname) == 0) { FLUID_LOG(FLUID_DBG, "Selected midi device number: %d\n", i); midi_num = i; @@ -280,7 +275,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if(midi_num != i) { - FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname); + FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", dev_name); goto error_recovery; } } @@ -347,20 +342,10 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, goto error_recovery; } - if(devname) - { - FLUID_FREE(devname); /* -- free device name */ - } - return (fluid_midi_driver_t *) dev; error_recovery: - if(devname) - { - FLUID_FREE(devname); /* -- free device name */ - } - delete_fluid_winmidi_driver((fluid_midi_driver_t *) dev); return NULL; } From 26325edb22082482f02a1a0fdb77692c4d7f5cb9 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 21:13:25 +0100 Subject: [PATCH 061/101] Optimize device search Search of the MIDI device can be done before allocating the struct for the driver. --- src/drivers/fluid_winmidi.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index ab630639..68b18f37 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -224,24 +224,11 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, return NULL; } - dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t)); - - if(dev == NULL) - { - return NULL; - } - - FLUID_MEMSET(dev, 0, sizeof(fluid_winmidi_driver_t)); - - dev->hmidiin = NULL; - dev->driver.handler = handler; - dev->driver.data = data; - /* get the device name. if none is specified, use the default device. */ if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK) { FLUID_LOG(FLUID_ERR, "Error getting MIDI device name"); - goto error_recovery; + return NULL; } /* check if there any midi devices installed */ @@ -250,7 +237,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if(num == 0) { FLUID_LOG(FLUID_ERR, "no MIDI in devices found"); - goto error_recovery; + return NULL; } /* find the device */ @@ -276,10 +263,23 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if(midi_num != i) { FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", dev_name); - goto error_recovery; + return NULL; } } + dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t)); + + if(dev == NULL) + { + return NULL; + } + + FLUID_MEMSET(dev, 0, sizeof(fluid_winmidi_driver_t)); + + dev->hmidiin = NULL; + dev->driver.handler = handler; + dev->driver.data = data; + /* try opening the device */ res = midiInOpen(&dev->hmidiin, midi_num, (DWORD_PTR) fluid_winmidi_callback, From ebb383b47f572ebd28f4b325b6e757fee10e715a Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sun, 25 Nov 2018 21:20:18 +0100 Subject: [PATCH 062/101] Force default device if none is selected --- src/drivers/fluid_winmidi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 68b18f37..b568bcca 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -227,8 +227,8 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, /* get the device name. if none is specified, use the default device. */ if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK) { - FLUID_LOG(FLUID_ERR, "Error getting MIDI device name"); - return NULL; + FLUID_LOG(FLUID_DBG, "No MIDI in device selected, using \"default\""); + FLUID_STRCPY(dev_name, "default"); } /* check if there any midi devices installed */ From 749ce447530d3808f50ef8293e61141f923dc164 Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sat, 1 Dec 2018 14:55:18 +0100 Subject: [PATCH 063/101] Implement FDN Reverb (#380) Supersedes the previous Schroeder-Moorer based "Freeverb". --- src/rvoice/fluid_rev.c | 1746 +++++++++++++++++++++++++++++----------- 1 file changed, 1278 insertions(+), 468 deletions(-) diff --git a/src/rvoice/fluid_rev.c b/src/rvoice/fluid_rev.c index 198a06e5..5f72cb7b 100644 --- a/src/rvoice/fluid_rev.c +++ b/src/rvoice/fluid_rev.c @@ -1,263 +1,998 @@ -/* +/****************************************************************************** + * FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + * + * FDN REVERB + * + * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on + * Schroeder-Moorer reverberator: + * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html + * + * This FDN reverberation is based on jot FDN reverberator. + * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html + * Like Freeverb it is a late reverb which is convenient for Fluidsynth. + * + * + * .-------------------. + * .-----------------| | + * | - | Feedback | + * | .--------------| Matrix | + * | | |___________________| + * | | /|\ /|\ + * \|/ | .---------. .-------. | - | .------. + * .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->| |-> out + * .---------. | | |_________| |_______| | | | left + * |Tone | | | - - | |Stereo| + * In ->|corrector|--* | - - | | unit | + * mono |_________| | \|/ .---------. .-------. | | |-> out + * ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->| | right + * |_________| |_______| |______| + * /|\ /|\ /|\ /|\ + * | | | | + * roomsize --/ | width --/ | + * damp ------/ level ------/ + * + * It takes a monophonic input and produces a stereo output. + * + * The parameters are the same than for Freeverb. + * Also the default response of these parameters are the same than for Freeverb: + * - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s. + * This reverberation time is ofen called T60DC. + * + * - damp (0 to 1): controls the reverb time frequency dependency. + * This controls the reverb time for the frequency sample rate/2 + * + * When 0, the reverb time for high frequencies is the same as + * for DC frequency. + * When > 0, high frequencies have less reverb time than lower frequencies. + * + * - width (0 to 100): controls the left/right output separation. + * When 0, there are no separation and the signal on left and right. + * output is the same. This sounds like a monophonic signal. + * When 100, the separation between left and right is maximum. + * + * - level (0 to 1), controls the ouput level reverberation. + * + * This FDN reverb produces a better quality reverberation tail than Freeverb with + * far less ringing by using modulated delay lines that help to cancel + * the building of a lot of resonances in the reverberation tail even when + * using only 8 delays lines (NBR_DELAYS = 8) (default). + * + * Although 8 lines give good result, using 12 delays lines brings the overall + * frequency density quality a bit higher. This quality augmentation is noticeable + * particularly when using long reverb time (roomsize = 1) on solo instrument with + * long release time. Of course the cpu load augmentation is +50% relatively + * to 8 lines. + * + * As a general rule the reverberation tail quality is easier to perceive by ear + * when using: + * - percussive instruments (i.e piano and others). + * - long reverb time (roomsize = 1). + * - no damping (damp = 0). + * + * + * The cpu load for 8 lines is a bit lower than for freeverb (- 3%), + * but higher for 12 lines (+ 41%). + * + * + * The memory consumption is less than for freeverb. This saves 147480 bytes + * for 8 lines (- 72%) and 110152 bytes for 12 lines (- 54 %). + * (see the results table below). + * + * Two macros are usable at compiler time: + * - NBR_DELAYS: number of delay lines. 8 (default) or 12. + * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of + * roomsize parameter. + * When this macro is not defined (the default), roomsize has the same + * response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). + * + * When this macro is defined, roomsize behaves linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). + * This linear response is convenient when using GUI controls. + * + * -------------------------------------------------------------------------- + * Compare table: + * Note: the cpu load in % are relative each to other. These values are + * given by the fluidsynth profile commands. + * -------------------------------------------------------------------------- + * reverb | NBR_DELAYS | Performances | memory size | quality + * | | (cpu_load: %) | (bytes) | + * ========================================================================== + * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing + * | 2 x 4 all-pass | | | + * ----------|--------------------------------------------------------------- + * FDN | 8 | 0.650 % | 57136 | far less + * modulated | |(feeverb - 3%) |(freeverb - 72%) | ringing + * |--------------------------------------------------------------- + * | 12 | 0.942 % | 94464 | best than + * | |(freeverb + 41%) |(freeverb - 54%) | 8 lines + *--------------------------------------------------------------------------- + * + * + *---------------------------------------------------------------------------- + * 'Denormalise' method to avoid loss of performance. + * -------------------------------------------------- + * According to music-dsp thread 'Denormalise', Pentium processors + * have a hardware 'feature', that is of interest here, related to + * numeric underflow. We have a recursive filter. The output decays + * exponentially, if the input stops. So the numbers get smaller and + * smaller... At some point, they reach 'denormal' level. This will + * lead to drastic spikes in the CPU load. The effect was reproduced + * with the reverb - sometimes the average load over 10 s doubles!!. + * + * The 'undenormalise' macro fixes the problem: As soon as the number + * is close enough to denormal level, the macro forces the number to + * 0.0f. The original macro is: + * + * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + * + * This will zero out a number when it reaches the denormal level. + * Advantage: Maximum dynamic range Disadvantage: We'll have to check + * every sample, expensive. The alternative macro comes from a later + * mail from Jon Watte. It will zap a number before it reaches + * denormal level. Jon suggests to run it once per block instead of + * every sample. + */ - Freeverb - - Written by Jezar at Dreampoint, June 2000 - http://www.dreampoint.co.uk - This code is public domain - - Translated to C by Peter Hanappe, Mai 2001 -*/ - -#include "fluid_sys.h" +/* Denormalising part II: + * + * Another method fixes the problem cheaper: Use a small DC-offset in + * the filter calculations. Now the signals converge not against 0, + * but against the offset. The constant offset is invisible from the + * outside world (i.e. it does not appear at the output. There is a + * very small turn-on transient response, which should not cause + * problems. + */ #include "fluid_rev.h" +#include "fluid_sys.h" -/*************************************************************** - * - * REVERB - */ +/*---------------------------------------------------------------------------- + Configuration macros at compiler time. -/* Denormalising: - * - * We have a recursive filter. The output decays exponentially, if the input - * stops. So the numbers get smaller and smaller... At some point, they reach - * 'denormal' level. On some platforms this will lead to drastic spikes in the - * CPU load. This is especially noticable on some older Pentium (especially - * Pentium 3) processors, but even more modern Intel Core processors still show - * reduced performance with denormals. While there are compile-time switches to - * treat denormals as zero for a lot of processors, those are not available or - * effective on all platforms. - * - * The fix used here: Use a small DC-offset in the filter calculations. Now - * the signals converge not against 0, but against the offset. The constant - * offset is invisible from the outside world (i.e. it does not appear at the - * output. There is a very small turn-on transient response, which should not - * cause problems. - */ -#define DC_OFFSET ((fluid_real_t)1e-8) + 3 macros are usable at compiler time: + - NBR_DELAYs: number of delay lines. 8 (default) or 12. + - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for + roomsize parameter. + - DENORMALISING enable denormalising handling. +-----------------------------------------------------------------------------*/ +/* Number of delay lines (must be only 8 or 12) + 8 is the default. + 12 produces a better quality but is +50% cpu expensive +*/ +#define NBR_DELAYS 8 /* default*/ -typedef struct _fluid_allpass fluid_allpass; -typedef struct _fluid_comb fluid_comb; +/* response curve of parameter roomsize */ +/* + The default response is the same as Freeverb: + - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). -struct _fluid_allpass -{ - fluid_real_t feedback; - fluid_real_t *buffer; - int bufsize; - int bufidx; -}; + when ROOMSIZE_RESPONSE_LINEAR is defined, the response is: + - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). +*/ +//#define ROOMSIZE_RESPONSE_LINEAR -void fluid_allpass_init(fluid_allpass *allpass); -void fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val); -fluid_real_t fluid_allpass_getfeedback(fluid_allpass *allpass); +/* DENORMALISING enable denormalising handling */ +#define DENORMALISING -void -fluid_allpass_setbuffer(fluid_allpass *allpass, int size) -{ - allpass->bufidx = 0; - allpass->buffer = FLUID_ARRAY(fluid_real_t, size); - allpass->bufsize = size; -} +#ifdef DENORMALISING +#define DC_OFFSET 1e-8 +#else +#define DC_OFFSET 0.0 +#endif -void -fluid_allpass_release(fluid_allpass *allpass) -{ - FLUID_FREE(allpass->buffer); -} - -void -fluid_allpass_init(fluid_allpass *allpass) -{ - int i; - int len = allpass->bufsize; - fluid_real_t *buf = allpass->buffer; - - for(i = 0; i < len; i++) - { - buf[i] = DC_OFFSET; /* this is not 100 % correct. */ - } -} - -void -fluid_allpass_setfeedback(fluid_allpass *allpass, fluid_real_t val) -{ - allpass->feedback = val; -} - -fluid_real_t -fluid_allpass_getfeedback(fluid_allpass *allpass) -{ - return allpass->feedback; -} - -#define fluid_allpass_process(_allpass, _input) \ -{ \ - fluid_real_t output; \ - fluid_real_t bufout; \ - bufout = _allpass.buffer[_allpass.bufidx]; \ - output = bufout-_input; \ - _allpass.buffer[_allpass.bufidx] = _input + (bufout * _allpass.feedback); \ - if (++_allpass.bufidx >= _allpass.bufsize) { \ - _allpass.bufidx = 0; \ - } \ - _input = output; \ -} - -struct _fluid_comb -{ - fluid_real_t feedback; - fluid_real_t filterstore; - fluid_real_t damp1; - fluid_real_t damp2; - fluid_real_t *buffer; - int bufsize; - int bufidx; -}; - -void fluid_comb_setbuffer(fluid_comb *comb, int size); -void fluid_comb_release(fluid_comb *comb); -void fluid_comb_init(fluid_comb *comb); -void fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val); -fluid_real_t fluid_comb_getdamp(fluid_comb *comb); -void fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val); -fluid_real_t fluid_comb_getfeedback(fluid_comb *comb); - -void -fluid_comb_setbuffer(fluid_comb *comb, int size) -{ - comb->filterstore = 0; - comb->bufidx = 0; - comb->buffer = FLUID_ARRAY(fluid_real_t, size); - comb->bufsize = size; -} - -void -fluid_comb_release(fluid_comb *comb) -{ - FLUID_FREE(comb->buffer); -} - -void -fluid_comb_init(fluid_comb *comb) -{ - int i; - fluid_real_t *buf = comb->buffer; - int len = comb->bufsize; - - for(i = 0; i < len; i++) - { - buf[i] = DC_OFFSET; /* This is not 100 % correct. */ - } -} - -void -fluid_comb_setdamp(fluid_comb *comb, fluid_real_t val) -{ - comb->damp1 = val; - comb->damp2 = 1 - val; -} - -fluid_real_t -fluid_comb_getdamp(fluid_comb *comb) -{ - return comb->damp1; -} - -void -fluid_comb_setfeedback(fluid_comb *comb, fluid_real_t val) -{ - comb->feedback = val; -} - -fluid_real_t -fluid_comb_getfeedback(fluid_comb *comb) -{ - return comb->feedback; -} - -#define fluid_comb_process(_comb, _input, _output) \ -{ \ - fluid_real_t _tmp = _comb.buffer[_comb.bufidx]; \ - _comb.filterstore = (_tmp * _comb.damp2) + (_comb.filterstore * _comb.damp1); \ - _comb.buffer[_comb.bufidx] = _input + (_comb.filterstore * _comb.feedback); \ - if (++_comb.bufidx >= _comb.bufsize) { \ - _comb.bufidx = 0; \ - } \ - _output += _tmp; \ -} - -#define numcombs 8 -#define numallpasses 4 -#define fixedgain 0.015f -/* scale_wet_width is a compensation weight factor to get an output +/*---------------------------------------------------------------------------- + Initial internal reverb settings (at reverb creation time) +-----------------------------------------------------------------------------*/ +/* SCALE_WET_WIDTH is a compensation weight factor to get an output amplitude (wet) rather independent of the width setting. 0: the output amplitude is fully dependant on the width setting. >0: the output amplitude is less dependant on the width setting. - With a scale_wet_width of 0.2 the output amplitude is rather + With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather independent of width setting (see fluid_revmodel_update()). */ -#define scale_wet_width 0.2f -#define scalewet 3.0f -#define scaledamp 1.0f -#define scaleroom 0.28f -#define offsetroom 0.7f -#define stereospread 23 +#define SCALE_WET_WIDTH 0.2f -/* - These values assume 44.1KHz sample rate - they will probably be OK for 48KHz sample rate - but would need scaling for 96KHz (or other) sample rates. - The values were obtained by listening tests. +/* It is best to inject the input signal less ofen. This contributes to obtain +a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */ +#define FIXED_GAIN 0.1f /* input gain */ + +/* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */ +#define SCALE_WET 5.0f /* scale output gain */ + +/*---------------------------------------------------------------------------- + Internal FDN late reverb settings +-----------------------------------------------------------------------------*/ + +/*-- Reverberation time settings ---------------------------------- + MIN_DC_REV_TIME est defined egal to the minimum value of freeverb: + MAX_DC_REV_TIME est defined egal to the maximum value of freeverb: + T60DC is computed from gi and the longuest delay line in freeverb: L8 = 1617 + T60 = -3 * Li * T / log10(gi) + T60 = -3 * Li * / (log10(gi) * sr) + + - Li: length of comb filter delay line. + - sr: sample rate. + - gi: the feedback gain. + + The minimum value for freeverb correspond to gi = 0.7. + with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s + + The maximum value for freeverb correspond to gi = 0.98. + with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s */ -#define combtuningL1 1116 -#define combtuningR1 (1116 + stereospread) -#define combtuningL2 1188 -#define combtuningR2 (1188 + stereospread) -#define combtuningL3 1277 -#define combtuningR3 (1277 + stereospread) -#define combtuningL4 1356 -#define combtuningR4 (1356 + stereospread) -#define combtuningL5 1422 -#define combtuningR5 (1422 + stereospread) -#define combtuningL6 1491 -#define combtuningR6 (1491 + stereospread) -#define combtuningL7 1557 -#define combtuningR7 (1557 + stereospread) -#define combtuningL8 1617 -#define combtuningR8 (1617 + stereospread) -#define allpasstuningL1 556 -#define allpasstuningR1 (556 + stereospread) -#define allpasstuningL2 441 -#define allpasstuningR2 (441 + stereospread) -#define allpasstuningL3 341 -#define allpasstuningR3 (341 + stereospread) -#define allpasstuningL4 225 -#define allpasstuningR4 (225 + stereospread) -struct _fluid_revmodel_t +#define MIN_DC_REV_TIME 0.7f /* minimum T60DC reverb time: seconds */ +#define MAX_DC_REV_TIME 12.5f /* maximumm T60DC time in seconds */ +#define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME) + +/* macro to compute internal reverberation time versus roomsize parameter */ +#define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize) + +/*-- Modulation related settings ----------------------------------*/ +/* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be: + + MOD_DEPTH: [3..6] (in samples). + MOD_FREQ: [0.5 ..2.0] (in Hz). + + Values below the lower limits are often not sufficient to cancel unwanted + "ringing"(resonant frequency). + Values above upper limits augment the unwanted "chorus". + + With NBR_DELAYS to 8: + MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6]. + With NBR_DELAYS to 12: + MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6] +*/ +#define MOD_DEPTH 4 /* modulation depth (samples)*/ +#define MOD_RATE 50 /* modulation rate (samples)*/ +#define MOD_FREQ 1.0f /* modulation frequency (Hz) */ +/* + Number of samples to add to the desired length of a delay line. This + allow to take account of modulation interpolation. + 1 is sufficient with MOD_DEPTH equal to 6. +*/ +#define INTERP_SAMPLES_NBR 1 + +/* phase offset between modulators waveform */ +#define MOD_PHASE (360.0/(float) NBR_DELAYS) + +#if (NBR_DELAYS == 8) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1129 +#elif (NBR_DELAYS == 12) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1093 + #define DELAY_L8 1129 + #define DELAY_L9 1151 + #define DELAY_L10 1171 + #define DELAY_L11 1187 +#endif + +/* Delay lines length table (in samples) */ +static const int delay_length[NBR_DELAYS] = { - fluid_real_t roomsize; - fluid_real_t damp; - fluid_real_t level, wet1, wet2; - fluid_real_t width; - fluid_real_t gain; - /* - The following are all declared inline - to remove the need for dynamic allocation - with its subsequent error-checking messiness - */ - /* Comb filters */ - fluid_comb combL[numcombs]; - fluid_comb combR[numcombs]; - /* Allpass filters */ - fluid_allpass allpassL[numallpasses]; - fluid_allpass allpassR[numallpasses]; + 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 }; -static void fluid_revmodel_update(fluid_revmodel_t *rev); -static void fluid_revmodel_init(fluid_revmodel_t *rev); -void fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate); +/*---------------------------------------------------------------------------*/ +/* The FDN late feed back matrix: A + T + A = P - 2 / N * u * u + N N N N + + N: the matrix dimension (i.e NBR_DELAYS). + P: permutation matrix. + u: is a colomn vector of 1. + +*/ +#define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS) + +/*---------------------------------------------------------------------------- + Internal FDN late structures and static functions +-----------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------- + Delay absorbent low pass filter +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t buffer; + fluid_real_t b0, a1; /* filter coefficients */ +} fdn_delay_lpf; + +/*----------------------------------------------------------------------------- + Sets coefficients for delay absorbent low pass filter. + @param lpf pointer on low pass filter structure. + @param b0,a1 coefficients. +-----------------------------------------------------------------------------*/ +static void set_fdn_delay_lpf(fdn_delay_lpf *lpf, + fluid_real_t b0, fluid_real_t a1) +{ + lpf->b0 = b0; + lpf->a1 = a1; +} + +/*----------------------------------------------------------------------------- + Process delay absorbent low pass filter. + @param mod_delay modulated delay line. + @param in, input sample. + @param out output sample. +-----------------------------------------------------------------------------*/ +/* process low pass damping filter (input, output, delay) */ +#define process_damping_filter(in,out,mod_delay) \ +{\ + out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \ + mod_delay->dl.damping.a1;\ + mod_delay->dl.damping.buffer = out;\ +}\ + + +/*----------------------------------------------------------------------------- + Delay line : + The delay line is composed of the line plus an absorbent low pass filter + to get frequency dependant reverb time. +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t *line; /* buffer line */ + int size; /* effective internal size (in samples) */ + /*-------------*/ + int line_in; /* line in position */ + int line_out; /* line out position */ + /*-------------*/ + fdn_delay_lpf damping; /* damping low pass filter */ +} delay_line; + + +/*----------------------------------------------------------------------------- + Clears a delay line to DC_OFFSET float value. + @param dl pointer on delay line structure +-----------------------------------------------------------------------------*/ +static void clear_delay_line(delay_line *dl) +{ + int i; + + for(i = 0; i < dl->size; i++) + { + dl->line[i] = DC_OFFSET; + } +} + +/*----------------------------------------------------------------------------- + Push a sample val into the delay line +-----------------------------------------------------------------------------*/ +#define push_in_delay_line(dl, val) \ +{\ + dl->line[dl->line_in] = val;\ + /* Incrementation and circular motion if necessary */\ + if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ +}\ + +/*----------------------------------------------------------------------------- + Modulator for modulated delay line +-----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + Sinusoidal modulator +-----------------------------------------------------------------------------*/ +/* modulator are integrated in modulated delay line */ +typedef struct +{ + fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ + fluid_real_t buffer1; /* buffer1 */ + fluid_real_t buffer2; /* buffer2 */ + fluid_real_t reset_buffer2;/* reset value of buffer2 */ +} sinus_modulator; + +/*----------------------------------------------------------------------------- + Sets the frequency of sinus oscillator. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param phase initial phase of the oscillator in degree (0 to 360). +-----------------------------------------------------------------------------*/ +static void set_mod_frequency(sinus_modulator *mod, + float freq, float sample_rate, float phase) +{ + fluid_real_t w = 2 * M_PI * freq / sample_rate; /* intial angle */ + + mod->a1 = 2 * cos(w); + mod->buffer2 = sin(2 * M_PI * phase / 360 - w); /* y(n-1) = sin(-intial angle) */ + mod->buffer1 = sin(2 * M_PI * phase / 360); /* y(n) = sin(initial phase) */ + mod->reset_buffer2 = sin(M_PI / 2.0 - w); /* reset value for PI/2 */ +} + +/*----------------------------------------------------------------------------- + Gets current value of sinus modulator: + y(n) = a1 . y(n-1) - y(n-2) + out = a1 . buffer1 - buffer2 + + @param pointer on modulator structure. + @return current value of the modulator sine wave. +-----------------------------------------------------------------------------*/ +static inline fluid_real_t get_mod_sinus(sinus_modulator *mod) +{ + fluid_real_t out; + out = mod->a1 * mod->buffer1 - mod->buffer2; + mod->buffer2 = mod->buffer1; + + if(out >= 1.0) /* reset in case of instability near PI/2 */ + { + out = 1.0; /* forces output to the right value */ + mod->buffer2 = mod->reset_buffer2; + } + + if(out <= -1.0) /* reset in case of instability near -PI/2 */ + { + out = -1.0; /* forces output to the right value */ + mod->buffer2 = - mod->reset_buffer2; + } + + mod->buffer1 = out; + return out; +} + +/*----------------------------------------------------------------------------- + Modulated delay line. The line is composed of: + - the delay line with its damping low pass filter. + - the sinusoidal modulator. + - center output position modulated by the modulator. + - variable rate control of center output position. + - first order All-Pass interpolator. +-----------------------------------------------------------------------------*/ +typedef struct +{ + /* delay line with damping low pass filter member */ + delay_line dl; /* delayed line */ + /*---------------------------*/ + /* Sinusoidal modulator member */ + sinus_modulator mod; /* sinus modulator */ + /*-------------------------*/ + /* center output position members */ + fluid_real_t center_pos_mod; /* center output position modulated by modulator */ + int mod_depth; /* modulation depth (in samples) */ + /*-------------------------*/ + /* variable rate control of center output position */ + int index_rate; /* index rate to know when to update center_pos_mod */ + int mod_rate; /* rate at which center_pos_mod is updated */ + /*-------------------------*/ + /* first order All-Pass interpolator members */ + fluid_real_t frac_pos_mod; /* fractional position part between samples) */ + /* previous value used when interpolating using fractional */ + fluid_real_t buffer; +} mod_delay_line; + + +/*----------------------------------------------------------------------------- + Modulated delay line initialization. + + Sets the length line ( alloc delay samples). + Remark: the function sets the internal size accordling to the length delay_length. + As the delay line is a modulated line, its internal size is augmented by mod_depth. + The size is also augmented by INTERP_SAMPLES_NBR to take account of interpolation. + + @param mdl, pointer on modulated delay line. + @param delay_length the length of the delay line in samples. + @param mod_depth depth of the modulation in samples (amplitude of the sine wave). + @param mod_rate the rate of the modulation in samples. + @return FLUID_OK if success , FLUID_FAILED if memory error. + + Return FLUID_OK if success, FLUID_FAILED if memory error. +-----------------------------------------------------------------------------*/ +static int set_mod_delay_line(mod_delay_line *mdl, + int delay_length, + int mod_depth, + int mod_rate + ) +{ + /*-----------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) + { + return FLUID_FAILED; + } + + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + FLUID_LOG(FLUID_INFO, + "fdn reverb: modulation depth has been limited"); + mod_depth = delay_length - 1; + } + + mdl->mod_depth = mod_depth; + /*----------------------------------------------------------------------- + allocates delay_line and initialize members: + - line, size, line_in, line_out... + */ + { + /* total size of the line: + size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ + mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; + mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); + + if(! mdl->dl.line) + { + return FLUID_FAILED; + } + + clear_delay_line(&mdl->dl); /* clears the buffer */ + + /* Initializes line_in to the start of the buffer */ + mdl->dl.line_in = 0; + /* Initializes line_out index INTERP_SAMPLES_NBR samples after line_in */ + /* so that the delay between line_out and line_in is: + mod_depth + delay_length */ + mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; + } + + /* Damping low pass filter -------------------*/ + mdl->dl.damping.buffer = 0; + /*------------------------------------------------------------------------ + Initializes modulation members: + - modulated center position: center_pos_mod + - index rate to know when to update center_pos_mod:index_rate + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - interpolator member: buffer, frac_pos_mod + -------------------------------------------------------------------------*/ + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + mdl->mod_rate = 1; /* default modulation rate: every one sample */ + + if(mod_rate > mdl->dl.size) + { + FLUID_LOG(FLUID_INFO, + "fdn reverb: modulation rate is out of range"); + } + else + { + mdl->mod_rate = mod_rate; + } + + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between line_out and center_pos_mod is mod_depth. + - the delay between center_pos_mod and line_in is delay_length. + */ + mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; + + /* index rate to control when to update center_pos_mod */ + /* Important: must be set to get center_pos_mod immediatly used for the + reading of first sample (see get_mod_delay()) */ + mdl->index_rate = mdl->mod_rate; + + /* initializes 1st order All-Pass interpolator members */ + mdl->buffer = 0; /* previous delay sample value */ + mdl->frac_pos_mod = 0; /* fractional position (between consecutives sample) */ + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + Reads the sample value out of the modulated delay line. + @param mdl, pointer on modulated delay line. + @return the sample value. +-----------------------------------------------------------------------------*/ +static inline fluid_real_t get_mod_delay(mod_delay_line *mdl) +{ + fluid_real_t out_index; /* new modulated index position */ + int int_out_index; /* integer part of out_index */ + fluid_real_t out; /* value to return */ + + /* Checks if the modulator must be updated (every mod_rate samples). */ + /* Important: center_pos_mod must be used immediatly for the + first sample. So, mdl->index_rate must be initialized + to mdl->mod_rate (set_mod_delay_line()) */ + + if(++mdl->index_rate >= mdl->mod_rate) + { + mdl->index_rate = 0; + + /* out_index = center position (center_pos_mod) + sinus waweform */ + out_index = mdl->center_pos_mod + + get_mod_sinus(&mdl->mod) * mdl->mod_depth; + + /* extracts integer part in int_out_index */ + if(out_index >= 0.0f) + { + int_out_index = (int)out_index; /* current integer part */ + + /* forces read index (line_out) with integer modulation value */ + /* Boundary check and circular motion as needed */ + if((mdl->dl.line_out = int_out_index) >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + } + else /* negative */ + { + int_out_index = (int)(out_index - 1); /* previous integer part */ + /* forces read index (line_out) with integer modulation value */ + /* circular motion as needed */ + mdl->dl.line_out = int_out_index + mdl->dl.size; + } + + /* extracts fractionnal part. (it will be used when interpolating + between line_out and line_out +1) and memorize it. + Memorizing is necessary for modulation rate above 1 */ + mdl->frac_pos_mod = out_index - int_out_index; + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size) + { + mdl->center_pos_mod -= mdl->dl.size; + } + } + + /* First order all-pass interpolation ----------------------------------*/ + /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ + /* begins interpolation: read current sample */ + out = mdl->dl.line[mdl->dl.line_out]; + + /* updates line_out to the next sample. + Boundary check and circular motion as needed */ + if(++mdl->dl.line_out >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + + /* Fractional interpolation beetween next sample (at next position) and + previous output added to current sample. + */ + out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer); + mdl->buffer = out; /* memorizes current output */ + return out; +} + +/*----------------------------------------------------------------------------- + Late structure +-----------------------------------------------------------------------------*/ +struct _fluid_late +{ + fluid_real_t samplerate; /* sample rate */ + /*----- High pass tone corrector -------------------------------------*/ + fluid_real_t tone_buffer; + fluid_real_t b1, b2; + /*----- Modulated delay lines lines ----------------------------------*/ + mod_delay_line mod_delay_lines[NBR_DELAYS]; + /*-----------------------------------------------------------------------*/ + /* Output coefficients for separate Left and right stereo outputs */ + fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */ + fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/ +}; + +typedef struct _fluid_late fluid_late; +/*----------------------------------------------------------------------------- + fluidsynth reverb structure +-----------------------------------------------------------------------------*/ +struct _fluid_revmodel_t +{ + /* reverb parameters */ + fluid_real_t roomsize; /* acting on reverb time */ + fluid_real_t damp; /* acting on frequency dependent reverb time */ + fluid_real_t level, wet1, wet2; /* output level */ + fluid_real_t width; /* width stereo separation */ + + /* fdn reverberation structure */ + fluid_late late; +}; + +/*----------------------------------------------------------------------------- + Updates Reverb time and absorbent filters coefficients from parameters: + + @param late pointer on late structure. + @param roomsize (0 to 1): acting on reverb time. + @param damping (0 to 1): acting on absorbent damping filter. + + Design formulas: + https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html + https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html +-----------------------------------------------------------------------------*/ +static void update_rev_time_damping(fluid_late *late, + fluid_real_t roomsize, fluid_real_t damp) +{ + int i; + fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */ + fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */ + + fluid_real_t alpha; + + /*-------------------------------------------- + Computes dc_rev_time and alpha + ----------------------------------------------*/ + { + fluid_real_t gi_tmp, ai_tmp; +#ifdef ROOMSIZE_RESPONSE_LINEAR + /* roomsize parameter behave linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 10 s). + * This linear response is convenient when using GUI controls. + */ + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + dc_rev_time = GET_DC_REV_TIME(roomsize); + /* computes gi_tmp from dc_rev_time using relation E2 */ + gi_tmp = (fluid_real_t) pow(10, -3 * delay_length[NBR_DELAYS - 1] * + sample_period / dc_rev_time); /* E2 */ +#else + /* roomsize parameters have the same response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s). + */ + { + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + fluid_real_t gi_min, gi_max; + /* values gi_min et gi_max are computed using E2 for the line with + maximum delay */ + gi_max = (fluid_real_t)pow(10, -3 * delay_length[NBR_DELAYS - 1] * + sample_period / MAX_DC_REV_TIME); /* E2 */ + gi_min = (fluid_real_t)pow(10, -3 * delay_length[NBR_DELAYS - 1] * + sample_period / MIN_DC_REV_TIME); /* E2 */ + /* gi = f(roomsize, gi_max, gi_min) */ + gi_tmp = gi_min + roomsize * (gi_max - gi_min); + /* Computes T60DC from gi using inverse of relation E2.*/ + dc_rev_time = -3 * delay_length[NBR_DELAYS - 1] * sample_period / log10(gi_tmp); + } +#endif /* ROOMSIZE_RESPONSE_LINEAR */ + /*-------------------------------------------- + Computes alpha + ----------------------------------------------*/ + /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */ + /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */ + ai_tmp = 1.0 * damp; + alpha = sqrt(1 / (1 - ai_tmp / (20 * log10(gi_tmp) * log(10) / 80))); /* R */ + } + + /* updates tone corrector coefficients b1,b2 from alpha */ + { + /* + Beta = (1 - alpha) / (1 + alpha) + b1 = 1/(1-beta) + b2 = beta * b1 + */ + fluid_real_t beta = (1 - alpha) / (1 + alpha); + late->b1 = 1 / (1 - beta); + late->b2 = beta * late->b1; + late->tone_buffer = 0.0f; + } + + /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */ + for(i = 0; i < NBR_DELAYS; i++) + { + /* iir low pass filter gain */ + fluid_real_t gi = (fluid_real_t)pow(10, -3 * delay_length[i] * + sample_period / dc_rev_time); + + /* iir low pass filter feedback gain */ + fluid_real_t ai = (fluid_real_t)(20 * log10(gi) * log(10) / 80 * + (1 - 1 / pow(alpha, 2))); + /* b0 = gi * (1 - ai), a1 = - ai */ + set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping, + gi * (1 - ai), -ai); + } +} + +/*----------------------------------------------------------------------------- + Updates stereo coefficients + @param late pointer on late structure + @param wet level integrated in stereo coefficients. +-----------------------------------------------------------------------------*/ +static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1) +{ + int i; + + for(i = 0; i < NBR_DELAYS; i++) + { + /* delay lines output gains vectors Left and Right + + L R + 0 | 1 1| + 1 |-1 1| + 2 | 1 -1| + 3 |-1 -1| + + 4 | 1 1| + 5 |-1 1| + stereo gain = 6 | 1 -1| + 7 |-1 -1| + + 8 | 1 1| + 9 |-1 1| + 10| 1 -1| + 11|-1 -1| + */ + + late->out_left_gain[i] = wet1; + + /* Sets Left coefficients first */ + if(i % 2) /* Left is 1,-1, 1,-1, 1,-1,.... */ + { + late->out_left_gain[i] *= -1; + } + + /* 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]; + } + } +} + +/*----------------------------------------------------------------------------- + fluid_late destructor. + @param late pointer on late structure. +-----------------------------------------------------------------------------*/ +static void delete_fluid_rev_late(fluid_late *late) +{ + int i; + fluid_return_if_fail(late != NULL); + + /* free the delay lines */ + for(i = 0; i < NBR_DELAYS; i++) + { + FLUID_FREE(late->mod_delay_lines[i].dl.line); + } +} + +/*----------------------------------------------------------------------------- + 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) +{ + int result; /* return value */ + int i; + + FLUID_MEMSET(late, 0, sizeof(fluid_late)); + + late->samplerate = sample_rate; + + /*-------------------------------------------------------------------------- + First initialize the modulated delay lines + */ + + for(i = 0; i < NBR_DELAYS; i++) + { + /* sets local delay lines's parameters */ + result = set_mod_delay_line(&late->mod_delay_lines[i], + delay_length[i], MOD_DEPTH, MOD_RATE); + + if(result == FLUID_FAILED) + { + return FLUID_FAILED; + } + + /* Sets local Modulators parameters: frequency and phase + Each modulateur are shifted of MOD_PHASE degree + */ + set_mod_frequency(&late->mod_delay_lines[i].mod, + MOD_FREQ * MOD_RATE, + sample_rate, + (float)(MOD_PHASE * i)); + } + + /*-----------------------------------------------------------------------*/ + update_stereo_coefficient(late, 1.0f); + return FLUID_OK; +} + +/* + Clears the delay lines. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_init(fluid_revmodel_t *rev) +{ + int i; + + /* clears all the delay lines */ + for(i = 0; i < NBR_DELAYS; i ++) + { + clear_delay_line(&rev->late.mod_delay_lines[i].dl); + } +} + + +/* + updates internal parameters. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_update(fluid_revmodel_t *rev) +{ + /* Recalculate internal values after parameters change */ + + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 100). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH) + Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + fluid_real_t wet = (rev->level * SCALE_WET) / + (1.0f + rev->width * SCALE_WET_WIDTH); + + /* wet1 and wet2 are used by the stereo effect controled by the width setting + for producing a stereo ouptput from a monophonic reverb signal. + Please see the note above about a side effect tendency */ + + rev->wet1 = wet * (rev->width / 2.0f + 0.5f); + rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); + + /* integrates wet1 in stereo coefficient (this will save one multiply) */ + update_stereo_coefficient(&rev->late, rev->wet1); + + if(rev->wet1 > 0.0) + { + rev->wet2 /= rev->wet1; + } + + /* Reverberation time and damping */ + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); +} + +/*---------------------------------------------------------------------------- + Reverb API +-----------------------------------------------------------------------------*/ + +/* +* Creates a reverb. +* @param sample_rate sample rate in Hz. +* @return pointer on the new reverb or NULL if memory error. +* Reverb API. +*/ fluid_revmodel_t * new_fluid_revmodel(fluid_real_t sample_rate) { @@ -269,280 +1004,355 @@ new_fluid_revmodel(fluid_real_t sample_rate) return NULL; } - fluid_set_revmodel_buffers(rev, sample_rate); - - /* Set default values */ - fluid_allpass_setfeedback(&rev->allpassL[0], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[0], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[1], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[1], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[2], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[2], 0.5f); - fluid_allpass_setfeedback(&rev->allpassL[3], 0.5f); - fluid_allpass_setfeedback(&rev->allpassR[3], 0.5f); - - rev->gain = fixedgain; + /* create fdn reverb */ + if(create_fluid_rev_late(&rev->late, sample_rate) != FLUID_OK) + { + delete_fluid_revmodel(rev); + return NULL; + } return rev; } +/* +* free the reverb. +* @param pointer on rev to free. +* Reverb API. +*/ void delete_fluid_revmodel(fluid_revmodel_t *rev) { - int i; fluid_return_if_fail(rev != NULL); - - for(i = 0; i < numcombs; i++) - { - fluid_comb_release(&rev->combL[i]); - fluid_comb_release(&rev->combR[i]); - } - - for(i = 0; i < numallpasses; i++) - { - fluid_allpass_release(&rev->allpassL[i]); - fluid_allpass_release(&rev->allpassR[i]); - } - + delete_fluid_rev_late(&rev->late); FLUID_FREE(rev); } -void -fluid_set_revmodel_buffers(fluid_revmodel_t *rev, fluid_real_t sample_rate) -{ - - float srfactor = sample_rate / 44100.0f; - - fluid_comb_setbuffer(&rev->combL[0], combtuningL1 * srfactor); - fluid_comb_setbuffer(&rev->combR[0], combtuningR1 * srfactor); - fluid_comb_setbuffer(&rev->combL[1], combtuningL2 * srfactor); - fluid_comb_setbuffer(&rev->combR[1], combtuningR2 * srfactor); - fluid_comb_setbuffer(&rev->combL[2], combtuningL3 * srfactor); - fluid_comb_setbuffer(&rev->combR[2], combtuningR3 * srfactor); - fluid_comb_setbuffer(&rev->combL[3], combtuningL4 * srfactor); - fluid_comb_setbuffer(&rev->combR[3], combtuningR4 * srfactor); - fluid_comb_setbuffer(&rev->combL[4], combtuningL5 * srfactor); - fluid_comb_setbuffer(&rev->combR[4], combtuningR5 * srfactor); - fluid_comb_setbuffer(&rev->combL[5], combtuningL6 * srfactor); - fluid_comb_setbuffer(&rev->combR[5], combtuningR6 * srfactor); - fluid_comb_setbuffer(&rev->combL[6], combtuningL7 * srfactor); - fluid_comb_setbuffer(&rev->combR[6], combtuningR7 * srfactor); - fluid_comb_setbuffer(&rev->combL[7], combtuningL8 * srfactor); - fluid_comb_setbuffer(&rev->combR[7], combtuningR8 * srfactor); - fluid_allpass_setbuffer(&rev->allpassL[0], allpasstuningL1 * srfactor); - fluid_allpass_setbuffer(&rev->allpassR[0], allpasstuningR1 * srfactor); - fluid_allpass_setbuffer(&rev->allpassL[1], allpasstuningL2 * srfactor); - fluid_allpass_setbuffer(&rev->allpassR[1], allpasstuningR2 * srfactor); - fluid_allpass_setbuffer(&rev->allpassL[2], allpasstuningL3 * srfactor); - fluid_allpass_setbuffer(&rev->allpassR[2], allpasstuningR3 * srfactor); - fluid_allpass_setbuffer(&rev->allpassL[3], allpasstuningL4 * srfactor); - fluid_allpass_setbuffer(&rev->allpassR[3], allpasstuningR4 * srfactor); - - /* Clear all buffers */ - fluid_revmodel_init(rev); -} - - -static void -fluid_revmodel_init(fluid_revmodel_t *rev) -{ - int i; - - for(i = 0; i < numcombs; i++) - { - fluid_comb_init(&rev->combL[i]); - fluid_comb_init(&rev->combR[i]); - } - - for(i = 0; i < numallpasses; i++) - { - fluid_allpass_init(&rev->allpassL[i]); - fluid_allpass_init(&rev->allpassR[i]); - } -} - -void -fluid_revmodel_reset(fluid_revmodel_t *rev) -{ - fluid_revmodel_init(rev); -} - -void -fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int i, k = 0; - fluid_real_t outL, outR, input; - - for(k = 0; k < FLUID_BUFSIZE; k++) - { - - outL = outR = 0; - - /* The original Freeverb code expects a stereo signal and 'input' - * is set to the sum of the left and right input sample. Since - * this code works on a mono signal, 'input' is set to twice the - * input sample. */ - input = (2.0f * in[k] + DC_OFFSET) * rev->gain; - - /* Accumulate comb filters in parallel */ - for(i = 0; i < numcombs; i++) - { - fluid_comb_process(rev->combL[i], input, outL); - fluid_comb_process(rev->combR[i], input, outR); - } - - /* Feed through allpasses in series */ - for(i = 0; i < numallpasses; i++) - { - fluid_allpass_process(rev->allpassL[i], outL); - fluid_allpass_process(rev->allpassR[i], outR); - } - - /* Remove the DC offset */ - outL -= DC_OFFSET; - outR -= DC_OFFSET; - - /* Calculate output REPLACING anything already there */ - left_out[k] = outL * rev->wet1 + outR * rev->wet2; - right_out[k] = outR * rev->wet1 + outL * rev->wet2; - } -} - -void -fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, - fluid_real_t *left_out, fluid_real_t *right_out) -{ - int i, k = 0; - fluid_real_t outL, outR, input; - - for(k = 0; k < FLUID_BUFSIZE; k++) - { - - outL = outR = 0; - - /* The original Freeverb code expects a stereo signal and 'input' - * is set to the sum of the left and right input sample. Since - * this code works on a mono signal, 'input' is set to twice the - * input sample. */ - input = (2.0f * in[k] + DC_OFFSET) * rev->gain; - - /* Accumulate comb filters in parallel */ - for(i = 0; i < numcombs; i++) - { - fluid_comb_process(rev->combL[i], input, outL); - fluid_comb_process(rev->combR[i], input, outR); - } - - /* Feed through allpasses in series */ - for(i = 0; i < numallpasses; i++) - { - fluid_allpass_process(rev->allpassL[i], outL); - fluid_allpass_process(rev->allpassR[i], outR); - } - - /* Remove the DC offset */ - outL -= DC_OFFSET; - outR -= DC_OFFSET; - - /* Calculate output MIXING with anything already there */ - left_out[k] += outL * rev->wet1 + outR * rev->wet2; - right_out[k] += outR * rev->wet1 + outL * rev->wet2; - } -} - -static void -fluid_revmodel_update(fluid_revmodel_t *rev) -{ - /* Recalculate internal values after parameter change */ - int i; - - /* The stereo amplitude equation (wet1 and wet2 below) have a - tendency to produce high amplitude with high width values ( 1 < width < 100). - This results in an unwanted noisy output clipped by the audio card. - To avoid this dependency, we divide by (1 + rev->width * scale_wet_width) - Actually, with a scale_wet_width of 0.2, (regardless of level setting), - the output amplitude (wet) seems rather independent of width setting */ - fluid_real_t wet = (rev->level * scalewet) / - (1.0f + rev->width * scale_wet_width); - - /* wet1 and wet2 are used by the stereo effect controled by the width setting - for producing a stereo ouptput from a monophonic reverb signal. - Please see the note above about a side effect tendency */ - rev->wet1 = wet * (rev->width / 2.0f + 0.5f); - rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); - - for(i = 0; i < numcombs; i++) - { - fluid_comb_setfeedback(&rev->combL[i], rev->roomsize); - fluid_comb_setfeedback(&rev->combR[i], rev->roomsize); - } - - for(i = 0; i < numcombs; i++) - { - fluid_comb_setdamp(&rev->combL[i], rev->damp); - fluid_comb_setdamp(&rev->combR[i], rev->damp); - } -} - -/** - * Set one or more reverb parameters. - * @param rev Reverb instance - * @param set One or more flags from #fluid_revmodel_set_t indicating what - * parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters) - * @param roomsize Reverb room size - * @param damping Reverb damping - * @param width Reverb width - * @param level Reverb level - */ +/* +* Sets one or more reverb parameters. +* @param rev Reverb instance. +* @param set One or more flags from #fluid_revmodel_set_t indicating what +* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters). +* @param roomsize Reverb room size. +* @param damping Reverb damping. +* @param width Reverb width. +* @param level Reverb level. +* +* Reverb API. +*/ 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) { + /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_ROOMSIZE) { - /* With upper limit above 1.07, the output amplitude will grow - exponentially. So, keeping this upper limit to 1.0 seems sufficient - as it produces yet a long reverb time */ fluid_clip(roomsize, 0.0f, 1.0f); - rev->roomsize = (roomsize * scaleroom) + offsetroom; + rev->roomsize = roomsize; } + /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_DAMPING) { - rev->damp = damping * scaledamp; + fluid_clip(damping, 0.0f, 1.0f); + rev->damp = damping; } + /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_WIDTH) { rev->width = width; } + /*-----------------------------------*/ if(set & FLUID_REVMODEL_SET_LEVEL) { fluid_clip(level, 0.0f, 1.0f); rev->level = level; } + /* updates internal parameters */ fluid_revmodel_update(rev); } +/* +* Applies a sample rate change on the reverb. +* @param rev the reverb. +* @param sample_rate new sample rate value. +* +* Reverb API. +*/ void fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) { int i; - for(i = 0; i < numcombs; i++) + /* updates modulator frequency according to sample rate change */ + for(i = 0; i < NBR_DELAYS; i++) { - fluid_comb_release(&rev->combL[i]); - fluid_comb_release(&rev->combR[i]); + set_mod_frequency(&rev->late.mod_delay_lines[i].mod, + MOD_FREQ * MOD_RATE, + sample_rate, + (float)(MOD_PHASE * i)); } - for(i = 0; i < numallpasses; i++) - { - fluid_allpass_release(&rev->allpassL[i]); - fluid_allpass_release(&rev->allpassR[i]); - } + /* updates damping filter coefficients according to sample rate change */ + rev->late.samplerate = sample_rate; + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); - fluid_set_revmodel_buffers(rev, sample_rate); + /* clears all delay lines */ + fluid_revmodel_init(rev); +} + +/* +* Damps the reverb by clearing the delay lines. +* @param rev the reverb. +* +* Reverb API. +*/ +void +fluid_revmodel_reset(fluid_revmodel_t *rev) +{ + fluid_revmodel_init(rev); +} + +/*----------------------------------------------------------------------------- +* fdn reverb process replace. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE sample). +* @param left_out stereo left processed output (FLUID_BUFSIZE sample). +* @param right_out stereo right processed output (FLUID_BUFSIZE sample). +* +* The processed reverb is replacing anything there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void +fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix computation */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; + +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction. + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in delay_out[]. + - also matrix_factor is computed (to simplify further matrix product) + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These wil be use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line.Each input is a combination of + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + + /* Calculates stereo output REPLACING anything already there: */ + /* + left_out[k] = out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] = out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right we simplify previous + relation by suppression of one multiply as this: + + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + */ + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + } +} + + +/*----------------------------------------------------------------------------- +* fdn reverb process mix. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE samples). +* @param left_out stereo left processed output (FLUID_BUFSIZE samples). +* @param right_out stereo right processed output (FLUID_BUFSIZE samples). +* +* The processed reverb is mixed in out with samples already there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix term */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in local delay_out[]. + - also matrix_factor is computed (to simplify further matrix product). + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These wil be use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line. Each input is a combination of: + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + /* Calculates stereo output MIXING anything already there: */ + /* + left_out[k] += out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] += out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right we simplify previous + relation by suppression of one multiply as this: + + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + */ + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + } } From cfe2d158f49525f232f864b1e46798c1ce1d5d21 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 1 Dec 2018 15:04:26 +0100 Subject: [PATCH 064/101] Add WaveOut driver (#466) --- CMakeLists.txt | 7 + cmake_admin/report.cmake | 6 + src/CMakeLists.txt | 5 + src/config.cmake | 3 + src/drivers/fluid_adriver.c | 10 + src/drivers/fluid_adriver.h | 7 + src/drivers/fluid_waveout.c | 386 ++++++++++++++++++++++++++++++++++++ 7 files changed, 424 insertions(+) create mode 100644 src/drivers/fluid_waveout.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 298199dc..02918f27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ option ( enable-midishare "compile MidiShare support (if it is available)" on ) option ( enable-network "enable network support (requires BSD sockets)" on ) option ( enable-oss "compile OSS support (if it is available)" on ) option ( enable-dsound "compile DirectSound support (if it is available)" on ) +option ( enable-waveout "compile Windows WaveOut support (if it is available)" on ) option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) @@ -194,6 +195,7 @@ endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE unset ( WINDOWS_SUPPORT CACHE ) unset ( WINDOWS_LIBS CACHE ) unset ( DSOUND_SUPPORT CACHE ) +unset ( WAVEOUT_SUPPORT CACHE ) unset ( WINMIDI_SUPPORT CACHE ) unset ( MINGW32 CACHE ) if ( WIN32 ) @@ -221,6 +223,11 @@ if ( WIN32 ) set ( WINMIDI_SUPPORT 1 ) endif () + if ( enable-waveout AND HAVE_MMSYSTEM_H ) + set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" ) + set ( WAVEOUT_SUPPORT 1 ) + endif () + set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" ) set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" ) if ( MSVC ) diff --git a/cmake_admin/report.cmake b/cmake_admin/report.cmake index e25a989d..18abf5fa 100644 --- a/cmake_admin/report.cmake +++ b/cmake_admin/report.cmake @@ -80,6 +80,12 @@ else ( DSOUND_SUPPORT ) message ( "DSound: no" ) endif ( DSOUND_SUPPORT ) +if ( WAVEOUT_SUPPORT ) + message ( "WaveOut support: yes" ) +else ( WAVEOUT_SUPPORT ) + message ( "WaveOut support: no" ) +endif ( WAVEOUT_SUPPORT ) + if ( WINMIDI_SUPPORT ) message ( "WinMidi support: yes" ) else ( WINMIDI_SUPPORT ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf8f3369..b8480097 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,6 +82,10 @@ if ( DSOUND_SUPPORT ) set ( fluid_dsound_SOURCES drivers/fluid_dsound.c ) endif ( DSOUND_SUPPORT ) +if ( WAVEOUT_SUPPORT ) + set ( fluid_waveout_SOURCES drivers/fluid_waveout.c ) +endif ( WAVEOUT_SUPPORT ) + if ( WINMIDI_SUPPORT ) set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c ) endif ( WINMIDI_SUPPORT ) @@ -248,6 +252,7 @@ add_library ( libfluidsynth-OBJ OBJECT ${fluid_portaudio_SOURCES} ${fluid_pulse_SOURCES} ${fluid_dsound_SOURCES} + ${fluid_waveout_SOURCES} ${fluid_winmidi_SOURCES} ${libfluidsynth_SOURCES} ${public_HEADERS} diff --git a/src/config.cmake b/src/config.cmake index 2d1d786b..cd1f5bc5 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -196,6 +196,9 @@ /* Define to enable DirectSound driver */ #cmakedefine DSOUND_SUPPORT @DSOUND_SUPPORT@ +/* Define to enable Windows WaveOut driver */ +#cmakedefine WAVEOUT_SUPPORT @WAVEOUT_SUPPORT@ + /* Define to enable Windows MIDI driver */ #cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@ diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index f1ab772a..8419ab9f 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -99,6 +99,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif +#if WAVEOUT_SUPPORT + { + "waveout", + new_fluid_waveout_audio_driver, + NULL, + delete_fluid_waveout_audio_driver, + fluid_waveout_audio_driver_settings + }, +#endif + #if SNDMAN_SUPPORT { "sndman", diff --git a/src/drivers/fluid_adriver.h b/src/drivers/fluid_adriver.h index 1564168f..2ec9ded1 100644 --- a/src/drivers/fluid_adriver.h +++ b/src/drivers/fluid_adriver.h @@ -83,6 +83,13 @@ void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *p); void fluid_dsound_audio_driver_settings(fluid_settings_t *settings); #endif +#if WAVEOUT_SUPPORT +fluid_audio_driver_t *new_fluid_waveout_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *p); +void fluid_waveout_audio_driver_settings(fluid_settings_t *settings); +#endif + #if PORTAUDIO_SUPPORT void fluid_portaudio_driver_settings(fluid_settings_t *settings); fluid_audio_driver_t *new_fluid_portaudio_driver(fluid_settings_t *settings, diff --git a/src/drivers/fluid_waveout.c b/src/drivers/fluid_waveout.c new file mode 100644 index 00000000..7fbaddcc --- /dev/null +++ b/src/drivers/fluid_waveout.c @@ -0,0 +1,386 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * Copyright (C) 2018 Carlo Bramini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_adriver.h" +#include "fluid_settings.h" + +#if WAVEOUT_SUPPORT + +#include + +#define NOBITMAP +#include + +/* Number of buffers in the chain */ +#define NB_SOUND_BUFFERS 4 + +/* Milliseconds of a single sound buffer */ +#define MS_BUFFER_LENGTH 20 + +typedef struct +{ + fluid_audio_driver_t driver; + + fluid_synth_t *synth; + fluid_audio_callback_t write_ptr; + + HWAVEOUT hWaveOut; + WAVEHDR waveHeader[NB_SOUND_BUFFERS]; + + int sample_size; + int num_frames; + + HANDLE hThread; + DWORD dwThread; + + int nQuit; + HANDLE hQuit; + +} fluid_waveout_audio_driver_t; + + +/* Thread for playing sample buffers */ +static DWORD WINAPI fluid_waveout_synth_thread(void *data) +{ + fluid_waveout_audio_driver_t *dev; + WAVEHDR *pWave; + + MSG msg; + int code; + + /* Forces creation of message queue */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + for(;;) + { + code = GetMessage(&msg, NULL, 0, 0); + + if(code < 0) + { + FLUID_LOG(FLUID_ERR, "fluid_waveout_synth_thread: GetMessage() failed."); + break; + } + + if(msg.message == WM_CLOSE) + { + break; + } + + switch(msg.message) + { + case MM_WOM_DONE: + pWave = (WAVEHDR *)msg.lParam; + dev = (fluid_waveout_audio_driver_t *)pWave->dwUser; + + if(dev->nQuit > 0) + { + /* Release the sample buffer */ + waveOutUnprepareHeader((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR)); + + if(--dev->nQuit == 0) + { + SetEvent(dev->hQuit); + } + } + else + { + dev->write_ptr(dev->synth, dev->num_frames, pWave->lpData, 0, 2, pWave->lpData, 1, 2); + + waveOutWrite((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR)); + } + + break; + } + } + + return 0; +} + +void fluid_waveout_audio_driver_settings(fluid_settings_t *settings) +{ + UINT n, nDevs = waveOutGetNumDevs(); +#ifdef _UNICODE + char dev_name[MAXPNAMELEN]; +#endif + + fluid_settings_register_str(settings, "audio.waveout.device", "default", 0); + fluid_settings_add_option(settings, "audio.waveout.device", "default"); + + for(n = 0; n < nDevs; n++) + { + WAVEOUTCAPS caps; + MMRESULT res; + + res = waveOutGetDevCaps(n, &caps, sizeof(caps)); + + if(res == MMSYSERR_NOERROR) + { +#ifdef _UNICODE + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, dev_name, MAXPNAMELEN, 0, 0); + FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name); + fluid_settings_add_option(settings, "audio.waveout.device", dev_name); +#else + FLUID_LOG(FLUID_DBG, "Testing audio device: %s", caps.szPname); + fluid_settings_add_option(settings, "audio.waveout.device", caps.szPname); +#endif + } + } +} + + +/* + * new_fluid_waveout_audio_driver + */ +fluid_audio_driver_t * +new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) +{ + fluid_waveout_audio_driver_t *dev = NULL; + fluid_audio_callback_t write_ptr; + double sample_rate; + int periods, period_size, frequency, sample_size; + LPSTR ptrBuffer; + int lenBuffer; + int device; + int i; + WAVEFORMATEX wfx; + char dev_name[MAXPNAMELEN]; + MMRESULT errCode; + + /* Retrieve the settings */ + fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); + fluid_settings_getint(settings, "audio.periods", &periods); + fluid_settings_getint(settings, "audio.period-size", &period_size); + + /* Clear the format buffer */ + ZeroMemory(&wfx, sizeof(WAVEFORMATEX)); + + /* check the format */ + if(fluid_settings_str_equal(settings, "audio.sample-format", "float")) + { + FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); + + sample_size = sizeof(float); + write_ptr = fluid_synth_write_float; + + wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + } + else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) + { + FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format"); + + sample_size = sizeof(short); + write_ptr = fluid_synth_write_s16; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + } + else + { + FLUID_LOG(FLUID_ERR, "Unhandled sample format"); + return NULL; + } + + /* Set frequency to integer */ + frequency = (int)sample_rate; + + /* Compile the format buffer */ + wfx.nChannels = 2; + wfx.wBitsPerSample = sample_size * 8; + wfx.nSamplesPerSec = frequency; + wfx.nBlockAlign = sample_size * wfx.nChannels; + wfx.nAvgBytesPerSec = frequency * wfx.nBlockAlign; + + /* Calculate the length of a single buffer */ + lenBuffer = (MS_BUFFER_LENGTH * wfx.nAvgBytesPerSec + 999) / 1000; + + /* Round to 8-bytes size */ + lenBuffer = (lenBuffer + 7) & ~7; + + /* create and clear the driver data */ + dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(fluid_waveout_audio_driver_t) + lenBuffer * NB_SOUND_BUFFERS); + + if(dev == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + /* Save copy of synth */ + dev->synth = synth; + + /* Save copy of other variables */ + dev->write_ptr = write_ptr; + dev->sample_size = sample_size; + + /* Calculate the number of frames in a block */ + dev->num_frames = lenBuffer / wfx.nBlockAlign; + + /* Set default device to use */ + device = WAVE_MAPPER; + + /* get the selected device name. if none is specified, use default device. */ + if(fluid_settings_copystr(settings, "audio.waveout.device", dev_name, MAXPNAMELEN) == FLUID_OK + && dev_name[0] != '\0') + { + UINT nDevs = waveOutGetNumDevs(); + UINT n; +#ifdef _UNICODE + WCHAR lpwDevName[MAXPNAMELEN]; + + MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, lpwDevName, MAXPNAMELEN); +#endif + + for(n = 0; n < nDevs; n++) + { + WAVEOUTCAPS caps; + MMRESULT res; + + res = waveOutGetDevCaps(n, &caps, sizeof(caps)); + + if(res == MMSYSERR_NOERROR) + { +#ifdef _UNICODE + + if(wcsicmp(lpwDevName, caps.szPname) == 0) +#else + if(FLUID_STRCASECMP(dev_name, caps.szPname) == 0) +#endif + { + FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name); + device = n; + break; + } + } + } + } + + do + { + + dev->hQuit = CreateEvent(NULL, FALSE, FALSE, NULL); + + if(dev->hQuit == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create quit event"); + break; + } + + /* Create thread which processes re-adding SYSEX buffers */ + dev->hThread = CreateThread( + NULL, + 0, + (LPTHREAD_START_ROUTINE) + fluid_waveout_synth_thread, + dev, + 0, + &dev->dwThread); + + if(dev->hThread == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to create waveOut thread"); + break; + } + + errCode = waveOutOpen(&dev->hWaveOut, + device, + &wfx, + (DWORD_PTR)dev->dwThread, + 0, + CALLBACK_THREAD); + + if(errCode != MMSYSERR_NOERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to open waveOut device"); + break; + } + + /* Get pointer to sound buffer memory */ + ptrBuffer = (LPSTR)(dev + 1); + + /* Setup the sample buffers */ + for(i = 0; i < NB_SOUND_BUFFERS; i++) + { + /* Clear the sample buffer */ + memset(ptrBuffer, 0, lenBuffer); + + /* Clear descriptor buffer */ + memset(dev->waveHeader + i, 0, sizeof(WAVEHDR)); + + /* Compile descriptor buffer */ + dev->waveHeader[i].lpData = ptrBuffer; + dev->waveHeader[i].dwBufferLength = lenBuffer; + dev->waveHeader[i].dwUser = (DWORD_PTR)dev; + + waveOutPrepareHeader(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR)); + + ptrBuffer += lenBuffer; + } + + /* Play the sample buffers */ + for(i = 0; i < NB_SOUND_BUFFERS; i++) + { + waveOutWrite(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR)); + } + + return (fluid_audio_driver_t *) dev; + + } + while(0); + + delete_fluid_waveout_audio_driver(&dev->driver); + return NULL; +} + + +void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *d) +{ + int i; + + fluid_waveout_audio_driver_t *dev = (fluid_waveout_audio_driver_t *) d; + fluid_return_if_fail(dev != NULL); + + /* release all the allocated resources */ + if(dev->hWaveOut != NULL) + { + dev->nQuit = NB_SOUND_BUFFERS; + WaitForSingleObject(dev->hQuit, INFINITE); + + waveOutClose(dev->hWaveOut); + } + + if(dev->hThread != NULL) + { + PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0); + WaitForSingleObject(dev->hThread, INFINITE); + + CloseHandle(dev->hThread); + } + + if(dev->hQuit != NULL) + { + CloseHandle(dev->hQuit); + } + + HeapFree(GetProcessHeap(), 0, dev); +} + +#endif /* WAVEOUT_SUPPORT */ From ba9092ef98e300d9c93b84c4c8dc99db622d267c Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Sun, 2 Dec 2018 17:42:51 +0100 Subject: [PATCH 065/101] Reducing defsfont loader code (#480) --- src/sfloader/fluid_defsfont.c | 634 +++++++++++----------------------- 1 file changed, 210 insertions(+), 424 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 6e19eb71..1931d4ee 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1105,24 +1105,30 @@ new_fluid_preset_zone(char *name) } /* - * delete_fluid_preset_zone + * delete list of modulators. */ -void -delete_fluid_preset_zone(fluid_preset_zone_t *zone) +static void delete_fluid_list_mod(fluid_mod_t *mod) { - fluid_mod_t *mod, *tmp; - fluid_list_t *list; - - fluid_return_if_fail(zone != NULL); - - mod = zone->mod; - + fluid_mod_t *tmp; while(mod) /* delete the modulators */ { tmp = mod; mod = mod->next; delete_fluid_mod(tmp); } +} + +/* + * delete_fluid_preset_zone + */ +void +delete_fluid_preset_zone(fluid_preset_zone_t *zone) +{ + fluid_list_t *list; + + fluid_return_if_fail(zone != NULL); + + delete_fluid_list_mod(zone->mod); for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list)) { @@ -1187,52 +1193,226 @@ static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone } /* - * fluid_preset_zone_import_sfont + * fluid_zone_gen_import_sfont + * Imports generators from sfzone to gen and range. + * @param gen, pointer on destination generators table. + * @param range, pointer on destination range generators. + * @param sfzone, pointer on soundfont zone generators. */ -int -fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont) +static void +fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone *sfzone) { fluid_list_t *r; SFGen *sfgen; - SFInst *sfinst; - int count; - for(count = 0, r = sfzone->gen; r != NULL; count++) + for( r = sfzone->gen; r != NULL; ) { sfgen = (SFGen *)fluid_list_get(r); switch(sfgen->id) { case GEN_KEYRANGE: - zone->range.keylo = sfgen->amount.range.lo; - zone->range.keyhi = sfgen->amount.range.hi; + range->keylo = sfgen->amount.range.lo; + range->keyhi = sfgen->amount.range.hi; break; case GEN_VELRANGE: - zone->range.vello = sfgen->amount.range.lo; - zone->range.velhi = sfgen->amount.range.hi; + range->vello = sfgen->amount.range.lo; + range->velhi = sfgen->amount.range.hi; break; case GEN_ATTENUATION: /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at * preset and instrument level */ - zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; - zone->gen[sfgen->id].flags = GEN_SET; + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; + gen[sfgen->id].flags = GEN_SET; break; default: /* FIXME: some generators have an unsigne word amount value but i don't know which ones */ - zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; - zone->gen[sfgen->id].flags = GEN_SET; + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + gen[sfgen->id].flags = GEN_SET; break; } r = fluid_list_next(r); } +} + +/* + * fluid_zone_mod_source_import_sfont + * Imports source information from sf_source to src and flags. + * @param src, pointer on destination modulator source. + * @param flags, pointer on destination modulator flags. + * @param sf_source, soundfont modulator source. + * @return return TRUE if success, FALSE if source type is unknow. + */ +static int +fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source) +{ + int type; + unsigned char flags_dest; /* destination flags */ + + /* sources */ + *src = sf_source & 127; /* index of source, seven-bit value, SF2.01 section 8.2, page 50 */ + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + flags_dest = 0; + if(sf_source & (1 << 7)) + { + flags_dest |= FLUID_MOD_CC; + } + else + { + flags_dest |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if(sf_source & (1 << 8)) + { + flags_dest |= FLUID_MOD_NEGATIVE; + } + else + { + flags_dest |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if(sf_source & (1 << 9)) + { + flags_dest |= FLUID_MOD_BIPOLAR; + } + else + { + flags_dest |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = sf_source >> 10; + type &= 63; /* type is a 6-bit value */ + + if(type == 0) + { + flags_dest |= FLUID_MOD_LINEAR; + } + else if(type == 1) + { + flags_dest |= FLUID_MOD_CONCAVE; + } + else if(type == 2) + { + flags_dest |= FLUID_MOD_CONVEX; + } + else if(type == 3) + { + flags_dest |= FLUID_MOD_SWITCH; + } + else + { + *flags = flags_dest; + /* This shouldn't happen - unknown type! */ + return FALSE; + } + *flags = flags_dest; + return TRUE; +} + +/* + * fluid_zone_mod_import_sfont + * Imports modulators from sfzone to mod list. + * @param mod, pointer on modulator list to return. + * @param sfzone, pointer on soundfont zone. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int +fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) +{ + fluid_list_t *r; + int count; + + /* Import the modulators (only SF2.1 and higher) */ + for(count = 0, r = sfzone->mod; r != NULL; count++) + { + + SFMod *mod_src = (SFMod *)fluid_list_get(r); + fluid_mod_t *mod_dest = new_fluid_mod(); + + if(mod_dest == NULL) + { + return FLUID_FAILED; + } + + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ + + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; + + /* *** Source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src1, &mod_dest->flags1, mod_src->src)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + /* *** Dest *** */ + mod_dest->dest = mod_src->dest; /* index of controlled generator */ + + /* *** Amount source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src2, &mod_dest->flags2, mod_src->amtsrc)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + + /* *** Transform *** */ + /* SF2.01 only uses the 'linear' transform (0). + * Deactivate the modulator by setting the amount to 0 in any other case. + */ + if(mod_src->trans != 0) + { + mod_dest->amount = 0; + } + + /* Store the new modulator in the zone The order of modulators + * will make a difference, at least in an instrument context: The + * second modulator overwrites the first one, if they only differ + * in amount. */ + if(count == 0) + { + *mod = mod_dest; + } + else + { + fluid_mod_t *last_mod = *mod; + + /* Find the end of the list */ + while(last_mod->next != NULL) + { + last_mod = last_mod->next; + } + + last_mod->next = mod_dest; + } + + r = fluid_list_next(r); + } /* foreach modulator */ + + return FLUID_OK; +} + +/* + * fluid_preset_zone_import_sfont + */ +int +fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont) +{ + /* import the generators */ + fluid_zone_gen_import_sfont(zone->gen, &zone->range, sfzone); if((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { - sfinst = sfzone->instsamp->data; + SFInst *sfinst = sfzone->instsamp->data; zone->inst = find_inst_by_idx(defsfont, sfinst->idx); @@ -1253,182 +1433,7 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_ } /* Import the modulators (only SF2.1 and higher) */ - for(count = 0, r = sfzone->mod; r != NULL; count++) - { - - SFMod *mod_src = (SFMod *)fluid_list_get(r); - fluid_mod_t *mod_dest = new_fluid_mod(); - int type; - - if(mod_dest == NULL) - { - return FLUID_FAILED; - } - - mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ - - /* *** Amount *** */ - mod_dest->amount = mod_src->amount; - - /* *** Source *** */ - mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags1 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if(mod_src->src & (1 << 7)) - { - mod_dest->flags1 |= FLUID_MOD_CC; - } - else - { - mod_dest->flags1 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if(mod_src->src & (1 << 8)) - { - mod_dest->flags1 |= FLUID_MOD_NEGATIVE; - } - else - { - mod_dest->flags1 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if(mod_src->src & (1 << 9)) - { - mod_dest->flags1 |= FLUID_MOD_BIPOLAR; - } - else - { - mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->src) >> 10; - type &= 63; /* type is a 6-bit value */ - - if(type == 0) - { - mod_dest->flags1 |= FLUID_MOD_LINEAR; - } - else if(type == 1) - { - mod_dest->flags1 |= FLUID_MOD_CONCAVE; - } - else if(type == 2) - { - mod_dest->flags1 |= FLUID_MOD_CONVEX; - } - else if(type == 3) - { - mod_dest->flags1 |= FLUID_MOD_SWITCH; - } - else - { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Dest *** */ - mod_dest->dest = mod_src->dest; /* index of controlled generator */ - - /* *** Amount source *** */ - mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */ - mod_dest->flags2 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if(mod_src->amtsrc & (1 << 7)) - { - mod_dest->flags2 |= FLUID_MOD_CC; - } - else - { - mod_dest->flags2 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if(mod_src->amtsrc & (1 << 8)) - { - mod_dest->flags2 |= FLUID_MOD_NEGATIVE; - } - else - { - mod_dest->flags2 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if(mod_src->amtsrc & (1 << 9)) - { - mod_dest->flags2 |= FLUID_MOD_BIPOLAR; - } - else - { - mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->amtsrc) >> 10; - type &= 63; /* type is a 6-bit value */ - - if(type == 0) - { - mod_dest->flags2 |= FLUID_MOD_LINEAR; - } - else if(type == 1) - { - mod_dest->flags2 |= FLUID_MOD_CONCAVE; - } - else if(type == 2) - { - mod_dest->flags2 |= FLUID_MOD_CONVEX; - } - else if(type == 3) - { - mod_dest->flags2 |= FLUID_MOD_SWITCH; - } - else - { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Transform *** */ - /* SF2.01 only uses the 'linear' transform (0). - * Deactivate the modulator by setting the amount to 0 in any other case. - */ - if(mod_src->trans != 0) - { - mod_dest->amount = 0; - } - - /* Store the new modulator in the zone The order of modulators - * will make a difference, at least in an instrument context: The - * second modulator overwrites the first one, if they only differ - * in amount. */ - if(count == 0) - { - zone->mod = mod_dest; - } - else - { - fluid_mod_t *last_mod = zone->mod; - - /* Find the end of the list */ - while(last_mod->next != NULL) - { - last_mod = last_mod->next; - } - - last_mod->next = mod_dest; - } - - r = fluid_list_next(r); - } /* foreach modulator */ - - return FLUID_OK; + return fluid_zone_mod_import_sfont(&zone->mod, sfzone); } /* @@ -1661,18 +1666,9 @@ new_fluid_inst_zone(char *name) void delete_fluid_inst_zone(fluid_inst_zone_t *zone) { - fluid_mod_t *mod, *tmp; - fluid_return_if_fail(zone != NULL); - mod = zone->mod; - - while(mod) /* delete the modulators */ - { - tmp = mod; - mod = mod->next; - delete_fluid_mod(tmp); - } + delete_fluid_list_mod(zone->mod); FLUID_FREE(zone->name); FLUID_FREE(zone); @@ -1693,43 +1689,8 @@ fluid_inst_zone_next(fluid_inst_zone_t *zone) int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont) { - fluid_list_t *r; - SFGen *sfgen; - int count; - - for(count = 0, r = sfzone->gen; r != NULL; count++) - { - sfgen = (SFGen *)fluid_list_get(r); - - switch(sfgen->id) - { - case GEN_KEYRANGE: - inst_zone->range.keylo = sfgen->amount.range.lo; - inst_zone->range.keyhi = sfgen->amount.range.hi; - break; - - case GEN_VELRANGE: - inst_zone->range.vello = sfgen->amount.range.lo; - inst_zone->range.velhi = sfgen->amount.range.hi; - break; - - case GEN_ATTENUATION: - /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at - * preset and instrument level */ - inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; - inst_zone->gen[sfgen->id].flags = GEN_SET; - break; - - default: - /* FIXME: some generators have an unsigned word amount value but - i don't know which ones */ - inst_zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; - inst_zone->gen[sfgen->id].flags = GEN_SET; - break; - } - - r = fluid_list_next(r); - } + /* import the generators */ + fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, sfzone); /* FIXME */ /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ @@ -1743,182 +1704,7 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid } /* Import the modulators (only SF2.1 and higher) */ - for(count = 0, r = sfzone->mod; r != NULL; count++) - { - SFMod *mod_src = (SFMod *)fluid_list_get(r); - int type; - fluid_mod_t *mod_dest; - - mod_dest = new_fluid_mod(); - - if(mod_dest == NULL) - { - return FLUID_FAILED; - } - - mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ - - /* *** Amount *** */ - mod_dest->amount = mod_src->amount; - - /* *** Source *** */ - mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags1 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if(mod_src->src & (1 << 7)) - { - mod_dest->flags1 |= FLUID_MOD_CC; - } - else - { - mod_dest->flags1 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if(mod_src->src & (1 << 8)) - { - mod_dest->flags1 |= FLUID_MOD_NEGATIVE; - } - else - { - mod_dest->flags1 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if(mod_src->src & (1 << 9)) - { - mod_dest->flags1 |= FLUID_MOD_BIPOLAR; - } - else - { - mod_dest->flags1 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->src) >> 10; - type &= 63; /* type is a 6-bit value */ - - if(type == 0) - { - mod_dest->flags1 |= FLUID_MOD_LINEAR; - } - else if(type == 1) - { - mod_dest->flags1 |= FLUID_MOD_CONCAVE; - } - else if(type == 2) - { - mod_dest->flags1 |= FLUID_MOD_CONVEX; - } - else if(type == 3) - { - mod_dest->flags1 |= FLUID_MOD_SWITCH; - } - else - { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Dest *** */ - mod_dest->dest = mod_src->dest; /* index of controlled generator */ - - /* *** Amount source *** */ - mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */ - mod_dest->flags2 = 0; - - /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ - if(mod_src->amtsrc & (1 << 7)) - { - mod_dest->flags2 |= FLUID_MOD_CC; - } - else - { - mod_dest->flags2 |= FLUID_MOD_GC; - } - - /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ - if(mod_src->amtsrc & (1 << 8)) - { - mod_dest->flags2 |= FLUID_MOD_NEGATIVE; - } - else - { - mod_dest->flags2 |= FLUID_MOD_POSITIVE; - } - - /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ - if(mod_src->amtsrc & (1 << 9)) - { - mod_dest->flags2 |= FLUID_MOD_BIPOLAR; - } - else - { - mod_dest->flags2 |= FLUID_MOD_UNIPOLAR; - } - - /* modulator source types: SF2.01 section 8.2.1 page 52 */ - type = (mod_src->amtsrc) >> 10; - type &= 63; /* type is a 6-bit value */ - - if(type == 0) - { - mod_dest->flags2 |= FLUID_MOD_LINEAR; - } - else if(type == 1) - { - mod_dest->flags2 |= FLUID_MOD_CONCAVE; - } - else if(type == 2) - { - mod_dest->flags2 |= FLUID_MOD_CONVEX; - } - else if(type == 3) - { - mod_dest->flags2 |= FLUID_MOD_SWITCH; - } - else - { - /* This shouldn't happen - unknown type! - * Deactivate the modulator by setting the amount to 0. */ - mod_dest->amount = 0; - } - - /* *** Transform *** */ - /* SF2.01 only uses the 'linear' transform (0). - * Deactivate the modulator by setting the amount to 0 in any other case. - */ - if(mod_src->trans != 0) - { - mod_dest->amount = 0; - } - - /* Store the new modulator in the zone - * The order of modulators will make a difference, at least in an instrument context: - * The second modulator overwrites the first one, if they only differ in amount. */ - if(count == 0) - { - inst_zone->mod = mod_dest; - } - else - { - fluid_mod_t *last_mod = inst_zone->mod; - - /* Find the end of the list */ - while(last_mod->next != NULL) - { - last_mod = last_mod->next; - } - - last_mod->next = mod_dest; - } - - r = fluid_list_next(r); - } /* foreach modulator */ - - return FLUID_OK; + return fluid_zone_mod_import_sfont(&inst_zone->mod, sfzone); } /* From d5ba910f3c93b4a19648df3224d2656cfb1be907 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 4 Dec 2018 19:36:10 +0100 Subject: [PATCH 066/101] document waveout option for audio.driver --- doc/fluidsettings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index 7bc3adcc..b5289bfc 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -367,7 +367,7 @@ Developers: Settings can be deprecated by adding: SOME TEXT dart (OS/2) - jack, alsa, oss, pulseaudio, coreaudio, dsound, portaudio, sndman, dart, file + alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sndman, waveout The audio system to be used. From dde52c35ddda5f6e8b23e0613a0d168a8d8e3c2c Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 9 Dec 2018 10:35:21 +0100 Subject: [PATCH 067/101] update test_sfont_loading to call fluid_is_soundfont() --- test/test_sfont_loading.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_sfont_loading.c b/test/test_sfont_loading.c index 9f63b0c6..9cac3d07 100644 --- a/test/test_sfont_loading.c +++ b/test/test_sfont_loading.c @@ -21,6 +21,8 @@ int main(void) // no sfont loaded TEST_ASSERT(fluid_synth_sfcount(synth) == 0); + TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); + // load a sfont to synth TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); // one sfont loaded From 978283bbf0309191a441121b7ea867e41e329d3b Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Fri, 14 Dec 2018 16:43:35 +0100 Subject: [PATCH 068/101] Add Sdl2 driver (#478) --- CMakeLists.txt | 15 ++ cmake_admin/FindSDL2.cmake | 164 ++++++++++++++++++++++ cmake_admin/report.cmake | 6 + doc/fluidsettings.xml | 4 +- doc/fluidsynth-v20-devdoc.txt | 5 +- src/CMakeLists.txt | 7 + src/config.cmake | 3 + src/drivers/fluid_adriver.c | 10 ++ src/drivers/fluid_adriver.h | 7 + src/drivers/fluid_aufile.c | 4 + src/drivers/fluid_sdl2.c | 251 ++++++++++++++++++++++++++++++++++ 11 files changed, 472 insertions(+), 4 deletions(-) create mode 100644 cmake_admin/FindSDL2.cmake create mode 100644 src/drivers/fluid_sdl2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 02918f27..319a2d93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ option ( enable-oss "compile OSS support (if it is available)" on ) option ( enable-dsound "compile DirectSound support (if it is available)" on ) option ( enable-waveout "compile Windows WaveOut support (if it is available)" on ) option ( enable-winmidi "compile Windows MIDI support (if it is available)" on ) +option ( enable-sdl2 "compile SDL2 audio support (if it is available)" on ) option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on ) option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on ) option ( enable-readline "compile readline lib line editing (if it is available)" on ) @@ -559,6 +560,20 @@ if ( enable-oss ) set ( OSS_SUPPORT ${OSS_FOUND} ) endif ( enable-oss ) +unset ( SDL2_SUPPORT CACHE ) +unset ( SDL2_INCLUDE_DIR CACHE ) +unset ( SDL2_LIBRARY CACHE ) +if ( enable-sdl2 ) + find_package ( SDL2 ) + + if ( SDL2_FOUND ) + set ( SDL2_SUPPORT ${SDL2_FOUND} ) + else ( SDL2_FOUND) + unset ( SDL2_INCLUDE_DIR CACHE ) + unset ( SDL2_LIBRARY CACHE ) + endif ( SDL2_FOUND ) +endif ( enable-sdl2 ) + unset ( MIDISHARE_SUPPORT CACHE ) if ( enable-midishare ) find_package ( MidiShare QUIET ) diff --git a/cmake_admin/FindSDL2.cmake b/cmake_admin/FindSDL2.cmake new file mode 100644 index 00000000..860d2381 --- /dev/null +++ b/cmake_admin/FindSDL2.cmake @@ -0,0 +1,164 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDLmain.h and SDLmain.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +SET(SDL2_SEARCH_PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} ${SDL2_ROOT} + PATH_SUFFIXES include/SDL2 include + PATHS ${SDL2_SEARCH_PATHS} +) + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} ${SDL2_ROOT} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} +) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} ${SDL2_ROOT} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) + diff --git a/cmake_admin/report.cmake b/cmake_admin/report.cmake index 18abf5fa..88cd7ff3 100644 --- a/cmake_admin/report.cmake +++ b/cmake_admin/report.cmake @@ -92,6 +92,12 @@ else ( WINMIDI_SUPPORT ) message ( "WinMidi support: no" ) endif ( WINMIDI_SUPPORT ) +if ( SDL2_SUPPORT ) + message ( "SDL2 support: yes" ) +else ( SDL2_SUPPORT ) + message ( "SDL2 support: no" ) +endif ( SDL2_SUPPORT ) + if ( LADSPA_SUPPORT ) message ( "LADSPA support: yes" ) else ( LADSPA_SUPPORT ) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index b5289bfc..2d2e0d4a 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -367,9 +367,9 @@ Developers: Settings can be deprecated by adding: SOME TEXT dart (OS/2) - alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sndman, waveout + alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sdl2, sndman, waveout - The audio system to be used. + The audio system to be used. In order to use sdl2 as audio driver, the application is responsible for initializing SDL's audio subsystem. diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index b0e4630d..57f39e3c 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -199,7 +199,7 @@ For a full list of available synthesizer settings, please refer The synthesizer itself does not write any audio to the audio output. This allows application developers to manage the audio output themselves if they wish. The next section describes the use of the synthesizer without an audio driver in more detail. -Creating the audio driver is straightforward: set the appropriate settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description. +Creating the audio driver is straightforward: set the audio.driver settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description. - jack: JACK Audio Connection Kit (Linux, Mac OS X, Windows) - alsa: Advanced Linux Sound Architecture (Linux) @@ -211,6 +211,7 @@ Creating the audio driver is straightforward: set the appropriate settings and c - sndman: Apple SoundManager (Mac OS Classic) - dart: DART sound driver (OS/2) - file: Driver to output audio to a file +- sdl2*: Simple DirectMedia Layer (Linux, Windows, Mac OS X, iOS, Android, FreeBSD, Haiku, etc.) The default audio driver depends on the settings with which FluidSynth was compiled. You can get the default driver with fluid_settings_getstr_default(). To get the list of available drivers use the fluid_settings_foreach_option() function. Finally, you can set the driver with fluid_settings_setstr(). In most cases, the default driver should work out of the box. @@ -238,7 +239,7 @@ As soon as the audio driver is created, it will start playing. The audio driver There are a number of general audio driver settings. The audio.driver settings define the audio subsystem that will be used. The audio.periods and audio.period-size settings define the latency and robustness against scheduling delays. There are additional settings for the audio subsystems used. For a full list of available audio driver settings, please refer to FluidSettings Documentation. - +*Note: In order to use sdl2 as audio driver, the application is responsible for initializing SDL (e.g. with SDL_Init()). This must be done before the first call to new_fluid_settings()! Also make sure to call SDL_Quit() after all fluidsynth instances have been destroyed. \section UsingSynth Using the synthesizer without an audio driver diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8480097..25f29f45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ include_directories ( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${PTHREADS_INCLUDE_DIR} + ${SDL2_INCLUDE_DIR} ) include_directories ( @@ -90,6 +91,10 @@ if ( WINMIDI_SUPPORT ) set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c ) endif ( WINMIDI_SUPPORT ) +if ( SDL2_SUPPORT ) + set ( fluid_sdl2_SOURCES drivers/fluid_sdl2.c ) +endif ( SDL2_SUPPORT ) + if ( OSS_SUPPORT ) set ( fluid_oss_SOURCES drivers/fluid_oss.c ) endif ( OSS_SUPPORT ) @@ -254,6 +259,7 @@ add_library ( libfluidsynth-OBJ OBJECT ${fluid_dsound_SOURCES} ${fluid_waveout_SOURCES} ${fluid_winmidi_SOURCES} + ${fluid_sdl2_SOURCES} ${libfluidsynth_SOURCES} ${public_HEADERS} ${public_main_HEADER} @@ -321,6 +327,7 @@ target_link_libraries ( libfluidsynth ${PULSE_LIBRARIES} ${PORTAUDIO_LIBRARIES} ${LIBSNDFILE_LIBRARIES} + ${SDL2_LIBRARY} ${DBUS_LIBRARIES} ${READLINE_LIBS} ${DART_LIBS} diff --git a/src/config.cmake b/src/config.cmake index cd1f5bc5..a7259ba0 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -202,6 +202,9 @@ /* Define to enable Windows MIDI driver */ #cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@ +/* Define to enable SDL2 audio driver */ +#cmakedefine SDL2_SUPPORT @SDL2_SUPPORT@ + /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index 8419ab9f..a37ea2f8 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -139,6 +139,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] = }, #endif +#if SDL2_SUPPORT + { + "sdl2", + new_fluid_sdl2_audio_driver, + NULL, + delete_fluid_sdl2_audio_driver, + fluid_sdl2_audio_driver_settings + }, +#endif + #if AUFILE_SUPPORT { "file", diff --git a/src/drivers/fluid_adriver.h b/src/drivers/fluid_adriver.h index 2ec9ded1..271761c5 100644 --- a/src/drivers/fluid_adriver.h +++ b/src/drivers/fluid_adriver.h @@ -121,6 +121,13 @@ void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p); void fluid_dart_audio_driver_settings(fluid_settings_t *settings); #endif +#if SDL2_SUPPORT +fluid_audio_driver_t *new_fluid_sdl2_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *p); +void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings); +#endif + #if AUFILE_SUPPORT fluid_audio_driver_t *new_fluid_file_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); diff --git a/src/drivers/fluid_aufile.c b/src/drivers/fluid_aufile.c index 04973e0e..ab6a2586 100644 --- a/src/drivers/fluid_aufile.c +++ b/src/drivers/fluid_aufile.c @@ -29,6 +29,8 @@ #include "fluid_settings.h" +#if AUFILE_SUPPORT + /** fluid_file_audio_driver_t * * This structure should not be accessed directly. Use audio port @@ -129,3 +131,5 @@ static int fluid_file_audio_run_s16(void *d, unsigned int clock_time) return fluid_file_renderer_process_block(dev->renderer) == FLUID_OK ? 1 : 0; } + +#endif /* AUFILE_SUPPORT */ diff --git a/src/drivers/fluid_sdl2.c b/src/drivers/fluid_sdl2.c new file mode 100644 index 00000000..fa39f397 --- /dev/null +++ b/src/drivers/fluid_sdl2.c @@ -0,0 +1,251 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * Copyright (C) 2018 Carlo Bramini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_adriver.h" +#include "fluid_settings.h" + +#if SDL2_SUPPORT + +#include "SDL.h" + +typedef struct +{ + fluid_audio_driver_t driver; + + fluid_synth_t *synth; + fluid_audio_callback_t write_ptr; + + SDL_AudioDeviceID devid; + + int frame_size; + +} fluid_sdl2_audio_driver_t; + + +static void +SDLAudioCallback(void *data, void *stream, int len) +{ + fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *)data; + + len /= dev->frame_size; + + dev->write_ptr(dev->synth, len, stream, 0, 2, stream, 1, 2); +} + +void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings) +{ + int n, nDevs; + + fluid_settings_register_str(settings, "audio.sdl2.device", "default", 0); + fluid_settings_add_option(settings, "audio.sdl2.device", "default"); + + if(!SDL_WasInit(SDL_INIT_AUDIO)) + { + FLUID_LOG(FLUID_ERR, "SDL2 not initialized"); + return; + } + + nDevs = SDL_GetNumAudioDevices(0); + + for(n = 0; n < nDevs; n++) + { + const char *dev_name = SDL_GetAudioDeviceName(n, 0); + + if(dev_name != NULL) + { + FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name); + fluid_settings_add_option(settings, "audio.sdl2.device", dev_name); + } + } +} + + +/* + * new_fluid_sdl2_audio_driver + */ +fluid_audio_driver_t * +new_fluid_sdl2_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) +{ + fluid_sdl2_audio_driver_t *dev = NULL; + fluid_audio_callback_t write_ptr; + double sample_rate; + int period_size, sample_size; + SDL_AudioSpec aspec, rspec; + char *device; + const char *dev_name; + + /* Retrieve the settings */ + fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); + fluid_settings_getint(settings, "audio.period-size", &period_size); + + /* Lower values do not seem to give good results */ + if(period_size < 1024) + { + period_size = 1024; + } + else + + /* According to documentation, it MUST be a power of two */ + if((period_size & (period_size - 1)) != 0) + { + FLUID_LOG(FLUID_DBG, "\"audio.period-size\" must be a power of 2"); + return NULL; + } + + /* Clear the format buffer */ + FLUID_MEMSET(&aspec, 0, sizeof(aspec)); + + /* Setup mixing frequency */ + aspec.freq = (int)sample_rate; + + /* Check the format */ + if(fluid_settings_str_equal(settings, "audio.sample-format", "float")) + { + FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format"); + + sample_size = sizeof(float); + write_ptr = fluid_synth_write_float; + + aspec.format = AUDIO_F32SYS; + } + else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) + { + FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format"); + + sample_size = sizeof(short); + write_ptr = fluid_synth_write_s16; + + aspec.format = AUDIO_S16SYS; + } + else + { + FLUID_LOG(FLUID_ERR, "Unhandled sample format"); + return NULL; + } + + /* Compile the format buffer */ + aspec.channels = 2; + aspec.samples = aspec.channels * ((period_size + 7) & ~7); + aspec.callback = (SDL_AudioCallback)SDLAudioCallback; + + /* Check if SDL library has been started */ + if(!SDL_WasInit(SDL_INIT_AUDIO)) + { + FLUID_LOG(FLUID_ERR, "SDL2 not initialized"); + return NULL; + } + + /* Set default device to use */ + device = NULL; + dev_name = NULL; + + /* get the selected device name. if none is specified, use default device. */ + if(fluid_settings_dupstr(settings, "audio.sdl2.device", &device) == FLUID_OK + && device != NULL && device[0] != '\0') + { + int n, nDevs = SDL_GetNumAudioDevices(0); + + for(n = 0; n < nDevs; n++) + { + dev_name = SDL_GetAudioDeviceName(n, 0); + + if(FLUID_STRCASECMP(dev_name, device) == 0) + { + FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name); + break; + } + } + + if(n >= nDevs) + { + FLUID_LOG(FLUID_DBG, "Audio device %s, using \"default\"", device); + dev_name = NULL; + } + } + + if(device != NULL) + { + FLUID_FREE(device); + } + + do + { + /* create and clear the driver data */ + dev = FLUID_NEW(fluid_sdl2_audio_driver_t); + + if(dev == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + break; + } + + FLUID_MEMSET(dev, 0, sizeof(fluid_sdl2_audio_driver_t)); + + /* set device pointer to userdata */ + aspec.userdata = dev; + + /* Save copy of synth */ + dev->synth = synth; + + /* Save copy of other variables */ + dev->write_ptr = write_ptr; + dev->frame_size = sample_size * aspec.channels; + + /* Open audio device */ + dev->devid = SDL_OpenAudioDevice(dev_name, 0, &aspec, &rspec, 0); + + if(!dev->devid) + { + FLUID_LOG(FLUID_ERR, "Failed to open audio device"); + break; + } + + /* Start to play */ + SDL_PauseAudioDevice(dev->devid, 0); + + return (fluid_audio_driver_t *) dev; + } + while(0); + + delete_fluid_sdl2_audio_driver(&dev->driver); + return NULL; +} + + +void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *d) +{ + fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *) d; + + if(dev != NULL) + { + if(dev->devid) + { + /* Stop audio and close */ + SDL_PauseAudioDevice(dev->devid, 1); + SDL_CloseAudioDevice(dev->devid); + } + + FLUID_FREE(dev); + } +} + +#endif /* SDL2_SUPPORT */ From 73689925da8d33c9ec73d5ead39db5535fe96022 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 1 Dec 2018 13:56:51 +0100 Subject: [PATCH 069/101] disable clang-tidy for normal builds It may cause errors when cross compiling. Keep it for profiling builds though. Fixes #475. --- CMakeLists.txt | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 298199dc..a0445467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,6 +342,18 @@ if ( enable-profiling ) endif ( ) set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPT_FLAGS}" ) + + if ( CMAKE_VERSION VERSION_GREATER "3.6.0" ) + find_program( CLANG_TIDY + NAMES "clang-tidy" + DOC "Path to clang-tidy executable" ) + + if ( CLANG_TIDY ) + message ( STATUS "Found clang-tidy" ) + set ( CMAKE_C_CLANG_TIDY "clang-tidy" ) + endif ( CLANG_TIDY ) + endif ( CMAKE_VERSION VERSION_GREATER "3.6.0" ) + endif ( enable-profiling ) unset ( ENABLE_TRAPONFPE CACHE ) @@ -375,22 +387,6 @@ if ( CMAKE_BUILD_TYPE MATCHES "Debug" ) set ( DEBUG 1 ) endif ( CMAKE_BUILD_TYPE MATCHES "Debug" ) -if ( CMAKE_VERSION VERSION_GREATER "3.6.0" ) -find_program( CLANG_TIDY - NAMES "clang-tidy" - DOC "Path to clang-tidy executable" ) - - if ( CLANG_TIDY ) - message ( STATUS "Found clang-tidy" ) - # whenever clang-tidy is available, use it to automatically add braces after ever "make" - if ( WITH_PROFILING ) - set ( CMAKE_C_CLANG_TIDY "clang-tidy" ) - else ( WITH_PROFILING ) - set ( CMAKE_C_CLANG_TIDY "clang-tidy;-checks=-*,readability-braces-*;-format-style=file" ) - endif ( WITH_PROFILING ) - endif ( CLANG_TIDY ) -endif ( CMAKE_VERSION VERSION_GREATER "3.6.0" ) - # Additional targets to perform clang-format/clang-tidy # Get all project files file(GLOB_RECURSE From cb485806c3cfdb4b09e670c2a5a925bed26afae1 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 1 Dec 2018 14:03:34 +0100 Subject: [PATCH 070/101] remove unused io.h include --- CMakeLists.txt | 1 - src/config.cmake | 3 --- src/utils/fluidsynth_priv.h | 4 ---- 3 files changed, 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0445467..d1fa324f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,7 +201,6 @@ if ( WIN32 ) # Check presence of MS include files check_include_file ( windows.h HAVE_WINDOWS_H ) - check_include_file ( io.h HAVE_IO_H ) check_include_file ( dsound.h HAVE_DSOUND_H ) check_include_files ( "windows.h;mmsystem.h" HAVE_MMSYSTEM_H ) diff --git a/src/config.cmake b/src/config.cmake index 2d1d786b..ae7c7dbe 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -43,9 +43,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_IO_H @HAVE_IO_H@ - /* whether or not we are supporting lash */ #cmakedefine HAVE_LASH @HAVE_LASH@ diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index d5dbdf7e..79446925 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -106,10 +106,6 @@ #include #endif -#if HAVE_IO_H -#include -#endif - #if HAVE_SIGNAL_H #include #endif From 4f1f7349dbbd7be8ea55d2451318b09991fdabe6 Mon Sep 17 00:00:00 2001 From: derselbst Date: Fri, 14 Dec 2018 17:47:44 +0100 Subject: [PATCH 071/101] fix cmake not finding make_tables when cross-compiling Fixes #477. --- src/CMakeLists.txt | 2 +- src/gentables/CMakeLists.txt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf8f3369..a0ba53d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -367,6 +367,6 @@ ExternalProject_Add(gentables DOWNLOAD_COMMAND "" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gentables BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gentables - INSTALL_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gentables/make_tables${CMAKE_EXECUTABLE_SUFFIX} "${CMAKE_BINARY_DIR}/" + INSTALL_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gentables/make_tables.exe "${CMAKE_BINARY_DIR}/" ) add_dependencies(libfluidsynth-OBJ gentables) diff --git a/src/gentables/CMakeLists.txt b/src/gentables/CMakeLists.txt index 437027ae..64ec7b44 100644 --- a/src/gentables/CMakeLists.txt +++ b/src/gentables/CMakeLists.txt @@ -8,6 +8,9 @@ project (gentables C) set ( CMAKE_BUILD_TYPE Debug ) +# hardcode ".exe" as suffix to the binary, else in case of cross-platform cross-compiling the calling cmake will not know the suffix used here and fail to find the binary +set ( CMAKE_EXECUTABLE_SUFFIX ".exe" ) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) From a9662bf331316807e4bc42e94906a4c53f21f4b3 Mon Sep 17 00:00:00 2001 From: derselbst Date: Fri, 14 Dec 2018 19:42:08 +0100 Subject: [PATCH 072/101] fix gcc8 complaining about stringop-truncation --- src/synth/fluid_synth.c | 5 +++-- src/utils/fluidsynth_priv.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index faadefb2..15e167a1 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -1902,7 +1902,7 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, int bank = 0, prog, channels; double tunedata[128]; int keys[128]; - char name[17]; + char name[17]={0}; int note, frac, frac2; uint8_t chksum; int i, count, index; @@ -1973,7 +1973,8 @@ fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, } *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; for(i = 0; i < 128; i++) diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index 79446925..c7ddf546 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -261,7 +261,7 @@ typedef FILE *fluid_file; #define FLUID_STRNCPY(_dst,_src,_n) \ do { strncpy(_dst,_src,_n); \ - (_dst)[(_n)-1]=0; \ + (_dst)[(_n)-1]='\0'; \ }while(0) #define FLUID_STRCHR(_s,_c) strchr(_s,_c) From 06bca9b8946969bcefb66bd27bc7c10dc1df5467 Mon Sep 17 00:00:00 2001 From: jjceresa Date: Sun, 16 Dec 2018 07:57:51 +0100 Subject: [PATCH 073/101] Fix minor bug in lower_boundary_for_attenuation() - v variable is renamed min_val for clarity. - This fix a bug when calculating the possible minimun value (min_val) of a modulator. min_val should only be dependant of: - signs of mapped sources (ie bipolar [-1..+1] or unipolar [0..+1]) - sign of amount. - absolute value of amount. --- src/synth/fluid_voice.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 38b4bfe6..63bb88cf 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -1696,9 +1696,10 @@ int fluid_voice_get_velocity(const fluid_voice_t *voice) * A lower boundary for the attenuation (as in 'the minimum * attenuation of this voice, with volume pedals, modulators * 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, * voice->attenuation has to be initialized. + * (see fluid_voice_calculate_runtime_synthesis_parameters()) */ static fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) @@ -1725,30 +1726,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 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) - || (mod->flags1 & FLUID_MOD_BIPOLAR) + When at least one source mapping is 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->amount < 0)) { - /* Can this modulator produce a negative contribution? */ - v *= -1.0; + min_val *= -1.0; /* min_val = - |amount|*/ } else { /* No negative value possible. But still, the minimum contribution is 0. */ - v = 0; + min_val = 0; } /* For example: * - current_val=100 * - 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); } } } From b7af84fbd5eece8b93ced75629dfef192ff1e34b Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Wed, 19 Dec 2018 14:07:34 +0100 Subject: [PATCH 074/101] optimizing fluid_voice_modulate() (#486) This avoids recalculation of the same generator when several modulators have that generator as destination. --- src/synth/fluid_voice.c | 63 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 38b4bfe6..66337175 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -1162,24 +1162,23 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen) * iteration of the audio cycle (which would probably be feasible if * the synth was made in silicon). * - * The update is done in three steps: + * The update is done in two steps: * * - 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 - * will be changed because of the controller event. + * controller as a source. This will yield a generator that will be changed + * because of the controller event. * - * - step 2: 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 all the attached modulators. * The generator flag is set to indicate the parameters must be updated. - * - * - step 3: We need to avoid the risk to call 'fluid_voice_update_param' several + * This avoid the risk to call 'fluid_voice_update_param' several * times for the same generator if several modulators have that generator as - * destination. So every changed generators are updated only once + * 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. + 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. @@ -1200,7 +1199,7 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) uint32_t gen; fluid_real_t modval; - /* registered bits table of updated generators */ + /* 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); */ @@ -1210,36 +1209,36 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) mod = &voice->mod[i]; /* step 1: find all the modulators that have the changed controller - as input source. When ctrl is -1 all modulators's destination + as input source. When ctrl is -1 all modulators destination are updated */ if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) { gen = fluid_mod_get_dest(mod); - modval = 0.0; - /* step 2: for every changed modulator, calculate the modulation - * value of its associated generator */ - for(k = 0; k < voice->mod_count; k++) + /* Skip if this generator has already been updated */ + if(!is_gen_updated(updated_gen_bit, gen)) { - if(fluid_mod_has_dest(&voice->mod[k], gen)) - { - modval += fluid_mod_get_value(&voice->mod[k], voice); - } - } + modval = 0.0; - fluid_gen_set_mod(&voice->gen[gen], modval); - /* set the bit that indicates this generator is updated */ - set_gen_updated(updated_gen_bit, gen); - } - } - - /* step 3: now recalculate the parameter values that are derived from the - generator */ - for(gen = 0; gen < GEN_LAST; gen++) - { - if (is_gen_updated(updated_gen_bit, gen)) - { - fluid_voice_update_param(voice, gen); + /* step 2: for every attached modulator, calculate the modulation + * value for the 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); + + /* 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); + } } } From c9b8d40fc25bcaacae5ac8a18fccec5f9269e79a Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Wed, 19 Dec 2018 14:09:29 +0100 Subject: [PATCH 075/101] Fourcc support (#482) Have a direct comparison to the fourcc code rather than searching through chunk ids all the time with chunkid() --- src/midi/fluid_midi.c | 37 ++++- src/sfloader/fluid_sffile.c | 263 +++++++++++++++++++++--------------- src/utils/fluid_sys.c | 65 --------- src/utils/fluid_sys.h | 8 ++ 4 files changed, 199 insertions(+), 174 deletions(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index 4795fceb..f672124c 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -81,6 +81,41 @@ static int fluid_midi_file_get_division(fluid_midi_file *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 = 0; + + do + { + if(fp == NULL) + { + break; + } + + 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. * @internal @@ -1597,7 +1632,7 @@ new_fluid_player(fluid_synth_t *synth) fluid_settings_getint(synth->settings, "player.reset-synth", &i); fluid_player_handle_reset_synth(player, NULL, i); - + fluid_settings_callback_int(synth->settings, "player.reset-synth", fluid_player_handle_reset_synth, player); diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index f9382b10..0725e1f3 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -34,76 +34,79 @@ Borrowed from Smurf SoundFont Editor by Josh Green =================================================================*/ -/* - functions for loading data from sfont files, with appropriate byte swapping - on big endian machines. Sfont IDs are not swapped because the ID read is - equivalent to the matching ID list in memory regardless of LE/BE machine -*/ +/* FOURCC definitions */ +#define RIFF_FCC FLUID_FOURCC('R','I','F','F') +#define LIST_FCC FLUID_FOURCC('L','I','S','T') +#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 */ -enum -{ - RIFF_ID, - LIST_ID, - SFBK_ID, - INFO_ID, - SDTA_ID, - PDTA_ID, /* info/sample/preset */ +#define IFIL_FCC FLUID_FOURCC('i','f','i','l') +#define ISNG_FCC FLUID_FOURCC('i','s','n','g') +#define INAM_FCC FLUID_FOURCC('I','N','A','M') +#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */ +#define IVER_FCC FLUID_FOURCC('i','v','e','r') +#define ICRD_FCC FLUID_FOURCC('I','C','R','D') +#define IENG_FCC FLUID_FOURCC('I','E','N','G') +#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */ +#define ICOP_FCC FLUID_FOURCC('I','C','O','P') +#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, - ISNG_ID, - INAM_ID, - IROM_ID, /* info ids (1st byte of info strings) */ - IVER_ID, - ICRD_ID, - IENG_ID, - IPRD_ID, /* more info ids */ - ICOP_ID, - ICMT_ID, - ISFT_ID, /* and yet more info ids */ +#define SNAM_FCC FLUID_FOURCC('s','n','a','m') +#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */ +#define PHDR_FCC FLUID_FOURCC('p','h','d','r') +#define PBAG_FCC FLUID_FOURCC('p','b','a','g') +#define PMOD_FCC FLUID_FOURCC('p','m','o','d') +#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */ +#define IHDR_FCC FLUID_FOURCC('i','n','s','t') +#define IBAG_FCC FLUID_FOURCC('i','b','a','g') +#define IMOD_FCC FLUID_FOURCC('i','m','o','d') +#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */ +#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */ +#define SM24_FCC FLUID_FOURCC('s','m','2','4') - SNAM_ID, - SMPL_ID, /* sample ids */ - 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, - - UNKN_ID -}; +/* Set when the FCC code is unknown */ +#define UNKN_ID FLUID_N_ELEMENTS(idlist) /* - * This declares a char array containing the SF2 chunk identifiers. This - * 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 + * This declares a uint32_t array containing the SF2 chunk identifiers. */ -static const union fluid_idlist +static const uint32_t idlist[] = { - /* - * Cannot be char c[ ], because in C89, arrays wraped in unions - * must have a fixed size. Otherwise the size of the union would depend - * on the initialization of its first member, which results in - * different sizes for different instances of the same union type. - */ - char c[116]; - uint32_t i; -} idlist = {"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD" - "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdrsm24" - }; + RIFF_FCC, + LIST_FCC, + SFBK_FCC, + INFO_FCC, + SDTA_FCC, + PDTA_FCC, + 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 */ typedef enum @@ -206,8 +209,6 @@ static const unsigned short invalid_preset_gen[] = }; -#define CHNKIDSTR(id) &idlist.c[(id - 1) * 4] - /* sfont file chunk sizes */ #define SF_PHDR_SIZE (38) #define SF_BAG_SIZE (4) @@ -323,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_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 = 0; + + do + { + if(fp == NULL) + { + break; + } + + 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. * @@ -512,11 +563,10 @@ void fluid_sffile_close(SFData *sf) static int chunkid(uint32_t id) { 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) { break; } @@ -532,7 +582,7 @@ static int load_header(SFData *sf) READCHUNK(sf, &chunk); /* load RIFF chunk */ - if(chunkid(chunk.id) != RIFF_ID) + if(chunk.id != RIFF_FCC) { /* error if not RIFF */ FLUID_LOG(FLUID_ERR, "Not a RIFF file"); @@ -541,7 +591,7 @@ static int load_header(SFData *sf) READID(sf, &chunk.id); /* load file ID */ - if(chunkid(chunk.id) != SFBK_ID) + if(chunk.id != SFBK_FCC) { /* error if not SFBK_ID */ FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); @@ -560,7 +610,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != INFO_ID) + if(chunk.id != INFO_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); return FALSE; @@ -577,7 +627,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != SDTA_ID) + if(chunk.id != SDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); return FALSE; @@ -594,7 +644,7 @@ static int load_header(SFData *sf) return FALSE; } - if(chunkid(chunk.id) != PDTA_ID) + if(chunk.id != PDTA_FCC) { FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); return FALSE; @@ -639,7 +689,7 @@ static int read_listchunk(SFData *sf, SFChunk *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"); return FALSE; @@ -653,8 +703,11 @@ static int read_listchunk(SFData *sf, SFChunk *chunk) static int process_info(SFData *sf, int size) { SFChunk chunk; - unsigned char id; - char *item; + union + { + char *chr; + uint32_t *fcc; + } item; unsigned short ver; while(size > 0) @@ -662,9 +715,7 @@ static int process_info(SFData *sf, int size) READCHUNK(sf, &chunk); size -= 8; - id = chunkid(chunk.id); - - if(id == IFIL_ID) + if(chunk.id == IFIL_FCC) { /* sound font version chunk? */ if(chunk.size != 4) @@ -705,7 +756,7 @@ static int process_info(SFData *sf, int size) return FALSE; } } - else if(id == IVER_ID) + else if(chunk.id == IVER_FCC) { /* ROM version chunk? */ if(chunk.size != 4) @@ -719,34 +770,35 @@ static int process_info(SFData *sf, int size) READW(sf, 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", &chunk.id, chunk.size); return FALSE; } - /* alloc for chunk id and da chunk */ - if(!(item = FLUID_MALLOC(chunk.size + 1))) + /* alloc for chunk fcc and da chunk */ + if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1))) { FLUID_LOG(FLUID_ERR, "Out of memory"); return FALSE; } /* 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; } - /* force terminate info item (don't forget uint8 info ID) */ - *(item + chunk.size) = '\0'; + /* force terminate info item */ + item.chr[chunk.size] = '\0'; } else { @@ -779,7 +831,7 @@ static int process_sdta(SFData *sf, unsigned int size) READCHUNK(sf, &chunk); size -= 8; - if(chunkid(chunk.id) != SMPL_ID) + if(chunk.id != SMPL_FCC) { FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); return FALSE; @@ -812,7 +864,7 @@ static int process_sdta(SFData *sf, unsigned int size) READCHUNK(sf, &chunk); size -= 8; - if(chunkid(chunk.id) == SM24_ID) + if(chunk.id == SM24_FCC) { int sm24size, sdtahalfsize; @@ -852,29 +904,24 @@ ret: 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); *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; } 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; } 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; } @@ -885,7 +932,7 @@ static int process_pdta(SFData *sf, int size) { SFChunk chunk; - if(!pdtahelper(sf, PHDR_ID, SF_PHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size)) { return FALSE; } @@ -895,7 +942,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PBAG_ID, SF_BAG_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } @@ -905,7 +952,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PMOD_ID, SF_MOD_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } @@ -915,7 +962,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, PGEN_ID, SF_GEN_SIZE, &chunk, &size)) + if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } @@ -925,7 +972,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IHDR_ID, SF_IHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size)) { return FALSE; } @@ -935,7 +982,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IBAG_ID, SF_BAG_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size)) { return FALSE; } @@ -945,7 +992,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IMOD_ID, SF_MOD_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size)) { return FALSE; } @@ -955,7 +1002,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, IGEN_ID, SF_GEN_SIZE, &chunk, &size)) + if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size)) { return FALSE; } @@ -965,7 +1012,7 @@ static int process_pdta(SFData *sf, int size) return FALSE; } - if(!pdtahelper(sf, SHDR_ID, SF_SHDR_SIZE, &chunk, &size)) + if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size)) { return FALSE; } diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index 3df88fc2..4ac1344b 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -274,71 +274,6 @@ fluid_error() 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. * @param milliseconds to wait. diff --git a/src/utils/fluid_sys.h b/src/utils/fluid_sys.h index 47cc95c1..72b32302 100644 --- a/src/utils/fluid_sys.h +++ b/src/utils/fluid_sys.h @@ -77,6 +77,14 @@ #define FLUID_LE32TOH(x) GINT32_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) \ if(cond) \ From 8da7f11a11f5b1d1a5082991ad3ffe4c882d3cdf Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Sat, 15 Dec 2018 20:42:53 +0100 Subject: [PATCH 076/101] Only retrieve modification time once and remove warning if failed This change removes the warning message if retrieving the soundfont file modification time fails in the sample cache loader. The warning made sense while we didn't have mtime support on all platforms, but after switching to GLibs g_stat, it's no longer needed. To reduce the number of calls to fluid_get_file_modification_time and also get rid of a possible race condition, getting the mtime has been moved to fluid_samplecache_load. This change also fixes #483 because it removes the bogus warning messages if a soundfont was loaded from memory by abusing the filename to store a pointer. --- src/sfloader/fluid_samplecache.c | 36 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/sfloader/fluid_samplecache.c b/src/sfloader/fluid_samplecache.c index 773e19fe..036964f5 100644 --- a/src/sfloader/fluid_samplecache.c +++ b/src/sfloader/fluid_samplecache.c @@ -60,8 +60,10 @@ struct _fluid_samplecache_entry_t static fluid_list_t *samplecache_list = NULL; 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 *get_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, + 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 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; int ret; + time_t mtime; 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) { - 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) { @@ -180,7 +188,8 @@ unlock_exit: static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, - int sample_type) + int sample_type, + time_t mtime) { fluid_samplecache_entry_t *entry; @@ -202,12 +211,6 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, 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_samplesize = sf->samplesize; 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_end = sample_end; 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_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, unsigned int sample_start, unsigned int sample_end, - int sample_type) + int sample_type, + time_t mtime) { - time_t mtime; fluid_list_t *entry_list; 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; while(entry_list) From b659fd82a5037b2b0e49d7c2a59e86252fe16c39 Mon Sep 17 00:00:00 2001 From: jjceresa Date: Wed, 19 Dec 2018 18:12:09 +0100 Subject: [PATCH 077/101] Import modulator source src2 correctly. - When secondary source input (src2) is set to General Controller 'No Controller', output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). That means that this source will behave unipolar only. We need to force the unipolar flags to ensure the modulator will behave correctly later in fluid_voice_get_lower_boundary_for_attenuation(). --- src/sfloader/fluid_defsfont.c | 10 ++++++++++ src/synth/fluid_mod.c | 23 ++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 1931d4ee..9a8f3245 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1364,6 +1364,16 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } + /* Note: When secondary source input (src2) is set to General Controller 'No Controller', + output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). + That means that this source will behave unipolar only. We need to force the + unipolar flags to ensure the modulator will behave correctly later. + */ + if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src2 == FLUID_MOD_NONE)) + { + mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR; + } /* *** Transform *** */ /* SF2.01 only uses the 'linear' transform (0). diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index 9a48ed45..db59642b 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -364,7 +364,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_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) @@ -418,6 +433,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) /* transform the input value */ 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 { return 0.0; @@ -437,6 +455,9 @@ fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) /* transform the second input value */ 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 { v2 = 1.0f; From d0813be1c54e26455f0f7c45063e7c765d3c1041 Mon Sep 17 00:00:00 2001 From: derselbst Date: Thu, 20 Dec 2018 17:44:08 +0100 Subject: [PATCH 078/101] add missing getters for lyric and text midi events fixes #468 --- doc/fluidsynth-v20-devdoc.txt | 7 +++++ include/fluidsynth/midi.h | 4 +++ src/midi/fluid_midi.c | 52 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index b0e4630d..fae89f12 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -21,6 +21,7 @@ All the source code examples in this document are in the public domain; you can - \ref Disclaimer - \ref Introduction +- \ref NewIn2_0_3 - \ref NewIn2_0_2 - \ref NewIn2_0_0 - \ref CreatingSettings @@ -61,6 +62,12 @@ What is FluidSynth? - FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org +\section NewIn2_0_3 Whats new in 2.0.3? + +- add missing getters for midi events: + - fluid_midi_event_get_text() + - fluid_midi_event_get_lyrics() + \section NewIn2_0_2 Whats new in 2.0.2? diff --git a/include/fluidsynth/midi.h b/include/fluidsynth/midi.h index cd30e208..9ddeef05 100644 --- a/include/fluidsynth/midi.h +++ b/include/fluidsynth/midi.h @@ -53,8 +53,12 @@ FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *dat int size, int dynamic); FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, 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, 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. diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index f672124c..d4404ce0 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -36,6 +36,7 @@ static long fluid_getlength(unsigned char *s); */ 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_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size); #define READ_FULL_INITIAL_BUFLEN 1024 static fluid_track_t *new_fluid_track(int num); @@ -1334,6 +1335,25 @@ fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dyn 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. * @param evt MIDI event structure @@ -1353,6 +1373,25 @@ fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int d 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) { evt->type = type; @@ -1361,6 +1400,19 @@ static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, 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 From 54a9f29305d0f95ca24f7f7696756c2bef07b5b4 Mon Sep 17 00:00:00 2001 From: derselbst Date: Thu, 20 Dec 2018 17:46:10 +0100 Subject: [PATCH 079/101] remove public comments about internal details --- src/midi/fluid_midi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index d4404ce0..59ad3173 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -1306,8 +1306,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 * with delete_fluid_midi_event()) * @return Always returns #FLUID_OK - * - * @note Unlike the other event assignment functions, this one sets evt->type. */ int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) @@ -1326,7 +1324,6 @@ fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dy * @return Always returns #FLUID_OK * * @since 2.0.0 - * @note Unlike the other event assignment functions, this one sets evt->type. */ int fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) @@ -1364,7 +1361,6 @@ int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size) * @return Always returns #FLUID_OK * * @since 2.0.0 - * @note Unlike the other event assignment functions, this one sets evt->type. */ int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) From e8dd6ae3892d8aa8d524b46f38dda6227805cfa0 Mon Sep 17 00:00:00 2001 From: jjceresa Date: Fri, 21 Dec 2018 12:13:28 +0100 Subject: [PATCH 080/101] Import modulator source src1 correctly. - When primary source input (src1) is set to General Controller 'No Controller', output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()). That means that the minimum value of the modulator will be always 0.0. We need to force amount value to 0 to ensure a correct evaluation of the minimum value later (see fluid_voice_get_lower_boundary_for_attenuation()). --- src/sfloader/fluid_defsfont.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 9a8f3245..b81aaca2 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1354,7 +1354,20 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; } - /* *** Dest *** */ + + /* Note: When primary source input (src1) is set to General Controller 'No Controller', + output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()). + That means that the minimum value of the modulator will be always 0.0. + We need to force amount value to 0 to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). + */ + if(((mod_dest->flags1 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src1 == FLUID_MOD_NONE)) + { + mod_dest->amount = 0; + } + + /* *** Dest *** */ mod_dest->dest = mod_src->dest; /* index of controlled generator */ /* *** Amount source *** */ @@ -1367,7 +1380,8 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) /* Note: When secondary source input (src2) is set to General Controller 'No Controller', output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). That means that this source will behave unipolar only. We need to force the - unipolar flags to ensure the modulator will behave correctly later. + unipolar flag to ensure to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). */ if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) && (mod_dest->src2 == FLUID_MOD_NONE)) From e9b0f0d24b2cd60e2d7f9e1f6770d3edaaeccac1 Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri, 21 Dec 2018 19:42:00 +0100 Subject: [PATCH 081/101] Add modulators checking at soundfont loading time (#467) Actually some basic modulators check are done at noteon time (in fluid_voice_add_mod()). That means that we know if a modulator (modx) is invalid only when a MIDI noteon is received and only for a preset modx belongs to. This is not appropriate. This moves the modulator checking at soundfont loading time. Enhancements are: 1) A better verbose modulator integrity check, for any soundfont loaded at appropriate time. 1.1) All modulators are checked (preset zone (local/global), instrument zone (local/global). 1.2.1) Modulators check are enforced to source src1 and src2 (for non-CC and CC sources) (following SF specs (except for CC LSB) ( see comment in fluid_synth_cc_LOCAL()). Modulators CC sources checking is coherent with the actual behaviour in fluid_synth_cc_LOCAL() in regard of modulation triggering. 1.2.2) Also, identical modulator in the same zone are detected. 1.2.3) Any invalid modulator(sources invalid, or modulator identical) is removed at loading time with a warning message displaying the cause and name of the modulators. 2) This fix a bug in noteon, in the case of identical modulators in global preset zone. Assuming 2 identical modulator (m1 and m2) in a preset global zone, the actual noteon doesn't check this case (the actual code detect identical modulator in all others zones (instrument (local or global), preset(local)) but not preset global). 3) NoteOn is faster. 3.1)There is no more modulators checks at noteon making this more efficient. 3.2) As there are no identical modulator in the same zone, there is no more identity modulator check (i.e local zone against local zone), (i.e global zone against global zone). This result in a faster code and the bug described in (2) is gone. 4) Modulators sources checking as been added in API functions fluid_synth_add_default_mod() and fluid_voice_add_mod(). Please --- src/sfloader/fluid_defsfont.c | 408 ++++++++++++++++++++++++---------- src/synth/fluid_mod.c | 171 ++++++++++++++ src/synth/fluid_mod.h | 1 + src/synth/fluid_synth.c | 35 ++- src/synth/fluid_voice.c | 58 +++-- src/synth/fluid_voice.h | 1 + 6 files changed, 517 insertions(+), 157 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 1931d4ee..e4a2663f 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -682,6 +682,140 @@ fluid_defpreset_next(fluid_defpreset_t *defpreset) return defpreset->next; } +/* + * Adds global and local modulators list to the voice. This is done in 2 steps: + * - Step 1: Local modulators replace identic global modulators. + * - Step 2: global + local modulators are added to the voice using mode. + * + * Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE. + * Preset zone list (local/global) must be added using FLUID_VOICE_ADD. + * + * @param voice voice instance. + * @param global_mod global list of modulators. + * @param local_mod local list of modulators. + * @param mode Determines how to handle an existing identical modulator. + * #FLUID_VOICE_ADD to add (offset) the modulator amounts, + * #FLUID_VOICE_OVERWRITE to replace the modulator, +*/ +static void +fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, + fluid_mod_t *global_mod, fluid_mod_t *local_mod, + int mode) +{ + fluid_mod_t *mod; + /* list for 'sorting' global/local modulators */ + fluid_mod_t *mod_list[FLUID_NUM_MOD]; + int mod_list_count, i; + + /* identity_limit_count is the modulator upper limit number to handle with + * existing identical modulators. + * When identity_limit_count is below the actual number of modulators, this + * will restrict identity check to this upper limit, + * This is useful when we know by advance that there is no duplicate with + * modulators at index above this limit. This avoid wasting cpu cycles at + * noteon. + */ + int identity_limit_count; + + /* Step 1: Local modulators replace identic global modulators. */ + + /* local (instrument zone/preset zone), modulators: Put them all into a list. */ + mod_list_count = 0; + + while(local_mod) + { + /* As modulators number in local_mod list was limited to FLUID_NUM_MOD at + soundfont loading time (fluid_limit_mod_list()), here we don't need + to check if mod_list is full. + */ + mod_list[mod_list_count++] = local_mod; + local_mod = local_mod->next; + } + + /* global (instrument zone/preset zone), modulators. + * Replace modulators with the same definition in the global list: + * (Instrument zone: SF 2.01 page 69, 'bullet' 8) + * (Preset zone: SF 2.01 page 69, second-last bullet). + * + * mod_list contains local modulators. Now we know that there + * is no global modulator identic to another global modulator (this has + * been checked at soundfont loading time). So global modulators + * are only checked against local modulators number. + */ + + /* Restrict identity check to the number of local modulators */ + identity_limit_count = mod_list_count; + + while(global_mod) + { + /* 'Identical' global modulators are ignored. + * SF2.01 section 9.5.1 + * page 69, 'bullet' 3 defines 'identical'. */ + + for(i = 0; i < identity_limit_count; i++) + { + if(fluid_mod_test_identity(global_mod, mod_list[i])) + { + break; + } + } + + /* Finally add the new modulator to the list. */ + if(i >= identity_limit_count) + { + /* Although local_mod and global_mod lists was limited to + FLUID_NUM_MOD at soundfont loading time, it is possible that + local + global modulators exceeds FLUID_NUM_MOD. + So, checks if mod_list_count reachs the limit. + */ + if(mod_list_count >= FLUID_NUM_MOD) + { + /* mod_list is full, we silently forget this modulator and + next global modulators. When mod_list will be added to the + voice, a warning will be displayed if the voice list is full. + (see fluid_voice_add_mod_local()). + */ + break; + } + + mod_list[mod_list_count++] = global_mod; + } + + global_mod = global_mod->next; + } + + /* Step 2: global + local modulators are added to the voice using mode. */ + + /* + * mod_list contains local and global modulators, we know that: + * - there is no global modulator identic to another global modulator, + * - there is no local modulator identic to another local modulator, + * So these local/global modulators are only checked against + * actual number of voice modulators. + */ + + /* Restrict identity check to the actual number of voice modulators */ + /* Acual number of voice modulators : defaults + [instruments] */ + identity_limit_count = voice->mod_count; + + for(i = 0; i < mod_list_count; i++) + { + + mod = mod_list[i]; + /* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */ + /* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */ + + if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0)) + { + /* Instrument modulators -supersede- existing (default) modulators. + SF 2.01 page 69, 'bullet' 6 */ + + /* Preset modulators -add- to existing instrument modulators. + SF2.01 page 70 first bullet on page */ + fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count); + } + } +} /* * fluid_defpreset_noteon @@ -695,9 +829,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c fluid_voice_zone_t *voice_zone; fluid_list_t *list; fluid_voice_t *voice; - fluid_mod_t *mod; - fluid_mod_t *mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */ - int mod_list_count; int i; global_preset_zone = fluid_defpreset_get_global_zone(defpreset); @@ -769,63 +900,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c } /* for all generators */ - /* global instrument zone, modulators: Put them all into a - * list. */ - - mod_list_count = 0; - - if(global_inst_zone) - { - mod = global_inst_zone->mod; - - while(mod) - { - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - } - - /* local instrument zone, modulators. - * Replace modulators with the same definition in the list: - * SF 2.01 page 69, 'bullet' 8 - */ - mod = inst_zone->mod; - - while(mod) - { - - /* 'Identical' modulators will be deleted by setting their - * list entry to NULL. The list length is known, NULL - * entries will be ignored later. SF2.01 section 9.5.1 - * page 69, 'bullet' 3 defines 'identical'. */ - - for(i = 0; i < mod_list_count; i++) - { - if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i])) - { - mod_list[i] = NULL; - } - } - - /* Finally add the new modulator to to the list. */ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - - /* Add instrument modulators (global / local) to the voice. */ - for(i = 0; i < mod_list_count; i++) - { - - mod = mod_list[i]; - - if(mod != NULL) /* disabled modulators CANNOT be skipped. */ - { - - /* Instrument modulators -supersede- existing (default) - * modulators. SF 2.01 page 69, 'bullet' 6 */ - fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE); - } - } + /* Adds instrument zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global instrument modulators */ + global_inst_zone ? global_inst_zone->mod : NULL, + inst_zone->mod, /* local instrument modulators */ + FLUID_VOICE_OVERWRITE); /* mode */ /* Preset level, generators */ @@ -868,57 +948,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c } } /* for all generators */ - - /* Global preset zone, modulators: put them all into a - * list. */ - mod_list_count = 0; - - if(global_preset_zone) - { - mod = global_preset_zone->mod; - - while(mod) - { - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - } - - /* Process the modulators of the local preset zone. Kick - * out all identical modulators from the global preset zone - * (SF 2.01 page 69, second-last bullet) */ - - mod = preset_zone->mod; - - while(mod) - { - for(i = 0; i < mod_list_count; i++) - { - if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i])) - { - mod_list[i] = NULL; - } - } - - /* Finally add the new modulator to the list. */ - mod_list[mod_list_count++] = mod; - mod = mod->next; - } - - /* Add preset modulators (global / local) to the voice. */ - for(i = 0; i < mod_list_count; i++) - { - mod = mod_list[i]; - - if((mod != NULL) && (mod->amount != 0)) /* disabled modulators can be skipped. */ - { - - /* Preset modulators -add- to existing instrument / - * default modulators. SF2.01 page 70 first bullet on - * page */ - fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD); - } - } + /* Adds preset zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global preset modulators */ + global_preset_zone ? global_preset_zone->mod : NULL, + preset_zone->mod, /* local preset modulators */ + FLUID_VOICE_ADD); /* mode */ /* add the synthesis process to the synthesis loop. */ fluid_synth_start_voice(synth, voice); @@ -930,7 +965,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c * class - for example when using stereo samples) */ } - } } @@ -1110,6 +1144,7 @@ new_fluid_preset_zone(char *name) static void delete_fluid_list_mod(fluid_mod_t *mod) { fluid_mod_t *tmp; + while(mod) /* delete the modulators */ { tmp = mod; @@ -1192,6 +1227,129 @@ static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone return FLUID_OK; } +/** + * Checks if modulator mod is identic to another modulator in the list + * (specs SF 2.0X 7.4, 7.8). + * @param mod, modulator list. + * @param name, if not NULL, pointer on a string displayed as warning. + * @return TRUE if mod is identic to another modulator, FALSE otherwise. + */ +static int +fluid_zone_is_mod_identic(fluid_mod_t *mod, char *name) +{ + fluid_mod_t *next = mod->next; + + while(next) + { + /* is mod identic to next ? */ + if(fluid_mod_test_identity(mod, next)) + { + if(name) + { + FLUID_LOG(FLUID_WARN, "Ignoring identic modulator %s", name); + } + + return TRUE; + } + + next = next->next; + } + + return FALSE; +} + +/** + * Limits the number of modulators in a modulator list. + * This is appropriate to internal synthesizer modulators tables + * which have a fixed size (FLUID_NUM_MOD). + * + * @param zone_name, zone name + * @param list_mod, address of pointer on modulator list. + */ +static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod) +{ + int mod_idx = 0; /* modulator index */ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + + while(mod) + { + if((mod_idx + 1) > FLUID_NUM_MOD) + { + /* truncation of list_mod */ + if(mod_idx) + { + prev_mod->next = NULL; + } + else + { + *list_mod = NULL; + } + + delete_fluid_list_mod(mod); + FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name, + FLUID_NUM_MOD); + break; + } + + mod_idx++; + prev_mod = mod; + mod = mod->next; + } +} + +/** + * Checks and remove invalid modulators from a zone modulators list. + * - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1). + * - checks identic modulators in the list (specs SF 2.01 7.4, 7.8). + * @param zone_name, zone name. + * @param list_mod, address of pointer on modulators list. + */ +static void +fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod) +{ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + int mod_idx = 0; /* modulator index */ + + while(mod) + { + char zone_mod_name[256]; + fluid_mod_t *next = mod->next; + + /* prepare modulator name: zonename/#modulator */ + FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx); + + /* has mod invalid sources ? */ + if(!fluid_mod_check_sources(mod, zone_mod_name) + /* or is mod identic to any following modulator ? */ + || fluid_zone_is_mod_identic(mod, zone_mod_name)) + { + /* the modulator is useless so we remove it */ + if(prev_mod) + { + prev_mod->next = next; + } + else + { + *list_mod = next; + } + + delete_fluid_mod(mod); /* freeing */ + } + else + { + prev_mod = mod; + } + + mod = next; + mod_idx++; + } + + /* limits the size of modulators list */ + fluid_limit_mod_list(zone_name, list_mod); +} + /* * fluid_zone_gen_import_sfont * Imports generators from sfzone to gen and range. @@ -1205,7 +1363,7 @@ fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone fluid_list_t *r; SFGen *sfgen; - for( r = sfzone->gen; r != NULL; ) + for(r = sfzone->gen; r != NULL;) { sfgen = (SFGen *)fluid_list_get(r); @@ -1258,6 +1416,7 @@ fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, uns /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ flags_dest = 0; + if(sf_source & (1 << 7)) { flags_dest |= FLUID_MOD_CC; @@ -1313,19 +1472,21 @@ fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, uns /* This shouldn't happen - unknown type! */ return FALSE; } + *flags = flags_dest; return TRUE; } /* * fluid_zone_mod_import_sfont - * Imports modulators from sfzone to mod list. - * @param mod, pointer on modulator list to return. + * Imports modulators from sfzone to modulators list mod. + * @param zone_name, zone name. + * @param mod, address of pointer on modulators list to return. * @param sfzone, pointer on soundfont zone. * @return FLUID_OK if success, FLUID_FAILED otherwise. */ static int -fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) +fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) { fluid_list_t *r; int count; @@ -1353,7 +1514,8 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; - } + } + /* *** Dest *** */ mod_dest->dest = mod_src->dest; /* index of controlled generator */ @@ -1363,7 +1525,7 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) /* This shouldn't happen - unknown type! * Deactivate the modulator by setting the amount to 0. */ mod_dest->amount = 0; - } + } /* *** Transform *** */ /* SF2.01 only uses the 'linear' transform (0). @@ -1398,6 +1560,8 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone) r = fluid_list_next(r); } /* foreach modulator */ + /* checks and removes invalid modulators in modulators list*/ + fluid_zone_check_mod(zone_name, mod); return FLUID_OK; } @@ -1433,7 +1597,7 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_ } /* Import the modulators (only SF2.1 and higher) */ - return fluid_zone_mod_import_sfont(&zone->mod, sfzone); + return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone); } /* @@ -1546,7 +1710,9 @@ fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_ { sfzone = (SFZone *)fluid_list_get(p); - FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", inst->name, count); + /* integrates preset zone name in instrument zone name */ + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%s/%d", preset_zone->name, + inst->name, count); inst_zone = new_fluid_inst_zone(zone_name); @@ -1704,7 +1870,7 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid } /* Import the modulators (only SF2.1 and higher) */ - return fluid_zone_mod_import_sfont(&inst_zone->mod, sfzone); + return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone); } /* diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index 9a48ed45..4265097b 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -486,6 +486,177 @@ size_t fluid_mod_sizeof() 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, 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. * @param mod1 First modulator diff --git a/src/synth/fluid_mod.h b/src/synth/fluid_mod.h index e834baa5..3e766174 100644 --- a/src/synth/fluid_mod.h +++ b/src/synth/fluid_mod.h @@ -44,6 +44,7 @@ struct _fluid_mod_t }; 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, char *name); #ifdef DEBUG void fluid_dump_modulator(fluid_mod_t *mod); diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 15e167a1..07bfc042 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -227,7 +227,7 @@ void fluid_synth_settings(fluid_settings_t *settings) #else fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); #endif - + 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); @@ -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(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); default_mod = synth->default_mod; @@ -3369,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(left != NULL, FLUID_FAILED); fluid_return_val_if_fail(right != NULL, FLUID_FAILED); - + /* First, take what's still available in the buffer */ count = 0; num = synth->cur; @@ -3522,7 +3529,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len, /** * mixes the samples of \p in to \p out - * + * * @param out the output sample buffer to mix to * @param ooff sample offset in \p out * @param in the rvoice_mixer input sample buffer to mix from @@ -3540,6 +3547,7 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out if(out != NULL) { int j; + for(j = 0; j < num; j++) { out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; @@ -3688,7 +3696,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], for(i = 0; i < nfxchan; i++) { int buf_idx = f * nfxchan + i; - + float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); @@ -3731,10 +3739,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], for(i = 0; i < nfxchan; i++) { int buf_idx = f * nfxchan + i; - + float *out_buf = fx[(buf_idx * 2) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); - + out_buf = fx[(buf_idx * 2 + 1) % nfx]; fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); } @@ -3785,7 +3793,7 @@ fluid_synth_write_float(fluid_synth_t *synth, int len, float cpu_load; fluid_profile_ref_var(prof_ref); - + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); fluid_return_val_if_fail(lout != NULL, FLUID_FAILED); fluid_return_val_if_fail(rout != NULL, FLUID_FAILED); @@ -3854,10 +3862,12 @@ static FLUID_INLINE int16_t round_clip_to_i16(float x) { long i; + if(x >= 0.0f) { i = (long)(x + 0.5f); - if (FLUID_UNLIKELY(i > 32767)) + + if(FLUID_UNLIKELY(i > 32767)) { i = 32767; } @@ -3865,12 +3875,13 @@ round_clip_to_i16(float x) else { i = (long)(x - 0.5f); - if (FLUID_UNLIKELY(i < -32768)) + + if(FLUID_UNLIKELY(i < -32768)) { i = -32768; } } - + return (int16_t)i; } @@ -4348,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 - 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 { - 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 diff --git a/src/synth/fluid_voice.c b/src/synth/fluid_voice.c index 66337175..9d82bcf4 100644 --- a/src/synth/fluid_voice.c +++ b/src/synth/fluid_voice.c @@ -1185,7 +1185,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen) */ #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 (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) @@ -1209,7 +1209,7 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) mod = &voice->mod[i]; /* step 1: find all the modulators that have the changed controller - as input source. When ctrl is -1 all modulators destination + as input source. When ctrl is -1 all modulators destination are updated */ if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) { @@ -1465,10 +1465,10 @@ fluid_voice_stop(fluid_voice_t *voice) } /** - * Adds a modulator to the voice. - * @param voice Voice instance - * @param mod Modulator info (copied) - * @param mode Determines how to handle an existing identical modulator + * Adds a modulator to the voice if the modulator has valid sources. + * @param voice Voice instance. + * @param mod Modulator info (copied). + * @param mode Determines how to handle an existing identical modulator. * #FLUID_VOICE_ADD to add (offset) the modulator amounts, * #FLUID_VOICE_OVERWRITE to replace the modulator, * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should @@ -1476,33 +1476,43 @@ fluid_voice_stop(fluid_voice_t *voice) */ void 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; - /* - * Some soundfonts come with a huge number of non-standard - * 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 */ + /* check_limit_count cannot be above voice->mod_count */ + if(check_limit_count > voice->mod_count) { - FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1); - return; + check_limit_count = voice->mod_count; } if(mode == FLUID_VOICE_ADD) { /* 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)) { @@ -1517,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) */ - 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)) { diff --git a/src/synth/fluid_voice.h b/src/synth/fluid_voice.h index 6038a1a9..fb0b2823 100644 --- a/src/synth/fluid_voice.h +++ b/src/synth/fluid_voice.h @@ -157,6 +157,7 @@ void fluid_voice_release(fluid_voice_t *voice); void fluid_voice_noteoff(fluid_voice_t *voice); void fluid_voice_off(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); int fluid_voice_kill_excl(fluid_voice_t *voice); From 458cea0aff9dfa54983963f61fefd1f76df2571e Mon Sep 17 00:00:00 2001 From: jjceresa <32781294+jjceresa@users.noreply.github.com> Date: Fri, 21 Dec 2018 19:54:15 +0100 Subject: [PATCH 082/101] Fix incorrect comment (#488) Normalized value of MIDI sources (before unipolar or bipolar mapping) is always in the range [0..+1], regardless of MIDI event (even for FLUID_MOD_PITCHWHEEL). --- src/synth/fluid_mod.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/synth/fluid_mod.c b/src/synth/fluid_mod.c index db59642b..127de3f0 100644 --- a/src/synth/fluid_mod.c +++ b/src/synth/fluid_mod.c @@ -245,10 +245,7 @@ fluid_mod_get_source_value(const unsigned char mod_src, static fluid_real_t 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] - * - * if val was retrieved from pitch_bend then [-0.5;0.5] - */ + /* normalized value, i.e. usually in the range [0;1] */ const fluid_real_t val_norm = val / range; /* we could also only switch case the lower nibble of mod_flags, however From 5fe56b32b27c9aa52932eee5fdabe57dc54b6115 Mon Sep 17 00:00:00 2001 From: derselbst Date: Fri, 21 Dec 2018 20:11:01 +0100 Subject: [PATCH 083/101] clarify availability sdl2 and waveout audio driver options --- doc/fluidsettings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index 2d2e0d4a..c0644926 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -369,7 +369,7 @@ Developers: Settings can be deprecated by adding: SOME TEXT alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sdl2, sndman, waveout - The audio system to be used. In order to use sdl2 as audio driver, the application is responsible for initializing SDL's audio subsystem. + The audio system to be used. In order to use sdl2 as audio driver, the application is responsible for initializing SDL's audio subsystem.

Note: sdl2 and waveout are available since fluidsynth 2.1.
From 06ec2d4e675415817afeaff4dea20bc7c69425d5 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 22 Dec 2018 16:01:12 +0100 Subject: [PATCH 084/101] Revert "remove unused io.h include" This reverts commit cb485806c3cfdb4b09e670c2a5a925bed26afae1. Fixes #491. --- CMakeLists.txt | 1 + src/config.cmake | 3 +++ src/utils/fluidsynth_priv.h | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1fa324f..a0445467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,7 @@ if ( WIN32 ) # Check presence of MS include files check_include_file ( windows.h HAVE_WINDOWS_H ) + check_include_file ( io.h HAVE_IO_H ) check_include_file ( dsound.h HAVE_DSOUND_H ) check_include_files ( "windows.h;mmsystem.h" HAVE_MMSYSTEM_H ) diff --git a/src/config.cmake b/src/config.cmake index ae7c7dbe..2d1d786b 100644 --- a/src/config.cmake +++ b/src/config.cmake @@ -43,6 +43,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@ +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_IO_H @HAVE_IO_H@ + /* whether or not we are supporting lash */ #cmakedefine HAVE_LASH @HAVE_LASH@ diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index c7ddf546..ed1b532f 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -106,6 +106,10 @@ #include #endif +#if HAVE_IO_H +#include +#endif + #if HAVE_SIGNAL_H #include #endif From d2078e63b4656c4984972b8fbdbae4281f8550aa Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 22 Dec 2018 16:04:32 +0100 Subject: [PATCH 085/101] clarify need for io.h --- src/utils/fluidsynth_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/fluidsynth_priv.h b/src/utils/fluidsynth_priv.h index ed1b532f..4df590e2 100644 --- a/src/utils/fluidsynth_priv.h +++ b/src/utils/fluidsynth_priv.h @@ -107,7 +107,7 @@ #endif #if HAVE_IO_H -#include +#include // _open(), _close(), read(), write() on windows #endif #if HAVE_SIGNAL_H From 828307ff092dee05631cd4cabdc96a4e553fdba4 Mon Sep 17 00:00:00 2001 From: carlo-bramini <30959007+carlo-bramini@users.noreply.github.com> Date: Sat, 22 Dec 2018 16:08:09 +0100 Subject: [PATCH 086/101] Fix error with MSVC (#492) FLUID_INLINE must be used instead of inline, otherwise MSVC fails to build fluidsynth. --- src/rvoice/fluid_rev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rvoice/fluid_rev.c b/src/rvoice/fluid_rev.c index 5f72cb7b..9acee961 100644 --- a/src/rvoice/fluid_rev.c +++ b/src/rvoice/fluid_rev.c @@ -447,7 +447,7 @@ static void set_mod_frequency(sinus_modulator *mod, @param pointer on modulator structure. @return current value of the modulator sine wave. -----------------------------------------------------------------------------*/ -static inline fluid_real_t get_mod_sinus(sinus_modulator *mod) +static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) { fluid_real_t out; out = mod->a1 * mod->buffer1 - mod->buffer2; @@ -613,7 +613,7 @@ static int set_mod_delay_line(mod_delay_line *mdl, @param mdl, pointer on modulated delay line. @return the sample value. -----------------------------------------------------------------------------*/ -static inline fluid_real_t get_mod_delay(mod_delay_line *mdl) +static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) { fluid_real_t out_index; /* new modulated index position */ int int_out_index; /* integer part of out_index */ From 2c9c176e1779b6162e627f4f33be60acfa329157 Mon Sep 17 00:00:00 2001 From: derselbst Date: Wed, 26 Dec 2018 21:03:59 +0100 Subject: [PATCH 087/101] fix documentation of fluid_sfont_iteration_next_t --- include/fluidsynth/sfont.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/fluidsynth/sfont.h b/include/fluidsynth/sfont.h index 55413a9e..0df4d6d4 100644 --- a/include/fluidsynth/sfont.h +++ b/include/fluidsynth/sfont.h @@ -184,12 +184,10 @@ typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); /** * Virtual SoundFont preset iteration function. * @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 * - * Should store preset information to the caller supplied \a preset structure - * and advance the internal iteration state to the next preset for subsequent - * calls. + * Returns preset information to the caller. The returned buffer is only valid until a subsequent + * call to this function. */ typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); From c8b18056c6ad717199f732c79b2039d4e2bfd89b Mon Sep 17 00:00:00 2001 From: derselbst Date: Fri, 28 Dec 2018 14:47:55 +0100 Subject: [PATCH 088/101] remove unused fluid_sample_t::userdata --- src/sfloader/fluid_sfont.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sfloader/fluid_sfont.h b/src/sfloader/fluid_sfont.h index 24773353..28609e96 100644 --- a/src/sfloader/fluid_sfont.h +++ b/src/sfloader/fluid_sfont.h @@ -183,8 +183,6 @@ struct _fluid_sample_t * @return Should return #FLUID_OK */ int (*notify)(fluid_sample_t *sample, int reason); - - void *userdata; /**< User defined data */ }; From 488da516cd1ec4497fd648d4fb43516b34233d5c Mon Sep 17 00:00:00 2001 From: derselbst Date: Fri, 28 Dec 2018 14:54:13 +0100 Subject: [PATCH 089/101] clarify documentation of fluid_sample_sizeof() --- src/sfloader/fluid_sfont.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfloader/fluid_sfont.c b/src/sfloader/fluid_sfont.c index 405bec6c..978ba591 100644 --- a/src/sfloader/fluid_sfont.c +++ b/src/sfloader/fluid_sfont.c @@ -515,6 +515,8 @@ delete_fluid_sample(fluid_sample_t *sample) * Useful in low latency scenarios e.g. to allocate a sample on the stack. * * @return Size of fluid_sample_t in bytes + * + * @note It is recommend to zero initialize the memory before using the object. */ size_t fluid_sample_sizeof() { From 4d612c22f4ad81cfb3dbc30d719b5445ce1a30b7 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Dec 2018 11:18:36 +0100 Subject: [PATCH 090/101] fix segfaults in fluid_is_soundfont() and fluid_is_midifile() --- src/midi/fluid_midi.c | 4 ++-- src/sfloader/fluid_sffile.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c index 59ad3173..1a394f81 100644 --- a/src/midi/fluid_midi.c +++ b/src/midi/fluid_midi.c @@ -94,13 +94,13 @@ int fluid_is_midifile(const char *filename) { FILE *fp = FLUID_FOPEN(filename, "rb"); uint32_t id; - int retcode = 0; + int retcode = FALSE; do { if(fp == NULL) { - break; + return retcode; } if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1) diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index 0725e1f3..302eea72 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -336,13 +336,13 @@ int fluid_is_soundfont(const char *filename) { FILE *fp = FLUID_FOPEN(filename, "rb"); uint32_t fcc; - int retcode = 0; + int retcode = FALSE; do { if(fp == NULL) { - break; + return retcode; } if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) From 15d27b7145075091a500bcd5a3a948334fa888d8 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Dec 2018 17:34:36 +0100 Subject: [PATCH 091/101] fix inverse logic in fluid_sample_set_sound_data() --- doc/fluidsynth-v20-devdoc.txt | 1 + src/sfloader/fluid_sfont.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index fae89f12..52a58426 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -64,6 +64,7 @@ What is FluidSynth? \section NewIn2_0_3 Whats new in 2.0.3? +- fix incorrect behaviour of fluid_sample_set_sound_data() - add missing getters for midi events: - fluid_midi_event_get_text() - fluid_midi_event_get_lyrics() diff --git a/src/sfloader/fluid_sfont.c b/src/sfloader/fluid_sfont.c index 978ba591..d7362498 100644 --- a/src/sfloader/fluid_sfont.c +++ b/src/sfloader/fluid_sfont.c @@ -565,7 +565,7 @@ fluid_sample_set_sound_data(fluid_sample_t *sample, fluid_return_val_if_fail(sample != 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 */ if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) From b7b420617ba658031d0f6d7db793781f407c0114 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Dec 2018 18:21:43 +0100 Subject: [PATCH 092/101] fix double free in fluid_sample_set_sound_data() --- src/sfloader/fluid_sfont.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sfloader/fluid_sfont.c b/src/sfloader/fluid_sfont.c index d7362498..09df0e37 100644 --- a/src/sfloader/fluid_sfont.c +++ b/src/sfloader/fluid_sfont.c @@ -572,9 +572,10 @@ fluid_sample_set_sound_data(fluid_sample_t *sample, { FLUID_FREE(sample->data); FLUID_FREE(sample->data24); - sample->data = NULL; - sample->data24 = NULL; } + + sample->data = NULL; + sample->data24 = NULL; if(copy_data) { @@ -637,6 +638,8 @@ error_rec: FLUID_LOG(FLUID_ERR, "Out of memory"); FLUID_FREE(sample->data); FLUID_FREE(sample->data24); + sample->data = NULL; + sample->data24 = NULL; return FLUID_FAILED; #undef SAMPLE_LOOP_MARGIN From 8634613509e2930a284373963a58d042c120a9e7 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Dec 2018 18:26:37 +0100 Subject: [PATCH 093/101] fix incorrect documentation of fluid_sample_sizeof() --- src/sfloader/fluid_sfont.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sfloader/fluid_sfont.c b/src/sfloader/fluid_sfont.c index 09df0e37..548a80ab 100644 --- a/src/sfloader/fluid_sfont.c +++ b/src/sfloader/fluid_sfont.c @@ -512,11 +512,13 @@ delete_fluid_sample(fluid_sample_t *sample) /** * 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 * * @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() { From 0f6c40c15e20c8da57c314d047e9e899fae8d84c Mon Sep 17 00:00:00 2001 From: derselbst Date: Sat, 29 Dec 2018 19:58:10 +0100 Subject: [PATCH 094/101] stop adding empty strings to readline's history --- src/utils/fluid_sys.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/fluid_sys.c b/src/utils/fluid_sys.c index 4ac1344b..d686737f 100644 --- a/src/utils/fluid_sys.c +++ b/src/utils/fluid_sys.c @@ -1214,7 +1214,11 @@ fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, FLUID_SNPRINTF(buf, len, "%s", line); buf[len - 1] = 0; - add_history(buf); + + if(buf[0] != '\0') + { + add_history(buf); + } free(line); return 1; From 1bae9b2fe1a958f54f4910c802a79673e0df9850 Mon Sep 17 00:00:00 2001 From: derselbst Date: Sun, 30 Dec 2018 12:42:00 +0100 Subject: [PATCH 095/101] bump to version 2.0.3 --- CMakeLists.txt | 6 +++--- doc/Doxyfile | 2 +- doc/fluidsynth-v20-devdoc.txt | 6 +++--- src/fluidsynth.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0445467..8bd9bcfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ set ( PACKAGE "fluidsynth" ) # FluidSynth package version set ( FLUIDSYNTH_VERSION_MAJOR 2 ) set ( FLUIDSYNTH_VERSION_MINOR 0 ) -set ( FLUIDSYNTH_VERSION_MICRO 2 ) +set ( FLUIDSYNTH_VERSION_MICRO 3 ) set ( VERSION "${FLUIDSYNTH_VERSION_MAJOR}.${FLUIDSYNTH_VERSION_MINOR}.${FLUIDSYNTH_VERSION_MICRO}" ) set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) @@ -43,8 +43,8 @@ set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" ) # if any interfaces have been removed/changed (compatibility broken): AGE=0 # This is not exactly the same algorithm as the libtool one, but the results are the same. set ( LIB_VERSION_CURRENT 2 ) -set ( LIB_VERSION_AGE 0 ) -set ( LIB_VERSION_REVISION 2 ) +set ( LIB_VERSION_AGE 1 ) +set ( LIB_VERSION_REVISION 0 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) diff --git a/doc/Doxyfile b/doc/Doxyfile index 7f3b2f46..4558d926 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = libfluidsynth -PROJECT_NUMBER = 2.0.2 +PROJECT_NUMBER = 2.0.3 OUTPUT_DIRECTORY = api CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index 52a58426..16c803d5 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -7,9 +7,9 @@ \author Josh Green \author David Henningsson \author Tom Moebert -\author Copyright © 2003-2018 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert -\version Revision 2.0.2 -\date 2018-11-18 +\author Copyright © 2003-2019 Peter Hanappe, Conrad Berhörster, Antoine Schmitt, Pedro López-Cabanillas, Josh Green, David Henningsson, Tom Moebert +\version Revision 2.0.3 +\date 2019-01-01 All the source code examples in this document are in the public domain; you can use them as you please. This document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ . The FluidSynth library is distributed under the GNU Lesser General Public License. A copy of the GNU Lesser General Public License is contained in the FluidSynth package; if not, visit http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/src/fluidsynth.c b/src/fluidsynth.c index 427c5541..bc0ed245 100644 --- a/src/fluidsynth.c +++ b/src/fluidsynth.c @@ -1034,7 +1034,7 @@ void print_welcome() { printf("FluidSynth runtime version %s\n" - "Copyright (C) 2000-2018 Peter Hanappe and others.\n" + "Copyright (C) 2000-2019 Peter Hanappe and others.\n" "Distributed under the LGPL license.\n" "SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n", fluid_version_str()); From cc3d93b56e31b158886f5ed2150d46524ada704a Mon Sep 17 00:00:00 2001 From: jjceresa Date: Thu, 3 Jan 2019 19:40:55 +0100 Subject: [PATCH 096/101] Change on prefixe zone name -Name of an instrument zone shouldn't prefixed by the preset zone name. This is due to the fact that instrument zone are common to all preset zone. Consequently, an instrument zone cannot identfied via a preset zone. -This new change, simply prefixe a zone as this: instrument zone name prefixed by iz: preset zone name prefixed by pz: --- src/sfloader/fluid_defsfont.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index d7960cbb..2e1634bb 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1015,7 +1015,7 @@ fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, while(p != NULL) { sfzone = (SFZone *)fluid_list_get(p); - FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", defpreset->name, count); + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "pz:%s/%d", defpreset->name, count); zone = new_fluid_preset_zone(zone_name); if(zone == NULL) @@ -1733,9 +1733,8 @@ fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_ { sfzone = (SFZone *)fluid_list_get(p); - /* integrates preset zone name in instrument zone name */ - FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%s/%d", preset_zone->name, - inst->name, count); + /* instrument zone name */ + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "iz:%s/%d", inst->name, count); inst_zone = new_fluid_inst_zone(zone_name); From b462e25ba3863a2dde63ad068b52943bbc711c6a Mon Sep 17 00:00:00 2001 From: jjceresa Date: Thu, 3 Jan 2019 19:57:27 +0100 Subject: [PATCH 097/101] Removing useless preset_zone parameter. --- src/sfloader/fluid_defsfont.c | 4 ++-- src/sfloader/fluid_defsfont.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 2e1634bb..35fce48e 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -1605,7 +1605,7 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_ if(zone->inst == NULL) { - zone->inst = fluid_inst_import_sfont(zone, sfinst, defsfont); + zone->inst = fluid_inst_import_sfont(sfinst, defsfont); } if(zone->inst == NULL) @@ -1697,7 +1697,7 @@ fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) * fluid_inst_import_sfont */ fluid_inst_t * -fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont) +fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont) { fluid_list_t *p; fluid_inst_t *inst; diff --git a/src/sfloader/fluid_defsfont.h b/src/sfloader/fluid_defsfont.h index 8b24934d..86f782f9 100644 --- a/src/sfloader/fluid_defsfont.h +++ b/src/sfloader/fluid_defsfont.h @@ -196,7 +196,7 @@ struct _fluid_inst_t }; fluid_inst_t *new_fluid_inst(void); -fluid_inst_t *fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_defsfont_t *defsfont); +fluid_inst_t *fluid_inst_import_sfont(SFInst *sfinst, fluid_defsfont_t *defsfont); void delete_fluid_inst(fluid_inst_t *inst); int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); From 241f67604724f56317c01fed968c1d0fec025775 Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Fri, 11 Jan 2019 15:19:59 +0100 Subject: [PATCH 098/101] Add note about changes to fluid_settings_set* functions introduced in #293 --- doc/fluidsynth-v20-devdoc.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index 16c803d5..6afee890 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -81,6 +81,7 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions Important changes that may not result in a compilation error but may cause your app to misbehave: - all public \c fluid_settings_* functions that return an integer which is not meant to be interpreted as bool consistently return either FLUID_OK or FLUID_FAILED +- fluid_settings_setstr() cannot be used to set integer (toggle) settings with "yes" or "no" values anymore. Use fluid_settings_setint() instead, for example:
fluid_settings_setint(settings, "synth.reverb.active", 0) instead of fluid_settings_setstr(settings, "synth.reverb.active", "no") - explicit client unregistering is required for fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() - all public functions consistently receive signed integers for soundfont ids, bank and program numbers - use unique device names for the "audio.portaudio.device" setting @@ -90,8 +91,9 @@ FluidSynths major version was bumped. The API was reworked, deprecated functions - all public \c delete_* functions return void and are safe when called with NULL - the shell command handler was decoupled internally, as a consequence the param list of new_fluid_server() and new_fluid_cmd_handler() was adapted +- \c fluid_settings_set* functions no longer silently register unknown settings but return an error instead - reverb: roomsize is now limited to an upper threshold of 1.0 to avoid exponential volume increase -- rename fluid_mod_new() and fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod() +- rename \c fluid_mod_new() and \c fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod() - rename chorus getters to match naming conventions: fluid_synth_get_chorus_speed() and fluid_synth_get_chorus_depth() - fluid_synth_remove_sfont() returns FLUID_OK or FLUID_FAILED - introduce a separate data type for sequencer client IDs: #fluid_seq_id_t From 53bed2d50602457b9e8bb5ef1a495c753e660058 Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Fri, 11 Jan 2019 15:21:37 +0100 Subject: [PATCH 099/101] Fix example code to deactivate reverb and chorus via fluid_settings_setint --- doc/fluidsynth-v20-devdoc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/fluidsynth-v20-devdoc.txt b/doc/fluidsynth-v20-devdoc.txt index 6afee890..5280f252 100644 --- a/doc/fluidsynth-v20-devdoc.txt +++ b/doc/fluidsynth-v20-devdoc.txt @@ -595,8 +595,8 @@ void createsynth() { fluid_settings_t* settings; settings = new_fluid_settings(); - fluid_settings_setstr(settings, "synth.reverb.active", "yes"); - fluid_settings_setstr(settings, "synth.chorus.active", "no"); + fluid_settings_setint(settings, "synth.reverb.active", 0); + fluid_settings_setint(settings, "synth.chorus.active", 0); synth = new_fluid_synth(settings); adriver = new_fluid_audio_driver(settings, synth); sequencer = new_fluid_sequencer2(0); From caf61baa04bf966f9d47dafc912899cb7923f1fc Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Fri, 11 Jan 2019 15:11:38 +0100 Subject: [PATCH 100/101] Increase verbosity for fluid_settings_* functions --- src/utils/fluid_settings.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/utils/fluid_settings.c b/src/utils/fluid_settings.c index 02be9a03..5b2b0874 100644 --- a/src/utils/fluid_settings.c +++ b/src/utils/fluid_settings.c @@ -457,7 +457,7 @@ fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_n else { /* path ends prematurely */ - FLUID_LOG(FLUID_WARN, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); + FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); return FLUID_FAILED; } @@ -550,7 +550,7 @@ fluid_settings_register_str(fluid_settings_t *settings, const char *name, const } else { - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name); } } @@ -612,7 +612,7 @@ fluid_settings_register_num(fluid_settings_t *settings, const char *name, double else { /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name); } } @@ -674,7 +674,7 @@ fluid_settings_register_int(fluid_settings_t *settings, const char *name, int de else { /* type mismatch */ - FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); + FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name); } } @@ -936,6 +936,7 @@ fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char * if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_STR_TYPE)) { + FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name); goto error_recovery; } @@ -1313,6 +1314,7 @@ fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_NUM_TYPE)) { + FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name); goto error_recovery; } @@ -1320,7 +1322,7 @@ fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) if(val < setting->min || val > setting->max) { - FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name); + FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name); goto error_recovery; } @@ -1497,6 +1499,7 @@ fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) if((fluid_settings_get(settings, name, &node) != FLUID_OK) || (node->type != FLUID_INT_TYPE)) { + FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name); goto error_recovery; } @@ -1504,7 +1507,7 @@ fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) if(val < setting->min || val > setting->max) { - FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name); + FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name); goto error_recovery; } From 09076e231d6c503c6821ec404b9a52cda5efe1b2 Mon Sep 17 00:00:00 2001 From: Tom M Date: Thu, 17 Jan 2019 19:52:27 +0100 Subject: [PATCH 101/101] Update issue_template.md --- .github/issue_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index d361226d..455e7abd 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -16,7 +16,7 @@ We strongly recommend to use it! Feel free to edit or remove inapplicable/unneed --> ### FluidSynth version -1.1.x +2.0.x ### Current behavior