mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-01-19 07:50:49 +00:00
Noteoff optimization fixed, mutex in iiwu_synth, ...
This commit is contained in:
parent
82b1c742c9
commit
75a32f5c75
8 changed files with 254 additions and 187 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue