diff --git a/src/config.c b/src/config.c
index 583605583..2b9026989 100644
--- a/src/config.c
+++ b/src/config.c
@@ -649,6 +649,12 @@ load_sound(void)
else
midi_device_current = 0;
+ p = config_get_string(cat, "midi_in_device", NULL);
+ if (p != NULL)
+ midi_input_device_current = midi_in_device_get_from_internal_name(p);
+ else
+ midi_input_device_current = 0;
+
mpu401_standalone_enable = !!config_get_int(cat, "mpu401_standalone", 0);
SSI2001 = !!config_get_int(cat, "ssi2001", 0);
@@ -1531,6 +1537,11 @@ save_sound(void)
else
config_set_string(cat, "midi_device", midi_device_get_internal_name(midi_device_current));
+ if (!strcmp(midi_in_device_get_internal_name(midi_input_device_current), "none"))
+ config_delete_var(cat, "midi_in_device");
+ else
+ config_set_string(cat, "midi_in_device", midi_in_device_get_internal_name(midi_input_device_current));
+
if (mpu401_standalone_enable == 0)
config_delete_var(cat, "mpu401_standalone");
else
diff --git a/src/device.h b/src/device.h
index e59952205..dbf9976a2 100644
--- a/src/device.h
+++ b/src/device.h
@@ -50,6 +50,7 @@
#define CONFIG_HEX16 7
#define CONFIG_HEX20 8
#define CONFIG_MAC 9
+#define CONFIG_MIDI_IN 10
enum {
diff --git a/src/plat_midi.h b/src/plat_midi.h
index 5070de68a..933e49ee6 100644
--- a/src/plat_midi.h
+++ b/src/plat_midi.h
@@ -7,3 +7,9 @@ extern int plat_midi_write(uint8_t val);
extern int plat_midi_get_num_devs(void);
extern void plat_midi_get_dev_name(int num, char *s);
+
+extern void plat_midi_input_init(void);
+extern void plat_midi_input_close(void);
+
+extern int plat_midi_in_get_num_devs(void);
+extern void plat_midi_in_get_dev_name(int num, char *s);
diff --git a/src/sound/midi.c b/src/sound/midi.c
index 1f5f0ed47..7648c8c92 100644
--- a/src/sound/midi.c
+++ b/src/sound/midi.c
@@ -8,7 +8,7 @@
*
* MIDI device core module.
*
- * Version: @(#)midi.c 1.0.1 2018/10/10
+ * Version: @(#)midi.c 1.0.2 2018/12/31
*
* Authors: Sarah Walker,
* Miran Grca,
@@ -37,28 +37,22 @@
#ifdef USE_MUNT
# include "midi_mt32.h"
#endif
+#include "midi_input.h"
-#define SYSEX_SIZE 1024
-#define RAWBUF 1024
-
int midi_device_current = 0;
static int midi_device_last = 0;
+int midi_input_device_current = 0;
+static int midi_input_device_last = 0;
+midi_t *midi = NULL;
-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;
+void (*input_msg)(uint8_t *msg);
+int (*input_sysex)(uint8_t *buffer, uint32_t len, int abort);
-static midi_t *midi = NULL;
+uint8_t MIDI_InSysexBuf[SYSEX_SIZE];
-static uint8_t MIDI_evt_len[256] = {
+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 */
@@ -85,7 +79,7 @@ typedef struct
{
const char *name, *internal_name;
const device_t *device;
-} MIDI_DEVICE;
+} MIDI_DEVICE, MIDI_IN_DEVICE;
static const MIDI_DEVICE devices[] =
{
@@ -101,6 +95,12 @@ static const MIDI_DEVICE devices[] =
{"", "", NULL}
};
+static const MIDI_IN_DEVICE midi_in_devices[] =
+{
+ {"None", "none", NULL},
+ {MIDI_INPUT_NAME, MIDI_INPUT_INTERNAL_NAME, &midi_input_device},
+ {"", "", NULL}
+};
int
midi_device_available(int card)
@@ -172,16 +172,39 @@ midi_init(midi_device_t* device)
midi = (midi_t *) malloc(sizeof(midi_t));
memset(midi, 0, sizeof(midi_t));
- midi->m_device = device;
+ midi->m_out_device = device;
+}
+
+void
+midi_in_init(midi_device_t* device, midi_t **mididev)
+{
+ *mididev = (midi_t *)malloc(sizeof(midi_t));
+ memset(*mididev, 0, sizeof(midi_t));
+
+ (*mididev)->m_in_device = device;
}
void
midi_close(void)
{
- if (midi && midi->m_device) {
- free(midi->m_device);
- midi->m_device = NULL;
+ if (midi && midi->m_out_device) {
+ free(midi->m_out_device);
+ midi->m_out_device = NULL;
+ }
+
+ if (midi) {
+ free(midi);
+ midi = NULL;
+ }
+}
+
+void
+midi_in_close(void)
+{
+ if (midi && midi->m_in_device) {
+ free(midi->m_in_device);
+ midi->m_in_device = NULL;
}
if (midi) {
@@ -194,36 +217,119 @@ midi_close(void)
void
midi_poll(void)
{
- if (midi && midi->m_device && midi->m_device->poll)
- midi->m_device->poll();
+ if (midi && midi->m_out_device && midi->m_out_device->poll)
+ midi->m_out_device->poll();
}
void
play_msg(uint8_t *msg)
{
- if (midi->m_device->play_msg)
- midi->m_device->play_msg(msg);
+ if (midi->m_out_device->play_msg)
+ midi->m_out_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);
+ if (midi->m_out_device->play_sysex)
+ midi->m_out_device->play_sysex(sysex, len);
}
+int
+midi_in_device_available(int card)
+{
+ if (midi_in_devices[card].device)
+ return device_available(midi_in_devices[card].device);
+
+ return 1;
+}
+
+char *
+midi_in_device_getname(int card)
+{
+ return (char *) midi_in_devices[card].name;
+}
+
+const device_t *
+midi_in_device_getdevice(int card)
+{
+ return midi_in_devices[card].device;
+}
+
+int
+midi_in_device_has_config(int card)
+{
+ if (!midi_in_devices[card].device)
+ return 0;
+ return midi_in_devices[card].device->config ? 1 : 0;
+}
+
+
+char *
+midi_in_device_get_internal_name(int card)
+{
+ return (char *) midi_in_devices[card].internal_name;
+}
+
+
+int
+midi_in_device_get_from_internal_name(char *s)
+{
+ int c = 0;
+
+ while (strlen(midi_in_devices[c].internal_name)) {
+ if (!strcmp(midi_in_devices[c].internal_name, s))
+ return c;
+ c++;
+ }
+
+ return 0;
+}
+
void
-midi_write(uint8_t val)
+midi_in_device_init()
+{
+ if (midi_in_devices[midi_input_device_current].device)
+ device_add(midi_in_devices[midi_input_device_current].device);
+ midi_input_device_last = midi_input_device_current;
+}
+
+void
+midi_raw_out_rt_byte(uint8_t val)
+{
+ if (!midi || !midi->m_out_device || !midi->m_in_device)
+ return;
+
+ if (!midi->midi_realtime)
+ return;
+
+ if ((!midi->midi_clockout && (val == 0xf8)))
+ return;
+
+ midi->midi_cmd_r = val << 24;
+ pclog("Play RT Byte msg\n");
+ play_msg((uint8_t *)&midi->midi_cmd_r);
+}
+
+void
+midi_raw_out_thru_rt_byte(uint8_t val)
+{
+ if (midi->thruchan)
+ midi_raw_out_rt_byte(val);
+}
+
+void
+midi_raw_out_byte(uint8_t val)
{
uint32_t passed_ticks;
- if (!midi || !midi->m_device)
+ if (!midi || !midi->m_out_device)
return;
- if (midi->m_device->write && midi->m_device->write(val))
+ if ((midi->m_out_device->write && midi->m_out_device->write(val)))
return;
if (midi->midi_sysex_start) {
@@ -289,3 +395,12 @@ midi_write(uint8_t val)
}
}
}
+
+void
+midi_clear_buffer(void)
+{
+ midi->midi_pos = 0;
+ midi->midi_status = 0x00;
+ midi->midi_cmd_pos = 0;
+ midi->midi_cmd_len = 0;
+}
\ No newline at end of file
diff --git a/src/sound/midi.h b/src/sound/midi.h
index 6268a0e0c..21441646c 100644
--- a/src/sound/midi.h
+++ b/src/sound/midi.h
@@ -2,18 +2,34 @@
# define EMU_SOUND_MIDI_H
-extern int midi_device_current;
+#define SYSEX_SIZE 8192
+extern uint8_t MIDI_InSysexBuf[SYSEX_SIZE];
+extern uint8_t MIDI_evt_len[256];
+
+extern int midi_device_current;
+extern int midi_input_device_current;
+
+extern void (*input_msg)(uint8_t *msg);
+extern int (*input_sysex)(uint8_t *buffer, uint32_t len, int abort);
int midi_device_available(int card);
+int midi_in_device_available(int card);
char *midi_device_getname(int card);
+char *midi_in_device_getname(int card);
#ifdef EMU_DEVICE_H
const device_t *midi_device_getdevice(int card);
+const device_t *midi_in_device_getdevice(int card);
#endif
int midi_device_has_config(int card);
+int midi_in_device_has_config(int card);
char *midi_device_get_internal_name(int card);
+char *midi_in_device_get_internal_name(int card);
int midi_device_get_from_internal_name(char *s);
+int midi_in_device_get_from_internal_name(char *s);
void midi_device_init();
+void midi_in_device_init();
+
typedef struct midi_device_t
{
@@ -23,9 +39,27 @@ typedef struct midi_device_t
int (*write)(uint8_t val);
} midi_device_t;
+typedef struct midi_t
+{
+ uint8_t midi_rt_buf[8], midi_cmd_buf[8],
+ midi_status, midi_sysex_data[SYSEX_SIZE];
+ int midi_cmd_pos, midi_cmd_len, midi_cmd_r,
+ midi_realtime, thruchan, midi_clockout;
+ unsigned int midi_sysex_start, midi_sysex_delay,
+ midi_pos;
+ midi_device_t *m_out_device, *m_in_device;
+} midi_t;
+
+extern midi_t *midi;
+
void midi_init(midi_device_t* device);
+void midi_in_init(midi_device_t* device, midi_t **mididev);
void midi_close();
-void midi_write(uint8_t val);
+void midi_in_close(void);
+void midi_raw_out_rt_byte(uint8_t val);
+void midi_raw_out_thru_rt_byte(uint8_t val);
+void midi_raw_out_byte(uint8_t val);
+void midi_clear_buffer(void);
void midi_poll();
#if 0
@@ -41,5 +75,7 @@ void midi_poll();
#define SYSTEM_MIDI_INTERNAL_NAME "system_midi"
#endif
+#define MIDI_INPUT_NAME "MIDI Input Device"
+#define MIDI_INPUT_INTERNAL_NAME "midi_in"
#endif /*EMU_SOUND_MIDI_H*/
diff --git a/src/sound/midi_input.h b/src/sound/midi_input.h
new file mode 100644
index 000000000..163d6fa91
--- /dev/null
+++ b/src/sound/midi_input.h
@@ -0,0 +1 @@
+extern const device_t midi_input_device;
diff --git a/src/sound/midi_system.c b/src/sound/midi_system.c
index d9a15c6a9..f3ace5c69 100644
--- a/src/sound/midi_system.c
+++ b/src/sound/midi_system.c
@@ -9,6 +9,7 @@
#include "../plat_midi.h"
#include "midi.h"
#include "midi_system.h"
+#include "midi_input.h"
void* system_midi_init(const device_t *info)
@@ -16,6 +17,8 @@ void* system_midi_init(const device_t *info)
midi_device_t* dev = malloc(sizeof(midi_device_t));
memset(dev, 0, sizeof(midi_device_t));
+ pclog("MIDI Output\n");
+
dev->play_msg = plat_midi_play_msg;
dev->play_sysex = plat_midi_play_sysex;
dev->write = plat_midi_write;
@@ -27,6 +30,24 @@ void* system_midi_init(const device_t *info)
return dev;
}
+void* midi_input_init(const device_t *info)
+{
+ midi_device_t* dev = malloc(sizeof(midi_device_t));
+ memset(dev, 0, sizeof(midi_device_t));
+
+ pclog("MIDI Input\n");
+
+ plat_midi_input_init();
+
+ midi_in_init(dev, &midi);
+
+ midi->midi_realtime = device_get_config_int("realtime");
+ midi->thruchan = device_get_config_int("thruchan");
+ midi->midi_clockout = device_get_config_int("clockout");
+
+ return dev;
+}
+
void system_midi_close(void* p)
{
plat_midi_close();
@@ -34,11 +55,23 @@ void system_midi_close(void* p)
midi_close();
}
+void midi_input_close(void* p)
+{
+ plat_midi_input_close();
+
+ midi_close();
+}
+
int system_midi_available(void)
{
return plat_midi_get_num_devs();
}
+int midi_input_available(void)
+{
+ return plat_midi_in_get_num_devs();
+}
+
static const device_config_t system_midi_config[] =
{
{
@@ -52,6 +85,37 @@ static const device_config_t system_midi_config[] =
}
};
+static const device_config_t midi_input_config[] =
+{
+ {
+ .name = "midi_input",
+ .description = "MIDI in device",
+ .type = CONFIG_MIDI_IN,
+ .default_int = 0
+ },
+ {
+ .name = "realtime",
+ .description = "MIDI Real time",
+ .type = CONFIG_BINARY,
+ .default_int = 0
+ },
+ {
+ .name = "thruchan",
+ .description = "MIDI Thru",
+ .type = CONFIG_BINARY,
+ .default_int = 1
+ },
+ {
+ .name = "clockout",
+ .description = "MIDI Clockout",
+ .type = CONFIG_BINARY,
+ .default_int = 1
+ },
+ {
+ .type = -1
+ }
+};
+
const device_t system_midi_device =
{
SYSTEM_MIDI_NAME,
@@ -64,3 +128,17 @@ const device_t system_midi_device =
NULL,
system_midi_config
};
+
+
+const device_t midi_input_device =
+{
+ MIDI_INPUT_NAME,
+ 0, 0,
+ midi_input_init,
+ midi_input_close,
+ NULL,
+ midi_input_available,
+ NULL,
+ NULL,
+ midi_input_config
+};
\ No newline at end of file
diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c
index c6bbefc6b..f24a383d3 100644
--- a/src/sound/snd_audiopci.c
+++ b/src/sound/snd_audiopci.c
@@ -13,6 +13,8 @@
#include "../pci.h"
#include "../timer.h"
#include "sound.h"
+#include "midi.h"
+#include "snd_mpu401.h"
#include "snd_audiopci.h"
@@ -23,6 +25,8 @@
static float low_fir_es1371_coef[ES1371_NCoef];
typedef struct {
+ mpu_t mpu;
+
uint8_t pci_command, pci_serr;
uint32_t base_addr;
@@ -116,14 +120,21 @@ typedef struct {
#define INT_DAC1_EN (1<<6)
#define INT_DAC2_EN (1<<5)
+#define INT_UART_EN (1<<3)
#define SI_P2_INTR_EN (1<<9)
#define SI_P1_INTR_EN (1<<8)
#define INT_STATUS_INTR (1<<31)
+#define INT_STATUS_UART (1<<3)
#define INT_STATUS_DAC1 (1<<2)
#define INT_STATUS_DAC2 (1<<1)
+#define UART_CTRL_TXINTEN (1<<5)
+
+#define UART_STATUS_TXINT (1<<2)
+#define UART_STATUS_TXRDY (1<<1)
+
#define FORMAT_MONO_8 0
#define FORMAT_STEREO_8 1
#define FORMAT_MONO_16 2
@@ -164,8 +175,13 @@ static void es1371_update_irqs(es1371_t *es1371)
if ((es1371->int_status & INT_STATUS_DAC1) && (es1371->si_cr & SI_P1_INTR_EN))
irq = 1;
- if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN))
+ if ((es1371->int_status & INT_STATUS_DAC2) && (es1371->si_cr & SI_P2_INTR_EN)) {
irq = 1;
+ }
+ /*MIDI input is unsupported for now*/
+ if ((es1371->int_status & INT_STATUS_UART) && (es1371->uart_status & UART_STATUS_TXINT)) {
+ irq = 1;
+ }
if (irq)
es1371->int_status |= INT_STATUS_INTR;
@@ -219,10 +235,10 @@ static uint8_t es1371_inb(uint16_t port, void *p)
case 0x07:
ret = (es1371->int_status >> 24) & 0xff;
break;
-
-
+
case 0x09:
- ret = es1371->uart_status;
+ ret = es1371->uart_status & 0xc7;
+ audiopci_log("ES1371 UART Status = %02x\n", es1371->uart_status);
break;
case 0x0c:
@@ -253,7 +269,7 @@ static uint8_t es1371_inb(uint16_t port, void *p)
audiopci_log("Bad es1371_inb: port=%04x\n", port);
}
-// audiopci_log("es1371_inb: port=%04x ret=%02x\n", port, ret);
+ audiopci_log("es1371_inb: port=%04x ret=%02x\n", port, ret);
// output = 3;
return ret;
}
@@ -340,31 +356,51 @@ static uint32_t es1371_inl(uint16_t port, void *p)
ret |= CODEC_READY;
break;
- case 0x34:
- switch (es1371->mem_page)
- {
+ case 0x30:
+ switch (es1371->mem_page) {
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x30 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+ }
+ break;
+ case 0x34:
+ switch (es1371->mem_page) {
case 0xc:
ret = es1371->dac[0].size | (es1371->dac[0].count << 16);
break;
case 0xd:
-
ret = es1371->adc.size | (es1371->adc.count << 16);
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x34 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
break;
+ case 0x38:
+ switch (es1371->mem_page) {
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x38 read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+ }
+ break;
+
case 0x3c:
- switch (es1371->mem_page)
- {
+ switch (es1371->mem_page) {
case 0xc:
ret = es1371->dac[1].size | (es1371->dac[1].count << 16);
break;
-
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x3c read UART FIFO: val = %02x\n", ret & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_inl: mem_page=%x port=%04x\n", es1371->mem_page, port);
}
@@ -374,7 +410,7 @@ static uint32_t es1371_inl(uint16_t port, void *p)
audiopci_log("Bad es1371_inl: port=%04x\n", port);
}
-// audiopci_log("es1371_inl: port=%04x ret=%08x %08x\n", port, ret, cpu_state.pc);
+ audiopci_log("es1371_inl: port=%04x ret=%08x\n", port, ret);
return ret;
}
@@ -382,7 +418,7 @@ static void es1371_outb(uint16_t port, uint8_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
-// audiopci_log("es1371_outb: port=%04x val=%02x %04x:%08x\n", port, val, cs, cpu_state.pc);
+ audiopci_log("es1371_outb: port=%04x val=%02x\n", port, val);
switch (port & 0x3f)
{
case 0x00:
@@ -412,8 +448,13 @@ static void es1371_outb(uint16_t port, uint8_t val, void *p)
es1371->int_ctrl = (es1371->int_ctrl & 0x00ffffff) | (val << 24);
break;
+ case 0x08:
+ midi_raw_out_byte(val);
+ break;
+
case 0x09:
- es1371->uart_ctrl = val;
+ es1371->uart_ctrl = val & 0xe3;
+ audiopci_log("ES1371 UART Cntrl = %02x\n", es1371->uart_ctrl);
break;
case 0x0c:
@@ -481,7 +522,7 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
-// audiopci_log("es1371_outl: port=%04x val=%08x %04x:%08x\n", port, val, CS, cpu_state.pc);
+ audiopci_log("es1371_outl: port=%04x val=%08x\n", port, val);
switch (port & 0x3f)
{
case 0x04:
@@ -593,6 +634,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
// audiopci_log("DAC1 addr %08x\n", val);
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x30 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
@@ -615,6 +660,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
es1371->adc.count = val >> 16;
break;
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x34 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
+
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
}
@@ -633,6 +682,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0xd:
break;
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x38 write UART FIFO: val = %02x\n", val & 0xff);
+ break;
default:
audiopci_log("Bad es1371_outl: mem_page=%x port=%04x val=%08x\n", es1371->mem_page, port, val);
@@ -649,6 +702,10 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0xc:
es1371->dac[1].size = val & 0xffff;
es1371->dac[1].count = val >> 16;
+ break;
+
+ case 0xe: case 0xf:
+ audiopci_log("ES1371 0x3c write UART FIFO: val = %02x\n", val & 0xff);
break;
default:
@@ -1107,10 +1164,33 @@ static void es1371_poll(void *p)
timer_advance_u64(&es1371->dac[1].timer, es1371->dac[1].latch);
- es1371_update(es1371);
-
- if (es1371->int_ctrl & INT_DAC1_EN)
- {
+ es1371_update(es1371);
+
+ if (es1371->int_ctrl & INT_UART_EN) {
+ audiopci_log("UART INT Enabled\n");
+ if (es1371->uart_ctrl & 0x80) { /*We currently don't implement MIDI Input.*/
+ /*But if anything sets MIDI Input and Output together we'd have to take account
+ of the MIDI Output case, and disable IRQ's and RX bits when MIDI Input is enabled as well but not in the MIDI Output portion*/
+ if (es1371->uart_ctrl & UART_CTRL_TXINTEN)
+ es1371->int_status |= INT_STATUS_UART;
+ else
+ es1371->int_status &= ~INT_STATUS_UART;
+ } else if (!(es1371->uart_ctrl & 0x80) && ((es1371->uart_ctrl & UART_CTRL_TXINTEN))) { /*Or enable the UART IRQ and the respective TX bits only when the MIDI Output is enabled*/
+ es1371->int_status |= INT_STATUS_UART;
+ }
+
+ if (es1371->uart_ctrl & 0x80) {
+ if (es1371->uart_ctrl & UART_CTRL_TXINTEN)
+ es1371->uart_status |= (UART_STATUS_TXINT | UART_STATUS_TXRDY);
+ else
+ es1371->uart_status &= ~(UART_STATUS_TXINT | UART_STATUS_TXRDY);
+ } else
+ es1371->uart_status |= (UART_STATUS_TXINT | UART_STATUS_TXRDY);
+
+ es1371_update_irqs(es1371);
+ }
+
+ if (es1371->int_ctrl & INT_DAC1_EN) {
int frac = es1371->dac[0].ac & 0x7fff;
int idx = es1371->dac[0].ac >> 15;
int samp1_l = es1371->dac[0].filtered_l[idx];
diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c
index 15105268f..f30517a2c 100644
--- a/src/sound/snd_mpu401.c
+++ b/src/sound/snd_mpu401.c
@@ -29,6 +29,7 @@
#define HAVE_STDARG_H
#include "../86box.h"
#include "../device.h"
+#include "../plat.h"
#include "../io.h"
#include "../machine/machine.h"
#include "../mca.h"
@@ -38,6 +39,8 @@
#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,
@@ -48,8 +51,13 @@ enum {
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);
+static mpu_t *mpuin;
+static mutex_t *mpu_lock;
#ifdef ENABLE_MPU401_LOG
int mpu401_do_log = ENABLE_MPU401_LOG;
@@ -71,8 +79,69 @@ mpu401_log(const char *fmt, ...)
#endif
+void
+mpu401_set_midi_in(mpu_t *src_mpu_in)
+{
+ mpuin = src_mpu_in;
+}
+
static void
-QueueByte(mpu_t *mpu, uint8_t data)
+MPU401_ReCalcClock(mpu_t *mpu)
+{
+ int32_t maxtempo = 240, mintempo = 16;
+
+ if (mpu->clock.timebase >= 168)
+ maxtempo = 179;
+ if (mpu->clock.timebase == 144)
+ maxtempo = 208;
+ if (mpu->clock.timebase >= 120)
+ maxtempo = 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) {
+ int32_t 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_QueueByte(mpu_t *mpu, uint8_t data)
{
if (mpu->state.block_ack) {
mpu->state.block_ack = 0;
@@ -88,22 +157,73 @@ QueueByte(mpu_t *mpu, uint8_t data)
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;
- } else
- mpu401_log("MPU401:Data queue full\n");
+ }
}
+static void
+MPU401_RecQueueBuffer(mpu_t *mpu, uint8_t *buf, uint32_t len, int block)
+{
+ uint32_t cnt;
+
+ if (block) {
+ if (mpu_lock)
+ thread_wait_mutex(mpu_lock);
+ else
+ return;
+ }
+
+ cnt = 0;
+ while (cnt < len) {
+ if (mpu->rec_queue_used < MPU401_INPUT_QUEUE) {
+ int 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 (block && mpu_lock)
+ thread_release_mutex(mpu_lock);
+ 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++;
+ }
+
+ if (block && mpu_lock)
+ thread_release_mutex(mpu_lock);
+}
static void
-ClrQueue(mpu_t *mpu)
+MPU401_ClrQueue(mpu_t *mpu)
{
- mpu->queue_used=0;
- mpu->queue_pos=0;
+ 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;
}
@@ -116,8 +236,10 @@ MPU401_Reset(mpu_t *mpu)
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;
@@ -129,27 +251,64 @@ MPU401_Reset(mpu_t *mpu)
mpu->state.cmask = 0xff;
mpu->state.amask = mpu->state.tmask = 0;
mpu->state.midi_mask = 0xffff;
- mpu->state.data_onoff = 0;
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 = 40;
+ mpu->clock.tempo_rel = mpu->clock.old_tempo_rel = 0x40;
+ mpu->clock.freq_mod = 1.0;
mpu->clock.tempo_grad = 0;
- mpu->clock.clock_to_host = 0;
- mpu->clock.cth_rate = 60;
+ 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;
- ClrQueue(mpu);
+ /*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.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);
+ }
}
@@ -163,6 +322,7 @@ MPU401_ResetDone(void *priv)
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;
@@ -173,160 +333,281 @@ MPU401_ResetDone(void *priv)
static void
MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
{
- uint8_t i, was_uart;
+ uint8_t i, j, was_uart;
if (mpu->state.reset)
mpu->state.cmd_pending = val + 1;
if ((val != 0x3f) && (val != 0xff) && !mpu->intelligent)
- return;
+ return;
- if (val <= 0x2f) {
- switch (val&3) { /* MIDI stop, start, continue */
- case 1:
- midi_write(0xfc);
- break;
+ thread_wait_mutex(mpu_lock);
- case 2:
- midi_write(0xfa);
- break;
+ /*hack:enable midi through after the first mpu401 command is written*/
+ mpu->midi_thru = 1;
- case 3:
- midi_write(0xfb);
- break;
+ 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;
+ thread_release_mutex(mpu_lock);
+ 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);
+ }
+ }
}
-
- switch (val & 0xc) {
- case 0x4: /* Stop */
- mpu->state.playing = 0;
- timer_disable(&mpu->mpu401_event_callback);
-
- for (i = 0xb0; i < 0xbf; i++) {
- /* All notes off */
- midi_write(i);
- midi_write(0x7b);
- midi_write(0);
- }
- break;
-
- case 0x8: /* Play */
- mpu->state.playing = 1;
- timer_set_delay_u64(&mpu->mpu401_event_callback, (MPU401_TIMECONSTANT / (mpu->clock.tempo*mpu->clock.timebase)) * 1000 * TIMER_USEC);
- ClrQueue(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)) {
+ uint8_t recmsg[3] = {mpu->clock.rec_counter, 0xc0 | i, mpu->filter.prchg_buf[i]};
+ MPU401_RecQueueBuffer(mpu, recmsg, 3, 0);
+ mpu->filter.prchg_mask &= ~(1 << i);
}
+ thread_release_mutex(mpu_lock);
} else if ((val >= 0xa0) && (val <= 0xa7)) { /* Request play counter */
- if (mpu->state.cmask & (1 << (val&7)))
- QueueByte(mpu, mpu->playbuf[val&7].counter);
+ MPU401_QueueByte(mpu, mpu->playbuf[val & 7].counter);
} else if ((val >= 0xd0) && (val <= 0xd7)) { /* Send data */
- mpu->state.old_chan = mpu->state.channel;
- mpu->state.channel= val & 7;
+ 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 0xdf: /* Send system message */
- mpu->state.wsd = 0;
- mpu->state.wsm = 1;
- mpu->state.wsd_start = 1;
+ case 0x30: /* Configuration 0x30 - 0x39 */
+ mpu->filter.allnotesoff_out = 0;
break;
-
- case 0x8e: /* Conductor */
- mpu->state.cond_set = 0;
+ case 0x32:
+ mpu->filter.rt_out = 0;
break;
-
- case 0x8f:
- mpu->state.cond_set = 1;
+ 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; i < 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->clock.clock_to_host = 0;
+ mpu->state.clock_to_host = 0;
+ MPU401_StopClock(mpu);
break;
-
case 0x95:
- mpu->clock.clock_to_host = 1;
+ mpu->state.clock_to_host = 1;
+ MPU401_StartClock(mpu);
break;
-
- case 0xc2: /* Internal timebase */
- mpu->clock.timebase = 48;
+ case 0x96: case 0x97: /* Sysex input allow */
+ mpu->filter.sysex_in = !!(val & 1);
+ if (val & 1)
+ mpu->filter.sysex_thru = 0;
break;
-
- case 0xc3:
- mpu->clock.timebase = 72;
+ 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;
-
- case 0xc4:
- mpu->clock.timebase = 96;
- break;
-
- case 0xc5:
- mpu->clock.timebase = 120;
- break;
-
- case 0xc6:
- mpu->clock.timebase = 144;
- break;
-
- case 0xc7:
- mpu->clock.timebase = 168;
- break;
- case 0xc8:
- mpu->clock.timebase = 192;
- 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;
-
/* Commands 0xa# returning data */
case 0xab: /* Request and clear recording counter */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, 0);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, 0);
+ thread_release_mutex(mpu_lock);
return;
-
case 0xac: /* Request version */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, MPU401_VERSION);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MPU401_VERSION);
+ thread_release_mutex(mpu_lock);
return;
-
case 0xad: /* Request revision */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, MPU401_REVISION);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MPU401_REVISION);
+ thread_release_mutex(mpu_lock);
return;
-
case 0xaf: /* Request tempo */
- QueueByte(mpu, MSG_MPU_ACK);
- QueueByte(mpu, mpu->clock.tempo);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, mpu->clock.tempo);
+ thread_release_mutex(mpu_lock);
return;
-
case 0xb1: /* Reset relative tempo */
mpu->clock.old_tempo_rel = mpu->clock.tempo_rel;
- mpu->clock.tempo_rel = 40;
+ mpu->clock.tempo_rel = 0x40;
break;
-
- case 0xb9: /* Clear play map */
case 0xb8: /* Clear play counters */
- for (i = 0xb0; i < 0xbf; i++) {
- /* All notes off */
- midi_write(i);
- midi_write(0x7b);
- midi_write(0);
- }
+ 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;
- if (!(mpu->state.conductor=mpu->state.cond_set))
- mpu->state.cond_req = 0;
mpu->state.amask = mpu->state.tmask;
- mpu->state.req_mask = 0;
- mpu->state.irq_pending = 1;
+ 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);
@@ -337,27 +618,23 @@ MPU401_WriteCommand(mpu_t *mpu, uint8_t val)
return; /* do not send ack in UART mode */
break;
- case 0x3f: /* UART mode */
- mpu401_log("MPU-401:Set UART mode %X\n",val);
- QueueByte(mpu, MSG_MPU_ACK);
- mpu->mode = M_UART;
- return;
-
/* default:
mpu401_log("MPU-401:Unhandled command %X",val); */
}
- QueueByte(mpu, MSG_MPU_ACK);
+ MPU401_QueueByte(mpu, MSG_MPU_ACK);
+ thread_release_mutex(mpu_lock);
}
static void
MPU401_WriteData(mpu_t *mpu, uint8_t val)
{
- static int length, cnt, posd;
+ static int length, cnt;
+ uint8_t i;
if (mpu->mode == M_UART) {
- midi_write(val);
+ midi_raw_out_byte(val);
return;
}
@@ -369,39 +646,55 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
switch (mpu->state.command_byte) { /* 0xe# command data */
case 0x00:
break;
-
case 0xe0: /* Set tempo */
mpu->state.command_byte = 0;
- mpu->clock.tempo = val;
+ 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;
- mpu->clock.cth_rate = val >> 2;
+ 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;
@@ -413,53 +706,49 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
return;
}
- if (mpu->state.wsd) {
+ 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:
- mpu->playbuf[mpu->state.channel].value[0] = val;
- length = 2;
+ 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:
- mpu->playbuf[mpu->state.channel].value[0] = val;
- length = 3;
+ 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.channel = mpu->state.old_chan;
+ mpu->state.track = mpu->state.old_track;
return;
default: /* MIDI with running status */
cnt++;
- midi_write(mpu->playbuf[mpu->state.channel].value[0]);
+ length = mpu->playbuf[mpu->state.track].length;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
}
}
if (cnt < length) {
- midi_write(val);
+ mpu->playbuf[mpu->state.track].value[cnt] = val;
cnt++;
}
if (cnt == length) {
+ MPU401_IntelligentOut(mpu, mpu->state.track);
mpu->state.wsd = 0;
- mpu->state.channel = mpu->state.old_chan;
+ mpu->state.track = mpu->state.old_track;
}
return;
}
- if (mpu->state.wsm) { /* Directly send system message */
- if (val == MSG_EOX) {
- midi_write(MSG_EOX);
- mpu->state.wsm = 0;
- 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;
@@ -481,12 +770,17 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
break;
default:
- length = 0;
+ 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_write(val);
+ midi_raw_out_byte(val);
cnt++;
}
@@ -496,130 +790,179 @@ MPU401_WriteData(mpu_t *mpu, uint8_t val)
return;
}
+ thread_wait_mutex(mpu_lock);
+
if (mpu->state.cond_req) {
/* Command */
switch (mpu->state.data_onoff) {
case -1:
+ thread_release_mutex(mpu_lock);
return;
-
case 0: /* Timing byte */
- mpu->condbuf.vlength = 0;
+ 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);
- return;
+ 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))
+ if ((val == 0xf8) || (val == 0xf9) || (val == 0xfc))
mpu->condbuf.type = T_OVERFLOW;
- mpu->condbuf.value[mpu->condbuf.vlength] = val;
- mpu->condbuf.vlength++;
- if ((val & 0xf0) != 0xe0)
- MPU401_EOIHandlerDispatch(mpu);
- else
+ 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.vlength]=val;
- mpu->condbuf.vlength++;
- MPU401_EOIHandlerDispatch(mpu);
+ mpu->condbuf.value[mpu->condbuf.length]=val;
+ mpu->condbuf.length++;
+ MPU401_EOIHandler(mpu);
+ mpu->state.data_onoff = -1;
+ mpu->state.cond_req = 0;
break;
}
+ thread_release_mutex(mpu_lock);
return;
}
switch (mpu->state.data_onoff) {
/* Data */
case -1:
- return;
-
+ break;
case 0: /* Timing byte */
if (val < 0xf0)
- mpu->state.data_onoff = 1;
+ mpu->state.data_onoff++;
else {
mpu->state.data_onoff = -1;
MPU401_EOIHandlerDispatch(mpu);
+ mpu->state.track_req = 0;
+ thread_release_mutex(mpu_lock);
return;
}
mpu->state.send_now = !val ? 1 : 0;
- mpu->playbuf[mpu->state.channel].counter = val;
+ mpu->playbuf[mpu->state.track].counter = val;
break;
-
case 1: /* MIDI */
- mpu->playbuf[mpu->state.channel].vlength++;
- posd=mpu->playbuf[mpu->state.channel].vlength;
- if (posd == 1) switch (val&0xf0) {
+ 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.channel].type = T_MARK;
- mpu->playbuf[mpu->state.channel].sys_val = val;
+ 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.channel].type = T_MIDI_SYS;
- mpu->playbuf[mpu->state.channel].sys_val = val;
+ mpu->playbuf[mpu->state.track].type = T_OVERFLOW;
}
- length = 1;
- break;
-
- case 0xc0: case 0xd0: /* MIDI Message */
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length = 2;
- break;
-
- case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0:
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length = 3;
- break;
-
- default: /* MIDI data with running status */
- posd++;
- mpu->playbuf[mpu->state.channel].vlength++;
- mpu->playbuf[mpu->state.channel].type = T_MIDI_NORM;
- length = mpu->playbuf[mpu->state.channel].length;
+ mpu->state.data_onoff = -1;
+ MPU401_EOIHandler(mpu);
+ mpu->state.track_req = 0;
+ thread_release_mutex(mpu_lock);
+ return;
+ default: /* MIDI with running status */
+ cnt++;
+ length = mpu->playbuf[mpu->state.track].length;
+ mpu->playbuf[mpu->state.track].type = T_MIDI_NORM;
break;
}
-
- if (!((posd == 1) && (val >= 0xf0)))
- mpu->playbuf[mpu->state.channel].value[posd-1] = val;
- if (posd == length)
- MPU401_EOIHandlerDispatch(mpu);
+ 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;
}
+
+ thread_release_mutex(mpu_lock);
+ return;
}
static void
-MPU401_IntelligentOut(mpu_t *mpu, uint8_t chan)
+MPU401_IntelligentOut(mpu_t *mpu, uint8_t track)
{
- uint8_t val;
+ uint8_t chan, chrefnum, key, msg;
+ int send, retrigger;
uint8_t i;
- switch (mpu->playbuf[chan].type) {
+ switch (mpu->playbuf[track].type) {
case T_OVERFLOW:
break;
case T_MARK:
- val=mpu->playbuf[chan].sys_val;
- if (val==0xfc) {
- midi_write(val);
- mpu->state.amask &= ~(1<state.req_mask &= ~(1<playbuf[track].sys_val == 0xfc) {
+ midi_raw_out_rt_byte(mpu->playbuf[track].sys_val);
+ mpu->state.amask&=~(1<