I am trying to digest this document.
http://www.usb.org/developers/devclass_docs/midi10.pdf
USB encapsulates midi data into 4 byte chunks and adds the notion of virtual cables.
The first byte in the packet is the cable number and something called the Code Index Number. The remaining 3 bytes are either midi or padding depending on the packet.
Most of the relevant midi status commands have a corresponding Code Index Number.
| USB Midi Codes with 1to1 Midi Status Mapping | |||
|---|---|---|---|
| c=virtual cable,n=midi channel | |||
| CIN | Status | Bytes | Description |
| 0xc8 | 0x8n | 2 | Note ON |
| 0xc9 | 0x9n | 2 | Note OFF |
| 0xcA | 0xAn | 2 | Key Pressure |
| 0xcB | 0xBn | 2 | Control Change |
| 0xcC | 0xCn | 1 | Program Change |
| 0xcD | 0xDn | 1 | Channel Pressure |
| 0xcE | 0xEx | 2 | Pitch Wheel |
The rest of it is a mess of what to do with the odd sysex bytes. To pass the input stream to midi should look something like this.
/* Select the Stream Out Endpoint */
Endpoint_SelectEndpoint(MIDI_STREAM_OUT_EPNUM);
if (Endpoint_ReadWriteAllowed())
{
/* Read the recieved data endpoint into the transmission buffer */
while (Endpoint_BytesInEndpoint())
{ cin=Endpoint_Read_Byte();
midibyte[0]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
midibyte[1]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
midibyte[2]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
i=0;
switch (cin&0x0F) { //strip off cable data.
case CIN_3BYTE_SYSTEM_COMMON:
case CIN_SYSEX_ENDS_IN_THREE:
case CIN_NOTE_ON:
case CIN_NOTE_OFF:
case CIN_PRESSURE:
case CIN_SYSEX_CONTINUES:
case CIN_PITCH_WHEEL:
case CIN_CONTROL_CHANGE:
while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
case CIN_SYSEX_ENDS_IN_TWO:
case CIN_2BYTE_SYSTEM_COMMON:
case CIN_PROGRAM_CHANGE:
case CIN_CHANNEL_PRESSURE:
while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
case CIN_SYSEX_ENDS_IN_ONE:
case CIN_SYSTEM_1BYTE:
while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
case CIN_RESERVED_FUNCTION_CODES:
case CIN_RESERVED_CABLE_EVENTS:
break;
}
Flash_RXD(); // do the pov thing
}
/* Clear the endpoint buffer */
Endpoint_ClearCurrentBank();
}
In other words since the midi data is complete in the packets. You just need to determine how many of the 4 bytes to send and strip off the cable information.
Going the other way is a little more convoluted.
void ParseMidiByte(const uint8_t theByte) {
if (theByte&0x80) {
thePacketIndex=0;
switch (theByte&0xF0) {
case MIDI_STATUS_NOTE_ON:
case MIDI_STATUS_NOTE_OFF:
case MIDI_STATUS_PRESSURE:
case MIDI_STATUS_CONTROL_CHANGE:
case MIDI_STATUS_PITCH_WHEEL:
thePacket[thePacketIndex++] = theCable|(theByte>>4);
thePacket[thePacketIndex++] = theByte;
theMidiState = midi_state_needs_2bytes;
break;
case MIDI_STATUS_PROGRAM_CHANGE:
case MIDI_STATUS_CHANNEL_PRESSURE:
thePacket[thePacketIndex++] = theCable|(theByte>>4);
thePacket[thePacketIndex++] = theByte;
theMidiState = midi_state_needs_1byte;
break;
case MIDI_STATUS_SYSTEM:
if (theByte & 0x08) {// System Real Time Messages
Endpoint_Write_Byte(theCable|CIN_SYSTEM_1BYTE);
Endpoint_Write_Byte(theByte);
Endpoint_Write_Byte(0);
Endpoint_Write_Byte(0);
Endpoint_ClearCurrentBank();
} else if (theByte == 0xF0) {
thePacket[thePacketIndex++] = theCable|CIN_SYSTEM_1BYTE;
thePacket[thePacketIndex++] = theByte;
theMidiState=midi_state_in_sysex;
} else if (theByte == 0xF3) {//Song Select
thePacket[thePacketIndex++] = theCable|CIN_2BYTE_SYSTEM_COMMON;
thePacket[thePacketIndex++] = theByte;
theMidiState=midi_state_needs_1byte;
} else if (theByte == 0xF2) { //Song Position Pointer
thePacket[thePacketIndex++] = theCable|CIN_3BYTE_SYSTEM_COMMON;
thePacket[thePacketIndex++] = theByte;
theMidiState=midi_state_needs_2bytes;
} else if (theByte == MIDI_EOX) {
if (thePacketIndex==3) {
thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_THREE;
} else if (thePacketIndex==2) {
thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_TWO;
} else {
thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_ONE;
}
thePacket[thePacketIndex]=0xF7;
theMidiState=midi_state_idle;
thePacketIndex=4;
} else {
// undefined -- bin it.
theMidiState=midi_state_idle;
}
}
} else {
switch(theMidiState) {
case midi_state_needs_2bytes:
thePacket[thePacketIndex++]=theByte;
theMidiState = midi_state_needs_1byte;
break;
case midi_state_needs_1byte:
thePacket[thePacketIndex++]=theByte;
thePacketIndex=4;
theMidiState = midi_state_idle;
break; //this was missing in the first post
case midi_state_in_sysex:
thePacket[thePacketIndex++]=theByte;
break;
}
if(thePacketIndex==4){
while (!(Endpoint_ReadWriteAllowed()));
Endpoint_Write_Byte(thePacket[0]);
Endpoint_Write_Byte(thePacket[1]);
Endpoint_Write_Byte(thePacket[2]);
Endpoint_Write_Byte(thePacket[3]);
Endpoint_ClearCurrentBank();
thePacket[0]=thePacket[1]=thePacket[2]=thePacket[3]=0;
thePacketIndex=0;
}
}
}