Control Surface master
MIDI Control Surface library for Arduino
USBMIDI_Parser.cpp
Go to the documentation of this file.
1#include "USBMIDI_Parser.hpp"
2#include <Settings/SettingsWrapper.hpp>
3
5
7 Cable cable) {
8 midimsg.header = packet[1];
9 midimsg.data1 = packet[2];
10 midimsg.data2 = packet[3];
11 midimsg.cable = cable;
13}
14
16 Cable cable) {
17#if !IGNORE_SYSEX
18 // If this is a SysEx start packet
19 if (packet[1] == uint8_t(MIDIMessageType::SYSEX_START)) {
20 startSysEx(cable); // start a new message
21 // (overwrites previous unfinished message)
22 }
23 // If we haven't received a SysExStart
24 else if (!receivingSysEx(cable)) {
25 DEBUGREF(F("No SysExStart received"));
26 return MIDIReadEvent::NO_MESSAGE; // ignore the data
27 }
28
29 // Check if the SysEx buffer has enough space to store the data
30 if (!hasSysExSpace(cable, 3)) {
31 storePacket(packet);
32 endSysExChunk(cable);
34 }
35
36 // Enough space available in buffer, store the data
37 addSysExBytes(cable, &packet[1], 3);
38#else
39 (void)packet;
40 (void)cable;
41#endif
42 return MIDIReadEvent::NO_MESSAGE; // SysEx is not finished yet
43}
44
45template <uint8_t NumBytes>
47 Cable cable) {
48 static_assert(NumBytes == 2 || NumBytes == 3,
49 "Only 2- or 3-byte SysEx packets are supported");
50
51#if !IGNORE_SYSEX
52 // This could be the a very short SysEx message that starts and ends with
53 // this packet
54 if (packet[1] == uint8_t(MIDIMessageType::SYSEX_START)) {
55 startSysEx(cable); // start a new message
56 // (overwrites previous unfinished message)
57 }
58 // If we haven't received a SysExStart
59 else if (!receivingSysEx(cable)) {
60 DEBUGFN(F("No SysExStart received"));
61 return MIDIReadEvent::NO_MESSAGE; // ignore the data
62 }
63
64 // Check if the SysEx buffer has enough space to store the end byte
65 if (!hasSysExSpace(cable, NumBytes)) {
66 storePacket(packet);
67 endSysExChunk(cable);
68 return MIDIReadEvent::SYSEX_CHUNK; // Buffer full
69 }
70
71 // Enough space available in buffer, finish the message
72 addSysExBytes(cable, &packet[1], NumBytes);
73 endSysEx(cable);
75#else
76 (void)packet;
77 (void)cable;
79#endif
80}
81
82template <>
83MIDIReadEvent USBMIDI_Parser::handleSysExEnd<1>(MIDIUSBPacket_t packet,
84 Cable cable) {
85 // Single-byte System Common Message
86 if (packet[1] != uint8_t(MIDIMessageType::SYSEX_END)) {
87 // System Common (1 byte)
88 midimsg.header = packet[1];
89 midimsg.cable = cable;
91 }
92
93#if !IGNORE_SYSEX
94 // SysEx ends with following single byte
95 else {
96 // If we haven't received a SysExStart
97 if (!receivingSysEx(cable)) {
98 DEBUGREF(F("No SysExStart received"));
99 return MIDIReadEvent::NO_MESSAGE; // ignore the data
100 }
101
102 // Check if the SysEx buffer has enough space to store the end byte
103 if (!hasSysExSpace(cable, 1)) {
104 storePacket(packet);
105 endSysExChunk(cable);
107 }
108
109 // Enough space available in buffer, finish the message
110 addSysExByte(cable, packet[1]);
111 endSysEx(cable);
113 }
114#else
115 (void)packet;
116 (void)cable;
118#endif
119}
120
122 Cable cable) {
123 midimsg.header = packet[1];
124 midimsg.data1 = packet[2];
125 midimsg.data2 = packet[3];
126 midimsg.cable = cable;
128}
129
130// Single Byte
132 Cable cable) {
133 rtmsg.message = packet[1];
134 rtmsg.cable = cable;
136}
137
138// https://usb.org/sites/default/files/midi10.pdf
140 // DEBUG("MIDIUSB packet:\t" << hex << packet[0] << ' ' << packet[1] << ' '
141 // << packet[2] << ' ' << packet[3] << dec);
142
143 // MIDI USB cable number and code index number
144 Cable cable = Cable(packet[0] >> 4);
145 MIDICodeIndexNumber CIN = MIDICodeIndexNumber(packet[0] & 0xF);
146
147 // Ignore all messages for cables that we don't have
148 if (cable.getRaw() >= USB_MIDI_NUMBER_OF_CABLES)
149 return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
150
151 using M = MIDICodeIndexNumber;
152 switch (CIN) {
153 case M::MISC_FUNCTION_CODES: break; // LCOV_EXCL_LINE
154 case M::CABLE_EVENTS: break; // LCOV_EXCL_LINE
155 case M::SYSTEM_COMMON_2B: // fallthrough
156 case M::SYSTEM_COMMON_3B: return handleSysCommon(packet, cable);
157 case M::SYSEX_START_CONT: return handleSysExStartCont(packet, cable);
158 case M::SYSEX_END_1B: return handleSysExEnd<1>(packet, cable);
159 case M::SYSEX_END_2B: return handleSysExEnd<2>(packet, cable);
160 case M::SYSEX_END_3B: return handleSysExEnd<3>(packet, cable);
161 case M::NOTE_OFF: // fallthrough
162 case M::NOTE_ON: // fallthrough
163 case M::KEY_PRESSURE: // fallthrough
164 case M::CONTROL_CHANGE: // fallthrough
165 case M::PROGRAM_CHANGE: // fallthrough
166 case M::CHANNEL_PRESSURE: // fallthrough
167 case M::PITCH_BEND: return handleChannelMessage(packet, cable);
168 case M::SINGLE_BYTE: return handleSingleByte(packet, cable);
169 default: break; // LCOV_EXCL_LINE
170 }
171
172 return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
173}
174
176#if !IGNORE_SYSEX
177 if (!hasStoredPacket())
179
181
182 // If a SysEx message was in progress
184 // Reset the buffer for the next chunk
186 }
187
188 return feed(packet);
189#else
191#endif
192}
193
MIDIReadEvent
Values returned by the MIDI reading functions.
@ CHANNEL_MESSAGE
A MIDI Channel message was received.
@ SYSEX_CHUNK
An incomplete System Exclusive message.
@ SYSCOMMON_MESSAGE
A MIDI System Common message was received.
@ NO_MESSAGE
No new messages were received.
@ SYSEX_MESSAGE
A MIDI System Exclusive message was received.
@ REALTIME_MESSAGE
A MIDI Real-Time message was received.
MIDICodeIndexNumber
MIDI USB Code Index Numbers.
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
#define USB_MIDI_NUMBER_OF_CABLES
A type-safe class for MIDI USB Cable numbers.
Definition: Cable.hpp:13
constexpr uint8_t getRaw() const
Get the cable as an integer.
Definition: Cable.hpp:29
MIDIMessage midimsg
Definition: MIDI_Parser.hpp:32
RealTimeMessage rtmsg
Definition: MIDI_Parser.hpp:33
bool hasSysExSpace(Cable cable, uint8_t amount) const
MIDIReadEvent handleSingleByte(MIDIUSBPacket_t packet, Cable cable)
MIDIReadEvent resume()
Resume the parser with the previously stored and unhandled packet.
MIDIReadEvent handleSysExStartCont(MIDIUSBPacket_t packet, Cable cable)
void endSysExChunk(Cable cable)
void storePacket(MIDIUSBPacket_t packet)
MIDIReadEvent handleSysExEnd(MIDIUSBPacket_t packet, Cable cable)
MIDIReadEvent handleChannelMessage(MIDIUSBPacket_t packet, Cable cable)
void startSysEx(Cable cable)
bool receivingSysEx(Cable cable) const
bool hasStoredPacket() const
MIDIUSBPacket_t popStoredPacket()
MIDIReadEvent feed(MIDIUSBPacket_t packet)
Feed a new packet to the parser.
void addSysExByte(Cable cable, uint8_t data)
void endSysEx(Cable cable)
void addSysExBytes(Cable cable, const uint8_t *data, uint8_t len)
MIDIReadEvent handleSysCommon(MIDIUSBPacket_t packet, Cable cable)
#define DEBUGFN(x)
Print an expression and its function (function name and line number) to the debug output if debugging...
Definition: Debug.hpp:118
#define DEBUGREF(x)
Print an expression and its location (file and line number) to the debug output if debugging is enabl...
Definition: Debug.hpp:108
uint8_t data2
First MIDI data byte.
uint8_t header
MIDI status byte (message type and channel).
uint8_t data1
First MIDI data byte.
Cable cable
USB MIDI cable number;.