LCOV - code coverage report
Current view: top level - src/MIDI_Parsers - SerialMIDI_Parser.hpp (source / functions) Hit Total Coverage
Test: 169c36a3797bc662d84b5726f34a3f37d3c58247 Lines: 24 24 100.0 %
Date: 2024-11-09 15:32:27 Functions: 13 13 100.0 %
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 1.15