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