LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces/BLEMIDI - BLEMIDIPacketBuilder.hpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 15 15 100.0 %
Date: 2024-12-21 21:28:55 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <MIDI_Parsers/MIDI_MessageTypes.hpp>
       4             : 
       5             : #include <AH/STL/vector>
       6             : 
       7             : BEGIN_CS_NAMESPACE
       8             : 
       9             : /// Class for building MIDI over Bluetooth Low Energy packets.
      10             : class BLEMIDIPacketBuilder {
      11             :   private:
      12             :     uint8_t runningHeader = 0;
      13             :     uint8_t runningTimestamp = 0;
      14             :     std::vector<uint8_t> buffer = std::vector<uint8_t>(0);
      15             : 
      16             :     constexpr static const uint8_t SysExStart =
      17             :         static_cast<uint8_t>(MIDIMessageType::SysExStart);
      18             :     constexpr static const uint8_t SysExEnd =
      19             :         static_cast<uint8_t>(MIDIMessageType::SysExEnd);
      20             : 
      21             :     /// Check if the buffer has space left.
      22         111 :     bool hasSpaceFor(size_t bytes) const {
      23         111 :         return bytes <= size_t(buffer.capacity() - buffer.size());
      24             :     }
      25             : 
      26             :     /// Timestamp[0]: 0b10hh hhhh
      27          71 :     constexpr static uint8_t getTimestampMSB(uint16_t timestamp) {
      28          71 :         return ((timestamp >> 7) & 0x3F) | 0x80;
      29             :     }
      30             :     /// Timestamp[1]: 0b1lll llll
      31          96 :     constexpr static uint8_t getTimestampLSB(uint16_t timestamp) {
      32          96 :         return timestamp | 0x80;
      33             :     }
      34             : 
      35             :     /// If this is the first byte/message in the packet, add the header
      36             :     /// containing the 6 most significant bits of the timestamp
      37         128 :     void initBuffer(uint16_t timestamp) {
      38         128 :         if (buffer.empty())
      39          71 :             buffer.push_back(getTimestampMSB(timestamp));
      40         128 :     }
      41             : 
      42             :     /** 
      43             :      * @brief   Try adding a 2-byte or 3-byte MIDI channel voice message to the
      44             :      *          packet.
      45             :      * 
      46             :      * @tparam  ThreeBytes 
      47             :      *          Set to `true` for a 3-byte message, `false` for a 2-byte message.
      48             :      * 
      49             :      * @param   header 
      50             :      *          MIDI status byte.
      51             :      * @param   data1 
      52             :      *          MIDI data byte 1.
      53             :      * @param   data2 
      54             :      *          MIDI data byte 2 (if `ThreeBytes == true`).
      55             :      * @param   timestamp 
      56             :      *          13-bit BLE-MIDI timestamp.
      57             :      * 
      58             :      * @retval  true
      59             :      *          Successfully added message to the packet.
      60             :      * @retval  false 
      61             :      *          Buffer is too full, send the current packet, reset the packet
      62             :      *          builder, and try again.
      63             :      */
      64             :     template <bool ThreeBytes>
      65             :     bool addImpl(uint8_t header, uint8_t data1, uint8_t data2,
      66             :                  uint16_t timestamp);
      67             : 
      68             :   public:
      69          66 :     BLEMIDIPacketBuilder(size_t capacity = 20) { buffer.reserve(capacity); }
      70             : 
      71             :     /// Reset the builder to start a new packet.
      72             :     void reset();
      73             : 
      74             :     /// Set the maximum capacity of the buffer. Set this to the MTU of the BLE
      75             :     /// link minus three bytes (for notify overhead).
      76             :     void setCapacity(uint16_t capacity);
      77             : 
      78             :     /// Get the size of the current packet.
      79          29 :     uint16_t getSize() const { return buffer.size(); }
      80             :     /// Get a pointer to the packet data buffer.
      81          22 :     const uint8_t *getBuffer() const { return buffer.data(); }
      82             :     /// Check if the packet buffer is empty.
      83         121 :     bool empty() const { return buffer.empty(); }
      84             : 
      85             :     /// Return the packet as a vector of bytes.
      86          50 :     const std::vector<uint8_t> &getPacket() const { return buffer; }
      87             : 
      88             :     /** 
      89             :      * @brief   Try adding a 3-byte MIDI channel voice message to the packet.
      90             :      * 
      91             :      * @param   header 
      92             :      *          MIDI status byte.
      93             :      * @param   data1 
      94             :      *          MIDI data byte 1.
      95             :      * @param   data2 
      96             :      *          MIDI data byte 2.
      97             :      * @param   timestamp 
      98             :      *          13-bit BLE-MIDI timestamp.
      99             :      * 
     100             :      * @retval  true
     101             :      *          Successfully added message to the packet.
     102             :      * @retval  false 
     103             :      *          Buffer is too full, send the current packet, reset the packet
     104             :      *          builder, and try again.
     105             :      */
     106             :     bool add3B(uint8_t header, uint8_t data1, uint8_t data2,
     107             :                uint16_t timestamp);
     108             : 
     109             :     /** 
     110             :      * @brief   Try adding a 2-byte MIDI channel voice message to the packet.
     111             :      * 
     112             :      * @param   header 
     113             :      *          MIDI status byte.
     114             :      * @param   data1 
     115             :      *          MIDI data byte 1.
     116             :      * @param   timestamp 
     117             :      *          13-bit BLE-MIDI timestamp.
     118             :      * 
     119             :      * @retval  true
     120             :      *          Successfully added message to the packet.
     121             :      * @retval  false 
     122             :      *          Buffer is too full, send the current packet, reset the packet
     123             :      *          builder, and try again.
     124             :      */
     125             :     bool add2B(uint8_t header, uint8_t data1, uint16_t timestamp);
     126             : 
     127             :     /** 
     128             :      * @brief   Try adding a MIDI real-time message to the packet.
     129             :      * 
     130             :      * @param   rt 
     131             :      *          MIDI real-time byte.
     132             :      * @param   timestamp 
     133             :      *          13-bit BLE-MIDI timestamp.
     134             :      * 
     135             :      * @retval  true
     136             :      *          Successfully added message to the packet.
     137             :      * @retval  false 
     138             :      *          Buffer is too full, send the current packet, reset the packet
     139             :      *          builder, and try again.
     140             :      */
     141             :     bool addRealTime(uint8_t rt, uint16_t timestamp);
     142             : 
     143             :     /** 
     144             :      * @brief   Try adding a MIDI system common message to the packet.
     145             :      * 
     146             :      * @param   num_data 
     147             :      *          The number of data bytes (0, 1 or 2).
     148             :      * @param   header
     149             :      *          System common status byte.
     150             :      * @param   data1 
     151             :      *          MIDI data byte 1.
     152             :      * @param   data2 
     153             :      *          MIDI data byte 2.
     154             :      * @param   timestamp 
     155             :      *          13-bit BLE-MIDI timestamp.
     156             :      * 
     157             :      * @retval  true
     158             :      *          Successfully added message to the packet.
     159             :      * @retval  false 
     160             :      *          Buffer is too full, send the current packet, reset the packet
     161             :      *          builder, and try again.
     162             :      */
     163             :     bool addSysCommon(uint8_t num_data, uint8_t header, uint8_t data1,
     164             :                       uint8_t data2, uint16_t timestamp);
     165             : 
     166             :     /**
     167             :      * @brief   Try adding (part of) a SysEx message to the packet.
     168             :      * 
     169             :      * @param[in,out]   data 
     170             :      *                  Pointer to the first byte of the SysEx message. 
     171             :      *                  At the end, this will point to the first byte to 
     172             :      *                  send in the next packet, or `nullptr` if the message was
     173             :      *                  finished.
     174             :      * @param[in,out]   length 
     175             :      *                  The number of bytes in the SysEx message. 
     176             :      *                  At the end, this will be set to remaining number of
     177             :      *                  bytes to send in the next packet.
     178             :      * @param[in]       timestamp 
     179             :      *                  13-bit BLE-MIDI timestamp.
     180             :      * 
     181             :      * @retval  true
     182             :      *          Successfully added (part of) the message to the packet.
     183             :      * @retval  false 
     184             :      *          Buffer is too full, send the current packet, reset the packet
     185             :      *          builder, and try again.
     186             :      * 
     187             :      * If the message fits in a single packet, `length` is set to `0` (no
     188             :      * remaining data bytes) and `data` is set to `nullptr`.
     189             :      * 
     190             :      * For example:
     191             :      * ~~~cpp
     192             :      * BLEMIDIPacketBuilder packetbuilder;
     193             :      * 
     194             :      * const uint8_t *data = (...);
     195             :      * size_t length = (...);
     196             :      * uint16_t timestamp = (...);
     197             :      * 
     198             :      * if (!packetbuilder.addSysEx(data, length, timestamp)) {
     199             :      *     sendnow(packetbuilder.getBuffer(), packetbuilder.getSize());
     200             :      *     packetbuilder.reset();
     201             :      *     packetbuilder.addSysEx(data, length, timestamp)
     202             :      * }
     203             :      * while (data) {
     204             :      *     sendnow(packetbuilder.getBuffer(), packetbuilder.getSize());
     205             :      *     packetbuilder.reset();
     206             :      *     packetbuilder.continueSysEx(data, length, timestamp);
     207             :      * }
     208             :      * ~~~
     209             :      */
     210             :     bool addSysEx(const uint8_t *&data, size_t &length, uint16_t timestamp);
     211             : 
     212             :     /**
     213             :      * @brief   Add a SysEx continuation to the packet.
     214             :      * 
     215             :      * @param[in,out]   data 
     216             :      *                  Pointer to the first byte of the SysEx message to send
     217             :      *                  in this continuation packet.
     218             :      *                  At the end, this will point to the first byte to send in
     219             :      *                  the next packet, or `nullptr` if the message was
     220             :      *                  finished.
     221             :      * @param[in,out]   length 
     222             :      *                  The number of remaining bytes in the SysEx message. 
     223             :      *                  At the end, this will be set to remaining number of
     224             :      *                  bytes to send in the next packet.
     225             :      * @param[in]       timestamp 
     226             :      *                  13-bit BLE-MIDI timestamp.
     227             :      * 
     228             :      * If the message can be completed in a single packet, `length` is set to 
     229             :      * `0` (no remaining data bytes) and `data` is set to `nullptr`.
     230             :      * 
     231             :      * @see @ref addSysEx()
     232             :      */
     233             :     void continueSysEx(const uint8_t *&data, size_t &length,
     234             :                        uint16_t timestamp);
     235             : };
     236             : 
     237             : END_CS_NAMESPACE

Generated by: LCOV version 1.15