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; } } }