diff --git a/src/unix/linux_midi_alsa.c b/src/unix/linux_midi_alsa.c new file mode 100644 index 000000000..53050ff5c --- /dev/null +++ b/src/unix/linux_midi_alsa.c @@ -0,0 +1,367 @@ +#include +#include <86box/86box.h> +#include <86box/config.h> +#include <86box/midi.h> +#include <86box/plat.h> + +#define MAX_MIDI_DEVICES 128 + +static struct +{ + int card; + int device; + int sub; + char name[50]; +} midi_devices[MAX_MIDI_DEVICES], midi_in_devices[MAX_MIDI_DEVICES]; + +static int midi_device_count = 0, midi_in_device_count = 0; + +static int midi_queried = 0, midi_in_queried = 0; + +static snd_rawmidi_t *midiout = NULL, *midiin = NULL; + +static void plat_midi_query() +{ + int status; + int card = -1; + + midi_queried = 1; + + if ((status = snd_card_next(&card)) < 0) + return; + + if (card < 0) + return; /*No cards*/ + + while (card >= 0) + { + char *shortname; + + if ((status = snd_card_get_name(card, &shortname)) >= 0) + { + snd_ctl_t *ctl; + char name[32]; + + sprintf(name, "hw:%i", card); + + if ((status = snd_ctl_open(&ctl, name, 0)) >= 0) + { + int device = -1; + + do + { + status = snd_ctl_rawmidi_next_device(ctl, &device); + if (status >= 0 && device != -1) + { + snd_rawmidi_info_t *info; + int sub_nr, sub; + + snd_rawmidi_info_alloca(&info); + snd_rawmidi_info_set_device(info, device); + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT); + snd_ctl_rawmidi_info(ctl, info); + sub_nr = snd_rawmidi_info_get_subdevices_count(info); + //pclog("sub_nr=%i\n",sub_nr); + + for (sub = 0; sub < sub_nr; sub++) + { + snd_rawmidi_info_set_subdevice(info, sub); + + if (snd_ctl_rawmidi_info(ctl, info) == 0) + { + //pclog("%s: MIDI device=%i:%i:%i\n", shortname, card, device,sub); + + midi_devices[midi_device_count].card = card; + midi_devices[midi_device_count].device = device; + midi_devices[midi_device_count].sub = sub; + snprintf(midi_devices[midi_device_count].name, 50, "%s (%i:%i:%i)", shortname, card, device, sub); + midi_device_count++; + if (midi_device_count >= MAX_MIDI_DEVICES) + return; + } + } + } + } while (device >= 0); + } + } + + if (snd_card_next(&card) < 0) + break; + } +} + +void plat_midi_init() +{ + char portname[32]; + int midi_id; + + if (!midi_queried) + plat_midi_query(); + + midi_id = config_get_int(SYSTEM_MIDI_NAME, "midi", 0); + + sprintf(portname, "hw:%i,%i,%i", midi_devices[midi_id].card, + midi_devices[midi_id].device, + midi_devices[midi_id].sub); + //pclog("Opening MIDI port %s\n", portname); + + if (snd_rawmidi_open(NULL, &midiout, portname, SND_RAWMIDI_SYNC) < 0) + { + //pclog("Failed to open MIDI\n"); + return; + } +} + +void plat_midi_close() +{ + if (midiout != NULL) + { + snd_rawmidi_close(midiout); + midiout = NULL; + } +} + +static int midi_pos, midi_len; +static uint8_t midi_command[4]; +static int midi_lengths[8] = {3, 3, 3, 3, 2, 2, 3, 1}; +static int midi_insysex; +static uint8_t midi_sysex_data[65536]; + +void plat_midi_write(uint8_t val) +{ + //pclog("Write MIDI %02x\n", val); + if (!midiout) return; + + if ((val & 0x80) && !(val == 0xf7 && midi_insysex)) + { + midi_pos = 0; + midi_len = midi_lengths[(val >> 4) & 7]; + midi_command[0] = midi_command[1] = midi_command[2] = midi_command[3] = 0; + if (val == 0xf0) + midi_insysex = 1; + } + + if (midi_insysex) + { + midi_sysex_data[midi_pos++] = val; + + if (val == 0xf7 || midi_pos >= 65536) + { +/* pclog("MIDI send sysex %i: ", midi_pos); + for (int i = 0; i < midi_pos; i++) + pclog("%02x ", midi_sysex_data[i]); + pclog("\n");*/ + snd_rawmidi_write(midiout, midi_sysex_data, midi_pos); +// pclog("Sent sysex\n"); + midi_insysex = 0; + } + return; + } + + if (midi_len) + { + if (midi_pos < 3) + { + midi_command[midi_pos] = val; + + midi_pos++; + + if (midi_pos == midi_len) + { +// pclog("MIDI send %i: %02x %02x %02x %02x\n", midi_len, midi_command[0], midi_command[1], midi_command[2], midi_command[3]); + snd_rawmidi_write(midiout, midi_command, midi_len); + } + } + } +} + +void plat_midi_play_sysex(uint8_t *sysex, unsigned int len) +{ + if (midiout) + { + snd_rawmidi_write(midiout, (const void*)sysex, (size_t)len); + } +} + +void plat_midi_play_msg(uint8_t *msg) +{ + plat_midi_write(msg[0]); + if (midi_len > 1) plat_midi_write(msg[1]); + if (midi_len > 2) plat_midi_write(msg[2]); +} + +int plat_midi_get_num_devs() +{ + if (!midi_queried) + plat_midi_query(); + + return midi_device_count; +} + +void plat_midi_get_dev_name(int num, char *s) +{ + strcpy(s, midi_devices[num].name); +} + +static void plat_midi_query_in() +{ + int status; + int card = -1; + + midi_in_queried = 1; + + if ((status = snd_card_next(&card)) < 0) + return; + + if (card < 0) + return; /*No cards*/ + + while (card >= 0) + { + char *shortname; + + if ((status = snd_card_get_name(card, &shortname)) >= 0) + { + snd_ctl_t *ctl; + char name[32]; + + sprintf(name, "hw:%i", card); + + if ((status = snd_ctl_open(&ctl, name, 0)) >= 0) + { + int device = -1; + + do + { + status = snd_ctl_rawmidi_next_device(ctl, &device); + if (status >= 0 && device != -1) + { + snd_rawmidi_info_t *info; + int sub_nr, sub; + + snd_rawmidi_info_alloca(&info); + snd_rawmidi_info_set_device(info, device); + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); + snd_ctl_rawmidi_info(ctl, info); + sub_nr = snd_rawmidi_info_get_subdevices_count(info); + //pclog("sub_nr=%i\n",sub_nr); + + for (sub = 0; sub < sub_nr; sub++) + { + snd_rawmidi_info_set_subdevice(info, sub); + + if (snd_ctl_rawmidi_info(ctl, info) == 0) + { + //pclog("%s: MIDI device=%i:%i:%i\n", shortname, card, device,sub); + + midi_in_devices[midi_device_count].card = card; + midi_in_devices[midi_device_count].device = device; + midi_in_devices[midi_device_count].sub = sub; + snprintf(midi_in_devices[midi_device_count].name, 50, "%s (%i:%i:%i)", shortname, card, device, sub); + midi_in_device_count++; + if (midi_in_device_count >= MAX_MIDI_DEVICES) + return; + } + } + } + } while (device >= 0); + } + } + + if (snd_card_next(&card) < 0) + break; + } +} +mutex_t* midiinmtx = NULL; + +static void plat_midi_in_thread(void* param) +{ + int sysexpos = 0; + uint8_t midimsg[3]; + while(1) + { + thread_wait_mutex(midiinmtx); + if (midiin == NULL) + { + thread_release_mutex(midiinmtx); + break; + } + if (snd_rawmidi_read(midiin, midimsg, 1) > 0) + { + if (midimsg[0] == 0xF0) + { + MIDI_InSysexBuf[sysexpos++] = 0xF0; + while(1) + { + snd_rawmidi_read(midiin, &MIDI_InSysexBuf[sysexpos++], 1); + if (MIDI_InSysexBuf[sysexpos - 1] == 0xF7) + { + midi_in_sysex(MIDI_InSysexBuf, sysexpos); + } + } + } + else if (midimsg[0] & 0x80) + { + int lengthofmsg = midi_lengths[(midimsg[0] >> 4) & 7] - 1; + snd_rawmidi_read(midiin, midimsg + 1, lengthofmsg); + midi_in_msg(midimsg); + } + } + thread_release_mutex(midiinmtx); + } +} + +void plat_midi_input_init(void) +{ + char portname[32]; + int midi_id; + snd_rawmidi_params_t* params; + int err; + + snd_rawmidi_params_malloc(¶ms); + if (!params) return; + if (!midi_in_queried) + plat_midi_query_in(); + + + midi_id = config_get_int(MIDI_INPUT_NAME, "midi_input", 0); + + sprintf(portname, "hw:%i,%i,%i", midi_in_devices[midi_id].card, + midi_in_devices[midi_id].device, + midi_in_devices[midi_id].sub); + //pclog("Opening MIDI port %s\n", portname); + + if (snd_rawmidi_open(NULL, &midiin, portname, SND_RAWMIDI_NONBLOCK) < 0) + { + //pclog("Failed to open MIDI\n"); + return; + } + midiin = thread_create_mutex(); + thread_create(plat_midi_in_thread, NULL); +} + +void plat_midi_input_close(void) +{ + thread_wait_mutex(midiinmtx); + if (midiin != NULL) + { + snd_rawmidi_close(midiin); + midiin = NULL; + } + thread_release_mutex(midiinmtx); + thread_close_mutex(midiinmtx); + midiinmtx = NULL; +} + +int plat_midi_in_get_num_devs(void) +{ + if (!midi_queried) + plat_midi_query_in(); + + return midi_in_device_count; +} + +void plat_midi_in_get_dev_name(int num, char *s) +{ + strcpy(s, midi_in_devices[num].name); +}