Noteoff optimization fixed, mutex in iiwu_synth, ...

This commit is contained in:
Markus Nentwig 2003-05-29 15:43:52 +00:00
parent 82b1c742c9
commit 75a32f5c75
8 changed files with 254 additions and 187 deletions

View file

@ -1,3 +1,21 @@
2003-05-29 root <mn@bongo>
* 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 <nentwig@users.sourceforge.net>
* 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 <as@gratin.org>
* src/fluid_defsfont.c: inst_zone lokey is now properly inialized to 0

View file

@ -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

View file

@ -20,8 +20,8 @@
#include "fluid_defsfont.h"
/* Todo: Get rid of that 'include' */
#include "fluid_sys.h"
#include "fluid_voice.h"
/***************************************************************
*
@ -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) */

View file

@ -329,6 +329,8 @@ new_fluid_synth(fluid_settings_t *settings)
}
FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t));
fluid_mutex_init(synth->busy);
synth->settings = settings;
synth->with_reverb = fluid_settings_str_equal(settings, "synth.reverb.active", "yes");
@ -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)

View file

@ -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

View file

@ -18,9 +18,6 @@
* 02111-1307, USA
*/
int print_pan = 1;
#include "fluidsynth_priv.h"
#include "fluid_voice.h"
#include "fluid_mod.h"
@ -42,14 +39,17 @@ 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
#define FLUID_MIN_LOOP_PAD 1
@ -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); */
/* 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
/* 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); */
/* 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;
}

View file

@ -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

View file

@ -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"
*/
switch(fluid_settings_get_type(settings, optarg)){
case FLUID_NUM_TYPE:
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);
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);
}
}