diff --git a/README.md b/README.md index 3d230cf6..f053fffb 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ You can convert USB MIDI keyboard to legacy serial MIDI. * [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: . +For more information see : . ### [amBX Library](AMBX.cpp) diff --git a/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino b/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino index 377a5a63..645131ad 100644 --- a/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino +++ b/examples/USBH_MIDI/USBH_MIDI_dump/USBH_MIDI_dump.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI dump utility - * Copyright (C) 2013-2017 Yuuichi Akagawa + * Copyright (C) 2013-2021 Yuuichi Akagawa * * for use with USB Host Shield 2.0 from Circuitsathome.com * https://github.com/felis/USB_Host_Shield_2.0 @@ -13,35 +13,37 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - USB Usb; -//USBHub Hub(&Usb); +USBHub Hub(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); -uint16_t pid, vid; +void onInit() +{ + char buf[20]; + uint16_t vid = Midi.idVendor(); + uint16_t pid = Midi.idProduct(); + sprintf(buf, "VID:%04X, PID:%04X", vid, pid); + Serial.println(buf); +} void setup() { - vid = pid = 0; Serial.begin(115200); if (Usb.Init() == -1) { while (1); //halt }//if (Usb.Init() == -1... delay( 200 ); + + // Register onInit() function + Midi.attachOnInit(onInit); } void loop() { Usb.Task(); - //uint32_t t1 = (uint32_t)micros(); if ( Midi ) { MIDI_poll(); } @@ -50,23 +52,16 @@ void loop() // Poll USB MIDI Controler and send to serial MIDI void MIDI_poll() { - char buf[20]; - uint8_t bufMidi[64]; + char buf[16]; + uint8_t bufMidi[MIDI_EVENT_PACKET_SIZE]; uint16_t rcvd; - if (Midi.idVendor() != vid || Midi.idProduct() != pid) { - vid = Midi.idVendor(); - pid = Midi.idProduct(); - sprintf(buf, "VID:%04X, PID:%04X", vid, pid); - Serial.println(buf); - } if (Midi.RecvData( &rcvd, bufMidi) == 0 ) { uint32_t time = (uint32_t)millis(); - sprintf(buf, "%04X%04X: ", (uint16_t)(time >> 16), (uint16_t)(time & 0xFFFF)); // Split variable to prevent warnings on the ESP8266 platform + sprintf(buf, "%04X%04X:%3d:", (uint16_t)(time >> 16), (uint16_t)(time & 0xFFFF), rcvd); // Split variable to prevent warnings on the ESP8266 platform Serial.print(buf); - Serial.print(rcvd); - Serial.print(':'); - for (int i = 0; i < 64; i++) { + + for (int i = 0; i < MIDI_EVENT_PACKET_SIZE; i++) { sprintf(buf, " %02X", bufMidi[i]); Serial.print(buf); } diff --git a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino index 1f5d1561..4f988183 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter/USB_MIDI_converter.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2020 Yuuichi Akagawa + * Copyright (C) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -13,12 +13,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - #ifdef USBCON #define _MIDI_SERIAL_PORT Serial1 #else @@ -40,7 +34,6 @@ USB Usb; USBH_MIDI Midi(&Usb); void MIDI_poll(); -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime); void setup() { @@ -55,12 +48,12 @@ void setup() void loop() { Usb.Task(); - uint32_t t1 = (uint32_t)micros(); + if ( Midi ) { MIDI_poll(); } - //delay(1ms) - doDelay(t1, (uint32_t)micros(), 1000); + //delay(1ms) if you want + //delayMicroseconds(1000); } // Poll USB MIDI Controler and send to serial MIDI @@ -79,14 +72,3 @@ void MIDI_poll() } } while (size > 0); } - -// Delay time (max 16383 us) -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime) -{ - uint32_t t3; - - t3 = t2 - t1; - if ( t3 < delayTime ) { - delayMicroseconds(delayTime - t3); - } -} diff --git a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino index 5c841a0c..ebbf63ad 100644 --- a/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino +++ b/examples/USBH_MIDI/USB_MIDI_converter_multi/USB_MIDI_converter_multi.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI to Legacy Serial MIDI converter - * Copyright (C) 2012-2020 Yuuichi Akagawa + * Copyright (C) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -13,12 +13,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - #ifdef USBCON #define _MIDI_SERIAL_PORT Serial1 #else @@ -41,8 +35,7 @@ USBHub Hub1(&Usb); USBH_MIDI Midi1(&Usb); USBH_MIDI Midi2(&Usb); -void MIDI_poll(); -void doDelay(uint32_t t1, uint32_t t2, uint32_t delayTime); +void MIDI_poll(USBH_MIDI &Midi); void setup() { @@ -57,15 +50,15 @@ void setup() void loop() { Usb.Task(); - uint32_t t1 = (uint32_t)micros(); + if ( Midi1 ) { MIDI_poll(Midi1); } if ( Midi2 ) { MIDI_poll(Midi2); } - //delay(1ms) - doDelay(t1, (uint32_t)micros(), 1000); + //delay(1ms) if you want + //delayMicroseconds(1000); } // Poll USB MIDI Controler and send to serial MIDI diff --git a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino index 18c7cc8a..ece2774f 100644 --- a/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino +++ b/examples/USBH_MIDI/bidirectional_converter/bidirectional_converter.ino @@ -16,12 +16,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - //Arduino MIDI library v4.2 compatibility #ifdef MIDI_CREATE_DEFAULT_INSTANCE MIDI_CREATE_DEFAULT_INSTANCE(); @@ -44,7 +38,6 @@ MIDI_CREATE_DEFAULT_INSTANCE(); ////////////////////////// USB Usb; -//USBHub Hub1(&Usb); USBH_MIDI Midi(&Usb); void MIDI_poll(); diff --git a/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino b/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino index c0e2afab..0b4e0bed 100644 --- a/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino +++ b/examples/USBH_MIDI/eVY1_sample/eVY1_sample.ino @@ -1,7 +1,7 @@ /* ******************************************************************************* * eVY1 Shield sample - Say 'Konnichiwa' - * Copyright (C) 2014-2016 Yuuichi Akagawa + * Copyright (C) 2014-2021 Yuuichi Akagawa * * This is sample program. Do not expect perfect behavior. ******************************************************************************* @@ -9,12 +9,6 @@ #include #include -// Satisfy the IDE, which needs to see the include statment in the ino too. -#ifdef dobogusinclude -#include -#endif -#include - USB Usb; //USBHub Hub(&Usb); USBH_MIDI Midi(&Usb); @@ -23,7 +17,6 @@ void MIDI_poll(); void noteOn(uint8_t note); void noteOff(uint8_t note); -uint16_t pid, vid; uint8_t exdata[] = { 0xf0, 0x43, 0x79, 0x09, 0x00, 0x50, 0x10, 'k', ' ', 'o', ',', //Ko @@ -34,15 +27,22 @@ uint8_t exdata[] = { 0x00, 0xf7 }; +void onInit() +{ + // Send Phonetic symbols via SysEx + Midi.SendSysEx(exdata, sizeof(exdata)); + delay(500); +} + void setup() { - vid = pid = 0; - Serial.begin(115200); - if (Usb.Init() == -1) { while (1); //halt }//if (Usb.Init() == -1... delay( 200 ); + + // Register onInit() function + Midi.attachOnInit(onInit); } void loop() @@ -61,13 +61,6 @@ void loop() void MIDI_poll() { uint8_t inBuf[ 3 ]; - - //first call? - if (Midi.idVendor() != vid || Midi.idProduct() != pid) { - vid = Midi.idVendor(); pid = Midi.idProduct(); - Midi.SendSysEx(exdata, sizeof(exdata)); - delay(500); - } Midi.RecvData(inBuf); } diff --git a/usbh_midi.cpp b/usbh_midi.cpp index 4de21770..e7bfcaf1 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI class driver for USB Host Shield 2.0 Library - * Copyright (c) 2012-2018 Yuuichi Akagawa + * Copyright (c) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -25,6 +25,9 @@ */ #include "usbh_midi.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data + ////////////////////////// // MIDI MESAGES // midi.org/techspecs/ @@ -79,24 +82,16 @@ //| 0xF | 1 |Single Byte //+-----+-----------+------------------------------------------------------------------- -const uint8_t USBH_MIDI::epDataInIndex = 1; -const uint8_t USBH_MIDI::epDataOutIndex = 2; -const uint8_t USBH_MIDI::epDataInIndexVSP = 3; -const uint8_t USBH_MIDI::epDataOutIndexVSP = 4; - USBH_MIDI::USBH_MIDI(USB *p) : pUsb(p), bAddress(0), -bNumEP(1), bPollEnable(false), -isMidiFound(false), readPtr(0) { // initialize endpoint data structures for(uint8_t i=0; igetConfDescr(bAddress, 0, i, &midiDescParser); + if(rcode) // Check error code goto FailGetConfDescr; - if (bNumEP > 1) + bNumEP += midiDescParser.getNumEPs(); + if(bNumEP > 1) {// All endpoints extracted + bConfNum = midiDescParser.getConfValue(); break; - } // for - - USBTRACE2("\r\nNumEP:", bNumEP); + } + } + USBTRACE2("STEP1: MIDI,NumEP:", bNumEP); + //Found the MIDI device? + if( bNumEP == 1 ){ //Device not found. + USBTRACE("MIDI not found.\r\nSTEP2: Attempts vendor specific bulk device\r\n"); + // STEP2: Check if attached device is a MIDI device and fill endpoint data structure + for(uint8_t i = 0; i < num_of_conf; i++) { + MidiDescParser midiDescParser(this, false); // Allow all devices, vendor specific class with Bulk transfer + rcode = pUsb->getConfDescr(bAddress, 0, i, &midiDescParser); + if(rcode) // Check error code + goto FailGetConfDescr; + bNumEP += midiDescParser.getNumEPs(); + if(bNumEP > 1) {// All endpoints extracted + bConfNum = midiDescParser.getConfValue(); + break; + } + } + USBTRACE2("\r\nSTEP2: Vendor,NumEP:", bNumEP); + } if( bNumEP < 2 ){ //Device not found. rcode = 0xff; goto FailGetConfDescr; } - 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; - epInfo[epDataOutIndex].maxPktSize = epInfo[epDataOutIndexVSP].maxPktSize; - } - // Assign epInfo to epinfo pointer rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); USBTRACE2("Conf:", bConfNum); @@ -242,9 +255,12 @@ uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed) // Set Configuration Value rcode = pUsb->setConf(bAddress, 0, bConfNum); - if (rcode) { + if (rcode) goto FailSetConfDescr; - } + + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + bPollEnable = true; USBTRACE("Init done.\r\n"); return 0; @@ -256,92 +272,10 @@ FailSetConfDescr: return rcode; } -/* get and parse config descriptor */ -uint8_t USBH_MIDI::parseConfigDescr( uint8_t addr, uint8_t conf ) -{ - uint8_t buf[ DESC_BUFF_SIZE ]; - uint8_t* buf_ptr = buf; - uint8_t rcode; - uint8_t descr_length; - uint8_t descr_type; - uint16_t total_length; - USB_ENDPOINT_DESCRIPTOR *epDesc; - bool isMidi = false; - - // get configuration descriptor (get descriptor size only) - rcode = pUsb->getConfDescr( addr, 0, 4, conf, buf ); - if( rcode ){ - return rcode; - } - total_length = buf[2] | ((int)buf[3] << 8); - if( total_length > DESC_BUFF_SIZE ) { //check if total length is larger than buffer - total_length = DESC_BUFF_SIZE; - } - - // get configuration descriptor (all) - rcode = pUsb->getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor - if( rcode ){ - return rcode; - } - - //parsing descriptors - while( buf_ptr < buf + total_length ) { - descr_length = *( buf_ptr ); - descr_type = *( buf_ptr + 1 ); - switch( descr_type ) { - case USB_DESCRIPTOR_CONFIGURATION : - 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{ - 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 & bTransferTypeMask) == USB_TRANSFER_TYPE_BULK) {//bulk - uint8_t index; - if( isMidi ) - index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; - else - index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndexVSP : epDataOutIndexVSP; - epInfo[index].epAddr = (epDesc->bEndpointAddress & 0x0F); - epInfo[index].maxPktSize = (uint8_t)epDesc->wMaxPacketSize; - bNumEP ++; -#ifdef DEBUG_USB_HOST - PrintEndpointDescriptor(epDesc); -#endif - } - break; - default: - break; - }//switch( descr_type - buf_ptr += descr_length; //advance buffer pointer - }//while( buf_ptr <=... - return 0; -} - /* Performs a cleanup after failed Init() attempt */ uint8_t USBH_MIDI::Release() { pUsb->GetAddressPool().FreeAddress(bAddress); - bNumEP = 1; //must have to be reset to 1 bAddress = 0; bPollEnable = false; readPtr = 0; @@ -375,7 +309,10 @@ uint8_t USBH_MIDI::RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { *bytes_rcvd = (uint16_t)epInfo[epDataInIndex].maxPktSize; uint8_t r = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr); - +#ifdef EXTRADEBUG + if( r ) + USBTRACE2("inTransfer():", r); +#endif if( *bytes_rcvd < (MIDI_EVENT_PACKET_SIZE-4)){ dataptr[*bytes_rcvd] = '\0'; dataptr[(*bytes_rcvd)+1] = '\0'; @@ -421,60 +358,52 @@ RecvData_return_from_buffer: *(outBuf++) = m = recvBuf[readPtr++]; *(outBuf++) = recvBuf[readPtr++]; *(outBuf++) = recvBuf[readPtr++]; - return lookupMsgSize(m, cin); -} -/* Receive raw data from MIDI device */ -uint8_t USBH_MIDI::RecvRawData(uint8_t *outBuf) -{ - return RecvData(outBuf, true); + return getMsgSizeFromCin(cin); } /* Send data to MIDI device */ uint8_t USBH_MIDI::SendData(uint8_t *dataptr, uint8_t nCable) { uint8_t buf[4]; - uint8_t msg; + uint8_t status = dataptr[0]; - msg = dataptr[0]; - // SysEx long message ? - if( msg == 0xf0 ) - { + uint8_t cin = convertStatus2Cin(status); + if ( status == 0xf0 ) { + // SysEx long message return SendSysEx(dataptr, countSysExDataSize(dataptr), nCable); } - buf[0] = (nCable << 4) | (msg >> 4); - if( msg < 0xf0 ) msg = msg & 0xf0; - - //Building USB-MIDI Event Packets + buf[0] = (uint8_t)(nCable << 4) | cin; buf[1] = dataptr[0]; - buf[2] = dataptr[1]; - buf[3] = dataptr[2]; - switch(lookupMsgSize(msg)) { + uint8_t msglen = getMsgSizeFromCin(cin); + switch(msglen) { //3 bytes message case 3 : - if(msg == 0xf2) {//system common message(SPP) - buf[0] = (nCable << 4) | 3; - } + buf[2] = dataptr[1]; + buf[3] = dataptr[2]; break; //2 bytes message case 2 : - if(msg == 0xf1 || msg == 0xf3) {//system common message(MTC/SongSelect) - buf[0] = (nCable << 4) | 2; - } + buf[2] = dataptr[1]; buf[3] = 0; break; //1 byte message case 1 : - default : buf[2] = 0; buf[3] = 0; break; + default : + break; } +#ifdef EXTRADEBUG + //Dump for raw USB-MIDI event packet + Notify(PSTR("SendData():"), 0x80), D_PrintHex((buf[0]), 0x80), D_PrintHex((buf[1]), 0x80), D_PrintHex((buf[2]), 0x80), D_PrintHex((buf[3]), 0x80), Notify(PSTR("\r\n"), 0x80); +#endif return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, 4, buf); } @@ -495,54 +424,13 @@ void USBH_MIDI::PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr ) /*Return */ /* 0 : undefined message */ /* 0<: Vaild message size(1-3) */ -uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin) +//uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin) +uint8_t USBH_MIDI::lookupMsgSize(uint8_t status, uint8_t cin) { - uint8_t msgSize = 0; - - //SysEx message? - cin = cin & 0x0f; - if( (cin & 0xc) == 4 ) { - if( cin == 4 || cin == 7 ) return 3; - if( cin == 6 ) return 2; - if( cin == 5 ) return 1; + if( cin == 0 ){ + cin = convertStatus2Cin(status); } - - if( midiMsg < 0xf0 ) midiMsg &= 0xf0; - switch(midiMsg) { - //3 bytes messages - case 0xf2 : //system common message(SPP) - case 0x80 : //Note off - case 0x90 : //Note on - case 0xa0 : //Poly KeyPress - case 0xb0 : //Control Change - case 0xe0 : //PitchBend Change - msgSize = 3; - break; - - //2 bytes messages - case 0xf1 : //system common message(MTC) - case 0xf3 : //system common message(SongSelect) - case 0xc0 : //Program Change - case 0xd0 : //Channel Pressure - msgSize = 2; - break; - - //1 byte messages - case 0xf8 : //system realtime message - case 0xf9 : //system realtime message - case 0xfa : //system realtime message - case 0xfb : //system realtime message - case 0xfc : //system realtime message - case 0xfe : //system realtime message - case 0xff : //system realtime message - msgSize = 1; - break; - - //undefine messages - default : - break; - } - return msgSize; + return getMsgSizeFromCin(cin); } /* SysEx data size counter */ @@ -555,11 +443,9 @@ uint16_t USBH_MIDI::countSysExDataSize(uint8_t *dataptr) } //Search terminator(0xf7) - while(*dataptr != 0xf7) - { + while(*dataptr != 0xf7) { dataptr++; c++; - //Limiter (default: 256 bytes) if(c > MIDI_MAX_SYSEX_SIZE){ c = 0; @@ -575,15 +461,15 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable uint8_t buf[MIDI_EVENT_PACKET_SIZE]; uint8_t rc = 0; uint16_t n = datasize; - uint16_t pktSize = (n*10/3+7)/10*4; //Calculate total USB MIDI packet size uint8_t wptr = 0; uint8_t maxpkt = epInfo[epDataInIndex].maxPktSize; - if( maxpkt > MIDI_EVENT_PACKET_SIZE ) maxpkt = MIDI_EVENT_PACKET_SIZE; - USBTRACE("SendSysEx:\r\t"); USBTRACE2(" Length:\t", datasize); +#ifdef EXTRADEBUG + uint16_t pktSize = (n+2)/3; //Calculate total USB MIDI packet size USBTRACE2(" Total pktSize:\t", pktSize); +#endif while(n > 0) { //Byte 0 @@ -595,14 +481,14 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable buf[wptr++] = *(dataptr++); buf[wptr++] = 0x00; buf[wptr++] = 0x00; - n = n - 1; + n = 0; break; 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; + n = 0; break; case 3 : buf[wptr] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes. @@ -621,19 +507,12 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable if( (rc = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, wptr, buf)) != 0 ){ break; } - wptr = 0; //rewind data pointer + wptr = 0; //rewind write 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); - -} - uint8_t USBH_MIDI::extractSysExData(uint8_t *p, uint8_t *buf) { uint8_t rc = 0; @@ -664,3 +543,150 @@ uint8_t USBH_MIDI::extractSysExData(uint8_t *p, uint8_t *buf) } return(rc); } + +// Configuration Descriptor Parser +// Copied from confdescparser.h and modifiy. +MidiDescParser::MidiDescParser(UsbMidiConfigXtracter *xtractor, bool modeMidi) : +theXtractor(xtractor), +stateParseDescr(0), +dscrLen(0), +dscrType(0), +nEPs(0), +isMidiSearch(modeMidi){ + theBuffer.pValue = varBuffer; + valParser.Initialize(&theBuffer); + theSkipper.Initialize(&theBuffer); +} +void MidiDescParser::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset __attribute__((unused))) { + uint16_t cntdn = (uint16_t)len; + uint8_t *p = (uint8_t*)pbuf; + + while(cntdn) + if(!ParseDescriptor(&p, &cntdn)) + return; +} + +bool MidiDescParser::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) { + USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast(varBuffer); + USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast(varBuffer); + switch(stateParseDescr) { + case 0: + theBuffer.valueSize = 2; + valParser.Initialize(&theBuffer); + stateParseDescr = 1; + // fall through + case 1: + if(!valParser.Parse(pp, pcntdn)) + return false; + dscrLen = *((uint8_t*)theBuffer.pValue); + dscrType = *((uint8_t*)theBuffer.pValue + 1); + stateParseDescr = 2; + // fall through + case 2: + // This is a sort of hack. Assuming that two bytes are all ready in the buffer + // the pointer is positioned two bytes ahead in order for the rest of descriptor + // to be read right after the size and the type fields. + // This should be used carefully. varBuffer should be used directly to handle data + // in the buffer. + theBuffer.pValue = varBuffer + 2; + stateParseDescr = 3; + // fall through + case 3: + switch(dscrType) { + case USB_DESCRIPTOR_INTERFACE: + isGoodInterface = false; + break; + case USB_DESCRIPTOR_CONFIGURATION: + case USB_DESCRIPTOR_ENDPOINT: + case HID_DESCRIPTOR_HID: + break; + } + theBuffer.valueSize = dscrLen - 2; + valParser.Initialize(&theBuffer); + stateParseDescr = 4; + // fall through + case 4: + switch(dscrType) { + case USB_DESCRIPTOR_CONFIGURATION: + if(!valParser.Parse(pp, pcntdn)) + return false; + confValue = ucd->bConfigurationValue; + break; + case USB_DESCRIPTOR_INTERFACE: + if(!valParser.Parse(pp, pcntdn)) + return false; + USBTRACE("Interface descriptor:\r\n"); + USBTRACE2(" Inf#:\t\t", uid->bInterfaceNumber); + USBTRACE2(" Alt:\t\t", uid->bAlternateSetting); + USBTRACE2(" EPs:\t\t", uid->bNumEndpoints); + USBTRACE2(" IntCl:\t\t", uid->bInterfaceClass); + USBTRACE2(" IntSubcl:\t", uid->bInterfaceSubClass); + USBTRACE2(" Protocol:\t", uid->bInterfaceProtocol); + // MIDI check mode ? + if( isMidiSearch ){ //true: MIDI Streaming, false: ALL + if( uid->bInterfaceClass == USB_CLASS_AUDIO && uid->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING ) { + // MIDI found. + USBTRACE("+MIDI found\r\n\r\n"); + }else{ + USBTRACE("-MIDI not found\r\n\r\n"); + break; + } + } + isGoodInterface = true; + // Initialize the counter if no two endpoints can be found in one interface. + if(nEPs < 2) + // reset endpoint counter + nEPs = 0; + break; + case USB_DESCRIPTOR_ENDPOINT: + if(!valParser.Parse(pp, pcntdn)) + return false; + if(isGoodInterface && nEPs < 2){ + USBTRACE(">Extracting endpoint\r\n"); + if( theXtractor->EndpointXtract(confValue, 0, 0, 0, (USB_ENDPOINT_DESCRIPTOR*)varBuffer) ) + nEPs++; + } + break; + + default: + if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2)) + return false; + } + theBuffer.pValue = varBuffer; + stateParseDescr = 0; + } + return true; +} + +/* Extracts endpoint information from config descriptor */ +bool USBH_MIDI::EndpointXtract(uint8_t conf __attribute__((unused)), + uint8_t iface __attribute__((unused)), + uint8_t alt __attribute__((unused)), + uint8_t proto __attribute__((unused)), + const USB_ENDPOINT_DESCRIPTOR *pep) +{ + uint8_t index; + +#ifdef DEBUG_USB_HOST + PrintEndpointDescriptor(pep); +#endif + // Is the endpoint transfer type bulk? + if((pep->bmAttributes & bTransferTypeMask) == USB_TRANSFER_TYPE_BULK) { + USBTRACE("+valid EP found.\r\n"); + index = (pep->bEndpointAddress & 0x80) == 0x80 ? epDataInIndex : epDataOutIndex; + } else { + USBTRACE("-No valid EP found.\r\n"); + return false; + } + + // Fill the rest of endpoint data structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + // The maximum packet size for the USB Host Shield 2.0 library is 64 bytes. + if(pep->wMaxPacketSize > MIDI_EVENT_PACKET_SIZE) { + epInfo[index].maxPktSize = MIDI_EVENT_PACKET_SIZE; + } else { + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + } + + return true; +} diff --git a/usbh_midi.h b/usbh_midi.h index 4f1c4494..5309a185 100644 --- a/usbh_midi.h +++ b/usbh_midi.h @@ -1,7 +1,7 @@ /* ******************************************************************************* * USB-MIDI class driver for USB Host Shield 2.0 Library - * Copyright (c) 2012-2018 Yuuichi Akagawa + * Copyright (c) 2012-2021 Yuuichi Akagawa * * Idea from LPK25 USB-MIDI to Serial MIDI converter * by Collin Cunningham - makezine.com, narbotic.com @@ -26,31 +26,67 @@ #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) +#define USBH_MIDI_VERSION 600 +#define MIDI_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN(MIDI), bulk_OUT(MIDI) #define USB_SUBCLASS_MIDISTREAMING 3 -#define DESC_BUFF_SIZE 256 #define MIDI_EVENT_PACKET_SIZE 64 #define MIDI_MAX_SYSEX_SIZE 256 -class USBH_MIDI; -class USBH_MIDI : public USBDeviceConfig +namespace _ns_USBH_MIDI { +const uint8_t cin2len[] PROGMEM = {0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1}; +const uint8_t sys2cin[] PROGMEM = {0, 2, 3, 2, 0, 0, 5, 0, 0xf, 0, 0xf, 0xf, 0xf, 0, 0xf, 0xf}; +} + +// Endpoint Descriptor extracter Class +class UsbMidiConfigXtracter { +public: + //virtual void ConfigXtract(const USB_CONFIGURATION_DESCRIPTOR *conf) = 0; + //virtual void InterfaceXtract(uint8_t conf, const USB_INTERFACE_DESCRIPTOR *iface) = 0; + + virtual bool EndpointXtract(uint8_t conf __attribute__((unused)), uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *ep __attribute__((unused))) { + return true; + }; +}; +// Configuration Descriptor Parser Class +class MidiDescParser : public USBReadParser { + UsbMidiConfigXtracter *theXtractor; + MultiValueBuffer theBuffer; + MultiByteValueParser valParser; + ByteSkipper theSkipper; + uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/]; + + uint8_t stateParseDescr; // ParseDescriptor state + + uint8_t dscrLen; // Descriptor length + uint8_t dscrType; // Descriptor type + uint8_t nEPs; // number of valid endpoint + bool isMidiSearch; //Configuration mode true: MIDI, false: Vendor specific + + bool isGoodInterface; // Apropriate interface flag + uint8_t confValue; // Configuration value + + bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn); + +public: + MidiDescParser(UsbMidiConfigXtracter *xtractor, bool modeMidi); + void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset); + inline uint8_t getConfValue() { return confValue; }; + inline uint8_t getNumEPs() { return nEPs; }; +}; + +/** This class implements support for a MIDI device. */ +class USBH_MIDI : public USBDeviceConfig, public UsbMidiConfigXtracter { protected: - static const uint8_t epDataInIndex; // DataIn endpoint index(MIDI) - static const uint8_t epDataOutIndex; // DataOUT endpoint index(MIDI) - static const uint8_t epDataInIndexVSP; // DataIn endpoint index(Vendor Specific Protocl) - static const uint8_t epDataOutIndexVSP; // DataOUT endpoint index(Vendor Specific Protocl) + static const uint8_t epDataInIndex = 1; // DataIn endpoint index(MIDI) + static const uint8_t epDataOutIndex= 2; // DataOUT endpoint index(MIDI) /* mandatory members */ USB *pUsb; uint8_t bAddress; - uint8_t bConfNum; // configuration number - uint8_t bNumEP; // total number of EP in the configuration bool bPollEnable; - bool isMidiFound; uint16_t pid, vid; // ProductID, VendorID uint8_t bTransferTypeMask; /* Endpoint data structure */ @@ -59,27 +95,36 @@ protected: uint8_t recvBuf[MIDI_EVENT_PACKET_SIZE]; uint8_t readPtr; - uint8_t parseConfigDescr(uint8_t addr, uint8_t conf); uint16_t countSysExDataSize(uint8_t *dataptr); void setupDeviceSpecific(); + inline uint8_t convertStatus2Cin(uint8_t status) { + return ((status < 0xf0) ? ((status & 0xF0) >> 4) : pgm_read_byte_near(_ns_USBH_MIDI::sys2cin + (status & 0x0F))); + }; + inline uint8_t getMsgSizeFromCin(uint8_t cin) { + return pgm_read_byte_near(_ns_USBH_MIDI::cin2len + cin); + }; + + /* UsbConfigXtracter implementation */ + bool EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + #ifdef DEBUG_USB_HOST void PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr ); #endif public: USBH_MIDI(USB *p); // Misc functions - operator bool() { return (pUsb->getUsbTaskState()==USB_STATE_RUNNING); } + operator bool() { return (bPollEnable); } uint16_t idVendor() { return vid; } uint16_t idProduct() { return pid; } // Methods for recieving and sending data uint8_t RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr); uint8_t RecvData(uint8_t *outBuf, bool isRaw=false); - uint8_t RecvRawData(uint8_t *outBuf); + inline uint8_t RecvRawData(uint8_t *outBuf) { return RecvData(outBuf, true); }; uint8_t SendData(uint8_t *dataptr, uint8_t nCable=0); + inline uint8_t SendRawData(uint16_t bytes_send, uint8_t *dataptr) { return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes_send, dataptr); }; uint8_t lookupMsgSize(uint8_t midiMsg, uint8_t cin=0); uint8_t SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable=0); uint8_t extractSysExData(uint8_t *p, uint8_t *buf); - 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); }; @@ -88,5 +133,12 @@ public: virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); virtual uint8_t Release(); virtual uint8_t GetAddress() { return bAddress; }; + + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; +private: + void (*pFuncOnInit)(void) = nullptr; // Pointer to function called in onInit() }; + #endif //_USBH_MIDI_H_