Skip to content

native_midi_linux_alsa: Add support for System Exclusive messages #679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 23, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions src/codecs/native_midi/native_midi_linux_alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
#include <string.h>
#include <sys/stat.h>

#include <assert.h>

//#define SDL_NATIVE_MIDI_ALSA_DYNAMIC "libasound.so.2"

static int load_alsa_syms(void);
Expand Down Expand Up @@ -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)))) {
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading