Add USB MIDI support to libraries/USB (#8166)
* Added USBMIDI support to libraries/USB * Added MIDI examples to libraries/USB * Added missing newline at end of file to MidiController.ino * Added USBMIDI.cpp to CMake file * Fix narrowing conversion warning in USBMIDI.cpp * Fix incomplete initializers warning in USBMIDI.cpp * Apply suggestions from code review Co-authored-by: Lucas Saavedra Vaz <lucassvaz@yahoo.com.br> * add skip files for C6+H2 * remove already patched workaroud for bug * move #define to top of file --------- Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com> Co-authored-by: Lucas Saavedra Vaz <lucas.vaz@espressif.com> Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Co-authored-by: Lucas Saavedra Vaz <lucassvaz@yahoo.com.br>
This commit is contained in:
parent
374280ccc8
commit
1a7a893497
23 changed files with 555 additions and 0 deletions
|
|
@ -115,6 +115,7 @@ set(LIBRARY_SRCS
|
||||||
libraries/Update/src/Updater.cpp
|
libraries/Update/src/Updater.cpp
|
||||||
libraries/Update/src/HttpsOTAUpdate.cpp
|
libraries/Update/src/HttpsOTAUpdate.cpp
|
||||||
libraries/USB/src/USBHID.cpp
|
libraries/USB/src/USBHID.cpp
|
||||||
|
libraries/USB/src/USBMIDI.cpp
|
||||||
libraries/USB/src/USBHIDMouse.cpp
|
libraries/USB/src/USBHIDMouse.cpp
|
||||||
libraries/USB/src/USBHIDKeyboard.cpp
|
libraries/USB/src/USBHIDKeyboard.cpp
|
||||||
libraries/USB/src/USBHIDGamepad.cpp
|
libraries/USB/src/USBHIDGamepad.cpp
|
||||||
|
|
|
||||||
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32h2
Normal file
0
libraries/USB/examples/MIDI/MidiController/.skip.esp32h2
Normal file
118
libraries/USB/examples/MIDI/MidiController/MidiController.ino
Normal file
118
libraries/USB/examples/MIDI/MidiController/MidiController.ino
Normal file
|
|
@ -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 <math.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2
Normal file
0
libraries/USB/examples/MIDI/MidiInterface/.skip.esp32h2
Normal file
70
libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino
Normal file
70
libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino
Normal file
|
|
@ -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 */
|
||||||
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2
Normal file
0
libraries/USB/examples/MIDI/MidiMusicBox/.skip.esp32h2
Normal file
59
libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino
Normal file
59
libraries/USB/examples/MIDI/MidiMusicBox/MidiMusicBox.ino
Normal file
|
|
@ -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 */
|
||||||
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c3
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32c6
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2
Normal file
0
libraries/USB/examples/MIDI/ReceiveMidi/.skip.esp32h2
Normal file
104
libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino
Normal file
104
libraries/USB/examples/MIDI/ReceiveMidi/ReceiveMidi.ino
Normal file
|
|
@ -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 */
|
||||||
142
libraries/USB/src/USBMIDI.cpp
Normal file
142
libraries/USB/src/USBMIDI.cpp
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include "USBMIDI.h"
|
||||||
|
#if SOC_USB_OTG_SUPPORTED
|
||||||
|
|
||||||
|
#if CONFIG_TINYUSB_MIDI_ENABLED
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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() {}
|
||||||
|
|
||||||
|
// uint compatible version of constrain
|
||||||
|
#define uconstrain(amt, low, high) ((amt) <= (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
|
||||||
|
|
||||||
|
#define STATUS(CIN, CHANNEL) \
|
||||||
|
static_cast<uint8_t>(((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) static_cast<uint8_t>(uconstrain(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), 0x0};
|
||||||
|
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), 0x0};
|
||||||
|
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 = static_cast<uint16_t>(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
|
||||||
|
|
||||||
|
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<int16_t>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||||
61
libraries/USB/src/USBMIDI.h
Normal file
61
libraries/USB/src/USBMIDI.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#if SOC_USB_OTG_SUPPORTED
|
||||||
|
#include "esp32-hal-tinyusb.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if CONFIG_TINYUSB_MIDI_ENABLED
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||||
Loading…
Reference in a new issue