forked from PAWPAW-Mirror/lib_xua
First commit
This commit is contained in:
1
module_usb_midi/README
Normal file
1
module_usb_midi/README
Normal file
@@ -0,0 +1 @@
|
||||
module README.
|
||||
11
module_usb_midi/module_build_info
Normal file
11
module_usb_midi/module_build_info
Normal file
@@ -0,0 +1,11 @@
|
||||
# You can set flags specifically for your module by using the MODULE_XCC_FLAGS
|
||||
# variable. So the following
|
||||
#
|
||||
# MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3
|
||||
#
|
||||
# specifies that everything in the modules should have the application
|
||||
# build flags with -O3 appended (so the files will build at
|
||||
# optimization level -O3).
|
||||
#
|
||||
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
|
||||
|
||||
1
module_usb_midi/module_description
Normal file
1
module_usb_midi/module_description
Normal file
@@ -0,0 +1 @@
|
||||
One line module description.
|
||||
120
module_usb_midi/src/midi_descriptor.h
Normal file
120
module_usb_midi/src/midi_descriptor.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* This file contains the MID device descriptor
|
||||
It is intended to be included in the main device descriptor definition */
|
||||
|
||||
/* MIDI Descriptors */
|
||||
/* Table B-3: MIDI Adapter Standard AC Interface Descriptor */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
|
||||
MIDI_INTERFACE_1, /* 2 bInterfaceNumber : Index of this interface. (field size 1 bytes) */
|
||||
0x00, /* 3 bAlternateSetting : Index of this setting. (field size 1 bytes) */
|
||||
0x00, /* 4 bNumEndpoints : 0 endpoints. (field size 1 bytes) */
|
||||
0x01, /* 5 bInterfaceClass : AUDIO. (field size 1 bytes) */
|
||||
0x01, /* 6 bInterfaceSubclass : AUDIO_CONTROL. (field size 1 bytes) */
|
||||
0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
|
||||
0x00, /* 8 iInterface : Unused. (field size 1 bytes) */
|
||||
// 9
|
||||
/* Table B-4: MIDI Adapter Class-specific AC Interface Descriptor */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : 0x24. (field size 1 bytes) */
|
||||
0x01, /* 2 bDescriptorSubtype : HEADER subtype. (field size 1 bytes) */
|
||||
0x00, /* 3 bcdADC : Revision of class specification - 1.0 (field size 2 bytes) */
|
||||
0x01, /* 4 bcdADC */
|
||||
0x09, /* 5 wTotalLength : Total size of class specific descriptors. (field size 2 bytes) */
|
||||
0x00, /* 6 wTotalLength */
|
||||
0x01, /* 7 bInCollection : Number of streaming interfaces. (field size 1 bytes) */
|
||||
0x01, /* 8 baInterfaceNr(1) : MIDIStreaming interface 1 belongs to this AudioControl interface */
|
||||
//9
|
||||
/* Table B-5: MIDI Adapter Standard MS Interface Descriptor */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
|
||||
MIDI_INTERFACE_2, /* 2 bInterfaceNumber : Index of this interface. (field size 1 bytes) */
|
||||
0x00, /* 3 bAlternateSetting : Index of this alternate setting. (field size 1 bytes) */
|
||||
0x02, /* 4 bNumEndpoints : 2 endpoints. (field size 1 bytes) */
|
||||
0x01, /* 5 bInterfaceClass : AUDIO. (field size 1 bytes) */
|
||||
0x03, /* 6 bInterfaceSubclass : MIDISTREAMING. (field size 1 bytes) */
|
||||
0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
|
||||
0x00, /* 8 iInterface : Unused. (field size 1 bytes) */
|
||||
//9
|
||||
/* Table B-6: MIDI Adapter Class-specific MS Interface Descriptor */
|
||||
0x07, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
|
||||
0x01, /* 2 bDescriptorSubtype : MS_HEADER subtype. (field size 1 bytes) */
|
||||
0x00, /* 3 BcdADC : Revision of this class specification. (field size 2 bytes) */
|
||||
0x01, /* 4 BcdADC */
|
||||
0x41, /* 5 wTotalLength : Total size of class-specific descriptors. (field size 2 bytes) */
|
||||
0x00, /* 6 wTotalLength */
|
||||
//7
|
||||
/* Table B-7: MIDI Adapter MIDI IN Jack Descriptor (Embedded) */
|
||||
0x06, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
|
||||
0x02, /* 2 bDescriptorSubtype : MIDI_IN_JACK subtype. (field size 1 bytes) */
|
||||
0x01, /* 3 bJackType : EMBEDDED. (field size 1 bytes) */
|
||||
0x01, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
|
||||
0x00, /* 5 iJack : Unused. (field size 1 bytes) */
|
||||
//6
|
||||
/* Table B-8: MIDI Adapter MIDI IN Jack Descriptor (External) */
|
||||
0x06, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
|
||||
0x02, /* 2 bDescriptorSubtype : MIDI_IN_JACK subtype. (field size 1 bytes) */
|
||||
0x02, /* 3 bJackType : EXTERNAL. (field size 1 bytes) */
|
||||
0x02, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
|
||||
0x00, /* 5 iJack : Unused. (field size 1 bytes) */
|
||||
//6
|
||||
/* Table B-9: MIDI Adapter MIDI OUT Jack Descriptor (Embedded) */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
|
||||
0x03, /* 2 bDescriptorSubtype : MIDI_OUT_JACK subtype. (field size 1 bytes) */
|
||||
0x01, /* 3 bJackType : EMBEDDED. (field size 1 bytes) */
|
||||
0x03, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
|
||||
0x01, /* 5 bNrInputPins : Number of Input Pins of this Jack. (field size 1 bytes) */
|
||||
0x02, /* 6 BaSourceID(1) : ID of the Entity to which this Pin is connected. (field size 1 bytes) */
|
||||
0x01, /* 7 BaSourcePin(1) : Output Pin number of the Entityt o which this Input Pin is connected. */
|
||||
0x00, /* 8 iJack : Unused. (field size 1 bytes) */
|
||||
//9
|
||||
/* Table B-10: MIDI Adapter MIDI OUT Jack Descriptor (External) */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
|
||||
0x03, /* 2 bDescriptorSubtype : MIDI_OUT_JACK subtype. (field size 1 bytes) */
|
||||
0x02, /* 3 bJackType : EXTERNAL. (field size 1 bytes) */
|
||||
0x04, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
|
||||
0x01, /* 5 bNrInputPins : Number of Input Pins of this Jack. (field size 1 bytes) */
|
||||
0x01, /* 6 BaSourceID(1) : ID of the Entity to which this Pin is connected. (field size 1 bytes) */
|
||||
0x01, /* 7 BaSourcePin(1) : Output Pin number of the Entity to which this Input Pin is connected. */
|
||||
0x00, /* 8 iJack : Unused. (field size 1 bytes) */
|
||||
//9
|
||||
/* Table B-11: MIDI Adapter Standard Bulk OUT Endpoint Descriptor */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x05, /* 1 bDescriptorType : ENDPOINT descriptor. (field size 1 bytes) */
|
||||
0x04, /* 2 bEndpointAddress : OUT Endpoint 4. (field size 1 bytes) */
|
||||
0x02, /* 3 bmAttributes : Bulk, not shared. (field size 1 bytes) */
|
||||
0x00, /* 4 wMaxPacketSize : 64 bytes per packet. (field size 2 bytes) */
|
||||
0x02, /* 5 wMaxPacketSize */
|
||||
0x00, /* 6 bInterval : Ignored for Bulk. Set to zero. (field size 1 bytes) */
|
||||
0x00, /* 7 bRefresh : Unused. (field size 1 bytes) */
|
||||
0x00, /* 8 bSynchAddress : Unused. (field size 1 bytes) */
|
||||
//9
|
||||
/* Table B-12: MIDI Adapter Class-specific Bulk OUT Endpoint Descriptor */
|
||||
0x05, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x25, /* 1 bDescriptorType : CS_ENDPOINT descriptor (field size 1 bytes) */
|
||||
0x01, /* 2 bDescriptorSubtype : MS_GENERAL subtype. (field size 1 bytes) */
|
||||
0x01, /* 3 bNumEmbMIDIJack : Number of embedded MIDI IN Jacks. (field size 1 bytes) */
|
||||
0x01, /* 4 BaAssocJackID(1) : ID of the Embedded MIDI IN Jack. (field size 1 bytes) */
|
||||
//5
|
||||
/* Table B-13: MIDI Adapter Standard Bulk IN Endpoint Descriptor */
|
||||
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x05, /* 1 bDescriptorType : ENDPOINT descriptor. (field size 1 bytes) */
|
||||
0x85, /* 2 bEndpointAddress : IN Endpoint 5. (field size 1 bytes) */
|
||||
0x02, /* 3 bmAttributes : Bulk, not shared. (field size 1 bytes) */
|
||||
0x00, /* 4 wMaxPacketSize : 64 bytes per packet. (field size 2 bytes) */
|
||||
0x02, /* 5 wMaxPacketSize */
|
||||
0x00, /* 6 bInterval : Ignored for Bulk. Set to zero. (field size 1 bytes) */
|
||||
0x00, /* 7 bRefresh : Unused. (field size 1 bytes) */
|
||||
0x00, /* 8 bSynchAddress : Unused. (field size 1 bytes) */
|
||||
//9
|
||||
/* Table B-14: MIDI Adapter Class-specific Bulk IN Endpoint Descriptor */
|
||||
0x05, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
|
||||
0x25, /* 1 bDescriptorType : CS_ENDPOINT descriptor (field size 1 bytes) */
|
||||
0x01, /* 2 bDescriptorSubtype : MS_GENERAL subtype. (field size 1 bytes) */
|
||||
0x01, /* 3 bNumEmbMIDIJack : Number of embedded MIDI OUT Jacks. (field size 1 bytes) */
|
||||
0x03, /* 4 BaAssocJackID(1) : ID of the Embedded MIDI OUT Jack. (field size 1 bytes) */
|
||||
//5
|
||||
24
module_usb_midi/src/midiinparse.h
Normal file
24
module_usb_midi/src/midiinparse.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef MIDIINPARSE_XH
|
||||
#define MIDIINPARSE_XH
|
||||
|
||||
#define INITIAL 0
|
||||
#define INCHANNEL_MSG 1
|
||||
#define INSYSCOMMON_MSG 2
|
||||
#define INSYSEX_MSG 3
|
||||
|
||||
struct midi_in_parse_state {
|
||||
// State for the parser
|
||||
unsigned expect_msg_len;
|
||||
unsigned msg_type;
|
||||
|
||||
unsigned receivebuffer[3];
|
||||
unsigned received;
|
||||
|
||||
unsigned codeIndexNumber;
|
||||
};
|
||||
|
||||
void dump_midi_in_parse_state(struct midi_in_parse_state &s);
|
||||
void reset_midi_state(struct midi_in_parse_state &mips);
|
||||
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &mips, unsigned cable_number, unsigned char b);
|
||||
|
||||
#endif
|
||||
218
module_usb_midi/src/midiinparse.xc
Normal file
218
module_usb_midi/src/midiinparse.xc
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* @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};
|
||||
}
|
||||
6
module_usb_midi/src/midioutparse.h
Normal file
6
module_usb_midi/src/midioutparse.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef MIDIOUTPARSE_XH
|
||||
#define MIDIOUTPARSE_XH
|
||||
|
||||
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event);
|
||||
|
||||
#endif
|
||||
68
module_usb_midi/src/midioutparse.xc
Normal file
68
module_usb_midi/src/midioutparse.xc
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file midioutparse.xc
|
||||
* @brief Parses USB-MIDI events into set of MIDI bytes
|
||||
* @author Russell Gallop, XMOS Semiconductor
|
||||
* @version 0.1
|
||||
*/
|
||||
|
||||
#include "midioutparse.h"
|
||||
|
||||
/**
|
||||
* @brief Breaks a USB-MIDI event into it's constituant fields
|
||||
*
|
||||
* @param[in] ev USB-MIDI event
|
||||
*/
|
||||
{unsigned, unsigned, unsigned, unsigned, unsigned} breakEvent(unsigned ev) {
|
||||
unsigned cable_number = (ev >> 28) & 0xf;
|
||||
unsigned codeIndexNumber = (ev >> 24) & 0xf;
|
||||
unsigned midi0 = (ev >> 16) & 0xff;
|
||||
unsigned midi1 = (ev >> 8) & 0xff;
|
||||
unsigned midi2 = (ev >> 0) & 0xff;
|
||||
return {cable_number, codeIndexNumber, midi0, midi1, midi2};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a USB-MIDI event into the MIDI bytes and a length field
|
||||
*
|
||||
* @param[in] ev USB-MIDI event
|
||||
*/
|
||||
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event) {
|
||||
unsigned cable_number; // ignore this for now!
|
||||
unsigned codeIndexNumber;
|
||||
unsigned midi[3];
|
||||
unsigned size = 0;
|
||||
|
||||
{cable_number, codeIndexNumber, midi[0], midi[1], midi[2]} = breakEvent(event);
|
||||
// Not doing anything with cable number
|
||||
switch (codeIndexNumber) {
|
||||
case 0x3: // Three-byte system Common messages like SPP, etc.
|
||||
case 0x4: // SysEx starts or continues
|
||||
case 0x7: // SysEx ends with the following three bytes
|
||||
case 0x8: // Note-off
|
||||
case 0x9: // Note-on
|
||||
case 0xA: // Poly-KeyPress
|
||||
case 0xB: // Control Change
|
||||
case 0xE: // PitchBend Change
|
||||
{
|
||||
size = 3;
|
||||
break;
|
||||
}
|
||||
case 0x2: // Two-byte system Common messages like MTC, SongSelect, etc.
|
||||
case 0x6: // SysEx ends with the following two bytes
|
||||
case 0xC: // Program Change
|
||||
case 0xD: // Channel Pressure
|
||||
{
|
||||
size = 2;
|
||||
break;
|
||||
}
|
||||
case 0x5: // Single-byte System Common Message or SysEx ends with following single byte.
|
||||
case 0xF: // Single byte
|
||||
{
|
||||
size = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {midi[0], midi[1], midi[2], size};
|
||||
}
|
||||
63
module_usb_midi/src/usb_midi.h
Normal file
63
module_usb_midi/src/usb_midi.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef __usb_midi_h__
|
||||
#define __usb_midi_h__
|
||||
|
||||
|
||||
/** USB MIDI I/O thread.
|
||||
*
|
||||
* This function passes MIDI data from USB to UART I/O.
|
||||
*
|
||||
* \param p_midi_in 1-bit input port for MIDI
|
||||
* \param p_midi_out 1-bit output port for MIDI
|
||||
* \param clk_midi clock block used for clockin the UART; should have
|
||||
* a rate of 100MHz
|
||||
* \param c_midi chanend connected to the decouple() thread
|
||||
* \param cable_number the cable number of the MIDI implementation.
|
||||
* This should be set to 0.
|
||||
**/
|
||||
void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
|
||||
clock ?clk_midi,
|
||||
chanend c_midi,
|
||||
unsigned cable_number);
|
||||
|
||||
#define MAX_USB_MIDI_PACKET_SIZE 1024
|
||||
#define MIDI_USB_BUFFER_FROM_HOST_FIFO_SIZE (512+1024)
|
||||
#define MIDI_USB_BUFFER_TO_HOST_SIZE (256)
|
||||
#define MIDI_ACK 20
|
||||
#define USB_MIDI_DEVICE_OUT_FIFO_SIZE (1024)
|
||||
|
||||
#ifdef __MIDI_IMPL
|
||||
#define INLINE
|
||||
#else
|
||||
#define INLINE inline
|
||||
#endif
|
||||
|
||||
#ifdef NO_INLINE_MIDI_SELECT_HANDLER
|
||||
#pragma select handler
|
||||
void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum);
|
||||
#else
|
||||
#pragma select handler
|
||||
INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
|
||||
if (testct(c)) {
|
||||
is_ack = 1;
|
||||
(void) inct(c);
|
||||
(void) inuchar(c);
|
||||
(void) inuchar(c);
|
||||
(void) inuchar(c);
|
||||
}
|
||||
else {
|
||||
is_ack = 0;
|
||||
datum = inuint(c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
INLINE void midi_send_ack(chanend c) {
|
||||
outct(c, MIDI_ACK);
|
||||
outuchar(c, 0);
|
||||
outuchar(c, 0);
|
||||
outuchar(c, 0);
|
||||
}
|
||||
|
||||
|
||||
#endif // __usb_midi_h__
|
||||
322
module_usb_midi/src/usb_midi.xc
Normal file
322
module_usb_midi/src/usb_midi.xc
Normal file
@@ -0,0 +1,322 @@
|
||||
#include <xs1.h>
|
||||
#include <xclib.h>
|
||||
#include "usb_midi.h"
|
||||
#include "midiinparse.h"
|
||||
#include "midioutparse.h"
|
||||
#include <print.h>
|
||||
|
||||
//#define MIDI_LOOPBACK 1
|
||||
|
||||
static unsigned makeSymbol(unsigned data) {
|
||||
// Start and stop bits to the data packet
|
||||
return (data << 1) | 0x200;
|
||||
}
|
||||
|
||||
#define RATE 31250
|
||||
|
||||
static unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE;
|
||||
static unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2;
|
||||
|
||||
int mr_count = 0;
|
||||
int th_count = 0;
|
||||
|
||||
#ifdef MIDI_LOOPBACK
|
||||
static inline void handle_byte_from_uart(chanend c_midi, struct midi_in_parse_state &mips, int cable_number,
|
||||
int &got_next_event, int &next_event, int &waiting_for_ack, int byte)
|
||||
{
|
||||
int valid;
|
||||
unsigned event;
|
||||
{valid, event} = midi_in_parse(mips, cable_number, byte);
|
||||
if (valid && !got_next_event) {
|
||||
// data to send to host
|
||||
if (!waiting_for_ack) {
|
||||
// send data
|
||||
event = byterev(event);
|
||||
outuint(c_midi, event);
|
||||
th_count++;
|
||||
waiting_for_ack = 1;
|
||||
}
|
||||
else {
|
||||
event = byterev(event);
|
||||
next_event = event;
|
||||
got_next_event = 1;
|
||||
}
|
||||
}
|
||||
else if (valid) {
|
||||
// printstr("g\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int uout_count = 0;
|
||||
int uin_count = 0;
|
||||
|
||||
void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
|
||||
clock ?clk_midi,
|
||||
chanend c_midi,
|
||||
unsigned cable_number)
|
||||
{
|
||||
int is_ack;
|
||||
unsigned int datum;
|
||||
unsigned symbol = 0x0;
|
||||
unsigned outputting = 0;
|
||||
unsigned time;
|
||||
//unsigned inputPortState, newInputPortState;
|
||||
int waiting_for_ack = 0;
|
||||
// Receiver
|
||||
unsigned rxByte;
|
||||
int rxI;
|
||||
int rxT;
|
||||
int isRX = 0;
|
||||
timer t;
|
||||
timer t2;
|
||||
|
||||
// these two vars make a one place buffer for data going out to host
|
||||
int got_next_event = 0;
|
||||
int next_event;
|
||||
unsigned outputting_symbol, outputted_symbol;
|
||||
|
||||
struct midi_in_parse_state mips;
|
||||
|
||||
// the symbol fifo (to go out of uart)
|
||||
unsigned symbol_fifo[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
|
||||
int rdptr = 0;
|
||||
int wrptr = 0;
|
||||
unsigned rxPT, txPT;
|
||||
int midi_from_host_overflow = 0;
|
||||
int space_left;
|
||||
|
||||
|
||||
//configure_clock_rate(clk_midi, 100, 1);
|
||||
|
||||
configure_out_port_no_ready(p_midi_out, clk_midi, 1);
|
||||
configure_in_port(p_midi_in, clk_midi);
|
||||
|
||||
start_clock(clk_midi);
|
||||
start_port(p_midi_out);
|
||||
start_port(p_midi_in);
|
||||
|
||||
reset_midi_state(mips);
|
||||
|
||||
t :> time;
|
||||
t2 :> rxT;
|
||||
|
||||
#ifndef MIDI_LOOPBACK
|
||||
p_midi_out <: 1; // Start with high bit.
|
||||
// printstr("mout0");
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
while (1) {
|
||||
select
|
||||
{
|
||||
// Input to read the start bit
|
||||
#ifndef MIDI_LOOPBACK
|
||||
#ifdef MIDI_IN_4BIT_PORT
|
||||
case !isRX => p_midi_in when pinseq(0xE) :> void @ rxPT:
|
||||
#else
|
||||
case !isRX => p_midi_in when pinseq(0) :> void @ rxPT:
|
||||
#endif
|
||||
isRX = 1;
|
||||
t2 :> rxT;
|
||||
rxT += (bit_time + bit_time_2);
|
||||
rxPT += (bit_time + bit_time_2); // absorb start bit and set to halfway through the next bit
|
||||
rxI = 0;
|
||||
asm("setc res[%0],1"::"r"(p_midi_in));
|
||||
asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT));
|
||||
break;
|
||||
// Input to read the remaining bits
|
||||
case isRX => t2 when timerafter(rxT) :> int _ :
|
||||
if (rxI++ < 8)
|
||||
{
|
||||
unsigned bit;
|
||||
p_midi_in :> bit;
|
||||
rxByte = (bit << 31) | (rxByte >> 1);
|
||||
rxT += bit_time;
|
||||
rxPT += bit_time;
|
||||
asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned bit;
|
||||
// rcv and check stop bit
|
||||
p_midi_in :> bit;
|
||||
if ((bit & 0x1) == 1)
|
||||
{
|
||||
unsigned valid = 0;
|
||||
unsigned event = 0;
|
||||
uin_count++;
|
||||
rxByte >>= 24;
|
||||
// if (rxByte != outputted_symbol) {
|
||||
// printhexln(rxByte);
|
||||
// printhexln(outputted_symbol);
|
||||
// }
|
||||
|
||||
{valid, event} = midi_in_parse(mips, cable_number, rxByte);
|
||||
if (valid && !got_next_event) {
|
||||
event = byterev(event);
|
||||
// data to send to host - add to fifo
|
||||
if (!waiting_for_ack) {
|
||||
// send data
|
||||
// printstr("uart->decouple: ");
|
||||
outuint(c_midi, event);
|
||||
waiting_for_ack = 1;
|
||||
th_count++;
|
||||
}
|
||||
else {
|
||||
next_event = event;
|
||||
got_next_event = 1;
|
||||
}
|
||||
}
|
||||
else if (valid) {
|
||||
// printstr("g");
|
||||
}
|
||||
|
||||
}
|
||||
isRX = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
// Output
|
||||
// If outputting then feed the bits out one at a time
|
||||
// until symbol is zero expect pattern like 10'b1dddddddd0
|
||||
// This code will leave the output high afterwards due to the stop bit added with makeSymbol
|
||||
case outputting => t when timerafter(time) :> int _:
|
||||
if (symbol == 0)
|
||||
{
|
||||
uout_count++;
|
||||
outputted_symbol = outputting_symbol;
|
||||
// have we got another symbol to send to uart?
|
||||
if (rdptr != wrptr) {
|
||||
outputting_symbol = symbol_fifo[rdptr];
|
||||
symbol = makeSymbol(symbol_fifo[rdptr]);
|
||||
rdptr++;
|
||||
if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1)
|
||||
rdptr = 0;
|
||||
|
||||
space_left = rdptr - wrptr;
|
||||
if (space_left < 0)
|
||||
space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE;
|
||||
|
||||
if (space_left > 3 && midi_from_host_overflow) {
|
||||
midi_from_host_overflow = 0;
|
||||
midi_send_ack(c_midi);
|
||||
}
|
||||
|
||||
p_midi_out <: 1 @ txPT;
|
||||
// printstr("mout1\n");
|
||||
t :> time;
|
||||
time += bit_time;
|
||||
txPT += bit_time;
|
||||
}
|
||||
else
|
||||
outputting = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
time += bit_time;
|
||||
txPT += bit_time;
|
||||
p_midi_out @ txPT <: (symbol & 1);
|
||||
// printstr("mout2\n");
|
||||
symbol >>= 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case midi_get_ack_or_data(c_midi, is_ack, datum):
|
||||
if (is_ack) {
|
||||
// have we got more data to send
|
||||
//printstr("ack\n");
|
||||
if (got_next_event) {
|
||||
//printstr("uart->decouple\n");
|
||||
outuint(c_midi, next_event);
|
||||
th_count++;
|
||||
got_next_event = 0;
|
||||
}
|
||||
else {
|
||||
waiting_for_ack = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int event;
|
||||
unsigned midi[3];
|
||||
unsigned size;
|
||||
int valid;
|
||||
// received data from host
|
||||
event = byterev(datum);
|
||||
mr_count++;
|
||||
#ifdef MIDI_LOOPBACK
|
||||
if (!got_next_event) {
|
||||
// data to send to host
|
||||
if (!waiting_for_ack) {
|
||||
// send data
|
||||
event = byterev(event);
|
||||
outuint(c_midi, event);
|
||||
th_count++;
|
||||
waiting_for_ack = 1;
|
||||
}
|
||||
else {
|
||||
event = byterev(event);
|
||||
next_event = event;
|
||||
got_next_event = 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{midi[0], midi[1], midi[2], size} = midi_out_parse(event);
|
||||
for (int i = 0; i != size; i++) {
|
||||
// add symbol to fifo
|
||||
unsigned sym = midi[i];
|
||||
int new_wrptr = wrptr + 1;
|
||||
|
||||
if (new_wrptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1) {
|
||||
new_wrptr = 0;
|
||||
}
|
||||
|
||||
symbol_fifo[wrptr] = sym;
|
||||
wrptr = new_wrptr;
|
||||
}
|
||||
|
||||
|
||||
space_left = rdptr - wrptr;
|
||||
if (space_left < 0)
|
||||
space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE;
|
||||
|
||||
if (space_left > 3) {
|
||||
midi_send_ack(c_midi);
|
||||
}
|
||||
else {
|
||||
midi_from_host_overflow = 1;
|
||||
}
|
||||
|
||||
if (wrptr != rdptr && !outputting) {
|
||||
outputting_symbol = symbol_fifo[rdptr];
|
||||
symbol = makeSymbol(symbol_fifo[rdptr]);
|
||||
rdptr++;
|
||||
if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1)
|
||||
rdptr = 0;
|
||||
|
||||
if (space_left > 2 && midi_from_host_overflow) {
|
||||
midi_from_host_overflow = 0;
|
||||
midi_send_ack(c_midi);
|
||||
}
|
||||
|
||||
#ifdef MIDI_LOOPBACK
|
||||
handle_byte_from_uart(c_midi, mips, cable_number, got_next_event, next_event, waiting_for_ack, symbol);
|
||||
#else
|
||||
p_midi_out <: 1 @ txPT;
|
||||
t :> time;
|
||||
time += bit_time;
|
||||
txPT += bit_time;
|
||||
outputting = 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
module_usb_midi/src/usb_midi_support.xc
Normal file
3
module_usb_midi/src/usb_midi_support.xc
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <xs1.h>
|
||||
#define __MIDI_IMPL
|
||||
#include "usb_midi.h"
|
||||
Reference in New Issue
Block a user