/*
* 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.
*
* Roland MPU-401 emulation.
*
* Version: @(#)snd_mpu401.c 1.0.20 2020/01/23
*
* Authors: Sarah Walker,
* DOSBox Team,
* Miran Grca,
* TheCollector1995,
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2008-2020 DOSBox Team.
* Copyright 2016-2020 Miran Grca.
*/
#include
#include
#include
#include
#include
#include
#include
#define HAVE_STDARG_H
#include "../86box.h"
#include "../device.h"
#include "../plat.h"
#include "../io.h"
#include "../machine/machine.h"
#include "../mca.h"
#include "../pic.h"
#include "../timer.h"
#include "sound.h"
#include "snd_mpu401.h"
#include "midi.h"
static uint32_t MPUClockBase[8] = {48,72,96,120,144,168,192};
static uint8_t cth_data[16] = {0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0};
enum {
STATUS_OUTPUT_NOT_READY = 0x40,
STATUS_INPUT_NOT_READY = 0x80
};
int mpu401_standalone_enable = 0;
static void MPU401_WriteCommand(mpu_t *mpu, uint8_t val);
static void MPU401_IntelligentOut(mpu_t *mpu, uint8_t track);
static void MPU401_EOIHandler(void *priv);
static void MPU401_EOIHandlerDispatch(void *p);
static void MPU401_NotesOff(mpu_t *mpu, int i);
#ifdef ENABLE_MPU401_LOG
int mpu401_do_log = ENABLE_MPU401_LOG;
static void
mpu401_log(const char *fmt, ...)
{
va_list ap;
if (mpu401_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define mpu401_log(fmt, ...)
#endif
static void
MPU401_ReCalcClock(mpu_t *mpu)
{
int32_t maxtempo = 240, mintempo = 16;
int32_t freq;
if (mpu->clock.timebase < 72) {
maxtempo = 240;
mintempo = 32;
} else if (mpu->clock.timebase < 120) {
maxtempo = 240;
mintempo = 16;
} else if (mpu->clock.timebase < 168) {
maxtempo = 208;
mintempo = 8;
} else {
maxtempo = 179;
mintempo = 8;
}
mpu->clock.freq = ((uint32_t)(mpu->clock.tempo * 2 * mpu->clock.tempo_rel)) >> 6;
mpu->clock.freq = mpu->clock.timebase * (mpu->clock.freq < (mintempo * 2) ? mintempo :
((mpu->clock.freq / 2) < maxtempo ? (mpu->clock.freq / 2) : maxtempo));
if (mpu->state.sync_in) {
freq = (int32_t)((float)(mpu->clock.freq) * mpu->clock.freq_mod);
if ((freq > (mpu->clock.timebase * mintempo)) && (freq < (mpu->clock.timebase * maxtempo)))
mpu->clock.freq = freq;
}
}
static void
MPU401_StartClock(mpu_t *mpu)
{
if (mpu->clock.active)
return;
if (!(mpu->state.clock_to_host || mpu->state.playing || (mpu->state.rec == M_RECON)))
return;
mpu->clock.active = 1;
timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
}
static void
MPU401_StopClock(mpu_t *mpu)
{
if (mpu->state.clock_to_host || mpu->state.playing || (mpu->state.rec == M_RECON))
return;
mpu->clock.active = 0;
timer_disable(&mpu->mpu401_event_callback);
}
static void
MPU401_RunClock(mpu_t *mpu)
{
if (!mpu->clock.active) {
timer_disable(&mpu->mpu401_event_callback);
return;
}
timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
mpu401_log("Next event after %i us (time constant: %i)\n", (uint64_t) ((MPU401_TIMECONSTANT/mpu->clock.freq) * 1000 * TIMER_USEC), (int) MPU401_TIMECONSTANT);
}
static void
MPU401_QueueByteEx(mpu_t *mpu, uint8_t data, int irq)
{
if (mpu->state.block_ack) {
mpu->state.block_ack = 0;
return;
}
if (mpu->queue_used == 0) {
mpu->state.irq_pending = 1;
if (irq)
picint(1 << mpu->irq);
}
if (mpu->queue_used < MPU401_QUEUE) {
int pos = mpu->queue_used+mpu->queue_pos;
if (mpu->queue_pos >= MPU401_QUEUE)
mpu->queue_pos -= MPU401_QUEUE;
if (pos>=MPU401_QUEUE)
pos-=MPU401_QUEUE;
mpu->queue_used++;
mpu->queue[pos] = data;
}
}
static void
MPU401_QueueByte(mpu_t *mpu, uint8_t data)
{
MPU401_QueueByteEx(mpu, data, 1);
}
static void
MPU401_RecQueueBuffer(mpu_t *mpu, uint8_t *buf, uint32_t len, int block)
{
uint32_t cnt = 0;
int pos;
while (cnt < len) {
if (mpu->rec_queue_used < MPU401_INPUT_QUEUE) {
pos = mpu->rec_queue_used + mpu->rec_queue_pos;
if (pos >= MPU401_INPUT_QUEUE)
pos -= MPU401_INPUT_QUEUE;
mpu->rec_queue[pos] = buf[cnt];
mpu->rec_queue_used++;
if ((!mpu->state.sysex_in_finished) && (buf[cnt] == MSG_EOX)) {
/* finish sysex */
mpu->state.sysex_in_finished = 1;
break;
}
cnt++;
}
}
if (mpu->queue_used == 0) {
if (mpu->state.rec_copy || mpu->state.irq_pending) {
if (mpu->state.irq_pending) {
picintc(1 << mpu->irq);
mpu->state.irq_pending = 0;
}
return;
}
mpu->state.rec_copy = 1;
if (mpu->rec_queue_pos >= MPU401_INPUT_QUEUE)
mpu->rec_queue_pos -= MPU401_INPUT_QUEUE;
MPU401_QueueByte(mpu, mpu->rec_queue[mpu->rec_queue_pos]);
mpu->rec_queue_used--;
mpu->rec_queue_pos++;
}
}
static void
MPU401_ClrQueue(mpu_t *mpu)
{
mpu->queue_used = 0;
mpu->queue_pos = 0;
mpu->rec_queue_used = 0;
mpu->rec_queue_pos = 0;
mpu->state.sysex_in_finished = 1;
mpu->state.irq_pending = 0;
}
static void
MPU401_Reset(mpu_t *mpu)
{
uint8_t i;
if (mpu->mode == M_INTELLIGENT) {
picintc(1 << mpu->irq);
mpu->state.irq_pending = 0;
}
mpu->mode = M_INTELLIGENT;
mpu->midi_thru = 0;
mpu->state.rec = M_RECOFF;
mpu->state.eoi_scheduled = 0;
mpu->state.wsd = 0;
mpu->state.wsm = 0;
mpu->state.conductor = 0;
mpu->state.cond_req = 0;
mpu->state.cond_set = 0;
mpu->state.playing = 0;
mpu->state.run_irq = 0;
mpu->state.cmask = 0xff;
mpu->state.amask = mpu->state.tmask = 0;
mpu->state.midi_mask = 0xffff;
mpu->state.command_byte = 0;
mpu->state.block_ack = 0;
mpu->clock.tempo = mpu->clock.old_tempo = 100;
mpu->clock.timebase = mpu->clock.old_timebase = 120;
mpu->clock.tempo_rel = mpu->clock.old_tempo_rel = 0x40;
mpu->clock.freq_mod = 1.0;
mpu->clock.tempo_grad = 0;
MPU401_StopClock(mpu);
MPU401_ReCalcClock(mpu);
for (i = 0; i < 4; i++)
mpu->clock.cth_rate[i] = 60;
mpu->clock.cth_counter = 0;
mpu->clock.midimetro = 12;
mpu->clock.metromeas = 8;
mpu->filter.rec_measure_end = 1;
mpu->filter.rt_out = 1;
mpu->filter.rt_affection = 1;
mpu->filter.allnotesoff_out = 1;
mpu->filter.all_thru = 1;
mpu->filter.midi_thru = 1;
mpu->filter.commonmsgs_thru = 1;
/* Reset channel reference and input tables. */
for (i = 0; i < 4; i++) {
mpu->chanref[i].on = 1;
mpu->chanref[i].chan = i;
mpu->ch_toref[i] = i;
}
for (i = 0; i < 16; i++) {
mpu->inputref[i].on = 1;
mpu->inputref[i].chan = i;
if (i > 3)
mpu->ch_toref[i] = 4; /* Dummy reftable. */
}
MPU401_ClrQueue(mpu);
mpu->state.data_onoff = -1;
mpu->state.req_mask = 0;
mpu->condbuf.counter = 0;
mpu->condbuf.type = T_OVERFLOW;
for (i = 0; i < 8; i++) {
mpu->playbuf[i].type = T_OVERFLOW;
mpu->playbuf[i].counter = 0;
}
/* Clear MIDI buffers, terminate notes. */
midi_clear_buffer();
for (i = 0xb0; i <= 0xbf; i++) {
midi_raw_out_byte(i);
midi_raw_out_byte(0x7b);
midi_raw_out_byte(0);
}
}
static void
MPU401_ResetDone(void *priv)
{
mpu_t *mpu = (mpu_t *)priv;
mpu401_log("MPU-401 reset callback\n");
timer_disable(&mpu->mpu401_reset_callback);
mpu->state.reset = 0;
if (mpu->state.cmd_pending) {
MPU401_WriteCommand(mpu, mpu->state.cmd_pending - 1);
mpu->state.cmd_pending = 0;
}
}
static void
MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
{
uint8_t i, j, was_uart, recmsg[3];
if (mpu->state.reset)
mpu->state.cmd_pending = val + 1;
/* The only command recognized in UART mode is 0xFF: Reset and return to Intelligent mode. */
if ((val != 0xff) && (mpu->mode == M_UART))
return;
/* In Intelligent mode, UART-only variants of the MPU-401 only support commands 0x3F and 0xFF. */
if ((val != 0x3f) && (val != 0xff) && !mpu->intelligent)
return;
/* Hack: Enable midi through after the first mpu401 command is written. */
mpu->midi_thru = 1;
if (val <= 0x2f) { /* Sequencer state */
int send_prchg = 0;
if ((val & 0xf) < 0xc) {
switch (val & 3) { /* MIDI realtime messages */
case 1:
mpu->state.last_rtcmd = 0xfc;
if (mpu->filter.rt_out)
midi_raw_out_rt_byte(0xfc);
mpu->clock.meas_old = mpu->clock.measure_counter;
mpu->clock.cth_old = mpu->clock.cth_counter;
break;
case 2:
mpu->state.last_rtcmd = 0xfa;
if (mpu->filter.rt_out)
midi_raw_out_rt_byte(0xfb);
mpu->clock.measure_counter = mpu->clock.meas_old = 0;
mpu->clock.cth_counter = mpu->clock.cth_old = 0;
break;
case 3:
mpu->state.last_rtcmd = 0xfc;
if (mpu->filter.rt_out)
midi_raw_out_rt_byte(0xfa);
mpu->clock.measure_counter = mpu->clock.meas_old;
mpu->clock.cth_counter = mpu->clock.cth_old;
break;
}
switch (val & 0xc) { /* Playing */
case 0x4: /* Stop */
mpu->state.playing = 0;
MPU401_StopClock(mpu);
for (i = 0; i < 16; i++)
MPU401_NotesOff(mpu, i);
mpu->filter.prchg_mask = 0;
break;
case 0x8: /* Start */
mpu->state.playing = 1;
MPU401_StartClock(mpu);
break;
}
switch (val & 0x30) { /* Recording */
case 0: /* check if it waited for MIDI RT command */
if (((val & 3) < 2) || !mpu->filter.rt_affection || (mpu->state.rec != M_RECSTB))
break;
mpu->state.rec = M_RECON;
MPU401_StartClock(mpu);
if (mpu->filter.prchg_mask)
send_prchg = 1;
break;
case 0x10: /* Stop */
mpu->state.rec = M_RECOFF;
MPU401_StopClock(mpu);
MPU401_QueueByte(mpu, MSG_MPU_ACK);
MPU401_QueueByte(mpu, mpu->clock.rec_counter);
MPU401_QueueByte(mpu, MSG_MPU_END);
mpu->filter.prchg_mask = 0;
mpu->clock.rec_counter = 0;
return;
case 0x20: /* Start */
if (!(mpu->state.rec == M_RECON)) {
mpu->clock.rec_counter = 0;
mpu->state.rec = M_RECSTB;
}
if ((mpu->state.last_rtcmd == 0xfa) || (mpu->state.last_rtcmd == 0xfb)) {
mpu->clock.rec_counter = 0;
mpu->state.rec = M_RECON;
if (mpu->filter.prchg_mask)
send_prchg = 1;
MPU401_StartClock(mpu);
}
break;
}
}
MPU401_QueueByte(mpu, MSG_MPU_ACK);
/* record counter hack: needed by Prism, but sent only on cmd 0x20/0x26 (or breaks Ballade) */
uint8_t rec_cnt = mpu->clock.rec_counter;
if (((val == 0x20) || (val == 0x26)) && (mpu->state.rec == M_RECON))
MPU401_RecQueueBuffer(mpu, &rec_cnt, 1, 0);
if (send_prchg) {
for (i = 0; i < 16; i++) {
if (mpu->filter.prchg_mask & (1 << i)) {
recmsg[0] = mpu->clock.rec_counter;
recmsg[1] = 0xc0 | i;
recmsg[2] = mpu->filter.prchg_buf[i];
MPU401_RecQueueBuffer(mpu, recmsg, 3, 0);
mpu->filter.prchg_mask &= ~(1 << i);
}
}
}
} else if ((val >= 0xa0) && (val <= 0xa7)) /* Request play counter */
MPU401_QueueByte(mpu, mpu->playbuf[val & 7].counter);
else if ((val >= 0xd0) && (val <= 0xd7)) { /* Send data */
mpu->state.old_track = mpu->state.track;
mpu->state.track= val & 7;
mpu->state.wsd = 1;
mpu->state.wsm = 0;
mpu->state.wsd_start = 1;
} else if ((val < 0x80) && (val >= 0x40)) { /* Set reference table channel */
mpu->chanref[(val >> 4) - 4].on = 1;
mpu->chanref[(val >> 4) - 4].chan = val & 0x0f;
mpu->chanref[(val >> 4) - 4].trmask = 0;
for (i = 0; i < 4; i++)
mpu->chanref[(val >> 4) - 4].key[i] = 0;
for (i = 0; i < 16; i++) {
if (mpu->ch_toref[i] == ((val >> 4) - 4))
mpu->ch_toref[i] = 4;
}
mpu->ch_toref[val & 0x0f] = (val >> 4) - 4;
} else switch (val) {
case 0x30: /* Configuration 0x30 - 0x39 */
mpu->filter.allnotesoff_out = 0;
break;
case 0x32:
mpu->filter.rt_out = 0;
break;
case 0x33:
mpu->filter.all_thru = 0;
mpu->filter.commonmsgs_thru = 0;
mpu->filter.midi_thru = 0;
for (i = 0; i < 16; i++) {
mpu->inputref[i].on = 0;
for (j = 0; j < 4; j++)
mpu->inputref[i].key[j] = 0;
}
break;
case 0x34:
mpu->filter.timing_in_stop = 1;
break;
case 0x35:
mpu->filter.modemsgs_in = 1;
break;
case 0x37:
mpu->filter.sysex_thru = 1;
break;
case 0x38:
mpu->filter.commonmsgs_in = 1;
break;
case 0x39:
mpu->filter.rt_in = 1;
break;
case 0x3f: /* UART mode */
mpu401_log("MPU-401: Set UART mode %X\n",val);
MPU401_QueueByte(mpu, MSG_MPU_ACK);
mpu->mode = M_UART;
return;
case 0x80: /* Internal clock */
if (mpu->clock.active && mpu->state.sync_in) {
timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / mpu->clock.freq) * 1000 * TIMER_USEC);
mpu->clock.freq_mod = 1.0;
}
mpu->state.sync_in = 0;
break;
case 0x81: /* Sync to tape signal */
case 0x82: /* Sync to MIDI */
mpu->clock.ticks_in = 0;
mpu->state.sync_in = 1;
break;
case 0x86: case 0x87: /* Bender */
mpu->filter.bender_in = !!(val & 1);
break;
case 0x88: case 0x89: /* MIDI through */
mpu->filter.midi_thru = !!(val & 1);
for (i = 0; i < 16; i++) {
mpu->inputref[i].on = mpu->filter.midi_thru;
if (!(val & 1)) {
for (j = 0; j < 4; j++)
mpu->inputref[i].key[j] = 0;
}
}
break;
case 0x8a: case 0x8b: /* Data in stop */
mpu->filter.data_in_stop = !!(val & 1);
break;
case 0x8c: case 0x8d: /* Send measure end */
mpu->filter.rec_measure_end = !!(val & 1);
break;
case 0x8e: case 0x8f: /* Conductor */
mpu->state.cond_set = !!(val & 1);
break;
case 0x90: case 0x91: /* Realtime affection */
mpu->filter.rt_affection = !!(val & 1);
break;
case 0x94: /* Clock to host */
mpu->state.clock_to_host = 0;
MPU401_StopClock(mpu);
break;
case 0x95:
mpu->state.clock_to_host = 1;
MPU401_StartClock(mpu);
break;
case 0x96: case 0x97: /* Sysex input allow */
mpu->filter.sysex_in = !!(val & 1);
if (val & 1)
mpu->filter.sysex_thru = 0;
break;
case 0x98: case 0x99: case 0x9a: case 0x9b: /* Reference tables on/off */
case 0x9c: case 0x9d: case 0x9e: case 0x9f:
mpu->chanref[(val - 0x98) / 2].on = !!(val & 1);
break;
/* Commands 0xa# returning data */
case 0xab: /* Request and clear recording counter */
MPU401_QueueByte(mpu, MSG_MPU_ACK);
MPU401_QueueByte(mpu, 0);
return;
case 0xac: /* Request version */
MPU401_QueueByte(mpu, MSG_MPU_ACK);
MPU401_QueueByte(mpu, MPU401_VERSION);
return;
case 0xad: /* Request revision */
MPU401_QueueByte(mpu, MSG_MPU_ACK);
MPU401_QueueByte(mpu, MPU401_REVISION);
return;
case 0xaf: /* Request tempo */
MPU401_QueueByte(mpu, MSG_MPU_ACK);
MPU401_QueueByte(mpu, mpu->clock.tempo);
return;
case 0xb1: /* Reset relative tempo */
mpu->clock.old_tempo_rel = mpu->clock.tempo_rel;
mpu->clock.tempo_rel = 0x40;
break;
case 0xb8: /* Clear play counters */
mpu->state.last_rtcmd = 0;
for (i = 0; i < 8; i++) {
mpu->playbuf[i].counter = 0;
mpu->playbuf[i].type = T_OVERFLOW;
}
mpu->condbuf.counter = 0;
mpu->condbuf.type = T_OVERFLOW;
mpu->state.amask = mpu->state.tmask;
mpu->state.conductor = mpu->state.cond_set;
mpu->clock.cth_counter = mpu->clock.cth_old = 0;
mpu->clock.measure_counter = mpu->clock.meas_old = 0;
break;
case 0xb9: /* Clear play map */
for (i = 0; i < 16; i++)
MPU401_NotesOff(mpu, i);
for (i = 0; i < 8; i++) {
mpu->playbuf[i].counter = 0;
mpu->playbuf[i].type = T_OVERFLOW;
}
mpu->state.last_rtcmd = 0;
mpu->clock.cth_counter = mpu->clock.cth_old = 0;
mpu->clock.measure_counter = mpu->clock.meas_old = 0;
break;
case 0xba: /* Clear record counter */
mpu->clock.rec_counter = 0;
break;
case 0xc2: case 0xc3: case 0xc4: /* Internal timebase */
case 0xc5: case 0xc6: case 0xc7: case 0xc8:
mpu->clock.timebase = MPUClockBase[val-0xc2];
MPU401_ReCalcClock(mpu);
break;
case 0xdf: /* Send system message */
mpu->state.wsd = 0;
mpu->state.wsm = 1;
mpu->state.wsd_start = 1;
break;
/* Commands with data byte */
case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
mpu->state.command_byte = val;
break;
case 0xff: /* Reset MPU-401 */
mpu401_log("MPU-401: Reset %X\n", val);
timer_set_delay_u64(&mpu->mpu401_reset_callback, MPU401_RESETBUSY * 33LL * TIMER_USEC);
mpu->state.reset = 1;
was_uart = (mpu->mode == M_UART);
MPU401_Reset(mpu);
if (was_uart)
return;
break;
/* default:
mpu401_log("MPU-401:Unhandled command %X",val); */
}
MPU401_QueueByte(mpu, MSG_MPU_ACK);
}
static void
MPU401_WriteData(mpu_t *mpu, uint8_t val)
{
static int length, cnt;
uint8_t i;
if (mpu->mode == M_UART) {
midi_raw_out_byte(val);
return;
}
if (!mpu->intelligent) {
mpu->state.command_byte = 0;
return;
}
switch (mpu->state.command_byte) { /* 0xe# command data */
case 0x00:
break;
case 0xe0: /* Set tempo */
mpu->state.command_byte = 0;
if (mpu->clock.tempo < 8)
mpu->clock.tempo = 8;
else if (mpu->clock.tempo > 250)
mpu->clock.tempo = 250;
else
mpu->clock.tempo = val;
MPU401_ReCalcClock(mpu);
return;
case 0xe1: /* Set relative tempo */
mpu->state.command_byte = 0;
mpu->clock.old_tempo_rel = mpu->clock.tempo_rel;
mpu->clock.tempo_rel = val;
MPU401_ReCalcClock(mpu);
return;
case 0xe2: /* Set gradation for relative tempo */
mpu->clock.tempo_grad = val;
MPU401_ReCalcClock(mpu);
return;
case 0xe4: /* Set MIDI clocks for metronome ticks */
mpu->state.command_byte = 0;
mpu->clock.midimetro = val;
return;
case 0xe6: /* Set metronome ticks per measure */
mpu->state.command_byte = 0;
mpu->clock.metromeas = val;
return;
case 0xe7: /* Set internal clock to host interval */
mpu->state.command_byte = 0;
if (!val)
val = 64;
for (i = 0; i < 4; i++)
mpu->clock.cth_rate[i] = (val >> 2) + cth_data[(val & 3) * 4 + i];
mpu->clock.cth_mode = 0;
return;
case 0xec: /* Set active track mask */
mpu->state.command_byte = 0;
mpu->state.tmask = val;
return;
case 0xed: /* Set play counter mask */
mpu->state.command_byte = 0;
mpu->state.cmask = val;
return;
case 0xee: /* Set 1-8 MIDI channel mask */
mpu->state.command_byte = 0;
mpu->state.midi_mask &= 0xff00;
mpu->state.midi_mask |= val;
return;
case 0xef: /* Set 9-16 MIDI channel mask */
mpu->state.command_byte = 0;
mpu->state.midi_mask &= 0x00ff;
mpu->state.midi_mask |= ((uint16_t) val) << 8;
return;
default:
mpu->state.command_byte = 0;
return;
}
if (mpu->state.wsd && !mpu->state.track_req && !mpu->state.cond_req) {
/* Directly send MIDI message */
if (mpu->state.wsd_start) {
mpu->state.wsd_start = 0;
cnt = 0;
switch (val & 0xf0) {
case 0xc0: case 0xd0:
length = mpu->playbuf[mpu->state.track].length = 2;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
case 0x80: case 0x90: case 0xa0: case 0xb0:case 0xe0:
length = mpu->playbuf[mpu->state.track].length = 3;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
case 0xf0:
/* mpu401_log("MPU-401:Illegal WSD byte\n"); */
mpu->state.wsd = 0;
mpu->state.track = mpu->state.old_track;
return;
default: /* MIDI with running status */
cnt++;
length = mpu->playbuf[mpu->state.track].length;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
}
}
if (cnt < length) {
mpu->playbuf[mpu->state.track].value[cnt] = val;
cnt++;
}
if (cnt == length) {
MPU401_IntelligentOut(mpu, mpu->state.track);
mpu->state.wsd = 0;
mpu->state.track = mpu->state.old_track;
}
return;
}
if (mpu->state.wsm && !mpu->state.track_req && !mpu->state.cond_req) { /* Send system message */
if (mpu->state.wsd_start) {
mpu->state.wsd_start = 0;
cnt = 0;
switch (val) {
case 0xf2:
length = 3;
break;
case 0xf3:
length = 2;
break;
case 0xf6:
length = 1;
break;
case 0xf0:
length = 0;
break;
default:
mpu->state.wsm = 0;
return;
}
} else if (val & 0x80) {
midi_raw_out_byte(MSG_EOX);
mpu->state.wsm = 0;
return;
}
if (!length || (cnt < length)) {
midi_raw_out_byte(val);
cnt++;
}
if (cnt == length)
mpu->state.wsm = 0;
return;
}
if (mpu->state.cond_req) {
/* Command */
switch (mpu->state.data_onoff) {
case -1:
return;
case 0: /* Timing byte */
mpu->condbuf.length = 0;
if (val < 0xf0)
mpu->state.data_onoff++;
else {
mpu->state.cond_req = 0;
mpu->state.data_onoff = -1;
MPU401_EOIHandlerDispatch(mpu);
break;
}
mpu->state.send_now = !val ? 1 : 0;
mpu->condbuf.counter = val;
break;
case 1: /* Command byte #1 */
mpu->condbuf.type = T_COMMAND;
if ((val == 0xf8) || (val == 0xf9) || (val == 0xfc))
mpu->condbuf.type = T_OVERFLOW;
mpu->condbuf.value[mpu->condbuf.length] = val;
mpu->condbuf.length++;
if ((val & 0xf0) != 0xe0) { /*no cmd data byte*/
MPU401_EOIHandler(mpu);
mpu->state.data_onoff = -1;
mpu->state.cond_req = 0;
} else
mpu->state.data_onoff++;
break;
case 2:/* Command byte #2 */
mpu->condbuf.value[mpu->condbuf.length]=val;
mpu->condbuf.length++;
MPU401_EOIHandler(mpu);
mpu->state.data_onoff = -1;
mpu->state.cond_req = 0;
break;
}
return;
}
switch (mpu->state.data_onoff) {
/* Data */
case -1:
break;
case 0: /* Timing byte */
if (val < 0xf0)
mpu->state.data_onoff++;
else {
mpu->state.data_onoff = -1;
MPU401_EOIHandlerDispatch(mpu);
mpu->state.track_req = 0;
return;
}
mpu->state.send_now = !val ? 1 : 0;
mpu->playbuf[mpu->state.track].counter = val;
break;
case 1: /* MIDI */
cnt = 0;
mpu->state.data_onoff++;
switch (val & 0xf0) {
case 0xc0: case 0xd0: /* MIDI Message */
length = mpu->playbuf[mpu->state.track].length = 2;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
length = mpu->playbuf[mpu->state.track].length = 3;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
case 0xf0: /* System message or mark */
mpu->playbuf[mpu->state.track].sys_val = val;
if (val > 0xf7) {
mpu->playbuf[mpu->state.track].type = T_MARK;
if (val == 0xf9)
mpu->clock.measure_counter = 0;
} else {
/* mpu401_log("MPU-401:Illegal message"); */
mpu->playbuf[mpu->state.track].type = T_OVERFLOW;
}
mpu->state.data_onoff = -1;
MPU401_EOIHandler(mpu);
mpu->state.track_req = 0;
return;
default: /* MIDI with running status */
cnt++;
length = mpu->playbuf[mpu->state.track].length;
mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
}
break;
case 2:
if (cnt < length) {
mpu->playbuf[mpu->state.track].value[cnt] = val;
cnt++;
}
if (cnt == length) {
mpu->state.data_onoff = -1;
mpu->state.track_req = 0;
MPU401_EOIHandler(mpu);
}
break;
}
return;
}
static void
MPU401_IntelligentOut(mpu_t *mpu, uint8_t track)
{
uint8_t chan, chrefnum, key, msg;
int send, retrigger;
uint8_t i;
switch (mpu->playbuf[track].type) {
case T_OVERFLOW:
break;
case T_MARK:
if (mpu->playbuf[track].sys_val == 0xfc) {
midi_raw_out_rt_byte(mpu->playbuf[track].sys_val);
mpu->state.amask&=~(1<