forked from espressif/arduino-esp32
-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathUSBMIDI.cpp
134 lines (106 loc) · 4.59 KB
/
USBMIDI.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#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), CFG_TUD_ENDOINT_SIZE),
};
*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 */