by on
Dorkbot

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

	}


}

Leave a Reply

  • (will not be published)