Prevent samples accidentally having their loops disabled (#1018)

If a SoundFont sets `loopstart == loopend` and then uses loop-offset-modulators to fix up those loops assigning them with a valid position, the sample was previously switched to unlooped mode erroneously.

For the long story, see #1017.
This commit is contained in:
Tom M 2022-01-16 14:35:53 +01:00 committed by GitHub
parent b7a0264459
commit e3d8b3f2c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 10 deletions

View file

@ -376,6 +376,7 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat
fluid_sample_t *sample;
int sf3_file = (sfdata->version.major == 3);
int sample_parsing_result = FLUID_OK;
int invalid_loops_were_sanitized = FALSE;
/* For SF2 files, we load the sample data in one large block */
if(!sf3_file)
@ -404,7 +405,7 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat
{
/* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format
* anyway */
#pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result) default(none)
#pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result, invalid_loops_were_sanitized) default(none)
{
if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED)
{
@ -416,24 +417,46 @@ int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdat
}
else
{
fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
int modified = fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
if(modified)
{
#pragma omp critical
{
invalid_loops_were_sanitized = TRUE;
}
}
fluid_voice_optimize_sample(sample);
}
}
}
else
{
#pragma omp task firstprivate(sample, defsfont) default(none)
#pragma omp task firstprivate(sample, defsfont) shared(invalid_loops_were_sanitized) default(none)
{
int modified;
/* Data pointers of SF2 samples point to large sample data block loaded above */
sample->data = defsfont->sampledata;
sample->data24 = defsfont->sample24data;
fluid_sample_sanitize_loop(sample, defsfont->samplesize);
modified = fluid_sample_sanitize_loop(sample, defsfont->samplesize);
if(modified)
{
#pragma omp critical
{
invalid_loops_were_sanitized = TRUE;
}
}
fluid_voice_optimize_sample(sample);
}
}
}
if(invalid_loops_were_sanitized)
{
FLUID_LOG(FLUID_WARN,
"Some invalid sample loops were sanitized! If you experience audible glitches, "
"start fluidsynth in verbose mode for detailed information.");
}
return sample_parsing_result;
}

View file

@ -789,12 +789,19 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
if(sample->loopstart == sample->loopend)
{
/* Some SoundFonts disable loops by setting loopstart = loopend. While
* technically invalid, we decided to accept those samples anyway. Just
* ensure that those two pointers are within the sampledata by setting
* them to 0. Don't report the modification, as this change has no audible
* effect. */
sample->loopstart = sample->loopend = 0;
return FALSE;
* technically invalid, we decided to accept those samples anyway.
* Before fluidsynth 2.2.5 we've set those indices to zero, as this
* change was believed to be inaudible. This turned out to be an
* incorrect assumption, as the loop points may still be modified by
* loop offset modulators afterwards.
*/
if(sample->loopstart != sample->start)
{
// Many soundfonts set loopstart == loopend == sample->start to disabled to loop.
// Only report cases where it's not equal to the sample->start, to avoid spam.
FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway",
sample->name, sample->loopstart, sample->start);
}
}
else if(sample->loopstart > sample->loopend)
{