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