2021-12-03 20:12:44 +01:00
|
|
|
/*
|
2022-02-22 20:28:56 -05:00
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
2021-12-03 20:12:44 +01:00
|
|
|
*
|
2022-02-22 20:28:56 -05:00
|
|
|
* This file is part of the 86Box distribution.
|
2021-12-03 20:12:44 +01:00
|
|
|
*
|
2022-02-22 20:28:56 -05:00
|
|
|
* MIDI backend implemented using the RtMidi library.
|
2021-12-03 20:12:44 +01:00
|
|
|
*
|
2022-02-22 20:28:56 -05:00
|
|
|
* Author: Cacodemon345,
|
|
|
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* Copyright 2021 Cacodemon345.
|
|
|
|
|
* Copyright 2021 Miran Grca.
|
2021-12-03 20:12:44 +01:00
|
|
|
*/
|
2022-03-11 12:03:54 +06:00
|
|
|
|
2022-03-11 20:41:00 +01:00
|
|
|
#if defined __has_include
|
2022-07-21 22:01:01 -04:00
|
|
|
# if __has_include(<RtMidi.h>)
|
|
|
|
|
# include <RtMidi.h>
|
|
|
|
|
# endif
|
|
|
|
|
# if __has_include(<rtmidi/RtMidi.h>)
|
|
|
|
|
# include <rtmidi/RtMidi.h>
|
|
|
|
|
# endif
|
2021-12-03 20:12:44 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
2021-12-03 20:20:02 +01:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
2021-12-03 20:12:44 +01:00
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
|
{
|
|
|
|
|
#include <86box/86box.h>
|
2021-12-17 08:57:00 +01:00
|
|
|
#include <86box/device.h>
|
2021-12-03 20:12:44 +01:00
|
|
|
#include <86box/midi.h>
|
2021-12-17 08:57:00 +01:00
|
|
|
#include <86box/midi_rtmidi.h>
|
2022-09-10 13:32:46 +02:00
|
|
|
#include <86box/ini.h>
|
2021-12-03 20:12:44 +01:00
|
|
|
#include <86box/config.h>
|
|
|
|
|
|
2022-04-22 20:58:08 -04:00
|
|
|
// Disable c99-designator to avoid the warnings in rtmidi_*_device
|
|
|
|
|
#ifdef __clang__
|
2022-07-21 22:01:01 -04:00
|
|
|
# if __has_warning("-Wc99-designator")
|
|
|
|
|
# pragma clang diagnostic ignored "-Wc99-designator"
|
|
|
|
|
# endif
|
2022-04-22 20:58:08 -04:00
|
|
|
#endif
|
2021-12-03 20:12:44 +01:00
|
|
|
|
2022-07-21 22:01:01 -04:00
|
|
|
static RtMidiOut *midiout = nullptr;
|
|
|
|
|
static RtMidiIn *midiin = nullptr;
|
|
|
|
|
static int midi_out_id = 0, midi_in_id = 0;
|
|
|
|
|
static const int midi_lengths[8] = { 3, 3, 3, 3, 2, 2, 3, 1 };
|
2021-12-03 20:12:44 +01:00
|
|
|
|
|
|
|
|
int
|
2021-12-17 08:57:00 +01:00
|
|
|
rtmidi_write(uint8_t val)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-17 08:57:00 +01:00
|
|
|
void
|
|
|
|
|
rtmidi_play_msg(uint8_t *msg)
|
|
|
|
|
{
|
|
|
|
|
if (midiout)
|
2022-07-21 22:01:01 -04:00
|
|
|
midiout->sendMessage(msg, midi_lengths[(msg[0] >> 4) & 7]);
|
2021-12-17 08:57:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
rtmidi_play_sysex(uint8_t *sysex, unsigned int len)
|
|
|
|
|
{
|
|
|
|
|
if (midiout)
|
2022-07-21 22:01:01 -04:00
|
|
|
midiout->sendMessage(sysex, len);
|
2021-12-17 08:57:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void*
|
2022-03-13 09:00:03 -04:00
|
|
|
rtmidi_output_init(const device_t *info)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_device_t *dev = (midi_device_t *) malloc(sizeof(midi_device_t));
|
2021-12-17 08:57:00 +01:00
|
|
|
memset(dev, 0, sizeof(midi_device_t));
|
|
|
|
|
|
2022-07-21 22:01:01 -04:00
|
|
|
dev->play_msg = rtmidi_play_msg;
|
2021-12-17 08:57:00 +01:00
|
|
|
dev->play_sysex = rtmidi_play_sysex;
|
2022-07-21 22:01:01 -04:00
|
|
|
dev->write = rtmidi_write;
|
2021-12-17 08:57:00 +01:00
|
|
|
|
2021-12-03 20:12:44 +01:00
|
|
|
try {
|
2022-07-21 22:01:01 -04:00
|
|
|
if (!midiout)
|
|
|
|
|
midiout = new RtMidiOut;
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI output: %s\n", error.getMessage().c_str());
|
|
|
|
|
return nullptr;
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_out_id = config_get_int((char *) SYSTEM_MIDI_NAME, (char *) "midi", 0);
|
2022-02-22 20:28:56 -05:00
|
|
|
|
|
|
|
|
try {
|
2022-07-21 22:01:01 -04:00
|
|
|
midiout->openPort(midi_out_id);
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Fallback to default MIDI output port: %s\n", error.getMessage().c_str());
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
midiout->openPort(0);
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI output: %s\n", error.getMessage().c_str());
|
|
|
|
|
delete midiout;
|
|
|
|
|
midiout = nullptr;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
2021-12-17 08:57:00 +01:00
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
midi_out_init(dev);
|
2021-12-17 08:57:00 +01:00
|
|
|
|
|
|
|
|
return dev;
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-03-13 09:00:03 -04:00
|
|
|
rtmidi_output_close(void *p)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
if (!midiout)
|
2022-07-21 22:01:01 -04:00
|
|
|
return;
|
2021-12-03 20:12:44 +01:00
|
|
|
|
|
|
|
|
midiout->closePort();
|
|
|
|
|
|
|
|
|
|
delete midiout;
|
|
|
|
|
midiout = nullptr;
|
2021-12-17 08:57:00 +01:00
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
midi_out_close();
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2022-03-13 09:00:03 -04:00
|
|
|
rtmidi_out_get_num_devs(void)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
if (!midiout) {
|
2022-07-21 22:01:01 -04:00
|
|
|
try {
|
|
|
|
|
midiout = new RtMidiOut;
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI output: %s\n", error.getMessage().c_str());
|
|
|
|
|
}
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return midiout ? midiout->getPortCount() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-03-13 09:00:03 -04:00
|
|
|
rtmidi_out_get_dev_name(int num, char *s)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
strcpy(s, midiout->getPortName(num).c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-12-17 08:57:00 +01:00
|
|
|
rtmidi_input_callback(double timeStamp, std::vector<unsigned char> *message, void *userData)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
2022-01-07 08:18:15 +02:00
|
|
|
if (message->front() == 0xF0)
|
|
|
|
|
midi_in_sysex(message->data(), message->size());
|
2021-12-03 20:12:44 +01:00
|
|
|
else
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_in_msg(message->data(), message->size());
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-17 08:57:00 +01:00
|
|
|
void*
|
|
|
|
|
rtmidi_input_init(const device_t *info)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_device_t *dev = (midi_device_t *) malloc(sizeof(midi_device_t));
|
2021-12-17 08:57:00 +01:00
|
|
|
memset(dev, 0, sizeof(midi_device_t));
|
|
|
|
|
|
2021-12-03 20:12:44 +01:00
|
|
|
try {
|
2022-07-21 22:01:01 -04:00
|
|
|
if (!midiin)
|
|
|
|
|
midiin = new RtMidiIn;
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI input: %s\n", error.getMessage().c_str());
|
|
|
|
|
return nullptr;
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_in_id = config_get_int((char *) MIDI_INPUT_NAME, (char *) "midi_input", 0);
|
2021-12-03 20:12:44 +01:00
|
|
|
|
|
|
|
|
try {
|
2022-07-21 22:01:01 -04:00
|
|
|
midiin->openPort(midi_in_id);
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Fallback to default MIDI input port: %s\n", error.getMessage().c_str());
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
midiin->openPort(0);
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI input: %s\n", error.getMessage().c_str());
|
|
|
|
|
delete midiin;
|
|
|
|
|
midiin = nullptr;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-05 23:58:07 +01:00
|
|
|
midiin->setCallback(&rtmidi_input_callback);
|
|
|
|
|
|
|
|
|
|
// Don't ignore sysex, timing, or active sensing messages.
|
|
|
|
|
midiin->ignoreTypes(false, false, false);
|
2021-12-17 08:57:00 +01:00
|
|
|
|
|
|
|
|
midi_in_init(dev, &midi_in);
|
|
|
|
|
|
|
|
|
|
midi_in->midi_realtime = device_get_config_int("realtime");
|
2022-07-21 22:01:01 -04:00
|
|
|
midi_in->thruchan = device_get_config_int("thruchan");
|
2021-12-17 08:57:00 +01:00
|
|
|
midi_in->midi_clockout = device_get_config_int("clockout");
|
|
|
|
|
|
|
|
|
|
return dev;
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-07-21 22:01:01 -04:00
|
|
|
rtmidi_input_close(void *p)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
midiin->cancelCallback();
|
|
|
|
|
midiin->closePort();
|
|
|
|
|
|
|
|
|
|
delete midiin;
|
|
|
|
|
midiin = nullptr;
|
|
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
midi_out_close();
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2021-12-17 08:57:00 +01:00
|
|
|
rtmidi_in_get_num_devs(void)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
if (!midiin) {
|
2022-07-21 22:01:01 -04:00
|
|
|
try {
|
|
|
|
|
midiin = new RtMidiIn;
|
|
|
|
|
} catch (RtMidiError &error) {
|
|
|
|
|
pclog("Failed to initialize MIDI input: %s\n", error.getMessage().c_str());
|
|
|
|
|
}
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return midiin ? midiin->getPortCount() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-12-17 08:57:00 +01:00
|
|
|
rtmidi_in_get_dev_name(int num, char *s)
|
2021-12-03 20:12:44 +01:00
|
|
|
{
|
|
|
|
|
strcpy(s, midiin->getPortName(num).c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
static const device_config_t system_midi_config[] = {
|
2022-07-21 21:56:38 -04:00
|
|
|
// clang-format off
|
2021-12-17 08:57:00 +01:00
|
|
|
{
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = "midi",
|
|
|
|
|
.description = "MIDI out device",
|
|
|
|
|
.type = CONFIG_MIDI_OUT,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 0
|
2021-12-17 08:57:00 +01:00
|
|
|
},
|
2022-04-08 21:41:33 -04:00
|
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
2022-07-21 21:56:38 -04:00
|
|
|
// clang-format on
|
2021-12-17 08:57:00 +01:00
|
|
|
};
|
|
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
static const device_config_t midi_input_config[] = {
|
2022-07-21 21:56:38 -04:00
|
|
|
// clang-format off
|
2021-12-17 08:57:00 +01:00
|
|
|
{
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = "midi_input",
|
|
|
|
|
.description = "MIDI in device",
|
|
|
|
|
.type = CONFIG_MIDI_IN,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 0
|
2021-12-17 08:57:00 +01:00
|
|
|
},
|
|
|
|
|
{
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = "realtime",
|
|
|
|
|
.description = "MIDI Real time",
|
|
|
|
|
.type = CONFIG_BINARY,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 0
|
2021-12-17 08:57:00 +01:00
|
|
|
},
|
|
|
|
|
{
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = "thruchan",
|
|
|
|
|
.description = "MIDI Thru",
|
|
|
|
|
.type = CONFIG_BINARY,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 1
|
2021-12-17 08:57:00 +01:00
|
|
|
},
|
|
|
|
|
{
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = "clockout",
|
|
|
|
|
.description = "MIDI Clockout",
|
|
|
|
|
.type = CONFIG_BINARY,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 1
|
2021-12-17 08:57:00 +01:00
|
|
|
},
|
2022-04-08 21:41:33 -04:00
|
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
2022-07-21 21:56:38 -04:00
|
|
|
// clang-format on
|
2021-12-17 08:57:00 +01:00
|
|
|
};
|
|
|
|
|
|
2022-03-13 09:00:03 -04:00
|
|
|
const device_t rtmidi_output_device = {
|
2022-04-08 21:41:33 -04:00
|
|
|
.name = SYSTEM_MIDI_NAME,
|
|
|
|
|
.internal_name = SYSTEM_MIDI_INTERNAL_NAME,
|
|
|
|
|
.flags = 0,
|
|
|
|
|
.local = 0,
|
|
|
|
|
.init = rtmidi_output_init,
|
|
|
|
|
.close = rtmidi_output_close,
|
|
|
|
|
.reset = NULL,
|
|
|
|
|
{ .available = rtmidi_out_get_num_devs },
|
|
|
|
|
.speed_changed = NULL,
|
|
|
|
|
.force_redraw = NULL,
|
|
|
|
|
.config = system_midi_config
|
2021-12-17 08:57:00 +01:00
|
|
|
};
|
|
|
|
|
|
2022-04-08 21:41:33 -04:00
|
|
|
const device_t rtmidi_input_device = {
|
|
|
|
|
.name = MIDI_INPUT_NAME,
|
|
|
|
|
.internal_name = MIDI_INPUT_INTERNAL_NAME,
|
|
|
|
|
.flags = 0,
|
|
|
|
|
.local = 0,
|
|
|
|
|
.init = rtmidi_input_init,
|
|
|
|
|
.close = rtmidi_input_close,
|
|
|
|
|
.reset = NULL,
|
|
|
|
|
{ .available = rtmidi_in_get_num_devs },
|
|
|
|
|
.speed_changed = NULL,
|
|
|
|
|
.force_redraw = NULL,
|
|
|
|
|
.config = midi_input_config
|
2021-12-17 08:57:00 +01:00
|
|
|
};
|
2021-12-03 20:12:44 +01:00
|
|
|
}
|