LCOV - code coverage report
Current view: top level - src/MIDI_Parsers - USBMIDI_Parser.cpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 95.9 % 74 71
Test Date: 2026-06-06 17:44:35 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #include "USBMIDI_Parser.hpp"
       2              : #include <Settings/SettingsWrapper.hpp>
       3              : 
       4              : BEGIN_CS_NAMESPACE
       5              : 
       6            6 : MIDIReadEvent USBMIDI_Parser::handleChannelMessage(MIDIUSBPacket_t packet,
       7              :                                                    Cable cable) {
       8            6 :     midimsg.header = packet[1];
       9            6 :     midimsg.data1 = packet[2];
      10            6 :     midimsg.data2 = packet[3];
      11            6 :     midimsg.cable = cable;
      12            6 :     return MIDIReadEvent::CHANNEL_MESSAGE;
      13              : }
      14              : 
      15          102 : MIDIReadEvent USBMIDI_Parser::handleSysExStartCont(MIDIUSBPacket_t packet,
      16              :                                                    Cable cable) {
      17              : #if !IGNORE_SYSEX
      18              :     // If this is a SysEx start packet
      19          102 :     if (packet[1] == uint8_t(MIDIMessageType::SysExStart)) {
      20           10 :         startSysEx(cable); // start a new message
      21              :                            // (overwrites previous unfinished message)
      22              :     }
      23              :     // If we haven't received a SysExStart
      24           92 :     else if (!receivingSysEx(cable)) {
      25              :         DEBUGREF(F("No SysExStart received"));
      26            1 :         return MIDIReadEvent::NO_MESSAGE; // ignore the data
      27              :     }
      28              : 
      29              :     // Check if the SysEx buffer has enough space to store the data
      30          101 :     if (!hasSysExSpace(cable, 3)) {
      31            1 :         storePacket(packet);
      32            1 :         endSysExChunk(cable);
      33            1 :         return MIDIReadEvent::SYSEX_CHUNK;
      34              :     }
      35              : 
      36              :     // Enough space available in buffer, store the data
      37          100 :     addSysExBytes(cable, &packet[1], 3);
      38              : #else
      39              :     (void)packet;
      40              :     (void)cable;
      41              : #endif
      42          100 :     return MIDIReadEvent::NO_MESSAGE; // SysEx is not finished yet
      43              : }
      44              : 
      45              : template <uint8_t NumBytes>
      46           11 : MIDIReadEvent USBMIDI_Parser::handleSysExEnd(MIDIUSBPacket_t packet,
      47              :                                              Cable cable) {
      48              :     static_assert(NumBytes == 2 || NumBytes == 3,
      49              :                   "Only 2- or 3-byte SysEx packets are supported");
      50              : 
      51              : #if !IGNORE_SYSEX
      52              :     // This could be the a very short SysEx message that starts and ends with
      53              :     // this packet
      54           11 :     if (packet[1] == uint8_t(MIDIMessageType::SysExStart)) {
      55            2 :         startSysEx(cable); // start a new message
      56              :                            // (overwrites previous unfinished message)
      57              :     }
      58              :     // If we haven't received a SysExStart
      59            9 :     else if (!receivingSysEx(cable)) {
      60              :         DEBUGFN(F("No SysExStart received"));
      61            2 :         return MIDIReadEvent::NO_MESSAGE; // ignore the data
      62              :     }
      63              : 
      64              :     // Check if the SysEx buffer has enough space to store the end byte
      65            9 :     if (!hasSysExSpace(cable, NumBytes)) {
      66            1 :         storePacket(packet);
      67            1 :         endSysExChunk(cable);
      68            1 :         return MIDIReadEvent::SYSEX_CHUNK; // Buffer full
      69              :     }
      70              : 
      71              :     // Enough space available in buffer, finish the message
      72            8 :     addSysExBytes(cable, &packet[1], NumBytes);
      73            8 :     endSysEx(cable);
      74            8 :     return MIDIReadEvent::SYSEX_MESSAGE;
      75              : #else
      76              :     (void)packet;
      77              :     (void)cable;
      78              :     return MIDIReadEvent::NO_MESSAGE;
      79              : #endif
      80              : }
      81              : 
      82              : template <>
      83            7 : MIDIReadEvent USBMIDI_Parser::handleSysExEnd<1>(MIDIUSBPacket_t packet,
      84              :                                                 Cable cable) {
      85              :     // Single-byte System Common Message
      86            7 :     if (packet[1] != uint8_t(MIDIMessageType::SysExEnd)) {
      87              :         // System Common (1 byte)
      88            2 :         midimsg.header = packet[1];
      89            2 :         midimsg.cable = cable;
      90            2 :         return MIDIReadEvent::SYSCOMMON_MESSAGE;
      91              :     }
      92              : 
      93              : #if !IGNORE_SYSEX
      94              :     // SysEx ends with following single byte
      95              :     else {
      96              :         // If we haven't received a SysExStart
      97            5 :         if (!receivingSysEx(cable)) {
      98              :             DEBUGREF(F("No SysExStart received"));
      99            1 :             return MIDIReadEvent::NO_MESSAGE; // ignore the data
     100              :         }
     101              : 
     102              :         // Check if the SysEx buffer has enough space to store the end byte
     103            4 :         if (!hasSysExSpace(cable, 1)) {
     104            0 :             storePacket(packet);
     105            0 :             endSysExChunk(cable);
     106            0 :             return MIDIReadEvent::SYSEX_CHUNK;
     107              :         }
     108              : 
     109              :         // Enough space available in buffer, finish the message
     110            4 :         addSysExByte(cable, packet[1]);
     111            4 :         endSysEx(cable);
     112            4 :         return MIDIReadEvent::SYSEX_MESSAGE;
     113              :     }
     114              : #else
     115              :     (void)packet;
     116              :     (void)cable;
     117              :     return MIDIReadEvent::NO_MESSAGE;
     118              : #endif
     119              : }
     120              : 
     121            4 : MIDIReadEvent USBMIDI_Parser::handleSysCommon(MIDIUSBPacket_t packet,
     122              :                                               Cable cable) {
     123            4 :     midimsg.header = packet[1];
     124            4 :     midimsg.data1 = packet[2];
     125            4 :     midimsg.data2 = packet[3];
     126            4 :     midimsg.cable = cable;
     127            4 :     return MIDIReadEvent::SYSCOMMON_MESSAGE;
     128              : }
     129              : 
     130              : // Single Byte
     131            3 : MIDIReadEvent USBMIDI_Parser::handleSingleByte(MIDIUSBPacket_t packet,
     132              :                                                Cable cable) {
     133            3 :     rtmsg.message = packet[1];
     134            3 :     rtmsg.cable = cable;
     135            3 :     return MIDIReadEvent::REALTIME_MESSAGE;
     136              : }
     137              : 
     138              : // https://usb.org/sites/default/files/midi10.pdf
     139          133 : MIDIReadEvent USBMIDI_Parser::feed(MIDIUSBPacket_t packet) {
     140              :     // DEBUG("MIDIUSB packet:\t" << hex << packet[0] << ' ' << packet[1] << ' '
     141              :     //                           << packet[2] << ' ' << packet[3] << dec);
     142              : 
     143              :     // MIDI USB cable number and code index number
     144          133 :     Cable cable = Cable(packet[0] >> 4);
     145          133 :     MIDICodeIndexNumber CIN = MIDICodeIndexNumber(packet[0] & 0xF);
     146              : 
     147              :     // Ignore all messages for cables that we don't have
     148          133 :     if (cable.getRaw() >= USB_MIDI_NUMBER_OF_CABLES)
     149              :         return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
     150              : 
     151              :     using M = MIDICodeIndexNumber;
     152          133 :     switch (CIN) {
     153              :         case M::MiscFunctionCodes: break; // LCOV_EXCL_LINE
     154              :         case M::CableEvents: break;       // LCOV_EXCL_LINE
     155            4 :         case M::SystemCommon2B:           // fallthrough
     156            4 :         case M::SystemCommon3B: return handleSysCommon(packet, cable);
     157          102 :         case M::SysExStartCont: return handleSysExStartCont(packet, cable);
     158            7 :         case M::SysExEnd1B: return handleSysExEnd<1>(packet, cable);
     159            5 :         case M::SysExEnd2B: return handleSysExEnd<2>(packet, cable);
     160            6 :         case M::SysExEnd3B: return handleSysExEnd<3>(packet, cable);
     161            6 :         case M::NoteOff:         // fallthrough
     162              :         case M::NoteOn:          // fallthrough
     163              :         case M::KeyPressure:     // fallthrough
     164              :         case M::ControlChange:   // fallthrough
     165              :         case M::ProgramChange:   // fallthrough
     166              :         case M::ChannelPressure: // fallthrough
     167            6 :         case M::PitchBend: return handleChannelMessage(packet, cable);
     168            3 :         case M::SingleByte: return handleSingleByte(packet, cable);
     169              :         default: break; // LCOV_EXCL_LINE
     170              :     }
     171              : 
     172              :     return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
     173              : }
     174              : 
     175           39 : MIDIReadEvent USBMIDI_Parser::resume() {
     176              : #if !IGNORE_SYSEX
     177           39 :     if (!hasStoredPacket())
     178           37 :         return MIDIReadEvent::NO_MESSAGE;
     179              : 
     180            2 :     MIDIUSBPacket_t packet = popStoredPacket();
     181              : 
     182              :     // If a SysEx message was in progress
     183            2 :     if (receivingSysEx(activeCable)) {
     184              :         // Reset the buffer for the next chunk
     185            2 :         startSysEx(activeCable);
     186              :     }
     187              : 
     188            2 :     return feed(packet);
     189              : #else
     190              :     return MIDIReadEvent::NO_MESSAGE;
     191              : #endif
     192              : }
     193              : 
     194              : END_CS_NAMESPACE
        

Generated by: LCOV version 2.4-beta