diff --git a/hardware/arduino/sam/cores/arduino/USB/Midi.cpp b/hardware/arduino/sam/cores/arduino/USB/Midi.cpp new file mode 100644 index 00000000000..a95b1cb943c --- /dev/null +++ b/hardware/arduino/sam/cores/arduino/USB/Midi.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2015, Gary Grewal +/* +** Permission to use, copy, modify, and/or distribute this software for +** any purpose with or without fee is hereby granted, provided that the +** above copyright notice and this permission notice appear in all copies. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR +** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +** SOFTWARE. +*/ +#include "Arduino.h" +#include "USBAPI.h" +#include "Reset.h" + +#ifdef MIDI_ENABLED + +#define MIDI_BUFFER_SIZE 128 + +struct ring_bufferMIDI +{ + midiEventPacket_t midiEvent[MIDI_BUFFER_SIZE]; + volatile uint32_t head; + volatile uint32_t tail; +}; + +ring_bufferMIDI midi_rx_buffer = {{0,0,0,0 }, 0, 0}; + +_Pragma("pack(1)") +static const MIDIDescriptor _midiInterface = +{ +#ifdef CDC_ENABLED + D_IAD(MIDI_AC_INTERFACE,MIDI_INTERFACE_COUNT, MIDI_AUDIO, MIDI_AUDIO_CONTROL, 0), +#endif + D_INTERFACE(MIDI_AC_INTERFACE,0,MIDI_AUDIO,MIDI_AUDIO_CONTROL,0), + D_AC_INTERFACE(0x1, MIDI_INTERFACE), + D_INTERFACE(MIDI_INTERFACE,2, MIDI_AUDIO,MIDI_STREAMING,0), + D_AS_INTERFACE, + D_MIDI_INJACK(MIDI_JACK_EMD, 0x1), + D_MIDI_INJACK(MIDI_JACK_EXT, 0x2), + D_MIDI_OUTJACK(MIDI_JACK_EMD, 0x3, 1, 2, 1), + D_MIDI_OUTJACK(MIDI_JACK_EXT, 0x4, 1, 1, 1), + D_MIDI_JACK_EP(USB_ENDPOINT_OUT(MIDI_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,0x0040), + D_MIDI_AC_JACK_EP(1, 1), + D_MIDI_JACK_EP(USB_ENDPOINT_IN(MIDI_ENDPOINT_IN),USB_ENDPOINT_TYPE_BULK,0x0040), + D_MIDI_AC_JACK_EP (1, 3) +}; +_Pragma("pack()") + +int WEAK MIDI_GetInterface(uint8_t* interfaceNum) +{ + interfaceNum[0] += 2; // uses 2 + return USBD_SendControl(0,&_midiInterface,sizeof(_midiInterface)); +} +bool WEAK MIDI_Setup(Setup& setup) +{ + //Support requests here if needed. Typically these are optional + return false; +} + +void MIDI_::begin() +{ + //Nothing to do +} + +void MIDI_::accept(void) +{ + static uint32_t mguard = 0; + + // synchronized access to guard + do { + if (__LDREXW(&mguard) != 0) { + __CLREX(); + return; // busy + } + } while (__STREXW(1, &mguard) != 0); // retry until write succeed + + ring_bufferMIDI *buffer = &midi_rx_buffer; + uint32_t i = (uint32_t)(buffer->head+1) % MIDI_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + while (i != buffer->tail) { + int c; + midiEventPacket_t event; + if (!USBD_Available(MIDI_RX)) { + udd_ack_fifocon(MIDI_RX); + break; + } + c = USBD_Recv(MIDI_RX, &event, sizeof(event) ); + + //MIDI paacket has to be 4 bytes + if(c < 4) + return; + buffer->midiEvent[buffer->head] = event; + buffer->head = i; + + i = (i + 1) % MIDI_BUFFER_SIZE; + } + + // release the guard + mguard = 0; +} + +uint32_t MIDI_::available(void) +{ + + ring_bufferMIDI *buffer = &midi_rx_buffer; + return (uint32_t)(MIDI_BUFFER_SIZE + buffer->head - buffer->tail) % MIDI_BUFFER_SIZE; +} + + +midiEventPacket_t MIDI_::read(void) +{ + ring_bufferMIDI *buffer = &midi_rx_buffer; + midiEventPacket_t c = buffer->midiEvent[buffer->tail]; + c.header = 0; + c.byte1 = 0; + c.byte2 = 0; + c.byte3 = 0; + + // if the head isn't ahead of the tail, we don't have any characters + if (buffer->head == buffer->tail) + { + return c; + } + else + { + midiEventPacket_t c = buffer->midiEvent[buffer->tail]; + buffer->tail = (uint32_t)(buffer->tail + 1) % MIDI_BUFFER_SIZE; + if (USBD_Available(MIDI_RX)) + accept(); + return c; + } +} + +void MIDI_::flush(void) +{ + USBD_Flush(MIDI_TX); +} + +size_t MIDI_::write(const uint8_t *buffer, size_t size) +{ + /* only try to send bytes if the high-level MIDI connection itself + is open (not just the pipe) - the OS should set lineState when the port + is opened and clear lineState when the port is closed. + bytes sent before the user opens the connection or after + the connection is closed are lost - just like with a UART. */ + + // TODO - ZE - check behavior on different OSes and test what happens if an + // open connection isn't broken cleanly (cable is yanked out, host dies + // or locks up, or host virtual serial port hangs) + + int r = USBD_Send(MIDI_TX, buffer, size); + + if (r > 0) + { + return r; + } else + { + return 0; + } + return 0; +} + +void MIDI_::sendMIDI(midiEventPacket_t event) +{ + uint8_t data[4]; + data[0] = event.header; + data[1] = event.byte1; + data[2] = event.byte2; + data[3] = event.byte3; + write(data, 4); +} + +MIDI_ MidiUSB; +#endif diff --git a/hardware/arduino/sam/cores/arduino/USB/USBAPI.h b/hardware/arduino/sam/cores/arduino/USB/USBAPI.h index 3cf601e9e87..5b570112b40 100644 --- a/hardware/arduino/sam/cores/arduino/USB/USBAPI.h +++ b/hardware/arduino/sam/cores/arduino/USB/USBAPI.h @@ -64,6 +64,37 @@ class Serial_ : public Stream }; extern Serial_ SerialUSB; + +//Gurbrinder: Midi Interface +//================================================================================ +//================================================================================ +// MIDI USB class + +typedef struct +{ + uint8_t header; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; +}midiEventPacket_t; + +class MIDI_ +{ +private: + RingBuffer *_midi_rx_buffer; +public: + void begin(); + + virtual uint32_t available(void); + virtual void accept(void); + virtual midiEventPacket_t read(void); + virtual void flush(void); + virtual void sendMIDI(midiEventPacket_t event); + virtual size_t write(const uint8_t *buffer, size_t size); + operator bool(); +}; +extern MIDI_ MidiUSB; + //================================================================================ //================================================================================ // Mouse @@ -196,6 +227,15 @@ int CDC_GetOtherInterface(uint8_t* interfaceNum); int CDC_GetDescriptor(int i); bool CDC_Setup(Setup& setup); +//================================================================================ +//================================================================================ +// MIDI 'Driver' + +int MIDI_GetInterface(uint8_t* interfaceNum); +int MIDI_GetOtherInterface(uint8_t* interfaceNum); +int MIDI_GetDescriptor(int i); +bool MIDI_Setup(Setup& setup); + //================================================================================ //================================================================================ diff --git a/hardware/arduino/sam/cores/arduino/USB/USBCore.cpp b/hardware/arduino/sam/cores/arduino/USB/USBCore.cpp index c21fda7f4c9..7a5bbc047b1 100644 --- a/hardware/arduino/sam/cores/arduino/USB/USBCore.cpp +++ b/hardware/arduino/sam/cores/arduino/USB/USBCore.cpp @@ -19,6 +19,20 @@ #include "Reset.h" #include +// CDC Endpoints +#define EP_TYPE_BULK_IN_MIDI (UOTGHS_DEVEPTCFG_EPSIZE_64_BYTE | \ + UOTGHS_DEVEPTCFG_EPDIR_IN | \ + UOTGHS_DEVEPTCFG_EPTYPE_BLK | \ + UOTGHS_DEVEPTCFG_EPBK_1_BANK | \ + UOTGHS_DEVEPTCFG_NBTRANS_1_TRANS | \ + UOTGHS_DEVEPTCFG_ALLOC) + +#define EP_TYPE_BULK_OUT_MIDI (UOTGHS_DEVEPTCFG_EPSIZE_64_BYTE | \ + UOTGHS_DEVEPTCFG_EPTYPE_BLK | \ + UOTGHS_DEVEPTCFG_EPBK_1_BANK | \ + UOTGHS_DEVEPTCFG_NBTRANS_1_TRANS | \ + UOTGHS_DEVEPTCFG_ALLOC) + //#define TRACE_CORE(x) x #define TRACE_CORE(x) @@ -33,7 +47,12 @@ static const uint32_t EndPoints[] = #endif #ifdef HID_ENABLED - EP_TYPE_INTERRUPT_IN_HID // HID_ENDPOINT_INT + EP_TYPE_INTERRUPT_IN_HID, // HID_ENDPOINT_INT +#endif + +#ifdef MIDI_ENABLED + EP_TYPE_BULK_OUT_MIDI, // MIDI_ENDPOINT_OUT + EP_TYPE_BULK_IN_MIDI // MIDI_ENDPOINT_IN #endif }; @@ -51,6 +70,7 @@ extern const uint8_t STRING_PRODUCT[]; extern const uint8_t STRING_MANUFACTURER[]; extern const DeviceDescriptor USB_DeviceDescriptor; extern const DeviceDescriptor USB_DeviceDescriptorA; +extern const DeviceDescriptor USB_DeviceDescriptorB; const uint16_t STRING_LANGUAGE[2] = { (3<<8) | (2+2), @@ -92,6 +112,10 @@ const DeviceDescriptor USB_DeviceDescriptor = const DeviceDescriptor USB_DeviceDescriptorA = D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1); + +const DeviceDescriptor USB_DeviceDescriptorB = + D_DEVICE(0xEF,0x02,0x01,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1); + const DeviceDescriptor USB_DeviceQualifier = D_QUALIFIER(0x00,0x00,0x00,64,1); @@ -245,22 +269,32 @@ int USBD_SendControl(uint8_t flags, const void* d, uint32_t len) return length; } - +//Bug // Send a USB descriptor string. The string is stored as a // plain ASCII string but is sent out as UTF-16 with the // correct 2-byte prefix +// static bool USB_SendStringDescriptor(const uint8_t *string, int wLength) { +// uint16_t buff[64]; +// int l = 1; +// wLength-=2; +// while (*string && wLength>0) { +// buff[l++] = (uint8_t)(*string++); +// wLength-=2; +// } +// buff[0] = (3<<8) | (l*2); +// return USBD_SendControl(0, (uint8_t*)buff, l*2); +// } static bool USB_SendStringDescriptor(const uint8_t *string, int wLength) { uint16_t buff[64]; int l = 1; - wLength-=2; - while (*string && wLength>0) { + while (*string) { buff[l++] = (uint8_t)(*string++); - wLength-=2; } buff[0] = (3<<8) | (l*2); - return USBD_SendControl(0, (uint8_t*)buff, l*2); + return USBD_SendControl(0, (uint8_t*)buff, wLength); } + // Does not timeout or cross fifo boundaries // Will only work for transfers <= 64 bytes // TODO @@ -293,6 +327,12 @@ bool USBD_ClassInterfaceRequest(Setup& setup) return HID_Setup(setup); } #endif +#ifdef MIDI_ENABLED + if (MIDI_AC_INTERFACE == i) + { + return MIDI_Setup(setup); + } +#endif return false; } @@ -310,6 +350,10 @@ int USBD_SendInterfaces(void) total += HID_GetInterface(&interfaces); #endif +#ifdef MIDI_ENABLED + total += MIDI_GetInterface(&interfaces); +#endif + total = total; // Get rid of compiler warning TRACE_CORE(printf("=> USBD_SendInterfaces, total=%d interfaces=%d\r\n", total, interfaces);) return interfaces; @@ -328,6 +372,10 @@ int USBD_SendOtherInterfaces(void) total += HID_GetInterface(&interfaces); #endif +#ifdef MIDI_ENABLED + total += MIDI_GetInterface(&interfaces); +#endif + total = total; // Get rid of compiler warning TRACE_CORE(printf("=> USBD_SendInterfaces, total=%d interfaces=%d\r\n", total, interfaces);) return interfaces; @@ -410,10 +458,15 @@ static bool USBD_SendDescriptor(Setup& setup) { _cdcComposite = 1; } + +#if 1 + desc_addr = (const uint8_t*)&USB_DeviceDescriptorB; +#else desc_addr = _cdcComposite ? (const uint8_t*)&USB_DeviceDescriptorA : (const uint8_t*)&USB_DeviceDescriptor; - if( *desc_addr > setup.wLength ) { - desc_length = setup.wLength; - } +#endif + if( *desc_addr > setup.wLength ) { + desc_length = setup.wLength; + } } else if (USB_STRING_DESCRIPTOR_TYPE == t) { @@ -632,6 +685,23 @@ static void USB_ISR(void) } #endif +#ifdef MIDI_ENABLED + if (Is_udd_endpoint_interrupt(MIDI_RX)) + { + udd_ack_out_received(MIDI_RX); + + // Handle received bytes + if (USBD_Available(MIDI_RX)) + MidiUSB.accept(); + } + + if (Is_udd_sof()) + { + udd_ack_sof(); + // USBD_Flush(CDC_TX); // jcb + } +#endif + // EP 0 Interrupt if (Is_udd_endpoint_interrupt(0) ) { @@ -772,6 +842,11 @@ static void USB_ISR(void) // Enable interrupt for CDC reception from host (OUT packet) udd_enable_out_received_interrupt(CDC_RX); udd_enable_endpoint_interrupt(CDC_RX); +#endif +#ifdef MIDI_ENABLED + // Enable interrupt for CDC reception from host (OUT packet) + udd_enable_out_received_interrupt(MIDI_RX); + udd_enable_endpoint_interrupt(MIDI_RX); #endif } else diff --git a/hardware/arduino/sam/cores/arduino/USB/USBCore.h b/hardware/arduino/sam/cores/arduino/USB/USBCore.h index b01d7576a1e..6a19cf85042 100644 --- a/hardware/arduino/sam/cores/arduino/USB/USBCore.h +++ b/hardware/arduino/sam/cores/arduino/USB/USBCore.h @@ -125,6 +125,15 @@ #define HID_REPORT_DESCRIPTOR_TYPE 0x22 #define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 +#define MIDI_AUDIO 0x01 +#define MIDI_AUDIO_CONTROL 0x01 +#define MIDI_CS_INTERFACE 0x24 +#define MIDI_CS_ENDPOINT 0x25 +#define MIDI_STREAMING 0x3 +#define MIDI_JACK_EMD 0x01 +#define MIDI_JACK_EXT 0X02 + + _Pragma("pack(1)") // Device @@ -279,6 +288,91 @@ typedef struct EndpointDescriptor in; } HIDDescriptor; + +typedef struct +{ + uint8_t len; // 9 + uint8_t dtype; // 4 + uint8_t dsubType; + uint16_t bcdADc; + uint16_t wTotalLength; + uint8_t bInCollection; + uint8_t interfaceNumbers; +} MIDI_ACInterfaceDescriptor; + +typedef struct +{ + uint8_t len; // 9 + uint8_t dtype; // 4 + uint8_t dsubType; + uint8_t jackType; + uint8_t jackID; + uint8_t jackStrIndex; +} MIDIJackinDescriptor; + +typedef struct +{ + uint8_t len; // 9 + uint8_t dtype; // 4 + uint8_t dsubType; + uint8_t jackType; + uint8_t jackID; + uint8_t nPins; + uint8_t srcJackID; + uint8_t srcPinID; + uint8_t jackStrIndex; +} MIDIJackOutDescriptor; + +typedef struct +{ + EndpointDescriptor len; // 9 + uint8_t refresh; // 4 + uint8_t sync; +} MIDI_EPDescriptor; + +typedef struct +{ + uint8_t len; // 5 + uint8_t dtype; // 0x24 + uint8_t subtype; + uint8_t embJacks; + uint8_t jackID; +} MIDI_EP_ACDescriptor; + +typedef struct +{ + uint8_t len; // 9 + uint8_t dtype; // 4 + uint8_t dsubType; + uint16_t bcdADc; + uint16_t wTotalLength; +} MIDI_ASInterfaceDescriptor; + +typedef struct +{ +#ifdef CDC_ENABLED + // IAD + IADDescriptor iad; +#endif + // MIDI Audio Control Interface + InterfaceDescriptor Audio_ControlInterface; + MIDI_ACInterfaceDescriptor Audio_ControlInterface_SPC; + + // MIDI Audio Streaming Interface + InterfaceDescriptor Audio_StreamInterface; + MIDI_ASInterfaceDescriptor Audio_StreamInterface_SPC; + + MIDIJackinDescriptor MIDI_In_Jack_Emb; + MIDIJackinDescriptor MIDI_In_Jack_Ext; + MIDIJackOutDescriptor MIDI_Out_Jack_Emb; + MIDIJackOutDescriptor MIDI_Out_Jack_Ext; + + MIDI_EPDescriptor MIDI_In_Jack_Endpoint; + MIDI_EP_ACDescriptor MIDI_In_Jack_Endpoint_SPC; + MIDI_EPDescriptor MIDI_Out_Jack_Endpoint; + MIDI_EP_ACDescriptor MIDI_Out_Jack_Endpoint_SPC; +} MIDIDescriptor; + _Pragma("pack()") #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \ @@ -304,8 +398,27 @@ _Pragma("pack()") #define D_HIDREPORT(_descriptorLength) \ { 9, 0x21, 0x1, 0x1, 0, 1, 0x22, _descriptorLength, 0 } - + +#define D_AC_INTERFACE(_streamingInterfaces, _MIDIInterface) \ + { 9, MIDI_CS_INTERFACE, 0x1, 0x0100, 0x0009, _streamingInterfaces, _MIDIInterface } + +#define D_AS_INTERFACE \ + { 0x7, MIDI_CS_INTERFACE, 0x01,0x0100, 0x0041} + +#define D_MIDI_INJACK(jackProp, _jackID) \ + { 0x06, MIDI_CS_INTERFACE, 0x02, jackProp, _jackID, 0 } + +#define D_MIDI_OUTJACK(jackProp, _jackID, _nPins, _srcID, _srcPin) \ + { 0x09, MIDI_CS_INTERFACE, 0x3, jackProp, _jackID, _nPins, _srcID, _srcPin, 0 } + +#define D_MIDI_JACK_EP(_addr,_attr,_packetSize) \ + { 9, 5, _addr,_attr,_packetSize, 0, 0, 0} + +#define D_MIDI_AC_JACK_EP(_nMIDI, _iDMIDI) \ + { 5, MIDI_CS_ENDPOINT, 0x1, _nMIDI, _iDMIDI} + #define D_CDCCS(_subtype,_d0,_d1) { 5, 0x24, _subtype, _d0, _d1 } #define D_CDCCS4(_subtype,_d0) { 4, 0x24, _subtype, _d0 } + #endif diff --git a/hardware/arduino/sam/cores/arduino/USB/USBDesc.h b/hardware/arduino/sam/cores/arduino/USB/USBDesc.h index 878095e2450..9ecf852bf30 100644 --- a/hardware/arduino/sam/cores/arduino/USB/USBDesc.h +++ b/hardware/arduino/sam/cores/arduino/USB/USBDesc.h @@ -19,6 +19,7 @@ #define CDC_ENABLED #define HID_ENABLED +#define MIDI_ENABLED #ifdef CDC_ENABLED #define CDC_INTERFACE_COUNT 2 @@ -36,6 +37,14 @@ #define HID_ENPOINT_COUNT 0 #endif +#ifdef MIDI_ENABLED +#define MIDI_INTERFACE_COUNT 2 +#define MIDI_ENPOINT_COUNT 2 +#else +#define MIDI_INTERFACE_COUNT 0 +#define MIDI_ENPOINT_COUNT 0 +#endif + #define CDC_ACM_INTERFACE 0 // CDC ACM #define CDC_DATA_INTERFACE 1 // CDC Data #define CDC_FIRST_ENDPOINT 1 @@ -47,6 +56,12 @@ #define HID_FIRST_ENDPOINT (CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT) #define HID_ENDPOINT_INT (HID_FIRST_ENDPOINT) +#define MIDI_AC_INTERFACE (HID_INTERFACE + HID_INTERFACE_COUNT) // MIDI AC Interface +#define MIDI_INTERFACE MIDI_AC_INTERFACE+1 +#define MIDI_FIRST_ENDPOINT (HID_FIRST_ENDPOINT + HID_ENPOINT_COUNT) +#define MIDI_ENDPOINT_OUT (MIDI_FIRST_ENDPOINT) +#define MIDI_ENDPOINT_IN (MIDI_FIRST_ENDPOINT+1) + #define INTERFACE_COUNT (MSC_INTERFACE + MSC_INTERFACE_COUNT) #ifdef CDC_ENABLED @@ -58,6 +73,11 @@ #define HID_TX HID_ENDPOINT_INT #endif +#ifdef MIDI_ENABLED +#define MIDI_RX MIDI_ENDPOINT_OUT +#define MIDI_TX MIDI_ENDPOINT_IN +#endif + #define IMANUFACTURER 1 #define IPRODUCT 2 diff --git a/libraries/Audio/examples/SimpleAudioPlayer/USBMIDI.ino b/libraries/Audio/examples/SimpleAudioPlayer/USBMIDI.ino new file mode 100644 index 00000000000..aa497b0ca26 --- /dev/null +++ b/libraries/Audio/examples/SimpleAudioPlayer/USBMIDI.ino @@ -0,0 +1,55 @@ +/* + * MidiTest.cpp + * + * Created: 4/6/2015 10:47:08 AM + * Author: gurbrinder grewal + */ + + +#include "Arduino.h" + + + +// First parameter is the event type (0x09 = note on, 0x08 = note off). +// Second parameter is note-on/note-off, combined with the channel. +// Channel can be anything between 0-15. Typically reported to the user as 1-16. +// Third parameter is the note number (48 = middle C). +// Fourth parameter is the velocity (64 = normal, 127 = fastest). + +void noteOn(byte channel, byte pitch, byte velocity) { + midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity}; + MidiUSB.sendMIDI(noteOn); +} + +void noteOff(byte channel, byte pitch, byte velocity) { + midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity}; + MidiUSB.sendMIDI(noteOff); +} + +// First parameter is the event type (0x0B = control change). +// Second parameter is the event type, combined with the channel. +// Third parameter is the control number number (0-119). +// Fourth parameter is the control value (0-127). + +void controlChange(byte channel, byte control, byte value) { + midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value}; + MidiUSB.sendMIDI(event); +} + +void loop() { + SerialUSB.print("Hello world1"); + noteOn(0, 48, 64); // Channel 0, middle C, normal velocity + MidiUSB.flush(); + delay(500); + SerialUSB.print("Hello world2"); + noteOff(0, 48, 64); // Channel 0, middle C, normal velocity + MidiUSB.flush(); + delay(1500); + + // controlChange(0, 10, 65); // Set the value of controller 10 on channel 0 to 65 +} + +void setup() { + +} +