Control Surface
main
MIDI Control Surface library for Arduino
Toggle main menu visibility
Loading...
Searching...
No Matches
src
MIDI_Parsers
SerialMIDI_Parser.cpp
Go to the documentation of this file.
1
#include "
SerialMIDI_Parser.hpp
"
2
3
BEGIN_CS_NAMESPACE
4
5
MIDIReadEvent
SerialMIDI_Parser::handleRealTime
(uint8_t midiByte) {
6
rtmsg
.message = midiByte;
7
return
MIDIReadEvent::REALTIME_MESSAGE
;
8
}
9
10
MIDIReadEvent
SerialMIDI_Parser::handleNonRealTimeStatus
(uint8_t midiByte) {
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);
27
return
MIDIReadEvent::SYSEX_CHUNK
;
28
}
29
// Enough space is available in buffer, store the end byte and
30
// terminate the message.
31
addSysExByte
(uint8_t(
MIDIMessageType::SysExEnd
));
32
endSysEx
();
33
currentHeader
= 0;
34
runningHeader
= 0;
35
return
MIDIReadEvent::SYSEX_MESSAGE
;
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;
44
if
(
sysCommonCancelsRunningStatus
)
45
runningHeader
= 0;
46
currentHeader
= 0;
47
return
MIDIReadEvent::SYSCOMMON_MESSAGE
;
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
();
54
addSysExByte
(uint8_t(
MIDIMessageType::SysExStart
));
55
runningHeader
= 0;
56
currentHeader
= midiByte;
57
return
MIDIReadEvent::NO_MESSAGE
;
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"
));
62
return
MIDIReadEvent::NO_MESSAGE
;
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
;
72
return
MIDIReadEvent::NO_MESSAGE
;
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
85
MIDIReadEvent
SerialMIDI_Parser::handleStatus
(uint8_t midiByte) {
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
96
MIDIReadEvent
SerialMIDI_Parser::handleData
(uint8_t midiByte) {
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"
));
101
return
MIDIReadEvent::NO_MESSAGE
;
102
}
103
// If we have an active running status, use that as the current header
104
currentHeader
=
runningHeader
;
105
}
106
107
midimsg
.header =
currentHeader
;
108
109
// If this is the third byte of three (second data byte)
110
if
(
thirdByte
) {
111
// If it's a channel message
112
if
(
midimsg
.hasValidChannelMessageHeader()) {
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
;
117
runningHeader
=
midimsg
.header;
118
currentHeader
= 0;
119
return
MIDIReadEvent::CHANNEL_MESSAGE
;
120
}
121
// If it's a system common message
122
else
if
(
midimsg
.hasValidSystemCommonHeader()) {
123
midimsg
.data2 = midiByte;
124
thirdByte
=
false
;
125
if
(
sysCommonCancelsRunningStatus
)
126
runningHeader
= 0;
127
currentHeader
= 0;
128
return
MIDIReadEvent::SYSCOMMON_MESSAGE
;
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
137
else
if
(
midimsg
.hasValidChannelMessageHeader()) {
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
;
143
return
MIDIReadEvent::NO_MESSAGE
;
144
}
145
// If it's a channel message with one data byte
146
else
{
147
midimsg
.data1 = midiByte;
148
midimsg
.data2 = 0;
149
runningHeader
=
midimsg
.header;
150
currentHeader
= 0;
151
// The message is finished
152
return
MIDIReadEvent::CHANNEL_MESSAGE
;
153
}
154
}
155
156
// If it's a system common message
157
else
if
(
midimsg
.hasValidSystemCommonHeader()) {
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
;
163
return
MIDIReadEvent::NO_MESSAGE
;
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;
169
if
(
sysCommonCancelsRunningStatus
)
170
runningHeader
= 0;
171
currentHeader
= 0;
172
// The message is finished
173
return
MIDIReadEvent::SYSCOMMON_MESSAGE
;
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
185
return
MIDIReadEvent::SYSEX_CHUNK
;
186
}
187
188
addSysExByte
(midiByte);
189
return
MIDIReadEvent::NO_MESSAGE
;
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
199
MIDIReadEvent
SerialMIDI_Parser::feed
(uint8_t midiByte) {
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
212
MIDIReadEvent
SerialMIDI_Parser::resume
() {
213
if
(!
hasStoredByte
())
214
return
MIDIReadEvent::NO_MESSAGE
;
215
216
uint8_t midiByte =
popStoredByte
();
217
218
#if !IGNORE_SYSEX
219
// If a SysEx message was in progress
220
if
(
currentHeader
== uint8_t(
MIDIMessageType::SysExStart
)) {
221
// Reset the buffer for the next chunk
222
startSysEx
();
223
}
224
#endif
225
226
return
feed
(midiByte);
227
}
228
229
END_CS_NAMESPACE
MIDIReadEvent
MIDIReadEvent
Values returned by the MIDI reading functions.
Definition
MIDIReadEvent.hpp:11
MIDIReadEvent::CHANNEL_MESSAGE
@ CHANNEL_MESSAGE
A MIDI Channel message was received.
Definition
MIDIReadEvent.hpp:13
MIDIReadEvent::SYSEX_CHUNK
@ SYSEX_CHUNK
An incomplete System Exclusive message.
Definition
MIDIReadEvent.hpp:16
MIDIReadEvent::SYSCOMMON_MESSAGE
@ SYSCOMMON_MESSAGE
A MIDI System Common message was received.
Definition
MIDIReadEvent.hpp:17
MIDIReadEvent::NO_MESSAGE
@ NO_MESSAGE
No new messages were received.
Definition
MIDIReadEvent.hpp:12
MIDIReadEvent::SYSEX_MESSAGE
@ SYSEX_MESSAGE
A MIDI System Exclusive message was received.
Definition
MIDIReadEvent.hpp:14
MIDIReadEvent::REALTIME_MESSAGE
@ REALTIME_MESSAGE
A MIDI Real-Time message was received.
Definition
MIDIReadEvent.hpp:15
MIDIMessageType::TimingClock
@ TimingClock
Timing Clock System Real-Time message.
Definition
MIDI_MessageTypes.hpp:48
MIDIMessageType::SysExStart
@ SysExStart
Start of System Exclusive.
Definition
MIDI_MessageTypes.hpp:34
MIDIMessageType::TuneRequest
@ TuneRequest
Tune Request System Common message (1B).
Definition
MIDI_MessageTypes.hpp:44
MIDIMessageType::SysExEnd
@ SysExEnd
End of System Exclusive.
Definition
MIDI_MessageTypes.hpp:45
SerialMIDI_Parser.hpp
END_CS_NAMESPACE
#define END_CS_NAMESPACE
Definition
Settings/NamespaceSettings.hpp:14
BEGIN_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Definition
Settings/NamespaceSettings.hpp:11
MIDI_Parser::isStatus
static bool isStatus(uint8_t data)
Check if the given byte is a MIDI header/status byte.
Definition
MIDI_Parser.hpp:37
MIDI_Parser::midimsg
MIDIMessage midimsg
Definition
MIDI_Parser.hpp:32
MIDI_Parser::rtmsg
RealTimeMessage rtmsg
Definition
MIDI_Parser.hpp:33
SerialMIDI_Parser::resume
MIDIReadEvent resume()
Resume the parser with the previously stored and unhandled byte.
Definition
SerialMIDI_Parser.cpp:212
SerialMIDI_Parser::sysCommonCancelsRunningStatus
bool sysCommonCancelsRunningStatus
Accounts for running status differences between MIDI 1.0 and BLE-MIDI.
Definition
SerialMIDI_Parser.hpp:79
SerialMIDI_Parser::endSysEx
void endSysEx()
Definition
SerialMIDI_Parser.hpp:46
SerialMIDI_Parser::currentHeader
uint8_t currentHeader
Current header (not necessarily running), contains the header of the message that's currently being r...
Definition
SerialMIDI_Parser.hpp:86
SerialMIDI_Parser::handleNonRealTimeStatus
MIDIReadEvent handleNonRealTimeStatus(uint8_t midiByte)
Definition
SerialMIDI_Parser.cpp:10
SerialMIDI_Parser::handleRealTime
MIDIReadEvent handleRealTime(uint8_t midiByte)
Definition
SerialMIDI_Parser.cpp:5
SerialMIDI_Parser::runningHeader
uint8_t runningHeader
Running status header.
Definition
SerialMIDI_Parser.hpp:88
SerialMIDI_Parser::hasStoredByte
bool hasStoredByte() const
Check whether there's a stored byte.
Definition
SerialMIDI_Parser.hpp:64
SerialMIDI_Parser::popStoredByte
uint8_t popStoredByte()
Get the stored byte. Afterwards, hasStoredByte will return false.
Definition
SerialMIDI_Parser.hpp:66
SerialMIDI_Parser::feed
MIDIReadEvent feed(uint8_t midibyte)
Feed a new byte to the parser.
Definition
SerialMIDI_Parser.cpp:199
SerialMIDI_Parser::storeByte
void storeByte(uint8_t midiByte)
Store a byte to parse later.
Definition
SerialMIDI_Parser.hpp:61
SerialMIDI_Parser::handleData
MIDIReadEvent handleData(uint8_t midiByte)
Definition
SerialMIDI_Parser.cpp:96
SerialMIDI_Parser::thirdByte
bool thirdByte
Flag that remembers that the next data byte will be the third byte of a message.
Definition
SerialMIDI_Parser.hpp:82
SerialMIDI_Parser::startSysEx
void startSysEx()
Definition
SerialMIDI_Parser.hpp:45
SerialMIDI_Parser::hasSysExSpace
bool hasSysExSpace() const
Definition
SerialMIDI_Parser.hpp:44
SerialMIDI_Parser::addSysExByte
void addSysExByte(uint8_t data)
Definition
SerialMIDI_Parser.hpp:43
SerialMIDI_Parser::handleStatus
MIDIReadEvent handleStatus(uint8_t midiByte)
Definition
SerialMIDI_Parser.cpp:85
DEBUGREF
#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
ChannelMessage
Definition
MIDI_MessageTypes.hpp:205
SysCommonMessage
Definition
MIDI_MessageTypes.hpp:263
Generated by
1.17.0