LCOV - code coverage report
Current view: top level - src/MIDI_Parsers - USBMIDI_Parser.hpp (source / functions) Hit Total Coverage
Test: 3a807a259ebe0769dd942f7f612dca5273937539 Lines: 36 36 100.0 %
Date: 2024-03-24 17:16:54 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #include "MIDI_Parser.hpp"
       2             : #include "SysExBuffer.hpp"
       3             : #include <AH/Containers/Array.hpp>
       4             : 
       5             : #ifdef MIDI_NUM_CABLES
       6             : #define USB_MIDI_NUMBER_OF_CABLES MIDI_NUM_CABLES
       7             : #elif defined(USB_MIDI4_SERIAL) || defined(USB_MIDI4)
       8             : #define USB_MIDI_NUMBER_OF_CABLES 4
       9             : #elif defined(USB_MIDI16_AUDIO_SERIAL) || defined(USB_MIDI16_SERIAL) ||        \
      10             :     defined(USB_MIDI16)
      11             : // TODO: || defined(USB_EVERYTHING)
      12             : #define USB_MIDI_NUMBER_OF_CABLES 16
      13             : #elif !defined(ARDUINO) || defined(DOXYGEN)
      14             : #define USB_MIDI_NUMBER_OF_CABLES 16
      15             : #else
      16             : #define USB_MIDI_NUMBER_OF_CABLES 1
      17             : #endif
      18             : 
      19             : BEGIN_CS_NAMESPACE
      20             : 
      21             : /**
      22             :  * @brief   Parser for MIDI over USB packets.
      23             :  * 
      24             :  * @ingroup MIDIParsers
      25             :  */
      26             : class USBMIDI_Parser : public MIDI_Parser {
      27             :   public:
      28             :     using MIDIUSBPacket_t = AH::Array<uint8_t, 4>;
      29             : 
      30             :     /**
      31             :      * @brief   Parse one incoming MIDI message.
      32             :      * @param   puller
      33             :      *          The source of MIDI USB packets.
      34             :      * @return  The type of MIDI message available, or 
      35             :      *          `MIDIReadEvent::NO_MESSAGE` if `puller` ran out of packets
      36             :      *          before a complete message was parsed.
      37             :      */
      38             :     template <class BytePuller>
      39             :     MIDIReadEvent pull(BytePuller &&puller);
      40             : 
      41             :   protected:
      42             :     /// Feed a new packet to the parser.
      43             :     MIDIReadEvent feed(MIDIUSBPacket_t packet);
      44             :     /// Resume the parser with the previously stored and unhandled packet.
      45             :     MIDIReadEvent resume();
      46             : 
      47             :   public:
      48             : #if !IGNORE_SYSEX
      49             :     /// Get the latest SysEx message.
      50          30 :     SysExMessage getSysExMessage() const {
      51             :         return {
      52          30 :             sysexbuffers[activeCable.getRaw()].getBuffer(),
      53          30 :             sysexbuffers[activeCable.getRaw()].getLength(),
      54             :             activeCable,
      55          90 :         };
      56             :     }
      57             : #endif
      58             : 
      59             :   protected:
      60             :     MIDIReadEvent handleChannelMessage(MIDIUSBPacket_t packet, Cable cable);
      61             :     MIDIReadEvent handleSingleByte(MIDIUSBPacket_t packet, Cable cable);
      62             :     MIDIReadEvent handleSysExStartCont(MIDIUSBPacket_t packet, Cable cable);
      63             :     template <uint8_t NumBytes>
      64             :     MIDIReadEvent handleSysExEnd(MIDIUSBPacket_t packet, Cable cable);
      65             :     MIDIReadEvent handleSysCommon(MIDIUSBPacket_t packet, Cable cable);
      66             : 
      67             :   protected:
      68             : #if !IGNORE_SYSEX
      69          14 :     void startSysEx(Cable cable) { sysexbuffers[cable.getRaw()].start(); }
      70          12 :     void endSysEx(Cable cable) {
      71          12 :         sysexbuffers[cable.getRaw()].end();
      72          12 :         activeCable = cable;
      73          12 :     }
      74           2 :     void endSysExChunk(Cable cable) { activeCable = cable; }
      75         114 :     bool hasSysExSpace(Cable cable, uint8_t amount) const {
      76         114 :         return sysexbuffers[cable.getRaw()].hasSpaceLeft(amount);
      77             :     }
      78           4 :     void addSysExByte(Cable cable, uint8_t data) {
      79           4 :         sysexbuffers[cable.getRaw()].add(data);
      80           4 :     }
      81         108 :     void addSysExBytes(Cable cable, const uint8_t *data, uint8_t len) {
      82         108 :         sysexbuffers[cable.getRaw()].add(data, len);
      83         108 :     }
      84         108 :     bool receivingSysEx(Cable cable) const {
      85         108 :         return sysexbuffers[cable.getRaw()].isReceiving();
      86             :     }
      87             : 
      88           2 :     void storePacket(MIDIUSBPacket_t packet) { storedPacket = packet; }
      89          39 :     bool hasStoredPacket() const { return storedPacket[0] != 0x00; }
      90           2 :     MIDIUSBPacket_t popStoredPacket() {
      91           2 :         MIDIUSBPacket_t t = storedPacket;
      92           2 :         storedPacket[0] = 0x00;
      93           2 :         return t;
      94             :     }
      95             : 
      96             :     Cable activeCable = Cable_1;
      97             : 
      98             :   private:
      99             :     SysExBuffer sysexbuffers[USB_MIDI_NUMBER_OF_CABLES] = {};
     100             :     MIDIUSBPacket_t storedPacket = {{ 0x00 }};
     101             : #endif
     102             : };
     103             : 
     104             : template <class BytePuller>
     105          39 : inline MIDIReadEvent USBMIDI_Parser::pull(BytePuller &&puller) {
     106             :     // First try resuming the parser, we might have a stored packet that has to
     107             :     // be parsed first.
     108          39 :     MIDIReadEvent evt = resume();
     109          39 :     if (evt != MIDIReadEvent::NO_MESSAGE)
     110           1 :         return evt;
     111             : 
     112             :     // If resumption didn't produce a message, read new packets from the input
     113             :     // and parse them until either we get a message, or until the input runs out
     114             :     // of new packets.
     115          38 :     MIDIUSBPacket_t midiPacket;
     116         141 :     while (puller.pull(midiPacket)) {
     117         131 :         evt = feed(midiPacket);
     118         131 :         if (evt != MIDIReadEvent::NO_MESSAGE)
     119          28 :             return evt;
     120             :     }
     121          10 :     return MIDIReadEvent::NO_MESSAGE;
     122             : }
     123             : 
     124             : END_CS_NAMESPACE

Generated by: LCOV version 1.15