Skip to content

Commit 964fcca

Browse files
committed
Added USBMIDI support to libraries/USB
1 parent 1c3039e commit 964fcca

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

libraries/USB/src/USBMIDI.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include "USBMIDI.h"
2+
3+
#if CONFIG_TINYUSB_MIDI_ENABLED
4+
5+
#include "Arduino.h"
6+
#include "esp32-hal-tinyusb.h"
7+
8+
static bool tinyusb_midi_descriptor_loaded = false;
9+
static bool tinyusb_midi_interface_enabled = false;
10+
11+
extern "C" uint16_t tusb_midi_load_descriptor(uint8_t *dst, uint8_t *itf) {
12+
if (tinyusb_midi_descriptor_loaded) {
13+
return 0;
14+
}
15+
tinyusb_midi_descriptor_loaded = true;
16+
17+
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MIDI");
18+
uint8_t ep_in = tinyusb_get_free_in_endpoint();
19+
TU_VERIFY(ep_in != 0);
20+
uint8_t ep_out = tinyusb_get_free_out_endpoint();
21+
TU_VERIFY(ep_out != 0);
22+
uint8_t descriptor[TUD_MIDI_DESC_LEN] = {
23+
TUD_MIDI_DESCRIPTOR(*itf, str_index, ep_out, (uint8_t)(0x80 | ep_in), 64),
24+
};
25+
*itf += 2;
26+
memcpy(dst, descriptor, TUD_MIDI_DESC_LEN);
27+
28+
return TUD_MIDI_DESC_LEN;
29+
}
30+
31+
USBMIDI::USBMIDI() {
32+
if (!tinyusb_midi_interface_enabled) {
33+
tinyusb_midi_interface_enabled = true;
34+
tinyusb_enable_interface(USB_INTERFACE_MIDI, TUD_MIDI_DESC_LEN, tusb_midi_load_descriptor);
35+
} else {
36+
log_e("USBMIDI: Multiple instances of USBMIDI not supported!");
37+
}
38+
}
39+
40+
void USBMIDI::begin() {}
41+
void USBMIDI::end() {}
42+
43+
#define STATUS(CIN, CHANNEL) (((CIN & 0x7F) << 4) | (constrain(CHANNEL - 1, 0, 15) & 0x7F))
44+
45+
// Note: All the user-level API calls do extensive input constraining to prevent easy to make mistakes.
46+
// (You can thank me later.)
47+
#define _(x) constrain(x, 0, 127)
48+
49+
// Note On
50+
void USBMIDI::noteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
51+
midiEventPacket_t event = {MIDI_CIN_NOTE_ON, STATUS(MIDI_CIN_NOTE_ON, channel), _(note),
52+
_(velocity)};
53+
writePacket(&event);
54+
}
55+
56+
// Note Off
57+
void USBMIDI::noteOff(uint8_t note, uint8_t velocity, uint8_t channel) {
58+
midiEventPacket_t event = {MIDI_CIN_NOTE_OFF, STATUS(MIDI_CIN_NOTE_OFF, channel), _(note),
59+
_(velocity)};
60+
writePacket(&event);
61+
}
62+
63+
// Program Change
64+
void USBMIDI::programChange(uint8_t program, uint8_t channel) {
65+
midiEventPacket_t event = {MIDI_CIN_PROGRAM_CHANGE, STATUS(MIDI_CIN_PROGRAM_CHANGE, channel),
66+
_(program)};
67+
writePacket(&event);
68+
}
69+
70+
// Control Change (Continuous Controller)
71+
void USBMIDI::controlChange(uint8_t control, uint8_t value, uint8_t channel) {
72+
midiEventPacket_t event = {MIDI_CIN_CONTROL_CHANGE, STATUS(MIDI_CIN_CONTROL_CHANGE, channel),
73+
_(control), _(value)};
74+
writePacket(&event);
75+
}
76+
77+
// Polyphonic Key Pressure (Aftertouch)
78+
void USBMIDI::polyPressure(uint8_t note, uint8_t pressure, uint8_t channel) {
79+
midiEventPacket_t event = {MIDI_CIN_POLY_KEYPRESS, STATUS(MIDI_CIN_POLY_KEYPRESS, channel), _(note),
80+
_(pressure)};
81+
writePacket(&event);
82+
}
83+
84+
// Channel Pressure (Aftertouch)
85+
void USBMIDI::channelPressure(uint8_t pressure, uint8_t channel) {
86+
midiEventPacket_t event = {MIDI_CIN_CHANNEL_PRESSURE, STATUS(MIDI_CIN_CHANNEL_PRESSURE, channel),
87+
_(pressure)};
88+
writePacket(&event);
89+
}
90+
91+
// Pitch Bend Change [-8192,0,8191]
92+
void USBMIDI::pitchBend(int16_t value, uint8_t channel) {
93+
uint16_t pitchBendValue = constrain(value, -8192, 8191) + 8192;
94+
pitchBend(pitchBendValue);
95+
}
96+
97+
// Pitch Bend Change [0,8192,16383]
98+
void USBMIDI::pitchBend(uint16_t value, uint8_t channel) {
99+
uint16_t pitchBendValue = constrain(value, 0, 16383);
100+
// Split the 14-bit integer into two 7-bit values
101+
uint8_t lsb = pitchBendValue & 0x7F; // Lower 7 bits
102+
uint8_t msb = (pitchBendValue >> 7) & 0x7F; // Upper 7 bits
103+
104+
midiEventPacket_t event = {MIDI_CIN_PITCH_BEND_CHANGE, STATUS(MIDI_CIN_PITCH_BEND_CHANGE, channel),
105+
lsb, msb};
106+
writePacket(&event);
107+
}
108+
109+
// Pitch Bend Change [-1.0,0,1.0]
110+
void USBMIDI::pitchBend(double value, uint8_t channel) {
111+
// Multiply by 8191 and round to nearest integer
112+
int16_t pitchBendValue = static_cast<int16_t>(round(constrain(value, -1.0, 1.0) * 8191.0));
113+
114+
pitchBend(pitchBendValue, channel);
115+
}
116+
117+
bool USBMIDI::readPacket(midiEventPacket_t *packet) {
118+
return tud_midi_packet_read((uint8_t *)packet);
119+
}
120+
121+
bool USBMIDI::writePacket(midiEventPacket_t *packet) {
122+
return tud_midi_packet_write((uint8_t *)packet);
123+
}
124+
125+
// Default Cable Number (for simplified APIs that do not expose this)
126+
#define DEFAULT_CN 0
127+
128+
size_t USBMIDI::write(uint8_t c) {
129+
// MIDI_CIN_1BYTE_DATA => Verbatim MIDI byte-stream copy
130+
// (See also Table 4-1 of USB MIDI spec 1.0)
131+
midiEventPacket_t packet = {DEFAULT_CN | MIDI_CIN_1BYTE_DATA, c, 0, 0};
132+
133+
return tud_midi_packet_write((uint8_t *)&packet);
134+
}
135+
136+
#endif // CONFIG_TINYUSB_MIDI_ENABLED

libraries/USB/src/USBMIDI.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
#include "esp32-hal-tinyusb.h"
3+
#include "sdkconfig.h"
4+
5+
#if CONFIG_TINYUSB_MIDI_ENABLED
6+
7+
#pragma once
8+
9+
// Patch MIDI definition bug pending update/pull from upstream tinyusb repo.
10+
// See also:
11+
// - https://github.com/hathach/tinyusb/pull/1920
12+
// - https://github.com/espressif/arduino-esp32/pull/8117
13+
//
14+
#undef MIDI_CIN_NOTE_ON
15+
#define MIDI_CIN_NOTE_ON 9
16+
#undef MIDI_CIN_NOTE_OFF
17+
#define MIDI_CIN_NOTE_OFF 8
18+
19+
#define MIDI_EP_HEADER_CN_GET(x) (x >> 4)
20+
#define MIDI_EP_HEADER_CIN_GET(x) ((midi_code_index_number_t)((x)&0xF))
21+
22+
typedef struct {
23+
uint8_t header;
24+
uint8_t byte1;
25+
uint8_t byte2;
26+
uint8_t byte3;
27+
} midiEventPacket_t;
28+
29+
class USBMIDI {
30+
public:
31+
USBMIDI(void);
32+
void begin(void);
33+
void end(void);
34+
35+
//////////////////////////
36+
///// User-level API /////
37+
//
38+
// Note On
39+
void noteOn(uint8_t note, uint8_t velocity = 0, uint8_t channel = 1);
40+
// Note Off
41+
void noteOff(uint8_t note, uint8_t velocity = 0, uint8_t channel = 1);
42+
// Program Change
43+
void programChange(uint8_t inProgramNumber, uint8_t channel = 1);
44+
// Control Change (Continuous Controller)
45+
void controlChange(uint8_t inControlNumber, uint8_t inControlValue = 0, uint8_t channel = 1);
46+
// Polyphonic Key Pressure (Aftertouch)
47+
void polyPressure(uint8_t note, uint8_t pressure, uint8_t channel = 1);
48+
// Channel Pressure (Aftertouch)
49+
void channelPressure(uint8_t pressure, uint8_t channel = 1);
50+
// Pitch Bend Change [-8192,0,8191]
51+
void pitchBend(int16_t pitchBendValue, uint8_t channel = 1);
52+
// Pitch Bend Change [0,8192,16383]
53+
void pitchBend(uint16_t pitchBendValue, uint8_t channel = 1);
54+
// Pitch Bend Change [-1.0,0,1.0]
55+
void pitchBend(double pitchBendValue, uint8_t channel = 1);
56+
57+
//////////////////////////////////
58+
///// USB MIDI 1.0 interface /////
59+
//
60+
// Attempt to read a USB MIDI packet from the USB Bus
61+
bool readPacket(midiEventPacket_t *packet);
62+
// Attempt to write a USB MIDI packet to the USB Bus
63+
bool writePacket(midiEventPacket_t *packet);
64+
65+
/////////////////////////////////////
66+
///// Serial MIDI 1.0 interface /////
67+
//
68+
// Write a Serial MIDI byte (status or data) to the USB Bus
69+
size_t write(uint8_t c);
70+
};
71+
72+
#endif // CONFIG_TINYUSB_MIDI_ENABLED

0 commit comments

Comments
 (0)