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/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
|
||||
|
|
|
|||
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