Control Surface  1.1.0
MIDI Control Surface library for Arduino
USBMIDI_Interface.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "MIDI_Interface.hpp"
4 #include <AH/Error/Error.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 
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  */
44  public:
45  /**
46  * @brief Construct a new USBMIDI_Interface.
47  */
49 
50  private:
52 
53 // If this is a test on PC
54 #ifndef ARDUINO
55 
56  public:
57  W_SUGGEST_OVERRIDE_OFF
58 
59  MOCK_METHOD5(writeUSBPacket,
60  void(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t));
61  using MIDIUSBPacket_t = Array<uint8_t, 4>;
62  MOCK_METHOD0(readUSBPacket, MIDIUSBPacket_t(void));
63  void flush() {}
64 
65  W_SUGGEST_OVERRIDE_ON
66 
67  MIDI_read_t read() override {
68  for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
69  MIDIUSBPacket_t rx_packet = readUSBPacket();
70  if (!rx_packet[0])
71  return NO_MESSAGE;
72  MIDI_read_t parseResult = parser.parse(rx_packet.data);
73  if (parseResult != NO_MESSAGE)
74  return parseResult;
75  }
76  return NO_MESSAGE;
77  }
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  void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2,
144  uint8_t cn) override {
145  writeUSBPacket(cn, m >> 4, // CN|CIN
146  (m | c), // status
147  d1, // data 1
148  d2); // data 2
149  flush();
150  }
151 
152  void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override {
153  sendImpl(m, c, d1, 0, cn);
154  }
155 
156  void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override {
157  while (length > 3) {
158  writeUSBPacket(cn, 0x4, data[0], data[1], data[2]);
159  data += 3;
160  length -= 3;
161  }
162  switch (length) {
163  case 3: writeUSBPacket(cn, 0x7, data[0], data[1], data[2]); break;
164  case 2: writeUSBPacket(cn, 0x6, data[0], data[1], 0); break;
165  case 1: writeUSBPacket(cn, 0x5, data[0], 0, 0); break;
166  default: break;
167  }
168  flush();
169  }
170 
171  void sendImpl(uint8_t rt, uint8_t cn) override {
172  writeUSBPacket(cn, 0xF, // CN|CIN
173  rt, // single byte
174  0, // no data
175  0); // no data
176  flush();
177  }
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 
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 
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  */
310  public:
311  /**
312  * @brief Construct a new USBMIDI_Interface.
313  */
315 };
316 
318 
319 #endif
USBMIDI_Interface
A class for MIDI interfaces sending MIDI messages over a USB MIDI connection.
Definition: USBMIDI_Interface.hpp:43
MIDI_BAUD
constexpr auto MIDI_BAUD
Definition: MIDI_Interface.hpp:9
USBMIDI_Interface::sendImpl
void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2, uint8_t cn) override
Low-level function for sending a 3-byte MIDI message.
Definition: USBMIDI_Interface.hpp:143
USBMIDI_Interface::parser
USBMIDI_Parser parser
Definition: USBMIDI_Interface.hpp:51
Error.hpp
Parsing_MIDI_Interface
An abstract class for MIDI interfaces.
Definition: MIDI_Interface.hpp:188
SerialMIDI_Interface.hpp
USBMIDI_Interface::flush
void flush()
Definition: USBMIDI_Interface.hpp:123
MIDI_read_t
MIDI_read_t
Definition: MIDI_Parser.hpp:29
BEGIN_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:9
USBMIDI_Interface::sendImpl
void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override
Low-level function for sending a system exclusive MIDI message.
Definition: USBMIDI_Interface.hpp:156
USBMIDI_Parser.hpp
USBSerialMIDI_Interface
A class for MIDI interfaces sending and receiving MIDI messages over the Serial port of the USB conne...
Definition: SerialMIDI_Interface.hpp:135
END_CS_NAMESPACE
#define END_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:10
USBMIDI_Parser
Definition: USBMIDI_Parser.hpp:19
USBMIDI_Parser::parse
MIDI_read_t parse(uint8_t *packet)
Definition: USBMIDI_Parser.cpp:7
AH::Array
An array wrapper for easy copying, comparing, and iterating.
Definition: Array.hpp:36
USBMIDI_Interface::USBMIDI_Interface
USBMIDI_Interface()
Construct a new USBMIDI_Interface.
Definition: USBMIDI_Interface.hpp:48
TeensyUSBTypes.hpp
NO_MESSAGE
Definition: MIDI_Parser.hpp:30
USBMIDI_Interface::sendImpl
void sendImpl(uint8_t rt, uint8_t cn) override
Low-level function for sending a single-byte MIDI message.
Definition: USBMIDI_Interface.hpp:171
USBMIDI_Interface::writeUSBPacket
void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1, uint8_t d2)
Definition: USBMIDI_Interface.hpp:82
USBMIDI_Interface::read
MIDI_read_t read() override
Definition: USBMIDI_Interface.hpp:183
USBMIDI_Interface::rx_packet
usb_packet_t * rx_packet
Definition: USBMIDI_Interface.hpp:284
SYSEX_BUFFER_SIZE
constexpr size_t SYSEX_BUFFER_SIZE
The length of the maximum System Exclusive message that can be received.
Definition: Settings/Settings.hpp:53
MIDI_Interface.hpp
USBMIDI_Interface::sendImpl
void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override
Low-level function for sending a 2-byte MIDI message.
Definition: USBMIDI_Interface.hpp:152