From 75a32f5c7570ba0d2659f6eb01917628a12b1090 Mon Sep 17 00:00:00 2001 From: Markus Nentwig Date: Thu, 29 May 2003 15:43:52 +0000 Subject: [PATCH] Noteoff optimization fixed, mutex in iiwu_synth, ... --- fluidsynth/ChangeLog | 18 ++ fluidsynth/include/fluidsynth/voice.h | 16 ++ fluidsynth/src/fluid_defsfont.c | 7 +- fluidsynth/src/fluid_synth.c | 38 ++++ fluidsynth/src/fluid_synth.h | 5 + fluidsynth/src/fluid_voice.c | 314 ++++++++++++-------------- fluidsynth/src/fluid_voice.h | 12 +- fluidsynth/src/fluidsynth.c | 31 ++- 8 files changed, 254 insertions(+), 187 deletions(-) diff --git a/fluidsynth/ChangeLog b/fluidsynth/ChangeLog index 9f22a14c..f67e9b2b 100644 --- a/fluidsynth/ChangeLog +++ b/fluidsynth/ChangeLog @@ -1,3 +1,21 @@ +2003-05-29 root + + * src/fluid_synth.c (fluid_synth_one_block): Added a mutex that provides a small degree of + protection against noteons / noteoffs, when the audio thread is working. + + * src/fluid_synth.h (struct _fluid_synth_t): + + * src/fluid_voice.c (fluid_voice_optimize_sample): + +2003-05-29 Markus Nentwig + + * include/fluidsynth/voice.h: added fluid_voice_gen_incr to api + * src/fluidsynth.c: Added error message for command line parameter handling + * src/fluid_voice.c (fluid_voice_optimize_sample): Removed loop peak detection + at run time, because it caused dropouts. Now the sound font loader or application + is responsible to call fluid_voice_optimize_sample (if it doesn't, the turnoff optimization is + simply disabled). + 2003-04-029 Antoine Schmitt * src/fluid_defsfont.c: inst_zone lokey is now properly inialized to 0 diff --git a/fluidsynth/include/fluidsynth/voice.h b/fluidsynth/include/fluidsynth/voice.h index 9cec8f00..13b6b804 100644 --- a/fluidsynth/include/fluidsynth/voice.h +++ b/fluidsynth/include/fluidsynth/voice.h @@ -54,6 +54,8 @@ FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t* voice, int gen, float val /** Get the value of a generator */ FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t* voice, int gen); + /** Modify the value of a generator by val */ +FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); /** Return the unique ID of the noteon-event. A sound font loader @@ -72,6 +74,20 @@ FLUIDSYNTH_API unsigned int fluid_voice_get_id(fluid_voice_t* voice); FLUIDSYNTH_API int fluid_voice_is_playing(fluid_voice_t* voice); + /** If the peak volume during the loop is known, then the voice can + * be released earlier during the release phase. Otherwise, the + * voice will operate (inaudibly), until the envelope is at the + * nominal turnoff point. In many cases the loop volume is many dB + * below the maximum volume. For example, the loop volume for a + * typical acoustic piano is 20 dB below max. Taking that into + * account in the turn-off algorithm we can save 20 dB / 100 dB => + * 1/5 of the total release time. + * So it's a good idea to call fluid_voice_optimize_sample + * on each sample once. + */ + +FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t* s); + #ifdef __cplusplus diff --git a/fluidsynth/src/fluid_defsfont.c b/fluidsynth/src/fluid_defsfont.c index 5bc34c54..71654004 100644 --- a/fluidsynth/src/fluid_defsfont.c +++ b/fluidsynth/src/fluid_defsfont.c @@ -20,9 +20,9 @@ #include "fluid_defsfont.h" +/* Todo: Get rid of that 'include' */ #include "fluid_sys.h" -#include "fluid_voice.h" - + /*************************************************************** * * SFONT LOADER @@ -1381,9 +1381,10 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defs if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) { zone->sample = fluid_defsfont_get_sample(sfont, ((SFSample *) sfzone->instsamp->data)->name); if (zone->sample == NULL) { - FLUID_LOG(FLUID_ERR, "Couldnt fins sample name"); + FLUID_LOG(FLUID_ERR, "Couldn't find sample name"); return FLUID_FAILED; } + fluid_voice_optimize_sample(zone->sample); } /* Import the modulators (only SF2.1 and higher) */ diff --git a/fluidsynth/src/fluid_synth.c b/fluidsynth/src/fluid_synth.c index 8e842be5..f8be8726 100644 --- a/fluidsynth/src/fluid_synth.c +++ b/fluidsynth/src/fluid_synth.c @@ -328,6 +328,8 @@ new_fluid_synth(fluid_settings_t *settings) return NULL; } FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); + + fluid_mutex_init(synth->busy); synth->settings = settings; @@ -670,6 +672,7 @@ delete_fluid_synth(fluid_synth_t* synth) FLUID_FREE(synth->LADSPA_FxUnit); #endif + fluid_mutex_destroy(synth->busy); FLUID_FREE(synth); return FLUID_OK; @@ -695,6 +698,8 @@ fluid_synth_noteon(fluid_synth_t* synth, int chan, int key, int vel) { fluid_channel_t* channel; int r; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); /* check the ranges of the arguments */ if ((chan < 0) || (chan >= synth->midi_channels)) { @@ -747,6 +752,8 @@ fluid_synth_noteoff(fluid_synth_t* synth, int chan, int key) int i; fluid_voice_t* voice; int status = FLUID_FAILED; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; @@ -782,6 +789,8 @@ fluid_synth_damp_voices(fluid_synth_t* synth, int chan) int i; fluid_voice_t* voice; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; if ((voice->chan == chan) && _SUSTAINED(voice)) { @@ -799,6 +808,8 @@ fluid_synth_damp_voices(fluid_synth_t* synth, int chan) int fluid_synth_cc(fluid_synth_t* synth, int chan, int num, int val) { + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); /* check the ranges of the arguments */ if ((chan < 0) || (chan >= synth->midi_channels)) { FLUID_LOG(FLUID_WARN, "Channel out of range"); @@ -924,6 +935,8 @@ fluid_synth_modulate_voices(fluid_synth_t* synth, int chan, int is_cc, int ctrl) int i; fluid_voice_t* voice; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; if (voice->chan == chan) { @@ -945,6 +958,8 @@ fluid_synth_modulate_voices_all(fluid_synth_t* synth, int chan) { int i; fluid_voice_t* voice; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; @@ -962,6 +977,8 @@ int fluid_synth_pitch_bend(fluid_synth_t* synth, int chan, int val) { + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); /* check the ranges of the arguments */ if ((chan < 0) || (chan >= synth->midi_channels)) { FLUID_LOG(FLUID_WARN, "Channel out of range"); @@ -1100,6 +1117,8 @@ fluid_synth_program_change(fluid_synth_t* synth, int chan, int prognum) unsigned int banknum; unsigned int sfont_id; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); if ((prognum >= 0) && (prognum < FLUID_NUM_PROGRAMS) && (chan >= 0) && (chan < synth->midi_channels)) { @@ -1323,6 +1342,8 @@ int fluid_synth_set_reverb_preset(fluid_synth_t* synth, int num) void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double damping, double width, double level) { + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); fluid_revmodel_setroomsize(synth->reverb, roomsize); fluid_revmodel_setdamp(synth->reverb, damping); fluid_revmodel_setwidth(synth->reverb, width); @@ -1335,6 +1356,8 @@ void fluid_synth_set_reverb(fluid_synth_t* synth, double roomsize, double dampin void fluid_synth_set_chorus(fluid_synth_t* synth, int nr, double level, double speed, double depth_ms, int type) { + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); fluid_chorus_set_nr(synth->chorus, nr); fluid_chorus_set_level(synth->chorus, (fluid_real_t)level); fluid_chorus_set_speed_Hz(synth->chorus, (fluid_real_t)speed); @@ -1598,6 +1621,7 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) fluid_real_t* chorus_buf; int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t); double prof_ref = fluid_profile_ref(); + fluid_mutex_lock(synth->busy); /* Here comes the audio thread. Lock the synth. */ fluid_check_fpe("??? Just starting up ???"); @@ -1717,6 +1741,7 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out) {float num=1;while (num != 0){num*=0.5;};}; #endif fluid_check_fpe("??? Remainder of synth_one_block ???"); + fluid_mutex_unlock(synth->busy); /* Allow other threads to touch the synth */ return 0; } @@ -1737,6 +1762,9 @@ fluid_synth_free_voice_by_kill (fluid_synth_t* synth) fluid_voice_t* voice; int best_voice_index=-1; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); + for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; @@ -1809,6 +1837,9 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan, int i, k; fluid_voice_t* voice = NULL; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); + /* If there is another voice process on the same channel and key, advance it to the release phase. */ fluid_synth_release_voice_on_same_note(synth, chan, key); @@ -1935,12 +1966,16 @@ void fluid_synth_kill_by_exclusive_class(fluid_synth_t* synth, fluid_voice_t* ne */ void fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice) { + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); + /* Find the exclusive class of this voice. If set, kill all voices * that match the exclusive class and are younger than the first * voice process created by this noteon event. */ fluid_synth_kill_by_exclusive_class(synth, voice); /* Start the new voice */ + fluid_voice_start(voice); } @@ -2280,6 +2315,9 @@ double fluid_synth_get_reverb_width(fluid_synth_t* synth) void fluid_synth_release_voice_on_same_note(fluid_synth_t* synth, int chan, int key){ int i; fluid_voice_t* voice; + fluid_mutex_lock(synth->busy); /* Don't interfere with the audio thread */ + fluid_mutex_unlock(synth->busy); + for (i = 0; i < synth->nvoice; i++) { voice = synth->voice[i]; if (_PLAYING(voice) diff --git a/fluidsynth/src/fluid_synth.h b/fluidsynth/src/fluid_synth.h index 165881c8..15e81ea3 100644 --- a/fluidsynth/src/fluid_synth.h +++ b/fluidsynth/src/fluid_synth.h @@ -38,6 +38,7 @@ #include "fluid_chorus.h" #include "fluid_ladspa.h" #include "fluid_midi_router.h" +#include "fluid_sys.h" /*************************************************************** * @@ -131,6 +132,10 @@ struct _fluid_synth_t fluid_tuning_t* cur_tuning; /** current tuning in the iteration */ fluid_midi_router_t* midi_router; /* The midi router. Could be done nicer. */ + fluid_mutex_t busy; /* Indicates, whether the audio thread is currently running. + * Note: This simple scheme does -not- provide 100 % protection against + * thread problems, for example from MIDI thread and shell thread + */ #ifdef LADSPA fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /** Effects unit for LADSPA support */ #endif diff --git a/fluidsynth/src/fluid_voice.c b/fluidsynth/src/fluid_voice.c index 9d8140d1..93ecfb63 100644 --- a/fluidsynth/src/fluid_voice.c +++ b/fluidsynth/src/fluid_voice.c @@ -18,9 +18,6 @@ * 02111-1307, USA */ - -int print_pan = 1; - #include "fluidsynth_priv.h" #include "fluid_voice.h" #include "fluid_mod.h" @@ -42,13 +39,16 @@ float interp_coeff_sse_mem[FLUID_INTERP_MAX*4+4]; sse_t* interp_coeff_sse; #endif -/* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ -#define FLUID_NOISE_FLOOR 0.00003 - /* used for filter turn off optimization - if filter cutoff is above the specified value and filter q is below the other value, turn filter off */ #define FLUID_MAX_AUDIBLE_FILTER_FC 16000.0f #define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f + +/* Smallest amplitude that can be perceived (full scale is +/- 0.5) + * 16 bits => 96+4=100 dB dynamic range => 0.00001 + * 0.00001 * 2 is approximately 0.00003 :) + */ +#define FLUID_NOISE_FLOOR 0.00003 /* these should be the absolute minimum that FluidSynth can deal with */ #define FLUID_MIN_LOOP_SIZE 2 @@ -262,8 +262,11 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample, /* For a looped sample, this value will be overwritten as soon as the * loop parameters are initialized (they may depend on modulators). - * For a non-looped sample this is kept.*/ - voice->amplitude_that_reaches_noise_floor = FLUID_NOISE_FLOOR / voice->synth_gain; + * This value can be kept, it is a worst-case estimate. + */ + + voice->amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / voice->synth_gain; + voice->amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / voice->synth_gain; /* Increment the reference count of the sample to prevent the unloading of the soundfont while this voice is playing. */ @@ -498,14 +501,25 @@ fluid_voice_write(fluid_voice_t* voice, /* A voice can be turned off, when an estimate for the volume * (upper bound) falls below that volume, that will drop the - * sample below the noise floor. Start out with assuming a 0 dB - * sample... */ - amplitude_that_reaches_noise_floor = FLUID_NOISE_FLOOR; - if (voice->has_looped) { - /* For the loop we may have a better estimate for the volume of - * the actual sample (from peak detector): */ - amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor; - } + * sample below the noise floor. + */ + + /* If the loop amplitude is known, we can use it if the voice loop is within + * the sample loop + */ +#if 0 + printf("%i %i %i %i %i %i\n", voice->has_looped, voice->sample->amplitude_that_reaches_noise_floor_is_valid, voice->loop_start, voice->loop_end, voice->sample->loopstart, voice->sample->loopend ); +#endif + + /* Is the playing pointer already in the loop? */ + if (voice->has_looped){ + amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_loop; + } else { + amplitude_that_reaches_noise_floor = voice->amplitude_that_reaches_noise_floor_nonloop; + }; +#if 0 + printf("Retrieving %f\n", amplitude_that_reaches_noise_floor); +#endif /* voice->attenuation_min is a lower boundary for the attenuation * now and in the future (possibly 0 in the worst case). Now the @@ -524,7 +538,9 @@ fluid_voice_write(fluid_voice_t* voice, * can safely turn off the voice. Duh. */ if (amp_max < amplitude_that_reaches_noise_floor){ fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref); -/* printf("Voice turned off! Amp is %f\n", amp_max); */ +#if 0 + printf("Voice turned off! Amp is %f\n", amp_max); +#endif fluid_voice_off(voice); goto post_process; } @@ -705,7 +721,7 @@ fluid_voice_write(fluid_voice_t* voice, /* At which index does the loop point occur in the output buffer? * This calculates the first index in the buffer, which uses * sample data taken after the looparound. */ - end_in_buffer = fluid_phase_steps(dsp_phase, voice->loop_end_offset, incr); + end_in_buffer = fluid_phase_steps(dsp_phase, voice->loop_end, incr); if (end_in_buffer >= FLUID_BUFSIZE) { /* The loop occurs after the end of the buffer. @@ -731,11 +747,11 @@ fluid_voice_write(fluid_voice_t* voice, #include "fluid_dsp_core.c" /* loop */ - fluid_phase_sub_int(dsp_phase, voice->loop_end_offset - voice->loop_start_offset); - voice->has_looped=1; + fluid_phase_sub_int(dsp_phase, voice->loop_end - voice->loop_start); start = end_in_buffer; - end_in_buffer += fluid_phase_steps(dsp_phase, voice->loop_end_offset, incr); + end_in_buffer += fluid_phase_steps(dsp_phase, voice->loop_end, incr); } + voice->has_looped=1; dsp_start = start; dsp_end = FLUID_BUFSIZE; #include "fluid_dsp_core.c" @@ -745,7 +761,7 @@ fluid_voice_write(fluid_voice_t* voice, /* Not looping right now. */ dsp_start = 0; - end_in_buffer = fluid_phase_steps(dsp_phase, voice->end_offset, incr); + end_in_buffer = fluid_phase_steps(dsp_phase, voice->sample_end, incr); if (end_in_buffer >= FLUID_BUFSIZE) { /* Run the whole buffer at once */ @@ -813,7 +829,6 @@ void fluid_voice_start(fluid_voice_t* voice) voice->ref = fluid_profile_ref(); - fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(voice); voice->status = FLUID_VOICE_ON; } @@ -1271,7 +1286,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ if (voice->sample != NULL) { - voice->start_offset = (voice->sample->start + voice->sample_start = (voice->sample->start + (int) _GEN(voice, GEN_STARTADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS)); voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; @@ -1280,7 +1295,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ if (voice->sample != NULL) { - voice->end_offset = (voice->sample->end + voice->sample_end = (voice->sample->end + (int) _GEN(voice, GEN_ENDADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS)); voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; @@ -1289,7 +1304,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ if (voice->sample != NULL) { - voice->loop_start_offset = (voice->sample->loopstart + voice->loop_start = (voice->sample->loopstart + (int) _GEN(voice, GEN_STARTLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS)); voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; @@ -1299,7 +1314,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen) case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ if (voice->sample != NULL) { - voice->loop_end_offset = (voice->sample->loopend + voice->loop_end = (voice->sample->loopend + (int) _GEN(voice, GEN_ENDLOOPADDROFS) + 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS)); voice->check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; @@ -1781,91 +1796,6 @@ fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice return lower_bound; } -/* - * fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample - * - * Purpose: - * - * Determines the factor, that will drop the amplitude of the sample's - * loop to the noise floor. Typically, the algorithm is run only once - * for each sample, then the result is cached in the sample structure. - */ -fluid_real_t -fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice) -{ - fluid_sample_t* s = voice->sample; - int voiceloop_differs_from_sampleloop = 0; - fluid_real_t result; - - fluid_check_fpe("voice_determine_amplitude_that_reaches_noise_floor start"); - if (s == NULL || !s->valid){ - /* The voice has no sample. Therefore an arbitrarily high - * amplitude will make the resulting signal drop below the noise - * floor => return a high number */ - return 999.; - } - - /* Modulators can modify the loop parameters of a sample at playing - * time. In this case, the maximum amplitude of the loop will - * probably vary. Usually a voice uses the loop settings from the - * instrument without altering them. Caching works only in this - * case. */ - if (((int)s->loopstart != voice->loop_start_offset) - || ((int) s->loopend != voice->loop_end_offset)) { - voiceloop_differs_from_sampleloop=1; - } - - if (!s->amplitude_that_reaches_noise_floor_is_valid || voiceloop_differs_from_sampleloop) { - signed short peak_max = 0; - signed short peak_min = 0; - signed short peak; - int offset; - - /* SF specs: The first and last sample is identical. Therefore process start .. end-1. */ - for (offset = voice->loop_start_offset; offset < voice->loop_end_offset; offset++){ - signed short val = s->data[offset]; - if (val > peak_max) { - peak_max = val; - } else if (val < peak_min) { - peak_min = val; - } - } - if (peak_max >- peak_min){ - peak = peak_max; - } else { - peak =- peak_min; - } - if (peak == 0){ - /* The sample is empty. Turn off directly when reaching the loop - * by setting a threshold, that is higher than the highest - * possible total gain of 1. Note: not terminating an empty - * sample will crash the DSP loop! */ - result = 999.; - } else { - /* For example: Take a peak of 3277 (10 % of 32768). The - * normalized amplitude is 0.1 (10 % of 32768). An amplitude - * factor of 0.0001 (as opposed to the default 0.00001) will - * drop this sample to the noise floor. - */ - fluid_real_t normalized_amplitude_during_loop=((fluid_real_t)peak)/32768.; - result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; - if (!voiceloop_differs_from_sampleloop){ - /* Cache value in sample */ - s->amplitude_that_reaches_noise_floor = (double)result; - s->amplitude_that_reaches_noise_floor_is_valid = 1; -/* FLUID_LOG(FLUID_DBG, "Loop peak detection: smallest reasonable gain is %f, cached", result); */ - } else { -/* FLUID_LOG(FLUID_DBG, "Loop peak detection: smallest reasonable gain is %f, not cached", result); */ - } - } - } else { - /* Recall cached value from sample*/ - result = (fluid_real_t) s->amplitude_that_reaches_noise_floor; -/* FLUID_LOG(FLUID_DBG, "Loop peak detection: smallest reasonable gain is %f, recalled from cache", result); */ - } - fluid_check_fpe("voice_determine_amplitude_that_reaches_noise_floor"); - return result; -} /* Purpose: * @@ -1889,34 +1819,34 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice) #if 0 printf("Sample from %i to %i\n",voice->sample->start, voice->sample->end); printf("Sample loop from %i %i\n",voice->sample->loopstart, voice->sample->loopend); - printf("Playback from %i to %i\n", voice->start_offset, voice->end_offset); - printf("Playback loop from %i to %i\n",voice->loop_start_offset, voice->loop_end_offset); + printf("Playback from %i to %i\n", voice->sample_start, voice->sample_end); + printf("Playback loop from %i to %i\n",voice->loop_start, voice->loop_end); #endif /* Keep the start point within the sample data */ - if (voice->start_offset < min_index_nonloop){ - voice->start_offset = min_index_nonloop; - } else if (voice->start_offset > max_index_nonloop){ - voice->start_offset = max_index_nonloop; + if (voice->sample_start < min_index_nonloop){ + voice->sample_start = min_index_nonloop; + } else if (voice->sample_start > max_index_nonloop){ + voice->sample_start = max_index_nonloop; } /* Keep the end point within the sample data */ - if (voice->end_offset < min_index_nonloop){ - voice->end_offset = min_index_nonloop; - } else if (voice->end_offset > max_index_nonloop){ - voice->end_offset = max_index_nonloop; + if (voice->sample_end < min_index_nonloop){ + voice->sample_end = min_index_nonloop; + } else if (voice->sample_end > max_index_nonloop){ + voice->sample_end = max_index_nonloop; } /* Keep start and end point in the right order */ - if (voice->start_offset > voice->end_offset){ - int temp = voice->start_offset; - voice->start_offset = voice->end_offset; - voice->end_offset = temp; + if (voice->sample_start > voice->sample_end){ + int temp = voice->sample_start; + voice->sample_start = voice->sample_end; + voice->sample_end = temp; /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ } /* Zero length? */ - if (voice->start_offset == voice->end_offset){ + if (voice->sample_start == voice->sample_end){ fluid_voice_off(voice); return; } @@ -1924,31 +1854,45 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice) if ((_SAMPLEMODE(voice) == FLUID_LOOP) || (_SAMPLEMODE(voice) == FLUID_LOOP_DURING_RELEASE)) { /* Keep the loop start point within the sample data */ - if (voice->loop_start_offset < min_index_loop){ - voice->loop_start_offset = min_index_loop; - } else if (voice->loop_start_offset > max_index_loop){ - voice->loop_start_offset = max_index_loop; + if (voice->loop_start < min_index_loop){ + voice->loop_start = min_index_loop; + } else if (voice->loop_start > max_index_loop){ + voice->loop_start = max_index_loop; } /* Keep the loop end point within the sample data */ - if (voice->loop_end_offset < min_index_loop){ - voice->loop_end_offset = min_index_loop; - } else if (voice->loop_end_offset > max_index_loop){ - voice->loop_end_offset = max_index_loop; + if (voice->loop_end < min_index_loop){ + voice->loop_end = min_index_loop; + } else if (voice->loop_end > max_index_loop){ + voice->loop_end = max_index_loop; } /* Keep loop start and end point in the right order */ - if (voice->loop_start_offset > voice->loop_end_offset){ - int temp=voice->loop_start_offset; - voice->loop_start_offset=voice->loop_end_offset; - voice->loop_end_offset=temp; + if (voice->loop_start > voice->loop_end){ + int temp=voice->loop_start; + voice->loop_start=voice->loop_end; + voice->loop_end=temp; /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ } /* Loop too short? Then don't loop. */ - if (voice->loop_end_offset < voice->loop_start_offset + FLUID_MIN_LOOP_SIZE){ + if (voice->loop_end < voice->loop_start + FLUID_MIN_LOOP_SIZE){ voice->gen[GEN_SAMPLEMODE].val=FLUID_UNLOOPED; } + + /* The loop points may have changed. Obtain a new estimate for the loop volume. */ + /* Is the voice loop within the sample loop? */ + if ((int)voice->loop_start >= (int)voice->sample->loopstart + && (int)voice->loop_end <= (int)voice->sample->loopend){ + /* Is there a valid peak amplitude available for the loop? */ + if (voice->sample->amplitude_that_reaches_noise_floor_is_valid){ + voice->amplitude_that_reaches_noise_floor_loop=voice->sample->amplitude_that_reaches_noise_floor / voice->synth_gain; + } else { + /* Worst case */ + voice->amplitude_that_reaches_noise_floor_loop=voice->amplitude_that_reaches_noise_floor_nonloop; + }; + }; + } /* if sample mode is looped */ /* Run startup specific code (only once, when the voice is started) */ @@ -1962,7 +1906,7 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice) /* Set the initial phase of the voice (using the result from the start offset modulators). */ - fluid_phase_set_int(voice->phase, voice->start_offset); + fluid_phase_set_int(voice->phase, voice->sample_start); } /* if startup */ /* Is this voice run in loop mode, or does it run straight to the @@ -1982,40 +1926,18 @@ void fluid_voice_check_sample_sanity(fluid_voice_t* voice) * actions required. */ int index_in_sample = fluid_phase_index(voice->phase); - if (index_in_sample >= voice->loop_end_offset){ + if (index_in_sample >= voice->loop_end){ /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ - fluid_phase_set_int(voice->phase,voice->loop_start_offset); + fluid_phase_set_int(voice->phase,voice->loop_start); } } -/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start_offset, voice->end_offset, voice->loop_start_offset, voice->loop_end_offset); */ +/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->sample_start, voice->sample_end, voice->loop_start, voice->loop_end); */ - /* If the peak volume during the loop is known, then the voice can - * be released earlier during the release phase. Otherwise, the - * voice will operate (inaudibly), until the envelope is at the - * nominal turnoff point. In many cases the loop volume is many dB - * below the maximum volume. For example, the loop volume for a - * typical acoustic piano is 20 dB below max. Taking that into - * account in the turn-off algorithm we can save 20 dB / 100 dB => - * 1/5 of the total release time. */ -#if 1 - { - fluid_real_t a = fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(voice); - /* if for example the synth has a gain of 0.1, the noise floor - comes up by a factor of 10. */ - voice->amplitude_that_reaches_noise_floor = a / voice->synth_gain; -/* printf("Voice: smallest reasonable voice amplitude is %f\n", voice->amplitude_that_reaches_noise_floor); */ - } -#else - /* Sample-dependent voice-turnoff disabled. Do nothing here. The - * default value for 'voice->amplitude_that_reaches_noise_floor' - * works fine. It's just inefficient.*/ -#endif - /* Sample sanity has been assured. Don't check again, until some sample parameter is changed by modulation. */ voice->check_sample_sanity_flag=0; #if 0 - printf("Sane? playback loop from %i to %i\n",voice->loop_start_offset, voice->loop_end_offset); + printf("Sane? playback loop from %i to %i\n",voice->loop_start, voice->loop_end); #endif fluid_check_fpe("voice_check_sample_sanity"); } @@ -2043,3 +1965,61 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain) return FLUID_OK; } + +/* - Scan the loop + * - determine the peak level + * - Calculate, what factor will make the loop inaudible + * - Store in sample + */ +int fluid_voice_optimize_sample(fluid_sample_t* s) +{ + signed short peak_max = 0; + signed short peak_min = 0; + signed short peak; + double result; + int i; + + if (!s->amplitude_that_reaches_noise_floor_is_valid){ /* Only once */ + /* Scan the loop */ + for (i = (int)s->loopstart; i < (int) s->loopend; i ++){ + signed short val = s->data[i]; + if (val > peak_max) { + peak_max = val; + } else if (val < peak_min) { + peak_min = val; + } + } + + /* Determine the peak level */ + if (peak_max >- peak_min){ + peak = peak_max; + } else { + peak =- peak_min; + }; + if (peak == 0){ + /* Avoid division by zero */ + peak = 1; + }; + + /* Calculate what factor will make the loop inaudible + * For example: Take a peak of 3277 (10 % of 32768). The + * normalized amplitude is 0.1 (10 % of 32768). An amplitude + * factor of 0.0001 (as opposed to the default 0.00001) will + * drop this sample to the noise floor. + */ + + /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ + fluid_real_t normalized_amplitude_during_loop=((fluid_real_t)peak)/32768.; + result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; + + /* Store in sample */ + s->amplitude_that_reaches_noise_floor = (double)result; + s->amplitude_that_reaches_noise_floor_is_valid = 1; +#if 0 + printf("Sample peak detection: factor %f\n", (double)result); +#endif + }; + return FLUID_OK; +} + + diff --git a/fluidsynth/src/fluid_voice.h b/fluidsynth/src/fluid_voice.h index 6f691710..062aee22 100644 --- a/fluidsynth/src/fluid_voice.h +++ b/fluidsynth/src/fluid_voice.h @@ -106,10 +106,10 @@ struct _fluid_voice_t fluid_real_t root_pitch; /* sample and loop start and end points (offset in sample memory) */ - int start_offset; - int end_offset; - int loop_start_offset; - int loop_end_offset; + int sample_start; + int sample_end; + int loop_start; + int loop_end; /* master gain */ fluid_real_t synth_gain; @@ -119,7 +119,8 @@ struct _fluid_voice_t unsigned int volenv_count; int volenv_section; fluid_real_t volenv_val; - fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; /* mod env */ fluid_env_data_t modenv_data[FLUID_VOICE_ENVLAST]; @@ -230,7 +231,6 @@ int fluid_voice_kill_excl(fluid_voice_t* voice); fluid_real_t fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice); fluid_real_t fluid_voice_determine_amplitude_that_reaches_noise_floor_for_sample(fluid_voice_t* voice); void fluid_voice_check_sample_sanity(fluid_voice_t* voice); -void fluid_voice_gen_incr(fluid_voice_t* voice, int gen, float val); #define fluid_voice_set_id(_voice, _id) { (_voice)->id = (_id); } #define fluid_voice_get_chan(_voice) (_voice)->chan diff --git a/fluidsynth/src/fluidsynth.c b/fluidsynth/src/fluidsynth.c index db9d7437..5289af3e 100644 --- a/fluidsynth/src/fluidsynth.c +++ b/fluidsynth/src/fluidsynth.c @@ -119,18 +119,27 @@ void process_o_cmd_line_option(fluid_settings_t* settings, char* optarg){ } } - /* Don't know the type of the setting in question. So try - * all types. The wrong types will fail. + /* At this point: + * optarg => "synth.polyphony" + * val => "16" */ - if (fluid_settings_setnum(settings, optarg, atof(val))){ - printf("set %s to %s (float type)\n", optarg, val); - } else if (fluid_settings_setint(settings, optarg, atoi(val))){ - printf("set %s to %s (int type)\n", optarg, val); - } else if (fluid_settings_setstr(settings, optarg, val)){ - printf("set %s to %s (string type)\n", optarg, val); - } else { - printf("Failed to set %s to %s\n", optarg, val); - }; + switch(fluid_settings_get_type(settings, optarg)){ + case FLUID_NUM_TYPE: + if (fluid_settings_setnum(settings, optarg, atof(val))){ + break; + }; + case FLUID_INT_TYPE: + if (fluid_settings_setint(settings, optarg, atoi(val))){ + break; + }; + case FLUID_STR_TYPE: + if (fluid_settings_setstr(settings, optarg, val)){ + break; + }; + default: + fprintf (stderr, "Settings argument on command line: Failed to set \"%s\" to \"%s\".\n" + "Most likely the parameter \"%s\" does not exist.\n", optarg, val, optarg); + } }