219 lines
8.0 KiB
Plaintext
219 lines
8.0 KiB
Plaintext
/**
|
|
* @file midiinparse.xc
|
|
* @brief Generates USB MIDI events from MIDI events
|
|
* @author Russell Gallop, XMOS Semiconductor
|
|
* @version 0.1
|
|
*/
|
|
|
|
#include <print.h>
|
|
//#include <assert.h>
|
|
#include "midiinparse.h"
|
|
|
|
/**
|
|
* @brief Report state of the MIDI in parser (should be removed by deadcode elimination)
|
|
*
|
|
*/
|
|
void dump_midi_in_parse_state(struct midi_in_parse_state &s) {
|
|
printstr("expect_msg_len: 0x"); printhexln(s.expect_msg_len);
|
|
printstr("msg_type: 0x"); printhexln(s.msg_type);
|
|
printstr("receivebuffer: 0x"); printhex(s.receivebuffer[0]);
|
|
printstr(", 0x"); printhex(s.receivebuffer[1]);
|
|
printstr(", 0x"); printhexln(s.receivebuffer[2]);
|
|
printstr("received: 0x"); printhexln(s.received);
|
|
printstr("codeIndexNumber: 0x"); printhexln(s.codeIndexNumber);
|
|
}
|
|
|
|
/**
|
|
* @brief Reset state of MIDI parser
|
|
*
|
|
*/
|
|
void reset_midi_state(struct midi_in_parse_state &mips) {
|
|
mips.expect_msg_len = 0;
|
|
mips.msg_type = 0;
|
|
|
|
mips.receivebuffer[0] = 0;
|
|
mips.receivebuffer[1] = 0;
|
|
mips.receivebuffer[2] = 0;
|
|
mips.received = 0;
|
|
mips.codeIndexNumber = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Construct USB MIDI event
|
|
*
|
|
*/
|
|
unsigned makeEvent(unsigned cable_number, unsigned codeIndexNumber, unsigned midi0, unsigned midi1, unsigned midi2) {
|
|
unsigned event = (cable_number << 28);
|
|
event |= (codeIndexNumber << 24);
|
|
event |= (midi0 << 16);
|
|
event |= (midi1 << 8);
|
|
event |= (midi2 << 0);
|
|
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* @ brief MIDI input parser
|
|
*
|
|
*/
|
|
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &state, unsigned cable_number, unsigned char b) {
|
|
unsigned valid = 0;
|
|
unsigned data = 0xBADDF00D; // should never be returned along with valid = 1
|
|
|
|
unsigned highNibble = (b & 0xF0) >> 4;
|
|
unsigned lowNibble = (b & 0xF);
|
|
|
|
/*
|
|
assert(!(state.expect_msg_len && state.msg_type == INSYSEX_MSG));
|
|
assert((state.received >= 1) || (state.receivebuffer[0] == 0));
|
|
assert((state.received >= 2) || (state.receivebuffer[1] == 0));
|
|
assert((state.received == 3) || (state.receivebuffer[2] == 0));
|
|
assert(state.received < 3);
|
|
*/
|
|
|
|
if (b & 0x80) { // Is status byte
|
|
if (highNibble == 0xF) { // System message
|
|
if (lowNibble & 0x8) { // System real time
|
|
// System Real-Time Messages (can interleave system exclusive and between header and data (page 30 of complete MIDI spec))
|
|
//case 0x8: // Timing tick
|
|
//case 0x9: // Reserved
|
|
//case 0xA: // Start song
|
|
//case 0xB: // Continue song
|
|
//case 0xC: // Stop song
|
|
//case 0xD: // Reserved
|
|
//case 0xE: // Active sensing
|
|
//case 0xF: // System reset
|
|
// Have complete event, send out
|
|
valid = 1;
|
|
data = makeEvent(cable_number, highNibble, b, 0, 0);
|
|
} else {
|
|
if (b == 0xF7) { // End of SysEx
|
|
state.receivebuffer[state.received] = b;
|
|
state.received++;
|
|
// Compose sysex bytes that we've got and send them out.
|
|
// This will depend how many we have.
|
|
state.codeIndexNumber = state.received + 0x4;
|
|
valid = 1;
|
|
data = makeEvent(cable_number, state.codeIndexNumber,
|
|
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
|
|
reset_midi_state(state);
|
|
} else {
|
|
reset_midi_state(state);
|
|
state.receivebuffer[state.received] = b;
|
|
state.received++;
|
|
switch (lowNibble)
|
|
{
|
|
case 0x2: // Song Position Pointer (3 byte system common)
|
|
{
|
|
state.msg_type = INSYSCOMMON_MSG;
|
|
state.expect_msg_len = 3;
|
|
state.codeIndexNumber = 3;
|
|
break;
|
|
}
|
|
case 0x1: // MIDI Time Code (2 byte system common)
|
|
case 0x3: // Song Select (2 byte system common)
|
|
{
|
|
state.msg_type = INSYSCOMMON_MSG;
|
|
state.expect_msg_len = 2;
|
|
state.codeIndexNumber = 2;
|
|
break;
|
|
}
|
|
case 0x6: // Tune request (1 byte system common)
|
|
state.codeIndexNumber = 5;
|
|
valid = 1;
|
|
data = makeEvent(cable_number, state.codeIndexNumber,
|
|
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
|
|
break;
|
|
case 0x0: // Sysex start byte, never send based on just this
|
|
state.msg_type = INSYSEX_MSG;
|
|
break;
|
|
default:
|
|
// Could happen with unrecognised headers, e.g. 0xF4, 0xF5
|
|
// Just pass on
|
|
valid = 1;
|
|
data = makeEvent(cable_number, 0x0f, b, 0, 0);
|
|
reset_midi_state(state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { // Channel message
|
|
reset_midi_state(state);
|
|
state.receivebuffer[state.received] = b;
|
|
state.received++;
|
|
// code index number is always the high nibble for channel messages
|
|
state.codeIndexNumber = highNibble;
|
|
switch (highNibble)
|
|
{
|
|
case 0x8: // Note-off
|
|
case 0x9: // Note-on
|
|
case 0xA: // Poly-KeyPress
|
|
case 0xB: // Control Change
|
|
case 0xE: // PitchBend Change
|
|
{
|
|
state.msg_type = INCHANNEL_MSG;
|
|
state.expect_msg_len = 3;
|
|
break;
|
|
}
|
|
case 0xC: // Program Change
|
|
case 0xD: // Channel Pressure
|
|
{
|
|
state.msg_type = INCHANNEL_MSG;
|
|
state.expect_msg_len = 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { // data byte
|
|
state.receivebuffer[state.received] = b;
|
|
state.received++;
|
|
switch (state.msg_type) {
|
|
case INCHANNEL_MSG:
|
|
case INSYSCOMMON_MSG:
|
|
{
|
|
if (state.received == state.expect_msg_len) {
|
|
valid = 1;
|
|
data = makeEvent(cable_number, state.codeIndexNumber,
|
|
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
|
|
if (state.msg_type == INSYSCOMMON_MSG) {
|
|
// No running status on system common messages
|
|
reset_midi_state(state);
|
|
} else {
|
|
// Keep the first byte on channel messages, already received 1 byte
|
|
state.received = 1;
|
|
state.receivebuffer[1] = 0;
|
|
state.receivebuffer[2] = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case INSYSEX_MSG:
|
|
{
|
|
if ((state.received == 3)) {
|
|
// Output if have 3 using the SysEx starts or continues
|
|
state.codeIndexNumber = 0x4;
|
|
valid = 1;
|
|
data = makeEvent(cable_number, state.codeIndexNumber,
|
|
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
|
|
// reset buffer but not msg_type
|
|
state.received = 0;
|
|
state.receivebuffer[0] = 0;
|
|
state.receivebuffer[1] = 0;
|
|
state.receivebuffer[2] = 0;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Else data byte with no status so just send as single byte without parsing.
|
|
valid = 1;
|
|
data = makeEvent(cable_number, 0x0f, b, 0, 0);
|
|
reset_midi_state(state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {valid, data};
|
|
}
|