Merge pull request #232 from YuuichiAkagawa/pr_usbh_midi_031

Update MIDI driver v0.3.1
This commit is contained in:
Kristian Sloth Lauszus 2016-04-27 22:34:10 +02:00
commit 2295571e2d
7 changed files with 252 additions and 93 deletions

View file

@ -320,8 +320,8 @@ HID devices are also supported by the library. However these require you to writ
The library support MIDI devices.
You can convert USB MIDI keyboard to legacy serial MIDI.
* [USB_MIDI_converter.ino](USBH_MIDI/USB_MIDI_converter)
* [USB_MIDI_converter_multi.ino](USBH_MIDI/USB_MIDI_converter_multi)
* [USB_MIDI_converter.ino](examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino)
* [USB_MIDI_converter_multi.ino](examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino)
For information see the following page: <http://yuuichiakagawa.github.io/USBH_MIDI/>.

View file

@ -64,7 +64,7 @@ void loop()
// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll()
{
byte outBuf[ 3 ];
uint8_t outBuf[ 3 ];
uint8_t size;
do {

View file

@ -66,7 +66,7 @@ void loop()
// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll()
{
byte outBuf[ 3 ];
uint8_t outBuf[ 3 ];
uint8_t size;
do {

View file

@ -40,15 +40,28 @@ MIDI_CREATE_DEFAULT_INSTANCE();
//////////////////////////
USB Usb;
USBH_MIDI Midi(&Usb);
USBH_MIDI Midi(&Usb);
void MIDI_poll();
void doDelay(unsigned long t1, unsigned long t2, unsigned long delayTime);
//If you want handle System Exclusive message, enable this #define otherwise comment out it.
#define USBH_MIDI_SYSEX_ENABLE
#ifdef USBH_MIDI_SYSEX_ENABLE
MidiSysEx sysExData;
//SysEx:
void handle_sysex( byte* sysexmsg, unsigned sizeofsysex) {
Midi.SendSysEx(sysexmsg, sizeofsysex);
}
#endif
void setup()
{
MIDI.begin(MIDI_CHANNEL_OMNI);
#ifdef USBH_MIDI_SYSEX_ENABLE
MIDI.setHandleSystemExclusive(handle_sysex);
#endif
if (Usb.Init() == -1) {
while (1); //halt
}//if (Usb.Init() == -1...
@ -67,13 +80,17 @@ void loop()
MIDI_poll();
if (MIDI.read()) {
msg[0] = MIDI.getType();
if ( msg[0] == 0xf0 ) { //SysEX
//TODO
//SysEx implementation is not yet.
} else {
msg[1] = MIDI.getData1();
msg[2] = MIDI.getData2();
Midi.SendData(msg, 0);
switch (msg[0]) {
case midi::ActiveSensing :
break;
case midi::SystemExclusive :
//SysEx is handled by event.
break;
default :
msg[1] = MIDI.getData1();
msg[2] = MIDI.getData2();
Midi.SendData(msg, 0);
break;
}
}
}
@ -84,13 +101,53 @@ void loop()
// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll()
{
byte outBuf[ 3 ];
uint8_t size;
#ifdef USBH_MIDI_SYSEX_ENABLE
uint8_t recvBuf[MIDI_EVENT_PACKET_SIZE];
uint8_t rcode = 0; //return code
uint16_t rcvd;
uint8_t readPtr = 0;
if ( (size = Midi.RecvData(outBuf)) > 0 ) {
//MIDI Output
_MIDI_SERIAL_PORT.write(outBuf, size);
rcode = Midi.RecvData( &rcvd, recvBuf);
//data check
if (rcode != 0) return;
if ( recvBuf[0] == 0 && recvBuf[1] == 0 && recvBuf[2] == 0 && recvBuf[3] == 0 ) {
return ;
}
uint8_t *p = recvBuf;
while (readPtr < MIDI_EVENT_PACKET_SIZE) {
if (*p == 0 && *(p + 1) == 0) break; //data end
MidiSysEx::Status rc = sysExData.set(p);
switch (rc) {
case MidiSysEx::nonsysex : //No SysEx message send data to Serial MIDI
p++;
size = Midi.lookupMsgSize(*p);
_MIDI_SERIAL_PORT.write(p, size);
p += 3;
break;
case MidiSysEx::done : //SysEx end. send data to Serial MIDI
_MIDI_SERIAL_PORT.write(sysExData.get(), sysExData.getSize());
/* FALLTHROUGH */
case MidiSysEx::overflow : //SysEx buffer over. ignore and flush buffer.
sysExData.clear();
/* FALLTHROUGH */
default:
p += 4;
break;
}
readPtr += 4;
}
#else
uint8_t outBuf[ 3 ];
do {
if ( (size = Midi.RecvData(outBuf)) > 0 ) {
//MIDI Output
_MIDI_SERIAL_PORT.write(outBuf, size);
}
} while (size > 0);
#endif
}
// Delay time (max 16383 us)

View file

@ -61,7 +61,7 @@ void loop()
// Poll USB MIDI Controler
void MIDI_poll()
{
byte inBuf[ 3 ];
uint8_t inBuf[ 3 ];
//first call?
if (Midi.vid != vid || Midi.pid != pid) {

218
usbh_midi.cpp Executable file → Normal file
View file

@ -116,11 +116,10 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed)
EpInfo *oldep_ptr = NULL;
uint8_t num_of_conf; // number of configurations
USBTRACE("\rMIDI Init\r\n");
// get memory address of USB device address pool
AddressPool &addrPool = pUsb->GetAddressPool();
#ifdef DEBUG_USB_HOST
USBTRACE("\rMIDI Init\r\n");
#endif
// check if address has already been assigned to an instance
if (bAddress) {
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
@ -169,9 +168,8 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed)
bAddress = 0;
return rcode;
}//if (rcode...
#ifdef DEBUG_USB_HOST
USBTRACE2("Addr:", bAddress);
#endif
p->lowspeed = false;
//get pointer to assigned address record
@ -186,27 +184,29 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed)
// Assign epInfo to epinfo pointer
rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
if (rcode) {
#ifdef DEBUG_USB_HOST
USBTRACE("setEpInfoEntry failed");
#endif
goto FailSetDevTblEntry;
}
#ifdef DEBUG_USB_HOST
USBTRACE2("NC:", num_of_conf);
#endif
USBTRACE("VID:"), D_PrintHex(vid, 0x80);
USBTRACE(" PID:"), D_PrintHex(pid, 0x80);
USBTRACE2(" #Conf:", num_of_conf);
for (uint8_t i=0; i<num_of_conf; i++) {
parseConfigDescr(bAddress, i);
if (bNumEP > 1)
break;
} // for
#ifdef DEBUG_USB_HOST
USBTRACE2("NumEP:", bNumEP);
#endif
if( bConfNum == 0 ){ //Device not found.
USBTRACE2("\r\nNumEP:", bNumEP);
if( bNumEP < 3 ){ //Device not found.
rcode = 0xff;
goto FailGetConfDescr;
}
if( !isMidiFound ){ //MIDI Device not found. Try first Bulk transfer device
if( !isMidiFound ){ //MIDI Device not found. Try last Bulk transfer device
USBTRACE("MIDI not found. Attempts bulk device\r\n");
epInfo[epDataInIndex].epAddr = epInfo[epDataInIndexVSP].epAddr;
epInfo[epDataInIndex].maxPktSize = epInfo[epDataInIndexVSP].maxPktSize;
epInfo[epDataOutIndex].epAddr = epInfo[epDataOutIndexVSP].epAddr;
@ -215,18 +215,17 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed)
// Assign epInfo to epinfo pointer
rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
#ifdef DEBUG_USB_HOST
USBTRACE2("Conf:", bConfNum);
#endif
USBTRACE2("EPin :", (uint8_t)(epInfo[epDataInIndex].epAddr + 0x80));
USBTRACE2("EPout:", epInfo[epDataOutIndex].epAddr);
// Set Configuration Value
rcode = pUsb->setConf(bAddress, 0, bConfNum);
if (rcode) {
goto FailSetConfDescr;
}
#ifdef DEBUG_USB_HOST
USBTRACE("Init done.");
#endif
bPollEnable = true;
USBTRACE("Init done.\r\n");
return 0;
FailGetDevDescr:
FailSetDevTblEntry:
@ -237,13 +236,13 @@ FailSetConfDescr:
}
/* get and parse config descriptor */
void USBH_MIDI::parseConfigDescr( byte addr, byte conf )
void USBH_MIDI::parseConfigDescr( uint8_t addr, uint8_t conf )
{
uint8_t buf[ DESC_BUFF_SIZE ];
uint8_t* buf_ptr = buf;
byte rcode;
byte descr_length;
byte descr_type;
uint8_t rcode;
uint8_t descr_length;
uint8_t descr_type;
unsigned int total_length;
USB_ENDPOINT_DESCRIPTOR *epDesc;
boolean isMidi = false;
@ -273,18 +272,28 @@ void USBH_MIDI::parseConfigDescr( byte addr, byte conf )
bConfNum = buf_ptr[5];
break;
case USB_DESCRIPTOR_INTERFACE :
USBTRACE("\r\nConf:"), D_PrintHex(bConfNum, 0x80);
USBTRACE(" Int:"), D_PrintHex(buf_ptr[2], 0x80);
USBTRACE(" Alt:"), D_PrintHex(buf_ptr[3], 0x80);
USBTRACE(" EPs:"), D_PrintHex(buf_ptr[4], 0x80);
USBTRACE(" IntCl:"), D_PrintHex(buf_ptr[5], 0x80);
USBTRACE(" IntSubCl:"), D_PrintHex(buf_ptr[6], 0x80);
USBTRACE("\r\n");
if( buf_ptr[5] == USB_CLASS_AUDIO && buf_ptr[6] == USB_SUBCLASS_MIDISTREAMING ) { //p[5]; bInterfaceClass = 1(Audio), p[6]; bInterfaceSubClass = 3(MIDI Streaming)
isMidiFound = true; //MIDI device found.
isMidi = true;
USBTRACE("MIDI Device\r\n");
}else{
#ifdef DEBUG_USB_HOST
USBTRACE("No MIDI Device\n");
#endif
isMidi = false;
USBTRACE("No MIDI Device\r\n");
}
break;
case USB_DESCRIPTOR_ENDPOINT :
epDesc = (USB_ENDPOINT_DESCRIPTOR *)buf_ptr;
USBTRACE("-EPAddr:"), D_PrintHex(epDesc->bEndpointAddress, 0x80);
USBTRACE(" bmAttr:"), D_PrintHex(epDesc->bmAttributes, 0x80);
USBTRACE2(" MaxPktSz:", (uint8_t)epDesc->wMaxPacketSize);
if ((epDesc->bmAttributes & 0x02) == 2) {//bulk
uint8_t index;
if( isMidi )
@ -333,7 +342,7 @@ uint8_t USBH_MIDI::RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr)
/* Receive data from MIDI device */
uint8_t USBH_MIDI::RecvData(uint8_t *outBuf)
{
byte rcode = 0; //return code
uint8_t rcode = 0; //return code
uint16_t rcvd;
if( bPollEnable == false ) return false;
@ -370,10 +379,10 @@ RecvData_return_from_buffer:
}
/* Send data to MIDI device */
uint8_t USBH_MIDI::SendData(uint8_t *dataptr, byte nCable)
uint8_t USBH_MIDI::SendData(uint8_t *dataptr, uint8_t nCable)
{
byte buf[4];
byte msg;
uint8_t buf[4];
uint8_t msg;
msg = dataptr[0];
// SysEx long message ?
@ -407,7 +416,7 @@ uint8_t USBH_MIDI::SendData(uint8_t *dataptr, byte nCable)
buf[3] = 0;
break;
//1 bytes message
//1 byte message
case 1 :
default :
buf[2] = 0;
@ -420,20 +429,13 @@ uint8_t USBH_MIDI::SendData(uint8_t *dataptr, byte nCable)
#ifdef DEBUG_USB_HOST
void USBH_MIDI::PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr )
{
Notify(PSTR("Endpoint descriptor:"), 0x80);
Notify(PSTR("\r\nLength:\t\t"), 0x80);
PrintHex<uint8_t>(ep_ptr->bLength, 0x80);
Notify(PSTR("\r\nType:\t\t"), 0x80);
PrintHex<uint8_t>(ep_ptr->bDescriptorType, 0x80);
Notify(PSTR("\r\nAddress:\t"), 0x80);
PrintHex<uint8_t>(ep_ptr->bEndpointAddress, 0x80);
Notify(PSTR("\r\nAttributes:\t"), 0x80);
PrintHex<uint8_t>(ep_ptr->bmAttributes, 0x80);
Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
PrintHex<uint16_t>(ep_ptr->wMaxPacketSize, 0x80);
Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
PrintHex<uint8_t>(ep_ptr->bInterval, 0x80);
Notify(PSTR("\r\n"), 0x80);
USBTRACE("Endpoint descriptor:\r\n");
USBTRACE2(" Length:\t", ep_ptr->bLength);
USBTRACE2(" Type:\t\t", ep_ptr->bDescriptorType);
USBTRACE2(" Address:\t", ep_ptr->bEndpointAddress);
USBTRACE2(" Attributes:\t", ep_ptr->bmAttributes);
USBTRACE2(" MaxPktSize:\t", ep_ptr->wMaxPacketSize);
USBTRACE2(" Poll Intrv:\t", ep_ptr->bInterval);
}
#endif
@ -465,7 +467,7 @@ uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg)
msgSize = 2;
break;
//1 bytes messages
//1 byte messages
case 0xf8 : //system realtime message
case 0xf9 : //system realtime message
case 0xfa : //system realtime message
@ -508,43 +510,121 @@ unsigned int USBH_MIDI::countSysExDataSize(uint8_t *dataptr)
}
/* Send SysEx message to MIDI device */
uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, unsigned int datasize, byte nCable)
uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, unsigned int datasize, uint8_t nCable)
{
byte buf[4];
uint8_t buf[64];
uint8_t rc;
unsigned int n = datasize;
unsigned int pktSize = (n*10/3+7)/10*4; //Calculate total USB MIDI packet size
uint8_t wptr = 0;
uint8_t maxpkt = epInfo[epDataInIndex].maxPktSize;
USBTRACE("SendSysEx:\r\t");
USBTRACE2(" Length:\t", datasize);
USBTRACE2(" Total pktSize:\t", pktSize);
while(n > 0) {
//Byte 0
buf[0] = (nCable << 4) | 0x4; //x4 SysEx starts or continues
buf[wptr] = (nCable << 4) | 0x4; //x4 SysEx starts or continues
switch ( n ) {
case 1 :
buf[0] = (nCable << 4) | 0x5; //x5 SysEx ends with following single byte.
buf[1] = *(dataptr++);
buf[2] = 0x00;
buf[3] = 0x00;
case 1 :
buf[wptr++] = (nCable << 4) | 0x5; //x5 SysEx ends with following single byte.
buf[wptr++] = *(dataptr++);
buf[wptr++] = 0x00;
buf[wptr++] = 0x00;
n = n - 1;
break;
case 2 :
buf[0] = (nCable << 4) | 0x6; //x6 SysEx ends with following two bytes.
buf[1] = *(dataptr++);
buf[2] = *(dataptr++);
buf[3] = 0x00;
case 2 :
buf[wptr++] = (nCable << 4) | 0x6; //x6 SysEx ends with following two bytes.
buf[wptr++] = *(dataptr++);
buf[wptr++] = *(dataptr++);
buf[wptr++] = 0x00;
n = n - 2;
break;
case 3 :
buf[0] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes.
default :
buf[1] = *(dataptr++);
buf[2] = *(dataptr++);
buf[3] = *(dataptr++);
case 3 :
buf[wptr] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes.
default :
wptr++;
buf[wptr++] = *(dataptr++);
buf[wptr++] = *(dataptr++);
buf[wptr++] = *(dataptr++);
n = n - 3;
break;
}
rc = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, 4, buf);
if(rc != 0)
break;
if( wptr >= maxpkt || n == 0 ){ //Reach a maxPktSize or data end.
USBTRACE2(" wptr:\t", wptr);
if( (rc = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, wptr, buf)) != 0 ){
break;
}
wptr = 0; //rewind data pointer
}
}
return(rc);
}
/* Send raw data to MIDI device */
uint8_t USBH_MIDI::SendRawData(uint16_t bytes_send, uint8_t *dataptr)
{
return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes_send, dataptr);
}
//
// System Exclusive packet data management class
//
MidiSysEx::MidiSysEx()
{
clear();
}
void MidiSysEx::clear()
{
pos = 0;
buf[0] = 0;
}
MidiSysEx::Status MidiSysEx::set(uint8_t *p)
{
MidiSysEx::Status rc = MidiSysEx::ok;
uint8_t cin = *(p) & 0x0f;
//SysEx message?
if( (cin & 0xc) != 4 ) return MidiSysEx::nonsysex;
switch(cin) {
case 4:
case 7:
if( pos+2 < MIDI_EVENT_PACKET_SIZE ) {
buf[pos++] = *(p+1);
buf[pos++] = *(p+2);
buf[pos++] = *(p+3);
}else{
rc = MidiSysEx::overflow;
}
break;
case 5:
if( pos+1 < MIDI_EVENT_PACKET_SIZE ) {
buf[pos++] = *(p+1);
buf[pos++] = *(p+2);
}else{
rc = MidiSysEx::overflow;
}
break;
case 6:
if( pos < MIDI_EVENT_PACKET_SIZE ) {
buf[pos++] = *(p+1);
}else{
rc = MidiSysEx::overflow;
}
break;
default:
break;
}
//SysEx end?
if((cin & 0x3) != 0) {
rc = MidiSysEx::done;
}
return(rc);
}

34
usbh_midi.h Executable file → Normal file
View file

@ -26,6 +26,7 @@
#if !defined(_USBH_MIDI_H_)
#define _USBH_MIDI_H_
//#define DEBUG_USB_HOST
#include "Usb.h"
#define MIDI_MAX_ENDPOINTS 5 //endpoint 0, bulk_IN(MIDI), bulk_OUT(MIDI), bulk_IN(VSP), bulk_OUT(VSP)
@ -36,9 +37,6 @@ class USBH_MIDI;
class USBH_MIDI : public USBDeviceConfig
{
private:
uint8_t lookupMsgSize(uint8_t midiMsg);
protected:
static const uint8_t epDataInIndex; // DataIn endpoint index(MIDI)
static const uint8_t epDataOutIndex; // DataOUT endpoint index(MIDI)
@ -59,7 +57,7 @@ protected:
uint8_t recvBuf[MIDI_EVENT_PACKET_SIZE];
uint8_t readPtr;
void parseConfigDescr(byte addr, byte conf);
void parseConfigDescr(uint8_t addr, uint8_t conf);
unsigned int countSysExDataSize(uint8_t *dataptr);
#ifdef DEBUG_USB_HOST
void PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr );
@ -70,8 +68,10 @@ public:
// Methods for recieving and sending data
uint8_t RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr);
uint8_t RecvData(uint8_t *outBuf);
uint8_t SendData(uint8_t *dataptr, byte nCable=0);
uint8_t SendSysEx(uint8_t *dataptr, unsigned int datasize, byte nCable=0);
uint8_t SendData(uint8_t *dataptr, uint8_t nCable=0);
uint8_t lookupMsgSize(uint8_t midiMsg);
uint8_t SendSysEx(uint8_t *dataptr, unsigned int datasize, uint8_t nCable=0);
uint8_t SendRawData(uint16_t bytes_send, uint8_t *dataptr);
// backward compatibility functions
inline uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr){ return RecvData(bytes_rcvd, dataptr); };
inline uint8_t RcvData(uint8_t *outBuf){ return RecvData(outBuf); };
@ -82,4 +82,26 @@ public:
virtual uint8_t GetAddress() { return bAddress; };
};
//
// System Exclusive packet data management class
//
class MidiSysEx {
private:
uint8_t pos;
uint8_t buf[MIDI_EVENT_PACKET_SIZE];
public:
typedef enum {
nonsysex = 0,
ok = 1,
done = 0xfe,
overflow = 0xff
} Status;
MidiSysEx();
void clear();
MidiSysEx::Status set(uint8_t *p);
inline uint8_t *get(){return buf;};
inline uint8_t getSize(){return pos;};
};
#endif //_USBH_MIDI_H_