Line data Source code
1 : #pragma once
2 :
3 : #include "MIDI_Interface.hpp"
4 : #include <AH/Error/Error.hpp>
5 : #include <AH/Teensy/TeensyUSBTypes.hpp>
6 : #include <MIDI_Parsers/USBMIDI_Parser.hpp>
7 :
8 : #ifdef TEENSY_MIDIUSB_ENABLED
9 : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
10 : #include <usb_dev.h>
11 : #else // Teensy++ 2.0
12 : #include <usb_private.h>
13 : #endif
14 : #elif defined TEENSYDUINO
15 : #warning \
16 : "Teensy: USB MIDI not enabled. Enable it from the Tools > USB Type menu."
17 : #endif
18 :
19 : // If the main MCU has a USB connection but is not a Teensy
20 : #if defined(USBCON) && !defined(TEENSYDUINO)
21 : #include "MIDIUSB.h"
22 : #endif
23 :
24 : #ifndef ARDUINO
25 : #include <gmock-wrapper.h>
26 : #endif
27 :
28 : // If the main MCU has a USB connection or is a Teensy with MIDI USB type
29 : #if defined(USBCON) || defined(TEENSY_MIDIUSB_ENABLED) || !defined(ARDUINO)
30 :
31 : BEGIN_CS_NAMESPACE
32 :
33 : /**
34 : * @brief A class for MIDI interfaces sending MIDI messages over a USB MIDI
35 : * connection.
36 : *
37 : * @note See the [MIDI over USB Wiki]
38 : * (https://github.com/tttapa/MIDI_controller/wiki/MIDI-over-USB)
39 : * for more information.
40 : *
41 : * @ingroup MIDIInterfaces
42 : */
43 16 : class USBMIDI_Interface : public Parsing_MIDI_Interface {
44 : public:
45 : /**
46 : * @brief Construct a new USBMIDI_Interface.
47 : */
48 16 : USBMIDI_Interface() : Parsing_MIDI_Interface(parser) {}
49 :
50 : private:
51 : USBMIDI_Parser parser;
52 :
53 : // If this is a test on PC
54 : #ifndef ARDUINO
55 :
56 : public:
57 : W_SUGGEST_OVERRIDE_OFF
58 :
59 40 : MOCK_METHOD5(writeUSBPacket,
60 : void(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t));
61 : using MIDIUSBPacket_t = Array<uint8_t, 4>;
62 8 : MOCK_METHOD0(readUSBPacket, MIDIUSBPacket_t(void));
63 11 : void flush() {}
64 :
65 : W_SUGGEST_OVERRIDE_ON
66 :
67 3 : MIDI_read_t read() override {
68 5 : for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
69 5 : MIDIUSBPacket_t rx_packet = readUSBPacket();
70 5 : if (!rx_packet[0])
71 0 : return NO_MESSAGE;
72 5 : MIDI_read_t parseResult = parser.parse(rx_packet.data);
73 5 : if (parseResult != NO_MESSAGE)
74 3 : return parseResult;
75 2 : }
76 0 : return NO_MESSAGE;
77 3 : }
78 :
79 : // If it's a Teensy board
80 : #elif defined(TEENSYDUINO)
81 : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
82 : void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
83 : uint8_t d2) {
84 : usb_midi_write_packed((cn << 4) | cin | // CN|CIN
85 : (d0 << 8) | // status
86 : (d1 << 16) | // data 1
87 : (d2 << 24)); // data 2
88 : }
89 : #else // Teensy++ 2.0
90 : void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
91 : uint8_t d2) {
92 : uint8_t intr_state, timeout;
93 :
94 : if (!usb_configuration)
95 : return;
96 : intr_state = SREG;
97 : cli();
98 : UENUM = MIDI_TX_ENDPOINT;
99 : timeout = UDFNUML + 2;
100 : while (1) {
101 : // are we ready to transmit?
102 : if (UEINTX & (1 << RWAL))
103 : break;
104 : SREG = intr_state;
105 : if (UDFNUML == timeout)
106 : return;
107 : if (!usb_configuration)
108 : return;
109 : intr_state = SREG;
110 : cli();
111 : UENUM = MIDI_TX_ENDPOINT;
112 : }
113 : UEDATX = (cn << 4) | cin;
114 : UEDATX = d0;
115 : UEDATX = d1;
116 : UEDATX = d2;
117 : if (!(UEINTX & (1 << RWAL)))
118 : UEINTX = 0x3A;
119 : SREG = intr_state;
120 : }
121 : #endif // Teensy++ 2.0
122 :
123 : void flush() {}
124 :
125 : // If the main MCU has a USB connection but is not a Teensy
126 : #elif defined(USBCON)
127 :
128 : void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
129 : uint8_t d2) {
130 : midiEventPacket_t msg = {
131 : uint8_t((cn << 4) | cin), // CN|CIN
132 : d0, // status
133 : d1, // data 1
134 : d2, // data 2
135 : };
136 : MidiUSB.sendMIDI(msg);
137 : }
138 :
139 : void flush() { MidiUSB.flush(); }
140 :
141 : #endif
142 :
143 2 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2,
144 : uint8_t cn) override {
145 4 : writeUSBPacket(cn, m >> 4, // CN|CIN
146 2 : (m | c), // status
147 2 : d1, // data 1
148 2 : d2); // data 2
149 2 : flush();
150 2 : }
151 :
152 1 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override {
153 1 : sendImpl(m, c, d1, 0, cn);
154 1 : }
155 :
156 8 : void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override {
157 17 : while (length > 3) {
158 9 : writeUSBPacket(cn, 0x4, data[0], data[1], data[2]);
159 9 : data += 3;
160 9 : length -= 3;
161 : }
162 8 : switch (length) {
163 3 : case 3: writeUSBPacket(cn, 0x7, data[0], data[1], data[2]); break;
164 3 : case 2: writeUSBPacket(cn, 0x6, data[0], data[1], 0); break;
165 2 : case 1: writeUSBPacket(cn, 0x5, data[0], 0, 0); break;
166 0 : default: break;
167 : }
168 8 : flush();
169 8 : }
170 :
171 1 : void sendImpl(uint8_t rt, uint8_t cn) override {
172 2 : writeUSBPacket(cn, 0xF, // CN|CIN
173 1 : rt, // single byte
174 : 0, // no data
175 : 0); // no data
176 1 : flush();
177 1 : }
178 :
179 : public:
180 : // If it's a Teensy board
181 : #if defined(TEENSYDUINO)
182 : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
183 : MIDI_read_t read() override {
184 : for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
185 : if (rx_packet == nullptr) { // If there's no previous packet
186 : if (!usb_configuration) // Check USB configuration
187 : return NO_MESSAGE;
188 : rx_packet = usb_rx(
189 : MIDI_RX_ENDPOINT); // Read a new packet from the USB buffer
190 : if (rx_packet == nullptr) { // If there's no new packet, return
191 : return NO_MESSAGE;
192 : }
193 : if (rx_packet->len < 4) {
194 : // If the lenght is less than 4, it's not a valid MIDI USB
195 : // packet
196 : usb_free(rx_packet); // Free the packet
197 : rx_packet = nullptr; // Read new packet next time around
198 : return NO_MESSAGE;
199 : }
200 : }
201 :
202 : size_t index = rx_packet->index;
203 : uint8_t *data = rx_packet->buf + index; // A pointer to this packet
204 :
205 : MIDI_read_t parseResult = parser.parse(data);
206 :
207 : index += 4;
208 : if (index < rx_packet->len) {
209 : // If the packet is longer than 4 bytes
210 : rx_packet->index = index; // Change the index and read the next
211 : // four bytes on next read
212 : } else {
213 : // If the packet was only 4 bytes long
214 : usb_free(rx_packet); // Free the packet
215 : rx_packet = usb_rx(MIDI_RX_ENDPOINT); // Read the next packet
216 : }
217 : if (parseResult != NO_MESSAGE)
218 : return parseResult;
219 : }
220 : return NO_MESSAGE;
221 : }
222 : #else // Teensy++ 2.0
223 : MIDI_read_t read() override {
224 : for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
225 : uint8_t data[4];
226 :
227 : // https://github.com/PaulStoffregen/cores/blob/73ea157600a7082686d9cc48786a73caa7567da9/usb_midi/usb_api.cpp#L195
228 : uint8_t c, intr_state;
229 :
230 : intr_state = SREG;
231 : cli();
232 : if (!usb_configuration) {
233 : SREG = intr_state;
234 : return NO_MESSAGE;
235 : }
236 : UENUM = MIDI_RX_ENDPOINT;
237 : retry:
238 : c = UEINTX;
239 : if (!(c & (1 << RWAL))) {
240 : if (c & (1 << RXOUTI)) {
241 : UEINTX = 0x6B;
242 : goto retry;
243 : }
244 : SREG = intr_state;
245 : return NO_MESSAGE;
246 : }
247 : data[0] = UEDATX;
248 : data[1] = UEDATX;
249 : data[2] = UEDATX;
250 : data[3] = UEDATX;
251 : if (!(UEINTX & (1 << RWAL)))
252 : UEINTX = 0x6B;
253 : SREG = intr_state;
254 :
255 : MIDI_read_t parseResult = parser.parse(data);
256 :
257 : if (parseResult != NO_MESSAGE)
258 : return parseResult;
259 : }
260 : return NO_MESSAGE;
261 : }
262 : #endif // Teensy++ 2.0
263 :
264 : // If the main MCU has a USB connection but is not a Teensy → MIDIUSB library
265 : #elif defined(USBCON)
266 : MIDI_read_t read() override {
267 : for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
268 : midiEventPacket_t midipacket = MidiUSB.read();
269 : uint8_t rx_packet[4] = {midipacket.header, midipacket.byte1,
270 : midipacket.byte2, midipacket.byte3};
271 : if (!midipacket.header)
272 : return NO_MESSAGE;
273 : MIDI_read_t parseResult = parser.parse(rx_packet);
274 : if (parseResult != NO_MESSAGE)
275 : return parseResult;
276 : }
277 : return NO_MESSAGE;
278 : }
279 : #endif
280 :
281 : private:
282 : // If it's a Teensy board
283 : #if defined(TEENSYDUINO) && !defined(__AVR_AT90USB1286__)
284 : usb_packet_t *rx_packet = nullptr;
285 : #endif
286 : };
287 :
288 : END_CS_NAMESPACE
289 :
290 : // If the main MCU doesn't have a USB connection:
291 : // Fall back on Serial connection at the hardware MIDI baud rate.
292 : // (Can be used with HIDUINO or USBMidiKliK.)
293 : #elif !defined(USBCON) && !defined(TEENSYDUINO)
294 :
295 : #include "SerialMIDI_Interface.hpp"
296 :
297 : BEGIN_CS_NAMESPACE
298 :
299 : /**
300 : * @brief A class for MIDI interfaces sending MIDI messages over a USB MIDI
301 : * connection.
302 : *
303 : * @note See the [MIDI over USB Wiki]
304 : * (https://github.com/tttapa/MIDI_controller/wiki/MIDI-over-USB)
305 : * for more information.
306 : *
307 : * @ingroup MIDIInterfaces
308 : */
309 : class USBMIDI_Interface : public USBSerialMIDI_Interface {
310 : public:
311 : /**
312 : * @brief Construct a new USBMIDI_Interface.
313 : */
314 : USBMIDI_Interface() : USBSerialMIDI_Interface(MIDI_BAUD){};
315 : };
316 :
317 : END_CS_NAMESPACE
318 :
319 : #endif
|