LCOV - code coverage report
Current view: top level - src/MIDI_Parsers - SerialMIDI_Parser.cpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 98 98 100.0 %
Date: 2024-12-21 21:28:55 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.15