From e12630e7575d3a85a4c545a40024ae86faa79fce Mon Sep 17 00:00:00 2001 From: Tasos Sahanidis Date: Mon, 17 Mar 2025 03:01:11 +0200 Subject: [PATCH] native_midi_linux_alsa: Add support for System Exclusive messages --- .../native_midi/native_midi_linux_alsa.c | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/codecs/native_midi/native_midi_linux_alsa.c b/src/codecs/native_midi/native_midi_linux_alsa.c index e7d482312..219838170 100644 --- a/src/codecs/native_midi/native_midi_linux_alsa.c +++ b/src/codecs/native_midi/native_midi_linux_alsa.c @@ -42,6 +42,8 @@ #include #include +#include + //#define SDL_NATIVE_MIDI_ALSA_DYNAMIC "libasound.so.2" static int load_alsa_syms(void); @@ -438,7 +440,7 @@ static NativeMidiSong *currentsong = NULL; NativeMidiSong *native_midi_loadsong_IO(SDL_IOStream *src, bool closeio) { NativeMidiSong *song; - MIDIEvent *end; + MIDIEvent *event; int sv[2]; if (!(song = SDL_calloc(1, sizeof(NativeMidiSong)))) { @@ -455,7 +457,7 @@ NativeMidiSong *native_midi_loadsong_IO(SDL_IOStream *src, bool closeio) song->mainsock = sv[0]; song->threadsock = sv[1]; - end = song->evtlist = CreateMIDIEventList(src, &song->ppqn); + event = song->evtlist = CreateMIDIEventList(src, &song->ppqn); if (!song->evtlist) { close_sockpair(song); @@ -464,6 +466,37 @@ NativeMidiSong *native_midi_loadsong_IO(SDL_IOStream *src, bool closeio) return NULL; } + /* Since ALSA requires the starting F0 for SysEx, but MIDIEvent.extraData doesn't contain it, we must preprocess the list */ + /* In addition, since we're going through the list, store the last event's time for looping purposes */ + do { + /* Is this a SysEx? */ + if (event->status == MIDI_CMD_COMMON_SYSEX && event->extraLen) { + /* Sanity check in case something changes in the future */ + /* This is safe to do since we can't have an F0 manufacturer ID */ + assert(event->extraData[0] != MIDI_CMD_COMMON_SYSEX); + + /* Resize by + 1 */ + Uint8 *newData = SDL_realloc(event->extraData, event->extraLen + 1); + if (newData == NULL) { + close_sockpair(song); + /* Original allocation is still valid on failure */ + FreeMIDIEventList(song->evtlist); + SDL_free(song); + MIDI_SET_ERROR("Failed to preprocess MIDIEventList SysEx"); + return NULL; + } + + /* Prepend the F0 */ + event->extraData = newData; + SDL_memmove(event->extraData + 1, event->extraData, event->extraLen); + event->extraData[0] = MIDI_CMD_COMMON_SYSEX; + event->extraLen++; + } + + /* Store the end time */ + song->endtime = event->time; + } while ((event = event->next)); + if (!(song->seq = open_seq(&song->srcport))) { FreeMIDIEventList(song->evtlist); close_sockpair(song); @@ -478,11 +511,6 @@ NativeMidiSong *native_midi_loadsong_IO(SDL_IOStream *src, bool closeio) SDL_SetAtomicInt(&song->playerstate, NATIVE_MIDI_STOPPED); - /* Find the last event to get its time */ - while (end->next) - end = end->next; - - song->endtime = end->time; /* Since there's no reliable volume control solution it's better to leave the music playing instead of having hanging notes */ song->allow_pause = SDL_GetHintBoolean("SDL_NATIVE_MUSIC_ALLOW_PAUSE", false); @@ -692,6 +720,7 @@ static int native_midi_player_thread(void *d) const unsigned char channel = event->status & 0x0F; snd_seq_ev_set_dest(&evt, song->dstaddr.client, song->dstaddr.port); + snd_seq_ev_set_fixed(&evt); snd_seq_ev_schedule_tick(&evt, queue, 0, event->time); bool unhandled = false; @@ -733,6 +762,9 @@ static int native_midi_player_thread(void *d) snd_seq_ev_set_queue_tempo(&evt, queue, t); break; } + } else if (event->status == MIDI_CMD_COMMON_SYSEX) { + snd_seq_ev_set_sysex(&evt, event->extraLen, event->extraData); + break; } unhandled = true;