From 964fccad7b767556a34b16fe633382ed9d45a5f0 Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Sat, 6 May 2023 17:31:50 +0200 Subject: [PATCH 01/10] Added USBMIDI support to libraries/USB --- libraries/USB/src/USBMIDI.cpp | 136 ++++++++++++++++++++++++++++++++++ libraries/USB/src/USBMIDI.h | 72 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 libraries/USB/src/USBMIDI.cpp create mode 100644 libraries/USB/src/USBMIDI.h diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp new file mode 100644 index 00000000000..4a6d64a791c --- /dev/null +++ b/libraries/USB/src/USBMIDI.cpp @@ -0,0 +1,136 @@ +#include "USBMIDI.h" + +#if CONFIG_TINYUSB_MIDI_ENABLED + +#include "Arduino.h" +#include "esp32-hal-tinyusb.h" + +static bool tinyusb_midi_descriptor_loaded = false; +static bool tinyusb_midi_interface_enabled = false; + +extern "C" uint16_t tusb_midi_load_descriptor(uint8_t *dst, uint8_t *itf) { + if (tinyusb_midi_descriptor_loaded) { + return 0; + } + tinyusb_midi_descriptor_loaded = true; + + uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MIDI"); + uint8_t ep_in = tinyusb_get_free_in_endpoint(); + TU_VERIFY(ep_in != 0); + uint8_t ep_out = tinyusb_get_free_out_endpoint(); + TU_VERIFY(ep_out != 0); + uint8_t descriptor[TUD_MIDI_DESC_LEN] = { + TUD_MIDI_DESCRIPTOR(*itf, str_index, ep_out, (uint8_t)(0x80 | ep_in), 64), + }; + *itf += 2; + memcpy(dst, descriptor, TUD_MIDI_DESC_LEN); + + return TUD_MIDI_DESC_LEN; +} + +USBMIDI::USBMIDI() { + if (!tinyusb_midi_interface_enabled) { + tinyusb_midi_interface_enabled = true; + tinyusb_enable_interface(USB_INTERFACE_MIDI, TUD_MIDI_DESC_LEN, tusb_midi_load_descriptor); + } else { + log_e("USBMIDI: Multiple instances of USBMIDI not supported!"); + } +} + +void USBMIDI::begin() {} +void USBMIDI::end() {} + +#define STATUS(CIN, CHANNEL) (((CIN & 0x7F) << 4) | (constrain(CHANNEL - 1, 0, 15) & 0x7F)) + +// Note: All the user-level API calls do extensive input constraining to prevent easy to make mistakes. +// (You can thank me later.) +#define _(x) constrain(x, 0, 127) + +// Note On +void USBMIDI::noteOn(uint8_t note, uint8_t velocity, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_NOTE_ON, STATUS(MIDI_CIN_NOTE_ON, channel), _(note), + _(velocity)}; + writePacket(&event); +} + +// Note Off +void USBMIDI::noteOff(uint8_t note, uint8_t velocity, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_NOTE_OFF, STATUS(MIDI_CIN_NOTE_OFF, channel), _(note), + _(velocity)}; + writePacket(&event); +} + +// Program Change +void USBMIDI::programChange(uint8_t program, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_PROGRAM_CHANGE, STATUS(MIDI_CIN_PROGRAM_CHANGE, channel), + _(program)}; + writePacket(&event); +} + +// Control Change (Continuous Controller) +void USBMIDI::controlChange(uint8_t control, uint8_t value, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_CONTROL_CHANGE, STATUS(MIDI_CIN_CONTROL_CHANGE, channel), + _(control), _(value)}; + writePacket(&event); +} + +// Polyphonic Key Pressure (Aftertouch) +void USBMIDI::polyPressure(uint8_t note, uint8_t pressure, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_POLY_KEYPRESS, STATUS(MIDI_CIN_POLY_KEYPRESS, channel), _(note), + _(pressure)}; + writePacket(&event); +} + +// Channel Pressure (Aftertouch) +void USBMIDI::channelPressure(uint8_t pressure, uint8_t channel) { + midiEventPacket_t event = {MIDI_CIN_CHANNEL_PRESSURE, STATUS(MIDI_CIN_CHANNEL_PRESSURE, channel), + _(pressure)}; + writePacket(&event); +} + +// Pitch Bend Change [-8192,0,8191] +void USBMIDI::pitchBend(int16_t value, uint8_t channel) { + uint16_t pitchBendValue = constrain(value, -8192, 8191) + 8192; + pitchBend(pitchBendValue); +} + +// Pitch Bend Change [0,8192,16383] +void USBMIDI::pitchBend(uint16_t value, uint8_t channel) { + uint16_t pitchBendValue = constrain(value, 0, 16383); + // Split the 14-bit integer into two 7-bit values + uint8_t lsb = pitchBendValue & 0x7F; // Lower 7 bits + uint8_t msb = (pitchBendValue >> 7) & 0x7F; // Upper 7 bits + + midiEventPacket_t event = {MIDI_CIN_PITCH_BEND_CHANGE, STATUS(MIDI_CIN_PITCH_BEND_CHANGE, channel), + lsb, msb}; + writePacket(&event); +} + +// Pitch Bend Change [-1.0,0,1.0] +void USBMIDI::pitchBend(double value, uint8_t channel) { + // Multiply by 8191 and round to nearest integer + int16_t pitchBendValue = static_cast(round(constrain(value, -1.0, 1.0) * 8191.0)); + + pitchBend(pitchBendValue, channel); +} + +bool USBMIDI::readPacket(midiEventPacket_t *packet) { + return tud_midi_packet_read((uint8_t *)packet); +} + +bool USBMIDI::writePacket(midiEventPacket_t *packet) { + return tud_midi_packet_write((uint8_t *)packet); +} + +// Default Cable Number (for simplified APIs that do not expose this) +#define DEFAULT_CN 0 + +size_t USBMIDI::write(uint8_t c) { + // MIDI_CIN_1BYTE_DATA => Verbatim MIDI byte-stream copy + // (See also Table 4-1 of USB MIDI spec 1.0) + midiEventPacket_t packet = {DEFAULT_CN | MIDI_CIN_1BYTE_DATA, c, 0, 0}; + + return tud_midi_packet_write((uint8_t *)&packet); +} + +#endif // CONFIG_TINYUSB_MIDI_ENABLED diff --git a/libraries/USB/src/USBMIDI.h b/libraries/USB/src/USBMIDI.h new file mode 100644 index 00000000000..171636bc07e --- /dev/null +++ b/libraries/USB/src/USBMIDI.h @@ -0,0 +1,72 @@ + +#include "esp32-hal-tinyusb.h" +#include "sdkconfig.h" + +#if CONFIG_TINYUSB_MIDI_ENABLED + +#pragma once + +// Patch MIDI definition bug pending update/pull from upstream tinyusb repo. +// See also: +// - https://github.com/hathach/tinyusb/pull/1920 +// - https://github.com/espressif/arduino-esp32/pull/8117 +// +#undef MIDI_CIN_NOTE_ON +#define MIDI_CIN_NOTE_ON 9 +#undef MIDI_CIN_NOTE_OFF +#define MIDI_CIN_NOTE_OFF 8 + +#define MIDI_EP_HEADER_CN_GET(x) (x >> 4) +#define MIDI_EP_HEADER_CIN_GET(x) ((midi_code_index_number_t)((x)&0xF)) + +typedef struct { + uint8_t header; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; +} midiEventPacket_t; + +class USBMIDI { +public: + USBMIDI(void); + void begin(void); + void end(void); + + ////////////////////////// + ///// User-level API ///// + // + // Note On + void noteOn(uint8_t note, uint8_t velocity = 0, uint8_t channel = 1); + // Note Off + void noteOff(uint8_t note, uint8_t velocity = 0, uint8_t channel = 1); + // Program Change + void programChange(uint8_t inProgramNumber, uint8_t channel = 1); + // Control Change (Continuous Controller) + void controlChange(uint8_t inControlNumber, uint8_t inControlValue = 0, uint8_t channel = 1); + // Polyphonic Key Pressure (Aftertouch) + void polyPressure(uint8_t note, uint8_t pressure, uint8_t channel = 1); + // Channel Pressure (Aftertouch) + void channelPressure(uint8_t pressure, uint8_t channel = 1); + // Pitch Bend Change [-8192,0,8191] + void pitchBend(int16_t pitchBendValue, uint8_t channel = 1); + // Pitch Bend Change [0,8192,16383] + void pitchBend(uint16_t pitchBendValue, uint8_t channel = 1); + // Pitch Bend Change [-1.0,0,1.0] + void pitchBend(double pitchBendValue, uint8_t channel = 1); + + ////////////////////////////////// + ///// USB MIDI 1.0 interface ///// + // + // Attempt to read a USB MIDI packet from the USB Bus + bool readPacket(midiEventPacket_t *packet); + // Attempt to write a USB MIDI packet to the USB Bus + bool writePacket(midiEventPacket_t *packet); + + ///////////////////////////////////// + ///// Serial MIDI 1.0 interface ///// + // + // Write a Serial MIDI byte (status or data) to the USB Bus + size_t write(uint8_t c); +}; + +#endif // CONFIG_TINYUSB_MIDI_ENABLED From d779421d6200fd5d2e2b43b5180c8e27d370a70b Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Sat, 6 May 2023 17:38:14 +0200 Subject: [PATCH 02/10] Added MIDI examples to libraries/USB --- .../examples/MIDI/MidiController/.skip.esp32 | 0 .../MIDI/MidiController/.skip.esp32c3 | 0 .../MIDI/MidiController/MidiController.ino | 118 ++++++++++++++++++ .../examples/MIDI/MidiInterface/.skip.esp32 | 0 .../examples/MIDI/MidiInterface/.skip.esp32c3 | 0 .../MIDI/MidiInterface/MidiInterface.ino | 70 +++++++++++ .../examples/MIDI/MidiMusicBox/.skip.esp32 | 0 .../examples/MIDI/MidiMusicBox/.skip.esp32c3 | 0 .../MIDI/MidiMusicBox/MidiMusicBox.ino | 59 +++++++++ .../USB/examples/MIDI/ReceiveMidi/.skip.esp32 | 0 .../examples/MIDI/ReceiveMidi/.skip.esp32c3 | 0 .../examples/MIDI/ReceiveMidi/ReceiveMidi.ino | 104 +++++++++++++++ 12 files changed, 351 insertions(+) create mode 100644 libraries/USB/examples/MIDI/MidiController/.skip.esp32 create mode 100644 libraries/USB/examples/MIDI/MidiController/.skip.esp32c3 create mode 100644 libraries/USB/examples/MIDI/MidiController/MidiController.ino create mode 100644 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32 create mode 100644 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c3 create mode 100644 libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino create mode 100644 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32 create mode 100644 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c3 create mode 100644 libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino create mode 100644 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32 create mode 100644 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c3 create mode 100644 libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino diff --git a/libraries/USB/examples/MIDI/MidiController/.skip.esp32 b/libraries/USB/examples/MIDI/MidiController/.skip.esp32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiController/.skip.esp32c3 b/libraries/USB/examples/MIDI/MidiController/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiController/MidiController.ino b/libraries/USB/examples/MIDI/MidiController/MidiController.ino new file mode 100644 index 00000000000..30ea448eec1 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiController/MidiController.ino @@ -0,0 +1,118 @@ +/* +This is an example of a Simple MIDI Controller using an ESP32 with a native USB support stack (S2, S3, +etc.). + +For a hookup guide and more information on reading the ADC, please see: +https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ (Note: This sketch uses GPIO05) + +For best results, it is recommended to add an extra offset resistor between VCC and the potentiometer. +(For a standard 10kOhm potentiometer, 3kOhm - 4kOhm will do.) + +View this sketch in action on YouTube: https://youtu.be/Y9TLXs_3w1M +*/ +#if ARDUINO_USB_MODE +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else + +#include + +#include "USB.h" +#include "USBMIDI.h" +USBMIDI MIDI; + +#define MIDI_NOTE_C4 64 + +#define MIDI_CC_CUTOFF 74 + +///// ADC & Controller Input Handling ///// + +#define CONTROLLER_PIN 5 + +// ESP32 ADC needs a ton of smoothing +#define SMOOTHING_VALUE 1000 +static double controllerInputValue = 0; + +void updateControllerInputValue() { + controllerInputValue = + (controllerInputValue * (SMOOTHING_VALUE - 1) + analogRead(CONTROLLER_PIN)) / SMOOTHING_VALUE; +} + +void primeControllerInputValue() { + for (int i = 0; i < SMOOTHING_VALUE; i++) { + updateControllerInputValue(); + } +} + +uint16_t readControllerValue() { + // Lower ADC input amplitude to get a stable value + return round(controllerInputValue / 12); +} + +///// Button Handling ///// + +#define BUTTON_PIN 0 + +// Simple button state transition function with debounce +// (See also: https://tinyurl.com/simple-debounce) +#define PRESSED 0xff00 +#define RELEASED 0xfe1f +uint16_t getButtonEvent() { + static uint16_t state = 0; + state = (state << 1) | digitalRead(BUTTON_PIN) | 0xfe00; + return state; +} + +///// Arduino Hooks ///// + +void setup() { + Serial.begin(115200); + MIDI.begin(); + USB.begin(); + + primeControllerInputValue(); +} + +void loop() { + uint16_t newControllerValue = readControllerValue(); + static uint16_t lastControllerValue = 0; + + // Auto-calibrate the controller range + static uint16_t maxControllerValue = 0; + static uint16_t minControllerValue = 0xFFFF; + + if (newControllerValue < minControllerValue) { + minControllerValue = newControllerValue; + } + if (newControllerValue > maxControllerValue) { + maxControllerValue = newControllerValue; + } + + // Send update if the controller value has changed + if (lastControllerValue != newControllerValue) { + lastControllerValue = newControllerValue; + + // Can't map if the range is zero + if (minControllerValue != maxControllerValue) { + MIDI.controlChange(MIDI_CC_CUTOFF, + map(newControllerValue, minControllerValue, maxControllerValue, 0, 127)); + } + } + + updateControllerInputValue(); + + // Hook Button0 to a MIDI note so that we can observe + // the CC effect without the need for a MIDI keyboard. + switch (getButtonEvent()) { + case PRESSED: + MIDI.noteOn(MIDI_NOTE_C4, 64); + break; + case RELEASED: + MIDI.noteOff(MIDI_NOTE_C4, 0); + break; + default: + break; + } +} +#endif /* ARDUINO_USB_MODE */ \ No newline at end of file diff --git a/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32 b/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c3 b/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino new file mode 100644 index 00000000000..6fb99d8ab95 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino @@ -0,0 +1,70 @@ +/* +This is an example of using an ESP32 with a native USB support stack (S2, S3, etc.) as a Serial MIDI to +USB MIDI bridge (AKA "A MIDI Interface"). + +View this sketch in action on YouTube: https://youtu.be/BXG5i55I9s0 + +Receiving and decoding USB MIDI 1.0 packets is a more advanced topic than sending MIDI over USB, +please refer to the other examples in this library for a more basic example of sending MIDI over USB. + +This example should still be self explanatory, please refer to the USB MIDI 1.0 specification (the spec) +for a more in-depth explanation of the packet format. + +For the spec please visit: https://www.midi.org/specifications-old/item/usb-midi-1-0-specification + +Note: Because ESP32 works at VCC=3.3v normal schematics for Serial MIDI connections will not suffice, + Please refer to the Updated MIDI 1.1 Electrical Specification [1] for information on how to hookup + Serial MIDI for 3.3v devices. + +[1] - https://www.midi.org/specifications/midi-transports-specifications/5-pin-din-electrical-specs +*/ +#if ARDUINO_USB_MODE +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else + +#include "USB.h" +#include "USBMIDI.h" +USBMIDI MIDI; + +#define MIDI_RX 39 +#define MIDI_TX 40 + +void setup() { + // USBCDC Serial + Serial.begin(115200); + + // HW UART Serial + Serial1.begin(31250, SERIAL_8N1, MIDI_RX, MIDI_TX); + + MIDI.begin(); + USB.begin(); +} + +void loop() { + // MIDI Serial 1.0 to USB MIDI 1.0 + if (Serial1.available()) { + byte data = Serial1.read(); + MIDI.write(data); + } + + // USB MIDI 1.0 to MIDI Serial 1.0 + midiEventPacket_t midi_packet_in = {0}; + // See Chapter 4: USB-MIDI Event Packets (page 16) of the spec. + int8_t cin_to_midix_size[16] = {-1, -1, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1}; + + if (MIDI.readPacket(&midi_packet_in)) { + midi_code_index_number_t code_index_num = MIDI_EP_HEADER_CIN_GET(midi_packet_in.header); + int8_t midix_size = cin_to_midix_size[code_index_num]; + + // We skip Misc and Cable Events for simplicity + if (code_index_num >= 0x2) { + for (int i = 0; i < midix_size; i++) { + Serial1.write(((uint8_t *)&midi_packet_in)[i + 1]); + } + Serial1.flush(); + } + } +} +#endif /* ARDUINO_USB_MODE */ diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32 b/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c3 b/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino b/libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino new file mode 100644 index 00000000000..1d540324943 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino @@ -0,0 +1,59 @@ +/* +This is an example of a MIDI Music Box using an ESP32 with a native USB support stack (S2, S3, etc.). + +Every time the button on the ESP32 board (attached to pin 0) is pressed the next note of a melody is +played. It is up to the user to get the timing of the button presses right. + +One simple way of running this sketch is to download the Pianoteq evaluation version, because upon +application start it automatically listens to the first MIDI Input on Channel 1, which is the case, +if the ESP32 is the only MIDI device attached. + +View this sketch in action on YouTube: https://youtu.be/JFrc-wSmcus +*/ +#if ARDUINO_USB_MODE +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else + +#include "USB.h" +#include "USBMIDI.h" +USBMIDI MIDI; + +#define END_OF_SONG 255 +uint8_t notes[] = {END_OF_SONG, 71, 76, 79, 78, 76, 83, 81, 78, 76, 79, 78, 75, 77, 71}; +uint8_t noteIndex = 0; // From 0 to sizeof(notes) +#define SONG_LENGTH (sizeof(notes) - 1) // END_OF_SONG does not attribute to the length. + +#define BUTTON_PIN 0 + +// Simple button press check with debounce +// (See also: https://tinyurl.com/simple-debounce) +bool isButtonPressed() { + static uint16_t state = 0; + state = (state << 1) | digitalRead(BUTTON_PIN) | 0xfe00; + return (state == 0xff00); +} + +void setup() { + Serial.begin(115200); + // Make the BUTTON_PIN an input: + pinMode(BUTTON_PIN, INPUT_PULLUP); + + MIDI.begin(); + USB.begin(); +} + +void loop() { + if (isButtonPressed()) { + // Stop current note + MIDI.noteOff(notes[noteIndex]); + + // Play next note + noteIndex = noteIndex < SONG_LENGTH ? noteIndex + 1 : 0; + if (notes[noteIndex] != END_OF_SONG) { + MIDI.noteOn(notes[noteIndex], 64); + } + } +} +#endif /* ARDUINO_USB_MODE */ diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32 b/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c3 b/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino b/libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino new file mode 100644 index 00000000000..4f1bf4273e2 --- /dev/null +++ b/libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino @@ -0,0 +1,104 @@ +/* +This is an example of receiving USB MIDI 1.0 messages using an ESP32 with a native USB support stack +(S2, S3, etc.). + +Receiving and decoding USB MIDI 1.0 packets is a more advanced topic than sending MIDI over USB, +please refer to the other examples in this library for a more basic example of sending MIDI over USB. + +This example should still be self explanatory, please refer to the USB MIDI 1.0 specification (the spec) +for a more in-depth explanation of the packet format. + +For the spec please visit: https://www.midi.org/specifications-old/item/usb-midi-1-0-specification +*/ +#if ARDUINO_USB_MODE +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else + +#include "USB.h" +#include "USBMIDI.h" +USBMIDI MIDI; + +void setup() { + Serial.begin(115200); + + MIDI.begin(); + USB.begin(); +} + +void loop() { + midiEventPacket_t midi_packet_in = {0}; + + if (MIDI.readPacket(&midi_packet_in)) { + printDetails(midi_packet_in); + } +} + +void printDetails(midiEventPacket_t &midi_packet_in) { + // See Chapter 4: USB-MIDI Event Packets (page 16) of the spec. + uint8_t cable_num = MIDI_EP_HEADER_CN_GET(midi_packet_in.header); + midi_code_index_number_t code_index_num = MIDI_EP_HEADER_CIN_GET(midi_packet_in.header); + + Serial.println("Received a USB MIDI packet:"); + Serial.println(".----.-----.--------.--------.--------."); + Serial.println("| CN | CIN | STATUS | DATA 0 | DATA 1 |"); + Serial.println("+----+-----+--------+--------+--------+"); + Serial.printf("| %d | %X | %X | %X | %X |\n", cable_num, code_index_num, + midi_packet_in.byte1, midi_packet_in.byte2, midi_packet_in.byte3); + Serial.println("'----'-----'--------.--------'--------'\n"); + Serial.print("Description: "); + + switch (code_index_num) { + case MIDI_CIN_MISC: + Serial.println("This a Miscellaneous event"); + break; + case MIDI_CIN_CABLE_EVENT: + Serial.println("This a Cable event"); + break; + case MIDI_CIN_SYSCOM_2BYTE: // 2 byte system common message e.g MTC, SongSelect + case MIDI_CIN_SYSCOM_3BYTE: // 3 byte system common message e.g SPP + Serial.println("This a System Common (SysCom) event"); + break; + case MIDI_CIN_SYSEX_START: // SysEx starts or continue + case MIDI_CIN_SYSEX_END_1BYTE: // SysEx ends with 1 data, or 1 byte system common message + case MIDI_CIN_SYSEX_END_2BYTE: // SysEx ends with 2 data + case MIDI_CIN_SYSEX_END_3BYTE: // SysEx ends with 3 data + Serial.println("This a system exclusive (SysEx) event"); + break; + case MIDI_CIN_NOTE_ON: + Serial.printf("This a Note-On event of Note %d with a Velocity of %d\n", + midi_packet_in.byte2, midi_packet_in.byte3); + break; + case MIDI_CIN_NOTE_OFF: + Serial.printf("This a Note-Off event of Note %d with a Velocity of %d\n", + midi_packet_in.byte2, midi_packet_in.byte3); + break; + case MIDI_CIN_POLY_KEYPRESS: + Serial.printf("This a Poly Aftertouch event for Note %d and Value %d\n", + midi_packet_in.byte2, midi_packet_in.byte3); + break; + case MIDI_CIN_CONTROL_CHANGE: + Serial.printf("This a Control Change/Continuous Controller (CC) event of Controller %d " + "with a Value of %d\n", + midi_packet_in.byte2, midi_packet_in.byte3); + break; + case MIDI_CIN_PROGRAM_CHANGE: + Serial.printf("This a Program Change event with a Value of %d\n", midi_packet_in.byte2); + break; + case MIDI_CIN_CHANNEL_PRESSURE: + Serial.printf("This a Channel Pressure event with a Value of %d\n", midi_packet_in.byte2); + break; + case MIDI_CIN_PITCH_BEND_CHANGE: + Serial.printf("This a Pitch Bend Change event with a Value of %d\n", + ((uint16_t)midi_packet_in.byte2) << 7 | midi_packet_in.byte3); + break; + case MIDI_CIN_1BYTE_DATA: + Serial.printf("This an embedded Serial MIDI event byte with Value %X\n", + midi_packet_in.byte1); + break; + } + + Serial.println(); +} +#endif /* ARDUINO_USB_MODE */ From e06386c9b3630587f97123e1b1eab2e2a03e3c22 Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Sat, 6 May 2023 18:59:34 +0200 Subject: [PATCH 03/10] Added missing newline at end of file to MidiController.ino --- libraries/USB/examples/MIDI/MidiController/MidiController.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/USB/examples/MIDI/MidiController/MidiController.ino b/libraries/USB/examples/MIDI/MidiController/MidiController.ino index 30ea448eec1..a140f3831a7 100644 --- a/libraries/USB/examples/MIDI/MidiController/MidiController.ino +++ b/libraries/USB/examples/MIDI/MidiController/MidiController.ino @@ -115,4 +115,4 @@ void loop() { break; } } -#endif /* ARDUINO_USB_MODE */ \ No newline at end of file +#endif /* ARDUINO_USB_MODE */ From b244d43c189d3eb67dfcf603eb86f0a55b75c106 Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Fri, 12 May 2023 13:49:48 +0200 Subject: [PATCH 04/10] Added USBMIDI.cpp to CMake file --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2518b0be093..dbf83aa727a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ set(LIBRARY_SRCS libraries/Update/src/Updater.cpp libraries/Update/src/HttpsOTAUpdate.cpp libraries/USB/src/USBHID.cpp + libraries/USB/src/USBMIDI.cpp libraries/USB/src/USBHIDMouse.cpp libraries/USB/src/USBHIDKeyboard.cpp libraries/USB/src/USBHIDGamepad.cpp From 6345dea81e535cd33690f277fbaf73d6d7d067b5 Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Fri, 12 May 2023 13:52:20 +0200 Subject: [PATCH 05/10] Fix narrowing conversion warning in USBMIDI.cpp --- libraries/USB/src/USBMIDI.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index 4a6d64a791c..865d238215b 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -40,11 +40,15 @@ USBMIDI::USBMIDI() { void USBMIDI::begin() {} void USBMIDI::end() {} -#define STATUS(CIN, CHANNEL) (((CIN & 0x7F) << 4) | (constrain(CHANNEL - 1, 0, 15) & 0x7F)) +// uint compatible version of constrain +#define uconstrain(amt, low, high) ((amt) <= (low) ? (low) : ((amt) > (high) ? (high) : (amt))) + +#define STATUS(CIN, CHANNEL) \ + static_cast(((CIN & 0x7F) << 4) | (uconstrain(CHANNEL - 1, 0, 15) & 0x7F)) // Note: All the user-level API calls do extensive input constraining to prevent easy to make mistakes. // (You can thank me later.) -#define _(x) constrain(x, 0, 127) +#define _(x) static_cast(uconstrain(x, 0, 127)) // Note On void USBMIDI::noteOn(uint8_t note, uint8_t velocity, uint8_t channel) { @@ -96,7 +100,7 @@ void USBMIDI::pitchBend(int16_t value, uint8_t channel) { // Pitch Bend Change [0,8192,16383] void USBMIDI::pitchBend(uint16_t value, uint8_t channel) { - uint16_t pitchBendValue = constrain(value, 0, 16383); + uint16_t pitchBendValue = static_cast(uconstrain(value, 0, 16383)); // Split the 14-bit integer into two 7-bit values uint8_t lsb = pitchBendValue & 0x7F; // Lower 7 bits uint8_t msb = (pitchBendValue >> 7) & 0x7F; // Upper 7 bits From 7fc1f3e7efe74c6129cb6acb36e0586c397cc97c Mon Sep 17 00:00:00 2001 From: Marcel Toele Date: Fri, 12 May 2023 13:52:58 +0200 Subject: [PATCH 06/10] Fix incomplete initializers warning in USBMIDI.cpp --- libraries/USB/src/USBMIDI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index 865d238215b..073b0296cfa 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -67,7 +67,7 @@ void USBMIDI::noteOff(uint8_t note, uint8_t velocity, uint8_t channel) { // Program Change void USBMIDI::programChange(uint8_t program, uint8_t channel) { midiEventPacket_t event = {MIDI_CIN_PROGRAM_CHANGE, STATUS(MIDI_CIN_PROGRAM_CHANGE, channel), - _(program)}; + _(program), 0x0}; writePacket(&event); } @@ -88,7 +88,7 @@ void USBMIDI::polyPressure(uint8_t note, uint8_t pressure, uint8_t channel) { // Channel Pressure (Aftertouch) void USBMIDI::channelPressure(uint8_t pressure, uint8_t channel) { midiEventPacket_t event = {MIDI_CIN_CHANNEL_PRESSURE, STATUS(MIDI_CIN_CHANNEL_PRESSURE, channel), - _(pressure)}; + _(pressure), 0x0}; writePacket(&event); } From cc81cbc5e638120161a9c1a4fe2acf815ff7b5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:16:53 +0100 Subject: [PATCH 07/10] Apply suggestions from code review Co-authored-by: Lucas Saavedra Vaz --- libraries/USB/src/USBMIDI.cpp | 4 +++- libraries/USB/src/USBMIDI.h | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index 073b0296cfa..dbfe2a92253 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -1,4 +1,5 @@ #include "USBMIDI.h" +#if SOC_USB_OTG_SUPPORTED #if CONFIG_TINYUSB_MIDI_ENABLED @@ -137,4 +138,5 @@ size_t USBMIDI::write(uint8_t c) { return tud_midi_packet_write((uint8_t *)&packet); } -#endif // CONFIG_TINYUSB_MIDI_ENABLED +#endif /* CONFIG_TINYUSB_MIDI_ENABLED */ +#endif /* SOC_USB_OTG_SUPPORTED */ diff --git a/libraries/USB/src/USBMIDI.h b/libraries/USB/src/USBMIDI.h index 171636bc07e..5b5831f5ecc 100644 --- a/libraries/USB/src/USBMIDI.h +++ b/libraries/USB/src/USBMIDI.h @@ -1,4 +1,5 @@ - +#include "soc/soc_caps.h" +#if SOC_USB_OTG_SUPPORTED #include "esp32-hal-tinyusb.h" #include "sdkconfig.h" @@ -32,9 +33,8 @@ class USBMIDI { void begin(void); void end(void); - ////////////////////////// - ///// User-level API ///// - // + /* User-level API */ + // Note On void noteOn(uint8_t note, uint8_t velocity = 0, uint8_t channel = 1); // Note Off @@ -54,19 +54,18 @@ class USBMIDI { // Pitch Bend Change [-1.0,0,1.0] void pitchBend(double pitchBendValue, uint8_t channel = 1); - ////////////////////////////////// - ///// USB MIDI 1.0 interface ///// - // + /* USB MIDI 1.0 interface */ + // Attempt to read a USB MIDI packet from the USB Bus bool readPacket(midiEventPacket_t *packet); // Attempt to write a USB MIDI packet to the USB Bus bool writePacket(midiEventPacket_t *packet); - ///////////////////////////////////// - ///// Serial MIDI 1.0 interface ///// - // + /* Serial MIDI 1.0 interface */ + // Write a Serial MIDI byte (status or data) to the USB Bus size_t write(uint8_t c); }; -#endif // CONFIG_TINYUSB_MIDI_ENABLED +#endif /* CONFIG_TINYUSB_MIDI_ENABLED */ +#endif /* SOC_USB_OTG_SUPPORTED */ From ea51c9817c8512febfa28b569b89435bef79c6b3 Mon Sep 17 00:00:00 2001 From: Jan Prochazka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:19:56 +0100 Subject: [PATCH 08/10] add skip files for C6+H2 --- libraries/USB/examples/MIDI/MidiController/.skip.esp32c6 | 0 libraries/USB/examples/MIDI/MidiController/.skip.esp32h2 | 0 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6 | 0 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2 | 0 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6 | 0 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2 | 0 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6 | 0 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2 | 0 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 libraries/USB/examples/MIDI/MidiController/.skip.esp32c6 create mode 100644 libraries/USB/examples/MIDI/MidiController/.skip.esp32h2 create mode 100644 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6 create mode 100644 libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2 create mode 100644 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6 create mode 100644 libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2 create mode 100644 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6 create mode 100644 libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2 diff --git a/libraries/USB/examples/MIDI/MidiController/.skip.esp32c6 b/libraries/USB/examples/MIDI/MidiController/.skip.esp32c6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiController/.skip.esp32h2 b/libraries/USB/examples/MIDI/MidiController/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6 b/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2 b/libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6 b/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2 b/libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6 b/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2 b/libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d From 5935fa2461773aae5fe4da2a1746956cfdf3ba28 Mon Sep 17 00:00:00 2001 From: Jan Prochazka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:22:22 +0100 Subject: [PATCH 09/10] remove already patched workaroud for bug --- libraries/USB/src/USBMIDI.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libraries/USB/src/USBMIDI.h b/libraries/USB/src/USBMIDI.h index 5b5831f5ecc..44253fda51d 100644 --- a/libraries/USB/src/USBMIDI.h +++ b/libraries/USB/src/USBMIDI.h @@ -7,16 +7,6 @@ #pragma once -// Patch MIDI definition bug pending update/pull from upstream tinyusb repo. -// See also: -// - https://github.com/hathach/tinyusb/pull/1920 -// - https://github.com/espressif/arduino-esp32/pull/8117 -// -#undef MIDI_CIN_NOTE_ON -#define MIDI_CIN_NOTE_ON 9 -#undef MIDI_CIN_NOTE_OFF -#define MIDI_CIN_NOTE_OFF 8 - #define MIDI_EP_HEADER_CN_GET(x) (x >> 4) #define MIDI_EP_HEADER_CIN_GET(x) ((midi_code_index_number_t)((x)&0xF)) From 59a25c3ae31b81ea06e3e500c2c451ba9d36bc43 Mon Sep 17 00:00:00 2001 From: Jan Prochazka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:23:39 +0100 Subject: [PATCH 10/10] move #define to top of file --- libraries/USB/src/USBMIDI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index dbfe2a92253..b29b6fefe5d 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -6,6 +6,9 @@ #include "Arduino.h" #include "esp32-hal-tinyusb.h" +// Default Cable Number (for simplified APIs that do not expose this) +#define DEFAULT_CN 0 + static bool tinyusb_midi_descriptor_loaded = false; static bool tinyusb_midi_interface_enabled = false; @@ -127,9 +130,6 @@ bool USBMIDI::writePacket(midiEventPacket_t *packet) { return tud_midi_packet_write((uint8_t *)packet); } -// Default Cable Number (for simplified APIs that do not expose this) -#define DEFAULT_CN 0 - size_t USBMIDI::write(uint8_t c) { // MIDI_CIN_1BYTE_DATA => Verbatim MIDI byte-stream copy // (See also Table 4-1 of USB MIDI spec 1.0)