Merge branch '2.2.x' into master

This commit is contained in:
derselbst 2022-06-27 17:43:09 +02:00
commit 24898c126d
11 changed files with 125 additions and 42 deletions

View File

@ -53,6 +53,7 @@ variables:
# This is a symlink pointing to the real Android NDK # This is a symlink pointing to the real Android NDK
# Must be the same as $ANDROID_NDK_HOME see: # Must be the same as $ANDROID_NDK_HOME see:
# https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md
# We cannot use $ANDROID_NDK_HOME because this is an environment variable, but here, we need a compile-time constant.
NDK: '/usr/local/lib/android/sdk/ndk-bundle' NDK: '/usr/local/lib/android/sdk/ndk-bundle'
# All the built binaries, libs and their headers will be installed here # All the built binaries, libs and their headers will be installed here
@ -210,6 +211,11 @@ jobs:
displayName: 'apt-get install build-tools' displayName: 'apt-get install build-tools'
condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true')) condition: and(succeeded(), ne(variables.CACHE_RESTORED, 'true'))
- script: |
set -ex
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
displayName: 'Use NDK r22'
- script: | - script: |
set -e set -e

View File

@ -62,7 +62,6 @@ jobs:
echo Can execute `nproc` make jobs in parallel echo Can execute `nproc` make jobs in parallel
cmake --version cmake --version
cmake -E make_directory ${{github.workspace}}/build cmake -E make_directory ${{github.workspace}}/build
sudo ln -s /usr/bin/clang-tidy-10 /usr/bin/clang-tidy
which clang-tidy || true which clang-tidy || true
ls -la `which clang-tidy` || true ls -la `which clang-tidy` || true
echo $PATH echo $PATH

View File

@ -413,7 +413,7 @@ Developers:
<min>64</min> <min>64</min>
<max>8192</max> <max>8192</max>
<desc> <desc>
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.
</desc> </desc>
</setting> </setting>
<setting> <setting>

View File

@ -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 * 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. * 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, FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len,

View File

@ -333,7 +333,20 @@ new_fluid_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
if(def) 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) if(driver)
{ {

View File

@ -1347,6 +1347,26 @@ fluid_alsa_seq_run(void *d)
} }
break; break;
case SND_SEQ_EVENT_START:
evt.type = MIDI_START;
break;
case SND_SEQ_EVENT_CONTINUE:
evt.type = MIDI_CONTINUE;
break;
case SND_SEQ_EVENT_STOP:
evt.type = MIDI_STOP;
break;
case SND_SEQ_EVENT_CLOCK:
evt.type = MIDI_SYNC;
break;
case SND_SEQ_EVENT_RESET:
evt.type = MIDI_SYSTEM_RESET;
break;
default: default:
continue; /* unhandled event, next loop iteration */ continue; /* unhandled event, next loop iteration */
} }

View File

@ -83,12 +83,12 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
pa_sample_spec samplespec; pa_sample_spec samplespec;
pa_buffer_attr bufattr; pa_buffer_attr bufattr;
double sample_rate; double sample_rate;
int period_size, period_bytes, adjust_latency; int period_size, period_bytes, adjust_latency, periods;
char *server = NULL; char *server = NULL;
char *device = NULL; char *device = NULL;
char *media_role = NULL; char *media_role = NULL;
int realtime_prio = 0; int realtime_prio = 0;
int err; int err = 0;
float *left = NULL, float *left = NULL,
*right = NULL, *right = NULL,
*buf = NULL; *buf = NULL;
@ -103,6 +103,7 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
FLUID_MEMSET(dev, 0, sizeof(fluid_pulse_audio_driver_t)); FLUID_MEMSET(dev, 0, sizeof(fluid_pulse_audio_driver_t));
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getint(settings, "audio.period-size", &period_size);
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_dupstr(settings, "audio.pulseaudio.server", &server); /* ++ alloc server string */ fluid_settings_dupstr(settings, "audio.pulseaudio.server", &server); /* ++ alloc server string */
@ -143,7 +144,7 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
samplespec.rate = sample_rate; samplespec.rate = sample_rate;
period_bytes = period_size * sizeof(float) * 2; period_bytes = period_size * sizeof(float) * 2;
bufattr.maxlength = adjust_latency ? -1 : period_bytes; bufattr.maxlength = adjust_latency ? -1 : period_bytes * periods;
bufattr.tlength = period_bytes; bufattr.tlength = period_bytes;
bufattr.minreq = -1; bufattr.minreq = -1;
bufattr.prebuf = -1; /* Just initialize to same value as tlength */ bufattr.prebuf = -1; /* Just initialize to same value as tlength */
@ -155,9 +156,9 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
&bufattr, &bufattr,
&err); &err);
if(!dev->pa_handle) if(!dev->pa_handle || err != PA_OK)
{ {
FLUID_LOG(FLUID_ERR, "Failed to create PulseAudio connection"); FLUID_LOG(FLUID_ERR, "Failed to create PulseAudio connection, because pa_simple_new() failed with error: %s", pa_strerror(err));
goto error_recovery; goto error_recovery;
} }
@ -176,6 +177,7 @@ new_fluid_pulse_audio_driver2(fluid_settings_t *settings,
} }
buf = FLUID_ARRAY(float, period_size * 2); buf = FLUID_ARRAY(float, period_size * 2);
if(buf == NULL) if(buf == NULL)
{ {
FLUID_LOG(FLUID_ERR, "Out of memory."); FLUID_LOG(FLUID_ERR, "Out of memory.");
@ -242,10 +244,8 @@ fluid_pulse_audio_run(void *d)
{ {
fluid_pulse_audio_driver_t *dev = (fluid_pulse_audio_driver_t *) d; fluid_pulse_audio_driver_t *dev = (fluid_pulse_audio_driver_t *) d;
float *buf = dev->buf; float *buf = dev->buf;
int buffer_size; int buffer_size = dev->buffer_size;
int err; int err = 0;
buffer_size = dev->buffer_size;
while(dev->cont) while(dev->cont)
{ {
@ -254,9 +254,20 @@ fluid_pulse_audio_run(void *d)
if(pa_simple_write(dev->pa_handle, buf, if(pa_simple_write(dev->pa_handle, buf,
buffer_size * sizeof(float) * 2, &err) < 0) buffer_size * sizeof(float) * 2, &err) < 0)
{ {
FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection."); FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection: %s", pa_strerror(err));
break; break;
} }
#if 0
{
pa_usec_t pa_latency = pa_simple_get_latency(dev->pa_handle, &err);
if(err == PA_OK)
{
FLUID_LOG(FLUID_DBG, "PulseAudio latency: %d ms", (int) pa_latency / 1000);
}
}
#endif
} /* while (dev->cont) */ } /* while (dev->cont) */
return FLUID_THREAD_RETURN_VALUE; return FLUID_THREAD_RETURN_VALUE;
@ -271,12 +282,10 @@ fluid_pulse_audio_run2(void *d)
*right = dev->right, *right = dev->right,
*buf = dev->buf; *buf = dev->buf;
float *handle[2]; float *handle[2];
int buffer_size; int buffer_size = dev->buffer_size;
int err; int err = 0;
int i; int i;
buffer_size = dev->buffer_size;
handle[0] = left; handle[0] = left;
handle[1] = right; handle[1] = right;
@ -297,9 +306,20 @@ fluid_pulse_audio_run2(void *d)
if(pa_simple_write(dev->pa_handle, buf, if(pa_simple_write(dev->pa_handle, buf,
buffer_size * sizeof(float) * 2, &err) < 0) buffer_size * sizeof(float) * 2, &err) < 0)
{ {
FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection."); FLUID_LOG(FLUID_ERR, "Error writing to PulseAudio connection: %s", pa_strerror(err));
break; break;
} }
#if 0
{
pa_usec_t pa_latency = pa_simple_get_latency(dev->pa_handle, &err);
if(err == PA_OK)
{
FLUID_LOG(FLUID_DBG, "PulseAudio latency: %d ms", (int) pa_latency / 1000);
}
}
#endif
} /* while (dev->cont) */ } /* while (dev->cont) */
return FLUID_THREAD_RETURN_VALUE; return FLUID_THREAD_RETURN_VALUE;

View File

@ -145,6 +145,8 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
break; break;
case MIM_DATA: case MIM_DATA:
if(msg_param < 0xF0) /* Voice category message */
{
event.type = msg_type(msg_param); event.type = msg_type(msg_param);
event.channel = msg_chan(msg_param) + dev_infos->channel_map; event.channel = msg_chan(msg_param) + dev_infos->channel_map;
@ -161,6 +163,11 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param); event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param);
event.param2 = 0; event.param2 = 0;
} }
}
else /* System message */
{
event.type = msg_param;
}
(*dev->driver.handler)(dev->driver.data, &event); (*dev->driver.handler)(dev->driver.data, &event);
break; break;

View File

@ -899,13 +899,13 @@ int main(int argc, char **argv)
if(config_file == NULL) if(config_file == NULL)
{ {
config_file = fluid_get_userconf(buf, sizeof(buf)); config_file = fluid_get_userconf(buf, sizeof(buf));
if(config_file == NULL) if(config_file == NULL || !g_file_test(config_file, G_FILE_TEST_EXISTS))
{ {
config_file = fluid_get_sysconf(buf, sizeof(buf)); config_file = fluid_get_sysconf(buf, sizeof(buf));
} }
/* if the automatically selected command file does not exist, do not even attempt to open it */ /* if the automatically selected command file does not exist, do not even attempt to open it */
if(!fluid_file_test(config_file, FLUID_FILE_TEST_EXISTS)) if(config_file != NULL && !fluid_file_test(config_file, FLUID_FILE_TEST_EXISTS))
{ {
config_file = NULL; config_file = NULL;
} }

View File

@ -2336,6 +2336,12 @@ static void fluid_player_update_tempo(fluid_player_t *player)
int tempo; /* tempo in micro seconds by quarter note */ int tempo; /* tempo in micro seconds by quarter note */
float deltatime; float deltatime;
/* do nothing if the division is still unknown to avoid a div by zero */
if(player->division == 0)
{
return;
}
if(fluid_atomic_int_get(&player->sync_mode)) if(fluid_atomic_int_get(&player->sync_mode))
{ {
/* take internal tempo from MIDI file */ /* take internal tempo from MIDI file */
@ -2400,6 +2406,10 @@ static void fluid_player_update_tempo(fluid_player_t *player)
* #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal * #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal
* tempo. * tempo.
* *
* @warning If the function is called when no MIDI file is loaded or currently playing, it
* would have caused a division by zero in fluidsynth 2.2.7 and earlier. Starting with 2.2.8, the
* new tempo change will be stashed and applied later.
*
* @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters). * @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters).
* @since 2.2.0 * @since 2.2.0
*/ */
@ -2641,17 +2651,12 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
/* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle
* of another message. */ * of another message. */
if(c >= 0xF8) if(c >= 0xF8)
{
if(c == MIDI_SYSTEM_RESET)
{ {
parser->event.type = c; parser->event.type = c;
parser->status = 0; /* clear the status */ parser->status = 0; /* clear the status */
return &parser->event; return &parser->event;
} }
return NULL;
}
/* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */
if(c & 0x80) if(c & 0x80)
{ {

View File

@ -3717,7 +3717,8 @@ fluid_synth_get_active_voice_count(fluid_synth_t *synth)
* @param synth FluidSynth instance * @param synth FluidSynth instance
* @return Internal buffer size in audio frames. * @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 int
fluid_synth_get_internal_bufsize(fluid_synth_t *synth) fluid_synth_get_internal_bufsize(fluid_synth_t *synth)