Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
SerialMIDI_Parser.cpp
Go to the documentation of this file.
2
4
9
11#if !IGNORE_SYSEX
12 // If a SysEx message was being received, and now we receive another
13 // status byte, the status byte should terminate the SysEx message
14 // first, and then we can handle the new status byte later.
15 bool untermSysEx = currentHeader == uint8_t(MIDIMessageType::SysExStart);
16 if (untermSysEx) {
17 // Handle this new status byte later (unless it's just a SysEx End
18 // byte, in which case we can just terminate it now).
19 if (midiByte != uint8_t(MIDIMessageType::SysExEnd))
20 storeByte(midiByte);
21 // Terminate the SysEx message.
22 // Check if the SysEx buffer has enough space to store the end byte.
23 if (!hasSysExSpace()) {
24 // If not store the new status byte to handle it later, and
25 // return the chunk we have saved up to now.
26 storeByte(midiByte);
28 }
29 // Enough space is available in buffer, store the end byte and
30 // terminate the message.
32 endSysEx();
33 currentHeader = 0;
34 runningHeader = 0;
36 } else
37#endif
38 {
39 // Tune Request is a special System Common message of 1 byte.
40 if (midiByte == uint8_t(MIDIMessageType::TuneRequest)) {
41 midimsg.header = midiByte;
42 midimsg.data1 = 0;
43 midimsg.data2 = 0;
45 runningHeader = 0;
46 currentHeader = 0;
48 }
49#if !IGNORE_SYSEX
50 // If the new status byte is a SysExStart, reset the SysEx buffer
51 // and store the start byte.
52 else if (midiByte == uint8_t(MIDIMessageType::SysExStart)) {
53 startSysEx();
55 runningHeader = 0;
56 currentHeader = midiByte;
58 }
59 // This should already have been handled by the if (untermSysEx) above.
60 else if (midiByte == uint8_t(MIDIMessageType::SysExEnd)) {
61 DEBUGREF(F("Unexpected SysEx End"));
63 }
64#endif
65 // Otherwise, start a System Common or Channel message.
66 else {
67 // Save the newly received status byte.
68 currentHeader = midiByte;
69 // A new message starts, so we haven't received the second byte
70 // yet.
71 thirdByte = false;
73 }
74 }
75}
76
77/*
78 * Relevant sources about MIDI running status:
79 * - MIDI 1.0 Detailed Specification: A-2 (pdf p.65):
80 * “cleared when a System Exclusive or Common status message is received”
81 * - BLE-MIDI: p.4 (pdf p.7):
82 * “System Common and System Real-Time messages do not cancel Running Status”
83 */
84
86 // If it's a Real-Time message
87 if (midiByte >= uint8_t(MIDIMessageType::TimingClock)) {
88 return handleRealTime(midiByte);
89 }
90 // Normal header (channel message, system exclusive, system common):
91 else {
92 return handleNonRealTimeStatus(midiByte);
93 }
94}
95
97 if (currentHeader == 0) {
98 // If we didn't receive a header, we can't do anything with this data
99 if (runningHeader == 0) {
100 DEBUGREF(F("Data byte ignored"));
102 }
103 // If we have an active running status, use that as the current header
105 }
106
108
109 // If this is the third byte of three (second data byte)
110 if (thirdByte) {
111 // If it's a channel message
113 midimsg.data2 = midiByte;
114 // Next byte is either a header or the first data byte of the next
115 // message, so clear the thirdByte flag
116 thirdByte = false;
118 currentHeader = 0;
120 }
121 // If it's a system common message
123 midimsg.data2 = midiByte;
124 thirdByte = false;
126 runningHeader = 0;
127 currentHeader = 0;
129 }
130 }
131
132 // If this is not the third byte of three, it's either the second byte
133 // (first data byte) of a channel or system common message,
134 // or a SysEx data byte
135
136 // If it's a channel message
138 // If it's a channel message with two data bytes
139 if (ChannelMessage(midimsg).hasTwoDataBytes()) {
140 midimsg.data1 = midiByte;
141 // We've received the second byte, expect the third byte next
142 thirdByte = true;
144 }
145 // If it's a channel message with one data byte
146 else {
147 midimsg.data1 = midiByte;
148 midimsg.data2 = 0;
150 currentHeader = 0;
151 // The message is finished
153 }
154 }
155
156 // If it's a system common message
158 // If it's a system common message with two data bytes
159 if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 2) {
160 midimsg.data1 = midiByte;
161 // We've received the second byte, expect the third byte next
162 thirdByte = true;
164 }
165 // If it's a system common message with one data byte
166 else if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 1) {
167 midimsg.data1 = midiByte;
168 midimsg.data2 = 0;
170 runningHeader = 0;
171 currentHeader = 0;
172 // The message is finished
174 }
175 }
176
177 // Otherwise, it's not a channel message
178
179#if !IGNORE_SYSEX
180 // If we're receiving a SysEx message, it's a SysEx data byte
181 else if (currentHeader == uint8_t(MIDIMessageType::SysExStart)) {
182 // Check if the SysEx buffer has enough space to store the data
183 if (!hasSysExSpace()) {
184 storeByte(midiByte); // Remember to add it next time
186 }
187
188 addSysExByte(midiByte);
190 }
191#endif // IGNORE_SYSEX
192
193 DEBUGREF(F("Data byte after invalid header")); // LCOV_EXCL_LINE
194 runningHeader = 0; // LCOV_EXCL_LINE
195 currentHeader = 0; // LCOV_EXCL_LINE
196 return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
197}
198
200 // DEBUGREF(hex << NAMEDVALUE(midiByte) << dec);
201
202 // If it's a status byte (first byte of a message)
203 if (isStatus(midiByte)) {
204 return handleStatus(midiByte);
205 }
206 // If it's a data byte
207 else {
208 return handleData(midiByte);
209 }
210}
211
213 if (!hasStoredByte())
215
216 uint8_t midiByte = popStoredByte();
217
218#if !IGNORE_SYSEX
219 // If a SysEx message was in progress
221 // Reset the buffer for the next chunk
222 startSysEx();
223 }
224#endif
225
226 return feed(midiByte);
227}
228
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.
@ TimingClock
Timing Clock System Real-Time message.
@ SysExStart
Start of System Exclusive.
@ TuneRequest
Tune Request System Common message (1B).
@ SysExEnd
End of System Exclusive.
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
static bool isStatus(uint8_t data)
Check if the given byte is a MIDI header/status byte.
MIDIMessage midimsg
RealTimeMessage rtmsg
MIDIReadEvent resume()
Resume the parser with the previously stored and unhandled byte.
bool sysCommonCancelsRunningStatus
Accounts for running status differences between MIDI 1.0 and BLE-MIDI.
uint8_t currentHeader
Current header (not necessarily running), contains the header of the message that's currently being r...
MIDIReadEvent handleNonRealTimeStatus(uint8_t midiByte)
MIDIReadEvent handleRealTime(uint8_t midiByte)
uint8_t runningHeader
Running status header.
bool hasStoredByte() const
Check whether there's a stored byte.
uint8_t popStoredByte()
Get the stored byte. Afterwards, hasStoredByte will return false.
MIDIReadEvent feed(uint8_t midibyte)
Feed a new byte to the parser.
void storeByte(uint8_t midiByte)
Store a byte to parse later.
MIDIReadEvent handleData(uint8_t midiByte)
bool thirdByte
Flag that remembers that the next data byte will be the third byte of a message.
void addSysExByte(uint8_t data)
MIDIReadEvent handleStatus(uint8_t midiByte)
#define DEBUGREF(x)
Print an expression and its location (file and line number) to the debug output if debugging is enabl...
Definition Debug.hpp:105
uint8_t data2
First MIDI data byte.
bool hasValidChannelMessageHeader() const
Check whether the header is a valid header for a channel message.
uint8_t header
MIDI status byte (message type and channel).
uint8_t data1
First MIDI data byte.
bool hasValidSystemCommonHeader() const
Check whether the header is a valid header for a System Common message.