LCOV - code coverage report
Current view: top level - src/MIDI_Parsers - SerialMIDI_Parser.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 100.0 % 24 24
Test Date: 2026-06-06 17:44:35 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include "MIDI_Parser.hpp"
       4              : #include "SysExBuffer.hpp"
       5              : 
       6              : BEGIN_CS_NAMESPACE
       7              : 
       8              : /**
       9              :  * @brief   Parser for Serial MIDI streams (and BLE-MIDI).
      10              :  * 
      11              :  * @ingroup MIDIParsers
      12              :  */
      13              : class SerialMIDI_Parser : public MIDI_Parser {
      14              :   public:
      15          101 :     SerialMIDI_Parser(bool sysCommonCancelsRunningStatus = true)
      16          101 :         : sysCommonCancelsRunningStatus(sysCommonCancelsRunningStatus) {}
      17              : 
      18              :     /**
      19              :      * @brief   Parse one incoming MIDI message.
      20              :      * @param   puller
      21              :      *          The source of MIDI bytes.
      22              :      * @return  The type of MIDI message available, or 
      23              :      *          `MIDIReadEvent::NO_MESSAGE` if `puller` ran out of bytes before
      24              :      *          a complete message was parsed.
      25              :      */
      26              :     template <class BytePuller>
      27              :     MIDIReadEvent pull(BytePuller &&puller);
      28              : 
      29              :   protected:
      30              :     /// Feed a new byte to the parser.
      31              :     MIDIReadEvent feed(uint8_t midibyte);
      32              :     /// Resume the parser with the previously stored and unhandled byte.
      33              :     MIDIReadEvent resume();
      34              : 
      35              : #if !IGNORE_SYSEX
      36              :   public:
      37              :     /// Get the latest SysEx message.
      38           48 :     SysExMessage getSysExMessage() const {
      39           48 :         return {sysexbuffer.getBuffer(), sysexbuffer.getLength()};
      40              :     }
      41              : 
      42              :   protected:
      43          764 :     void addSysExByte(uint8_t data) { sysexbuffer.add(data); }
      44          738 :     bool hasSysExSpace() const { return sysexbuffer.hasSpaceLeft(); }
      45           34 :     void startSysEx() { sysexbuffer.start(); }
      46           30 :     void endSysEx() { sysexbuffer.end(); }
      47              : 
      48              :     SysExBuffer sysexbuffer;
      49              : #endif
      50              : 
      51              :   protected:
      52              :     MIDIReadEvent handleRealTime(uint8_t midiByte);
      53              :     MIDIReadEvent handleNonRealTimeStatus(uint8_t midiByte);
      54              :     MIDIReadEvent handleStatus(uint8_t midiByte);
      55              :     MIDIReadEvent handleData(uint8_t midiByte);
      56              : 
      57              :   protected:
      58              :     /// Store a byte to parse later. This is used when the SysEx buffer is full,
      59              :     /// for example. The byte cannot be added to the buffer now, so store it to
      60              :     /// add it the next time the parser is updated.
      61           11 :     void storeByte(uint8_t midiByte) { storedByte = midiByte; }
      62              :     /// Check whether there's a stored byte. If this is the case, this byte
      63              :     /// should be parsed before reading a new byte.
      64          200 :     bool hasStoredByte() const { return storedByte != 0xFF; }
      65              :     /// Get the stored byte. Afterwards, @ref hasStoredByte will return false.
      66           11 :     uint8_t popStoredByte() {
      67           11 :         uint8_t t = storedByte;
      68           11 :         storedByte = 0xFF;
      69           11 :         return t;
      70              :     }
      71              : 
      72              :   public:
      73              :     /// Clear the running status header for MIDI Channel messages.
      74              :     /// Internal method.
      75              :     void cancelRunningStatus() { runningHeader = 0; }
      76              : 
      77              :   private:
      78              :     /// Accounts for running status differences between MIDI 1.0 and BLE-MIDI.
      79              :     bool sysCommonCancelsRunningStatus;
      80              :     /// Flag that remembers that the next data byte will be the third byte of
      81              :     /// a message.
      82              :     bool thirdByte = false;
      83              :     /// Current header (not necessarily running), contains the header of the
      84              :     /// message that's currently being received. As soon as the message is
      85              :     /// complete, it is set to zero.
      86              :     uint8_t currentHeader = 0;
      87              :     /// Running status header.
      88              :     uint8_t runningHeader = 0;
      89              :     /// @see @ref storeByte
      90              :     uint8_t storedByte = 0xFF;
      91              : };
      92              : 
      93              : template <class BytePuller>
      94          200 : inline MIDIReadEvent SerialMIDI_Parser::pull(BytePuller &&puller) {
      95              :     // First try resuming the parser, we might have a stored byte that has to
      96              :     // be parsed first.
      97          200 :     MIDIReadEvent evt = resume();
      98          200 :     if (evt != MIDIReadEvent::NO_MESSAGE)
      99            2 :         return evt;
     100              : 
     101              :     // If resumption didn't produce a message, read new bytes from the input and
     102              :     // parse them until either we get a message, or until the input runs out of
     103              :     // new bytes.
     104          198 :     uint8_t midiByte;
     105         1077 :     while (puller.pull(midiByte)) {
     106          999 :         evt = feed(midiByte);
     107          999 :         if (evt != MIDIReadEvent::NO_MESSAGE)
     108          120 :             return evt;
     109              :     }
     110           78 :     return MIDIReadEvent::NO_MESSAGE;
     111              : }
     112              : 
     113              : END_CS_NAMESPACE
        

Generated by: LCOV version 2.4-beta