LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces/BLEMIDI - BLEMIDIPacketBuilder.cpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 100.0 % 94 94
Test Date: 2026-06-06 17:44:35 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #include "BLEMIDIPacketBuilder.hpp"
       2              : 
       3              : BEGIN_CS_NAMESPACE
       4              : 
       5              : template <bool ThreeBytes>
       6           56 : bool BLEMIDIPacketBuilder::addImpl(uint8_t header, uint8_t data1, uint8_t data2,
       7              :                                    uint16_t timestamp) {
       8           56 :     initBuffer(timestamp);
       9              : 
      10           56 :     uint8_t timestampLSB = getTimestampLSB(timestamp);
      11              : 
      12              :     // If the header is the same as the previous message, use running status
      13           56 :     if (header == runningHeader) {
      14              :         // If the timestamp is the same, no need to send it again
      15           17 :         if (timestampLSB == runningTimestamp) {
      16            9 :             if (!hasSpaceFor(1 + ThreeBytes))
      17            1 :                 return false; // Buffer full
      18            8 :             buffer.push_back(data1);
      19              :             if (ThreeBytes)
      20            8 :                 buffer.push_back(data2);
      21              :         }
      22              :         // Timestamp is different, send again
      23              :         else {
      24            8 :             if (!hasSpaceFor(2 + ThreeBytes))
      25            1 :                 return false; // Buffer full
      26            7 :             runningTimestamp = timestampLSB;
      27            7 :             buffer.push_back(timestampLSB);
      28            7 :             buffer.push_back(data1);
      29              :             if (ThreeBytes)
      30            7 :                 buffer.push_back(data2);
      31              :         }
      32              :     }
      33              :     // If the header is different, running status is not possible, send all
      34              :     else {
      35           39 :         if (!hasSpaceFor(3 + ThreeBytes))
      36            3 :             return false; // Buffer full
      37           36 :         runningHeader = header;
      38           36 :         runningTimestamp = timestampLSB;
      39           36 :         buffer.push_back(timestampLSB);
      40           36 :         buffer.push_back(header);
      41           36 :         buffer.push_back(data1);
      42              :         if (ThreeBytes)
      43           32 :             buffer.push_back(data2);
      44              :     }
      45           51 :     return true;
      46              : }
      47              : 
      48           34 : void BLEMIDIPacketBuilder::reset() {
      49           34 :     buffer.resize(0);
      50           34 :     runningHeader = 0;
      51           34 : }
      52              : 
      53           56 : void BLEMIDIPacketBuilder::setCapacity(uint16_t capacity) {
      54           56 :     if (capacity < 5)
      55              :         ERROR(F("capacity less than 5 bytes"), 0x2005); // LCOV_EXCL_LINE
      56           56 :     buffer.shrink_to_fit();
      57           56 :     buffer.reserve(capacity);
      58           56 : }
      59              : 
      60           51 : bool BLEMIDIPacketBuilder::add3B(uint8_t header, uint8_t data1, uint8_t data2,
      61              :                                  uint16_t timestamp) {
      62           51 :     constexpr bool ThreeBytes = true;
      63           51 :     return addImpl<ThreeBytes>(header, data1, data2, timestamp);
      64              : }
      65              : 
      66            5 : bool BLEMIDIPacketBuilder::add2B(uint8_t header, uint8_t data1,
      67              :                                  uint16_t timestamp) {
      68            5 :     constexpr bool ThreeBytes = false;
      69            5 :     return addImpl<ThreeBytes>(header, data1, 0, timestamp);
      70              : }
      71              : 
      72           10 : bool BLEMIDIPacketBuilder::addRealTime(uint8_t rt, uint16_t timestamp) {
      73           10 :     initBuffer(timestamp);
      74              : 
      75           10 :     if (!hasSpaceFor(2))
      76            3 :         return false; // Buffer full
      77              : 
      78            7 :     buffer.push_back(getTimestampLSB(timestamp));
      79            7 :     buffer.push_back(rt);
      80            7 :     runningTimestamp = 0; // Re-send the timestamp next time
      81              : 
      82            7 :     return true;
      83              : }
      84              : 
      85           10 : bool BLEMIDIPacketBuilder::addSysCommon(uint8_t num_data, uint8_t header,
      86              :                                         uint8_t data1, uint8_t data2,
      87              :                                         uint16_t timestamp) {
      88           10 :     initBuffer(timestamp);
      89              : 
      90           10 :     uint8_t timestampLSB = getTimestampLSB(timestamp);
      91              : 
      92           10 :     if (!hasSpaceFor(2 + num_data))
      93            3 :         return false; // Buffer full
      94            7 :     buffer.push_back(timestampLSB);
      95            7 :     buffer.push_back(header);
      96            7 :     if (num_data >= 1)
      97            5 :         buffer.push_back(data1);
      98            7 :     if (num_data >= 2)
      99            3 :         buffer.push_back(data2);
     100            7 :     runningTimestamp = 0; // Re-send the timestamp next time
     101              : 
     102            7 :     return true;
     103              : }
     104              : 
     105           20 : bool BLEMIDIPacketBuilder::addSysEx(const uint8_t *&data, size_t &length,
     106              :                                     uint16_t timestamp) {
     107           20 :     initBuffer(timestamp);
     108              : 
     109              :     // We can't do anything with an empty message
     110           20 :     if (length == 0)
     111            1 :         return true;
     112              : 
     113              :     // If the first byte is a SysExStart byte we first have to write a
     114              :     // timestamp + SysExStart.
     115           19 :     if (*data == SysExStart) {
     116              :         // We need space for at least the timestamp and a SysExStart
     117           14 :         if (!hasSpaceFor(2))
     118            3 :             return false; // Buffer full
     119              : 
     120              :         // Normal running status is interrupted by SysEx
     121           11 :         runningHeader = 0;
     122              : 
     123           11 :         const uint8_t timestampLSB = getTimestampLSB(timestamp);
     124              : 
     125              :         // Start of SysEx
     126           11 :         buffer.push_back(timestampLSB);
     127           11 :         buffer.push_back(SysExStart);
     128           11 :         ++data; // First byte was added
     129           11 :         --length;
     130              :     }
     131              : 
     132              :     // Copy the rest of the data, and terminate the message if necessary
     133           16 :     continueSysEx(data, length, timestamp);
     134           16 :     return true;
     135              : }
     136              : 
     137           32 : void BLEMIDIPacketBuilder::continueSysEx(const uint8_t *&data, size_t &length,
     138              :                                          uint16_t timestamp) {
     139           32 :     initBuffer(timestamp);
     140              : 
     141           32 :     if (length == 0) {
     142              :         // Message was finished, no continuation
     143            1 :         data = nullptr;
     144            1 :         return;
     145              :     }
     146              : 
     147              :     // Copy as much data as possible, but stop before the last byte, which
     148              :     // could be a SysExEnd (and should be handled differently than data bytes)
     149          102 :     while (length-- > 1 && buffer.size() < buffer.capacity())
     150           71 :         buffer.push_back(*data++);
     151              : 
     152              :     // If everything fit into the buffer
     153           31 :     if (length == 0) {
     154              :         // End of SysEx
     155           21 :         if (*data == SysExEnd) {
     156           17 :             if (hasSpaceFor(2)) {
     157           12 :                 buffer.push_back(getTimestampLSB(timestamp));
     158           12 :                 buffer.push_back(SysExEnd);
     159              :                 // Message was finished, no continuation
     160           12 :                 data = nullptr;
     161              :             } else {
     162              :                 // Send the SysExEnd byte next time
     163            5 :                 ++length;
     164              :             }
     165              :         }
     166              :         // End of chunk but not end of SysEx
     167              :         else {
     168            4 :             if (hasSpaceFor(1)) {
     169            3 :                 buffer.push_back(*data);
     170              :                 // Message was finished, no continuation
     171            3 :                 data = nullptr;
     172              :             } else {
     173              :                 // Send the last byte next time
     174            1 :                 ++length;
     175              :             }
     176              :         }
     177              :     }
     178              :     // If the while loop stopped because the buffer was full
     179              :     else {
     180              :         // data is not set to nullptr to let the caller know where to start
     181              :         // sending the next continuation packet,
     182           10 :         ++length;
     183              :     }
     184              : }
     185              : 
     186              : constexpr const uint8_t BLEMIDIPacketBuilder::SysExStart;
     187              : constexpr const uint8_t BLEMIDIPacketBuilder::SysExEnd;
     188              : 
     189              : END_CS_NAMESPACE
        

Generated by: LCOV version 2.4-beta