From 8e782637d61ad2fb35d401e4b6893ca1321e8bfb Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Sun, 4 Apr 2021 03:51:56 +0300
Subject: [PATCH] fluidsynth: respect library samplerate limits and fix sample
conversion
sample retrieval and AudioCVT code copied over from music_ogg.
fixes: https://bugzilla.libsdl.org/show_bug.cgi?id=3969 (corresponding
github bug: https://github.com/libsdl-org/SDL_mixer/issues/240)
---
CHANGES | 1 +
music_fluidsynth.c | 85 ++++++++++++++++++++++++++++++----------------
music_fluidsynth.h | 5 ++-
3 files changed, 60 insertions(+), 31 deletions(-)
diff --git a/CHANGES b/CHANGES
index 049ba34..fd3f8ad 100644
--- a/CHANGES
+++ b/CHANGES
@@ -26,6 +26,7 @@
- Native Midi (macosx): use newer apis with newer macOS (bug #1566)
- Native Midi (macosx): set volume before start playing (bug #5356)
- Native Midi (windows): fixed possible crash.
+- Midi (fluidsyth): respect synth.sample-rate limits (bug #3969)
- Midi (fluidsyth): fixed loading MIDI music leaks memory (bug #3018)
- Midi (fluidsyth): fixed calling Mix_Quit twice leading to segfault
(bug #2004)
diff --git a/music_fluidsynth.c b/music_fluidsynth.c
index 8702c0d..c5b2a58 100644
--- a/music_fluidsynth.c
+++ b/music_fluidsynth.c
@@ -34,6 +34,8 @@ static Uint16 format;
static Uint8 channels;
static int freq;
+#define CVT_BUFSIZE 4096
+
int SDLCALL fluidsynth_check_soundfont(const char *path, void *data)
{
FILE *file = fopen(path, "r");
@@ -72,6 +74,7 @@ static FluidSynthMidiSong *fluidsynth_loadsong_common(SDL_RWops *rw)
off_t rw_offset;
size_t rw_size;
char *rw_mem;
+ int src_freq;
int ret;
if (!Mix_Init(MIX_INIT_FLUIDSYNTH)) {
@@ -85,17 +88,30 @@ static FluidSynthMidiSong *fluidsynth_loadsong_common(SDL_RWops *rw)
SDL_memset(song, 0, sizeof(FluidSynthMidiSong));
- if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) < 0) {
+ /* fluidsynth limits: */
+ src_freq = freq;
+ if (src_freq < 8000) {
+ src_freq = 8000;
+ }
+ else if (src_freq > 96000) {
+ src_freq = 48000;
+ }
+ if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16SYS, 2, src_freq, format, channels, freq) < 0) {
Mix_SetError("Failed to set up audio conversion");
goto fail;
}
+ song->convert.buf = SDL_malloc(CVT_BUFSIZE * song->convert.len_mult);
+ if (!song->convert.buf) {
+ Mix_SetError("Insufficient memory for song");
+ goto fail;
+ }
if (!(song->settings = fluidsynth.new_fluid_settings())) {
Mix_SetError("Failed to create FluidSynth settings");
goto fail;
}
- fluidsynth.fluid_settings_setnum(song->settings, "synth.sample-rate", (double)freq);
+ fluidsynth.fluid_settings_setnum(song->settings, "synth.sample-rate", (double)src_freq);
if (!(song->synth = fluidsynth.new_fluid_synth(song->settings))) {
Mix_SetError("Failed to create FluidSynth synthesizer");
@@ -167,6 +183,7 @@ void fluidsynth_freesong(FluidSynthMidiSong *song)
if (song->settings) {
fluidsynth.delete_fluid_settings(song->settings);
}
+ SDL_free(song->convert.buf);
SDL_free(song);
}
@@ -174,11 +191,13 @@ void fluidsynth_start(FluidSynthMidiSong *song)
{
fluidsynth.fluid_player_set_loop(song->player, 1);
fluidsynth.fluid_player_play(song->player);
+ song->playing = 1;
}
void fluidsynth_stop(FluidSynthMidiSong *song)
{
fluidsynth.fluid_player_stop(song->player);
+ song->playing = 0;
}
int fluidsynth_active(FluidSynthMidiSong *song)
@@ -192,43 +211,49 @@ void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume)
fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
}
-int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len)
+static void fluid_get_samples(FluidSynthMidiSong *song)
{
- int result = -1;
- int frames = dest_len / channels / ((format & 0xFF) / 8);
- int src_len = frames * 4; /* 16-bit stereo */
- void *src = dest;
-
- if (dest_len < src_len) {
- if (!(src = SDL_malloc(src_len))) {
- Mix_SetError("Insufficient memory for audio conversion");
- return result;
- }
- }
+ Uint8 data[CVT_BUFSIZE];
+ SDL_AudioCVT *cvt;
- if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
+ if (fluidsynth.fluid_synth_write_s16(song->synth, CVT_BUFSIZE / 4, data, 0, 2, data, 1, 2) != FLUID_OK) {
+ fluidsynth_stop(song);
Mix_SetError("Error generating FluidSynth audio");
- goto finish;
+ return;
}
- song->convert.buf = src;
- song->convert.len = src_len;
-
- if (SDL_ConvertAudio(&song->convert) < 0) {
- Mix_SetError("Error during audio conversion");
- goto finish;
+ cvt = &song->convert;
+ memcpy(cvt->buf, data, CVT_BUFSIZE);
+ if (cvt->needed) {
+ cvt->len = CVT_BUFSIZE;
+ SDL_ConvertAudio(cvt);
+ } else {
+ cvt->len_cvt = CVT_BUFSIZE;
}
+ song->len_available = cvt->len_cvt;
+ song->snd_available = cvt->buf;
+}
- if (src != dest)
- memcpy(dest, src, dest_len);
-
- result = 0;
+int fluidsynth_playsome(FluidSynthMidiSong *song, Uint8 *dest, int len)
+{
+ int mixable;
-finish:
- if (src != dest)
- SDL_free(src);
+ while (len > 0 && song->playing) {
+ if (!song->len_available) {
+ fluid_get_samples(song);
+ }
+ mixable = len;
+ if (mixable > song->len_available) {
+ mixable = song->len_available;
+ }
+ memcpy(dest, song->snd_available, mixable);
+ song->len_available -= mixable;
+ song->snd_available += mixable;
+ len -= mixable;
+ dest += mixable;
+ }
- return result;
+ return len;
}
#endif /* USE_FLUIDSYNTH_MIDI */
diff --git a/music_fluidsynth.h b/music_fluidsynth.h
index 8c01e61..d053bac 100644
--- a/music_fluidsynth.h
+++ b/music_fluidsynth.h
@@ -36,6 +36,9 @@ typedef struct {
fluid_synth_t *synth;
fluid_settings_t *settings;
fluid_player_t* player;
+ int len_available;
+ Uint8 *snd_available;
+ int playing;
} FluidSynthMidiSong;
int fluidsynth_init(SDL_AudioSpec *mixer);
@@ -45,7 +48,7 @@ void fluidsynth_start(FluidSynthMidiSong *song);
void fluidsynth_stop(FluidSynthMidiSong *song);
int fluidsynth_active(FluidSynthMidiSong *song);
void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume);
-int fluidsynth_playsome(FluidSynthMidiSong *song, void *stream, int len);
+int fluidsynth_playsome(FluidSynthMidiSong *song, Uint8 *stream, int len);
#endif /* USE_FLUIDSYNTH_MIDI */