/*
* 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.
*
* This file is part of the 86Box distribution.
*
* MIDI device core module.
*
* Version: @(#)midi.c 1.0.1 2018/10/10
*
* Authors: Sarah Walker,
* Miran Grca,
* Bit,
* DOSBox Team,
*
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
* Copyright 2016-2018 Bit.
* Copyright 2008-2018 DOSBox Team.
*/
#include
#include
#include
#include
#include
#include "../86box.h"
#include "../device.h"
#include "../plat.h"
#include "../plat_midi.h"
#include "midi.h"
#include "midi_system.h"
#ifdef USE_FLUIDSYNTH
# include "midi_fluidsynth.h"
#endif
#ifdef USE_MUNT
# include "midi_mt32.h"
#endif
#define SYSEX_SIZE 1024
#define RAWBUF 1024
int midi_device_current = 0;
static int midi_device_last = 0;
typedef struct
{
uint8_t midi_rt_buf[1024], midi_cmd_buf[1024],
midi_status, midi_sysex_data[1026];
int midi_cmd_pos, midi_cmd_len;
unsigned int midi_sysex_start, midi_sysex_delay,
midi_pos;
midi_device_t* m_device;
} midi_t;
static midi_t *midi = NULL;
static uint8_t MIDI_evt_len[256] = {
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x00 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x10 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x20 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x30 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x40 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x50 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x60 */
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, /* 0x70 */
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, /* 0x80 */
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, /* 0x90 */
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, /* 0xa0 */
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, /* 0xb0 */
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, /* 0xc0 */
2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, /* 0xd0 */
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, /* 0xe0 */
0,2,3,2, 0,0,1,0, 1,0,1,1, 1,0,1,0 /* 0xf0 */
};
typedef struct
{
const char *name, *internal_name;
const device_t *device;
} MIDI_DEVICE;
static const MIDI_DEVICE devices[] =
{
{"None", "none", NULL},
#ifdef USE_FLUIDSYNTH
{"FluidSynth", "fluidsynth", &fluidsynth_device},
#endif
#ifdef USE_MUNT
{"Roland MT-32 Emulation", "mt32", &mt32_device},
{"Roland CM-32L Emulation", "cm32l", &cm32l_device},
#endif
{SYSTEM_MIDI_NAME, SYSTEM_MIDI_INTERNAL_NAME, &system_midi_device},
{"", "", NULL}
};
int
midi_device_available(int card)
{
if (devices[card].device)
return device_available(devices[card].device);
return 1;
}
char *
midi_device_getname(int card)
{
return (char *) devices[card].name;
}
const device_t *
midi_device_getdevice(int card)
{
return devices[card].device;
}
int
midi_device_has_config(int card)
{
if (!devices[card].device)
return 0;
return devices[card].device->config ? 1 : 0;
}
char *
midi_device_get_internal_name(int card)
{
return (char *) devices[card].internal_name;
}
int
midi_device_get_from_internal_name(char *s)
{
int c = 0;
while (strlen(devices[c].internal_name)) {
if (!strcmp(devices[c].internal_name, s))
return c;
c++;
}
return 0;
}
void
midi_device_init()
{
if (devices[midi_device_current].device)
device_add(devices[midi_device_current].device);
midi_device_last = midi_device_current;
}
void
midi_init(midi_device_t* device)
{
midi = (midi_t *) malloc(sizeof(midi_t));
memset(midi, 0, sizeof(midi_t));
midi->m_device = device;
}
void
midi_close(void)
{
if (midi && midi->m_device) {
free(midi->m_device);
midi->m_device = NULL;
}
if (midi) {
free(midi);
midi = NULL;
}
}
void
midi_poll(void)
{
if (midi && midi->m_device && midi->m_device->poll)
midi->m_device->poll();
}
void
play_msg(uint8_t *msg)
{
if (midi->m_device->play_msg)
midi->m_device->play_msg(msg);
}
void
play_sysex(uint8_t *sysex, unsigned int len)
{
if (midi->m_device->play_sysex)
midi->m_device->play_sysex(sysex, len);
}
void
midi_write(uint8_t val)
{
uint32_t passed_ticks;
if (!midi || !midi->m_device)
return;
if (midi->m_device->write && midi->m_device->write(val))
return;
if (midi->midi_sysex_start) {
passed_ticks = plat_get_ticks() - midi->midi_sysex_start;
if (passed_ticks < midi->midi_sysex_delay)
plat_delay_ms(midi->midi_sysex_delay - passed_ticks);
}
/* Test for a realtime MIDI message */
if (val >= 0xf8) {
midi->midi_rt_buf[0] = val;
play_msg(midi->midi_rt_buf);
return;
}
/* Test for a active sysex transfer */
if (midi->midi_status == 0xf0) {
if (!(val & 0x80)) {
if (midi->midi_pos < (SYSEX_SIZE-1))
midi->midi_sysex_data[midi->midi_pos++] = val;
return;
} else {
midi->midi_sysex_data[midi->midi_pos++] = 0xf7;
if ((midi->midi_sysex_start) && (midi->midi_pos >= 4) && (midi->midi_pos <= 9) &&
(midi->midi_sysex_data[1] == 0x41) && (midi->midi_sysex_data[3] == 0x16)) {
/* pclog("MIDI: Skipping invalid MT-32 SysEx MIDI message\n"); */
} else {
play_sysex(midi->midi_sysex_data, midi->midi_pos);
if (midi->midi_sysex_start) {
if (midi-> midi_sysex_data[5] == 0x7f)
midi->midi_sysex_delay = 290; /* All parameters reset */
else if ((midi->midi_sysex_data[5] == 0x10) && (midi->midi_sysex_data[6] == 0x00) &&
(midi->midi_sysex_data[7] == 0x04))
midi->midi_sysex_delay = 145; /* Viking Child */
else if ((midi->midi_sysex_data[5] == 0x10) && (midi->midi_sysex_data[6] == 0x00) &&
(midi->midi_sysex_data[7] == 0x01))
midi->midi_sysex_delay = 30; /* Dark Sun 1 */
else
midi->midi_sysex_delay = (unsigned int) (((float) (midi->midi_pos) * 1.25f) * 1000.0f / 3125.0f) + 2;
midi->midi_sysex_start = plat_get_ticks();
}
}
}
}
if (val & 0x80) {
midi->midi_status = val;
midi->midi_cmd_pos = 0;
midi->midi_cmd_len = MIDI_evt_len[val];
if (midi->midi_status == 0xf0) {
midi->midi_sysex_data[0] = 0xf0;
midi->midi_pos = 1;
}
}
if (midi->midi_cmd_len) {
midi->midi_cmd_buf[midi->midi_cmd_pos++] = val;
if (midi->midi_cmd_pos >= midi->midi_cmd_len) {
play_msg(midi->midi_cmd_buf);
midi->midi_cmd_pos = 1;
}
}
}