From a50680aafa449cdd17c25a40441bf93ae6f3793c Mon Sep 17 00:00:00 2001 From: Tom M Date: Mon, 27 Jun 2022 17:26:15 +0200 Subject: [PATCH] Add warnings and extend documentation for issue #1120 (#1126) --- doc/fluidsettings.xml | 2 +- include/fluidsynth/synth.h | 12 ++++++++++++ src/drivers/fluid_adriver.c | 15 ++++++++++++++- src/synth/fluid_synth.c | 3 ++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index f2bf8c72..3f4c79e2 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -418,7 +418,7 @@ and commit the results. Refresh with the following command: 64 8192 - The size of the audio buffers (in frames). + This is the number of audio samples most audio drivers will request from the synth at one time. In other words, it's the amount of samples the synth is allowed to render in one go when no state changes (events) are about to happen. Because of that, specifying too big numbers here may cause MIDI events to be poorly quantized (=untimed) when a MIDI driver or the synth's API directly is used, as fluidsynth cannot determine when those events are to arrive. This issue does not matter, when using the MIDI player or the MIDI sequencer, because in this case, fluidsynth does know when events will be received. diff --git a/include/fluidsynth/synth.h b/include/fluidsynth/synth.h index b8d0b0ab..8944e6a4 100644 --- a/include/fluidsynth/synth.h +++ b/include/fluidsynth/synth.h @@ -331,6 +331,18 @@ FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int p * render real-time audio, ensure that you call these functions from a high-priority * thread with little to no other duties other than calling the rendering functions. * + * @warning + * If a concurrently running thread calls any other sound affecting synth function + * (e.g. fluid_synth_noteon(), fluid_synth_cc(), etc.) it is unspecified whether the event triggered by such a call + * will be effective in the recently synthesized audio. While this is inaudible when only requesting small chunks from the + * synth with every call (cf. fluid_synth_get_internal_bufsize()), it will become evident when requesting larger sample chunks: + * With larger sample chunks it will get harder for the synth to react on those spontaneously occurring events in time + * (like events received from a MIDI driver, or directly made synth API calls). + * In those real-time scenarios, prefer requesting smaller + * sample chunks from the synth with each call, to avoid poor quantization of your events in the synthesized audio. + * This issue is not applicable when using the MIDI player or sequencer for event dispatching. Also + * refer to the documentation of \setting{audio_period-size}. + * * @{ */ FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len, diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c index 1de0380a..edc05f52 100644 --- a/src/drivers/fluid_adriver.c +++ b/src/drivers/fluid_adriver.c @@ -323,7 +323,20 @@ new_fluid_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) if(def) { - fluid_audio_driver_t *driver = (*def->new)(settings, synth); + fluid_audio_driver_t *driver; + double srate, midi_event_latency; + int period_size; + + fluid_settings_getint(settings, "audio.period-size", &period_size); + fluid_settings_getnum(settings, "synth.sample-rate", &srate); + + midi_event_latency = period_size / srate; + if(midi_event_latency >= 0.05) + { + FLUID_LOG(FLUID_WARN, "You have chosen 'audio.period-size' to be %d samples. Given a sample rate of %.1f this results in a latency of %.1f ms, which will cause MIDI events to be poorly quantized (=untimed) in the synthesized audio (also known as the 'drunken-drummer' syndrome). To avoid that, you're strongly advised to increase 'audio.periods' instead, while keeping 'audio.period-size' small enough to make this warning disappear.", period_size, srate, midi_event_latency*1000.0); + } + + driver = (*def->new)(settings, synth); if(driver) { diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index 68ce0965..7d058258 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -3717,7 +3717,8 @@ fluid_synth_get_active_voice_count(fluid_synth_t *synth) * @param synth FluidSynth instance * @return Internal buffer size in audio frames. * - * Audio is synthesized this number of frames at a time. Defaults to 64 frames. + * Audio is synthesized at this number of frames at a time. Defaults to 64 frames. I.e. the synth can only react to notes, + * control changes, and other audio affecting events after having processed 64 audio frames. */ int fluid_synth_get_internal_bufsize(fluid_synth_t *synth)