Line data Source code
1 : #include "SerialMIDI_Parser.hpp"
2 :
3 : BEGIN_CS_NAMESPACE
4 :
5 9 : MIDIReadEvent SerialMIDI_Parser::handleRealTime(uint8_t midiByte) {
6 9 : rtmsg.message = midiByte;
7 9 : return MIDIReadEvent::REALTIME_MESSAGE;
8 : }
9 :
10 131 : 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 131 : bool untermSysEx = currentHeader == uint8_t(MIDIMessageType::SysExStart);
16 131 : 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 31 : if (midiByte != uint8_t(MIDIMessageType::SysExEnd))
20 7 : storeByte(midiByte);
21 : // Terminate the SysEx message.
22 : // Check if the SysEx buffer has enough space to store the end byte.
23 31 : 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 1 : storeByte(midiByte);
27 1 : return MIDIReadEvent::SYSEX_CHUNK;
28 : }
29 : // Enough space is available in buffer, store the end byte and
30 : // terminate the message.
31 30 : addSysExByte(uint8_t(MIDIMessageType::SysExEnd));
32 30 : endSysEx();
33 30 : currentHeader = 0;
34 30 : runningHeader = 0;
35 30 : return MIDIReadEvent::SYSEX_MESSAGE;
36 : } else
37 : #endif
38 : {
39 : // Tune Request is a special System Common message of 1 byte.
40 100 : if (midiByte == uint8_t(MIDIMessageType::TuneRequest)) {
41 5 : midimsg.header = midiByte;
42 5 : midimsg.data1 = 0;
43 5 : midimsg.data2 = 0;
44 5 : if (sysCommonCancelsRunningStatus)
45 4 : runningHeader = 0;
46 5 : currentHeader = 0;
47 5 : 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 95 : else if (midiByte == uint8_t(MIDIMessageType::SysExStart)) {
53 30 : startSysEx();
54 30 : addSysExByte(uint8_t(MIDIMessageType::SysExStart));
55 30 : runningHeader = 0;
56 30 : currentHeader = midiByte;
57 30 : return MIDIReadEvent::NO_MESSAGE;
58 : }
59 : // This should already have been handled by the if (untermSysEx) above.
60 65 : else if (midiByte == uint8_t(MIDIMessageType::SysExEnd)) {
61 : DEBUGREF(F("Unexpected SysEx End"));
62 11 : 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 54 : currentHeader = midiByte;
69 : // A new message starts, so we haven't received the second byte
70 : // yet.
71 54 : thirdByte = false;
72 54 : 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 140 : MIDIReadEvent SerialMIDI_Parser::handleStatus(uint8_t midiByte) {
86 : // If it's a Real-Time message
87 140 : if (midiByte >= uint8_t(MIDIMessageType::TimingClock)) {
88 9 : return handleRealTime(midiByte);
89 : }
90 : // Normal header (channel message, system exclusive, system common):
91 : else {
92 131 : return handleNonRealTimeStatus(midiByte);
93 : }
94 : }
95 :
96 870 : MIDIReadEvent SerialMIDI_Parser::handleData(uint8_t midiByte) {
97 870 : if (currentHeader == 0) {
98 : // If we didn't receive a header, we can't do anything with this data
99 52 : if (runningHeader == 0) {
100 : DEBUGREF(F("Data byte ignored"));
101 32 : return MIDIReadEvent::NO_MESSAGE;
102 : }
103 : // If we have an active running status, use that as the current header
104 20 : currentHeader = runningHeader;
105 : }
106 :
107 838 : midimsg.header = currentHeader;
108 :
109 : // If this is the third byte of three (second data byte)
110 838 : if (thirdByte) {
111 : // If it's a channel message
112 57 : if (midimsg.hasValidChannelMessageHeader()) {
113 51 : 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 51 : thirdByte = false;
117 51 : runningHeader = midimsg.header;
118 51 : currentHeader = 0;
119 51 : return MIDIReadEvent::CHANNEL_MESSAGE;
120 : }
121 : // If it's a system common message
122 6 : else if (midimsg.hasValidSystemCommonHeader()) {
123 6 : midimsg.data2 = midiByte;
124 6 : thirdByte = false;
125 6 : if (sysCommonCancelsRunningStatus)
126 4 : runningHeader = 0;
127 6 : currentHeader = 0;
128 6 : 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 781 : else if (midimsg.hasValidChannelMessageHeader()) {
138 : // If it's a channel message with two data bytes
139 62 : if (ChannelMessage(midimsg).hasTwoDataBytes()) {
140 51 : midimsg.data1 = midiByte;
141 : // We've received the second byte, expect the third byte next
142 51 : thirdByte = true;
143 51 : return MIDIReadEvent::NO_MESSAGE;
144 : }
145 : // If it's a channel message with one data byte
146 : else {
147 11 : midimsg.data1 = midiByte;
148 11 : midimsg.data2 = 0;
149 11 : runningHeader = midimsg.header;
150 11 : currentHeader = 0;
151 : // The message is finished
152 11 : return MIDIReadEvent::CHANNEL_MESSAGE;
153 : }
154 : }
155 :
156 : // If it's a system common message
157 719 : else if (midimsg.hasValidSystemCommonHeader()) {
158 : // If it's a system common message with two data bytes
159 12 : if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 2) {
160 6 : midimsg.data1 = midiByte;
161 : // We've received the second byte, expect the third byte next
162 6 : thirdByte = true;
163 6 : return MIDIReadEvent::NO_MESSAGE;
164 : }
165 : // If it's a system common message with one data byte
166 6 : else if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 1) {
167 6 : midimsg.data1 = midiByte;
168 6 : midimsg.data2 = 0;
169 6 : if (sysCommonCancelsRunningStatus)
170 5 : runningHeader = 0;
171 6 : currentHeader = 0;
172 : // The message is finished
173 6 : 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 707 : else if (currentHeader == uint8_t(MIDIMessageType::SysExStart)) {
182 : // Check if the SysEx buffer has enough space to store the data
183 707 : if (!hasSysExSpace()) {
184 3 : storeByte(midiByte); // Remember to add it next time
185 3 : return MIDIReadEvent::SYSEX_CHUNK;
186 : }
187 :
188 704 : addSysExByte(midiByte);
189 704 : 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 1010 : 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 1010 : if (isStatus(midiByte)) {
204 140 : return handleStatus(midiByte);
205 : }
206 : // If it's a data byte
207 : else {
208 870 : return handleData(midiByte);
209 : }
210 : }
211 :
212 200 : MIDIReadEvent SerialMIDI_Parser::resume() {
213 200 : if (!hasStoredByte())
214 189 : return MIDIReadEvent::NO_MESSAGE;
215 :
216 11 : uint8_t midiByte = popStoredByte();
217 :
218 : #if !IGNORE_SYSEX
219 : // If a SysEx message was in progress
220 11 : if (currentHeader == uint8_t(MIDIMessageType::SysExStart)) {
221 : // Reset the buffer for the next chunk
222 4 : startSysEx();
223 : }
224 : #endif
225 :
226 11 : return feed(midiByte);
227 : }
228 :
229 : END_CS_NAMESPACE
|