LCOV - code coverage report
Current view: top level - src/Def - MIDIAddress.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 98.4 % 61 60
Test Date: 2026-06-06 17:44:35 Functions: 96.2 % 26 25
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* ✔ */
       2              : 
       3              : #pragma once
       4              : 
       5              : #include <Def/Def.hpp>
       6              : 
       7              : BEGIN_CS_NAMESPACE
       8              : 
       9              : /// A struct for saving a MIDI address consisting of a 7-bit address, a 4-bit
      10              : /// channel, and a 4-bit cable number.
      11              : /// A MIDI address can be marked "invalid". The MIDI sending functions
      12              : /// (@ref MIDI_Sender) will never send messages addressed to an invalid address.
      13              : struct __attribute__((packed)) RawMIDIAddress {
      14              :     bool valid : 1;
      15              :     uint8_t address : 7;
      16              :     uint8_t channel : 4;
      17              :     uint8_t cableNumber : 4;
      18              : };
      19              : 
      20              : /// A class for saving a MIDI channel and cable number.
      21              : /// A MIDI channel and cable number can be marked "invalid".
      22              : /// The MIDI sending functions (@ref MIDI_Sender) will never send messages
      23              : /// addressed to invalid channels or cables.
      24              : class MIDIChannelCable {
      25              :     friend class MIDIAddress;
      26              : 
      27              :   public:
      28              :     /// @name   Constructors
      29              :     /// @{
      30              : 
      31              :     constexpr MIDIChannelCable() : addresses {0, 0, 0, 0} {}
      32          245 :     constexpr MIDIChannelCable(Channel channel, Cable cableNumber = Cable_1)
      33          245 :         : addresses {1, 0, channel.getRaw(), cableNumber.getRaw()} {}
      34              : 
      35              :     constexpr static MIDIChannelCable invalid() { return {}; }
      36              : 
      37              :     /// @}
      38              : 
      39              :   public:
      40              :     /// @name   Member access
      41              :     /// @{
      42              : 
      43              :     /// Get the channel [1, 16].
      44           20 :     constexpr Channel getChannel() const { return Channel {addresses.channel}; }
      45              :     /// Get the channel as an integer [0, 15].
      46          234 :     constexpr uint8_t getRawChannel() const { return addresses.channel; }
      47              : 
      48              :     /// Get the cable number [Cable_1, Cable_16].
      49           20 :     constexpr Cable getCableNumber() const {
      50           20 :         return Cable(addresses.cableNumber);
      51              :     }
      52              :     /// Get the cable number as an integer [0, 15].
      53          234 :     constexpr uint8_t getRawCableNumber() const {
      54          234 :         return addresses.cableNumber;
      55              :     }
      56              : 
      57              :     /// @}
      58              : 
      59              :   public:
      60              :     /// @name   Checks
      61              :     /// @{
      62              : 
      63              :     /// Check for equality: two addresses are equal if and only if they are both
      64              :     /// valid addresses and the MIDI channel and MIDI USB cable number are
      65              :     /// equal.
      66              :     /// @note   Invalid addresses are not equal nor inequal.
      67            4 :     constexpr bool operator==(MIDIChannelCable rhs) const {
      68            8 :         return this->addresses.valid && rhs.addresses.valid &&
      69           11 :                this->addresses.channel == rhs.addresses.channel &&
      70            7 :                this->addresses.cableNumber == rhs.addresses.cableNumber;
      71              :     }
      72              : 
      73              :     /// Check for inequality: two addresses are not equal if and only if they
      74              :     /// are both valid addresses and have a MIDI channel or MIDI USB cable
      75              :     /// number that differs.
      76              :     /// @note   Invalid addresses are not equal nor inequal.
      77              :     constexpr bool operator!=(MIDIChannelCable rhs) const {
      78              :         return this->addresses.valid && rhs.addresses.valid &&
      79              :                !(this->addresses.channel == rhs.addresses.channel &&
      80              :                  this->addresses.cableNumber == rhs.addresses.cableNumber);
      81              :     }
      82              : 
      83              :     /// Check if the MIDI address is valid.
      84           20 :     constexpr bool isValid() const { return addresses.valid; }
      85              : 
      86              :     /// Check if the MIDI address is valid.
      87              :     /// @see    isValid
      88           20 :     constexpr explicit operator bool() const { return isValid(); }
      89              : 
      90              :     /// @}
      91              : 
      92              :   public:
      93              :     /// @name   Base functions for address pattern matching.
      94              :     /// @{
      95              : 
      96              :     /// Check if two addresses match (are equal).
      97            4 :     static bool matchSingle(MIDIChannelCable toMatch, MIDIChannelCable base) {
      98            4 :         return base == toMatch;
      99              :     }
     100              : 
     101              :     /// @}
     102              : 
     103              :   protected:
     104            9 :     constexpr MIDIChannelCable(RawMIDIAddress addresses)
     105            9 :         : addresses(addresses) {}
     106              : 
     107              :   private:
     108              :     RawMIDIAddress addresses;
     109              : };
     110              : 
     111              : /// A class for saving an offset to a MIDI address.
     112              : /// It can be added to a MIDIAddress.
     113              : class RelativeMIDIAddress {
     114              :     friend class MIDIAddress;
     115              : 
     116              :   public:
     117            0 :     constexpr RelativeMIDIAddress() : addresses {0, 0, 0, 0} {}
     118           40 :     constexpr RelativeMIDIAddress(int deltaAddress, int deltaChannel = 0,
     119              :                                   int deltaCableNumber = 0)
     120          120 :         : addresses {
     121              :               1,
     122           40 :               static_cast<uint8_t>(deltaAddress),
     123           40 :               static_cast<uint8_t>(deltaChannel),
     124           40 :               static_cast<uint8_t>(deltaCableNumber),
     125           40 :           } {}
     126              :     constexpr bool isValid() const { return addresses.valid; }
     127              : 
     128              :     /// Compound addition. Element-wise addition, result is valid if both
     129              :     /// operands were valid.
     130              :     RelativeMIDIAddress &operator+=(RelativeMIDIAddress that);
     131              : 
     132              :   private:
     133              :     RawMIDIAddress addresses;
     134              :     static_assert(((-1) & 0x7F) == 0x7F,
     135              :                   "Negative numbers must be two's complement");
     136              : };
     137              : 
     138              : /// A type-safe utility class for saving a MIDI address consisting of a 7-bit
     139              : /// address, a 4-bit channel, and a 4-bit cable number.
     140              : /// A MIDI address can be marked "invalid". The MIDI sending functions
     141              : /// (@ref MIDI_Sender) will never send messages addressed to invalid addresses.
     142              : ///
     143              : /// See @ref midi_md-midi-addresses "MIDI Tutorial: MIDI addresses" for a
     144              : /// tutorial on how to use MIDI addresses.
     145              : class MIDIAddress {
     146              :   public:
     147              :     /// @name   Constructors
     148              :     /// @{
     149              : 
     150              :     /// Default constructor, creates an invalid address.
     151              :     constexpr MIDIAddress()
     152              :         : addresses {
     153              :               0,
     154              :               0,
     155              :               0,
     156              :               0,
     157              :           } {}
     158              : 
     159              :     /** 
     160              :      * @brief Constructor.
     161              :      * 
     162              :      * @param   address 
     163              :      *          The 7-bit MIDI address. Depending on the message type, this can
     164              :      *          be the MIDI note number, the number of the MIDI Control Change
     165              :      *          Controller, etc.  
     166              :      *          Must be a number in the range [0, 127].
     167              :      * @param   channelCN 
     168              :      *          The MIDI Channel and the MIDI USB cable number.
     169              :      */
     170          234 :     constexpr MIDIAddress(int address, MIDIChannelCable channelCN)
     171          702 :         : addresses {
     172              :               1,
     173          234 :               static_cast<uint8_t>(address),
     174          234 :               channelCN.getRawChannel(),
     175          234 :               channelCN.getRawCableNumber(),
     176          234 :           } {} // Deliberate overflow for negative numbers
     177              : 
     178              :     /** 
     179              :      * @brief Constructor.
     180              :      * 
     181              :      * @param   address 
     182              :      *          The 7-bit MIDI address.  
     183              :      *          Depending on the message type, this can be the MIDI note number,
     184              :      *          the number of the MIDI Control Change Controller, etc.  
     185              :      *          Must be a number in the range [0, 127].
     186              :      * @param   channel
     187              :      *          The MIDI Channel.  
     188              :      *          Use the constants @ref Channel_1 through @ref Channel_16.
     189              :      * @param   cableNumber 
     190              :      *          The MIDI USB cable number.  
     191              :      *          Use the constants @ref Cable_1 through @ref Cable_16.
     192              :      */
     193           82 :     constexpr MIDIAddress(int address, Channel channel = Channel_1,
     194              :                           Cable cableNumber = Cable_1)
     195          246 :         : addresses {
     196              :               1,
     197           82 :               static_cast<uint8_t>(address),
     198           82 :               channel.getRaw(),
     199           82 :               cableNumber.getRaw(),
     200           82 :           } {} // Deliberate overflow for negative numbers
     201              : 
     202              :     /** 
     203              :      * @brief Constructor.
     204              :      * 
     205              :      * @param   address 
     206              :      *          The 7-bit MIDI address.  
     207              :      *          Depending on the message type, this can be the MIDI note number,
     208              :      *          the number of the MIDI Control Change Controller, etc.  
     209              :      *          Must be a number in the range [0, 127].
     210              :      * @param   cableNumber 
     211              :      *          The MIDI USB cable number.  
     212              :      *          Use the constants @ref Cable_1 through @ref Cable_16.
     213              :      */
     214              :     constexpr MIDIAddress(int address, Cable cableNumber)
     215              :         : addresses {
     216              :               1,
     217              :               static_cast<uint8_t>(address),
     218              :               0,
     219              :               cableNumber.getRaw(),
     220              :           } {} // Deliberate overflow for negative numbers
     221              : 
     222              :     /** 
     223              :      * @brief Constructor.
     224              :      * 
     225              :      * @param   channel
     226              :      *          The MIDI Channel.  
     227              :      *          Use the constants @ref Channel_1 through @ref Channel_16.
     228              :      * @param   cableNumber 
     229              :      *          The MIDI USB cable number.  
     230              :      *          Use the constants @ref Cable_1 through @ref Cable_16.
     231              :      */
     232              :     constexpr MIDIAddress(Channel channel, Cable cableNumber = Cable_1)
     233              :         : addresses {
     234              :               1,
     235              :               0,
     236              :               channel.getRaw(),
     237              :               cableNumber.getRaw(),
     238              :           } {}
     239              : 
     240              :     /** 
     241              :      * @brief Constructor.
     242              :      * 
     243              :      * @param   address 
     244              :      *          The MIDI Channel and the MIDI USB cable number.  
     245              :      */
     246           14 :     constexpr MIDIAddress(MIDIChannelCable address)
     247           14 :         : addresses(address.addresses) {}
     248              : 
     249              :     /// Return an invalid address.
     250              :     constexpr static MIDIAddress invalid() { return {}; }
     251              : 
     252              :     /// @}
     253              : 
     254              :   public:
     255              :     /// @name   Adding/subtracting offsets
     256              :     /// @{
     257              : 
     258              :     /// Add a relative offset to this address.
     259              :     MIDIAddress &operator+=(RelativeMIDIAddress rhs);
     260              : 
     261              :     /// Subtract a relative offset from this address.
     262              :     MIDIAddress &operator-=(RelativeMIDIAddress rhs);
     263              : 
     264              :     /// Add a relative offset.
     265              :     MIDIAddress operator+(RelativeMIDIAddress rhs) const;
     266              : 
     267              :     /// Subtract a relative offset.
     268              :     MIDIAddress operator-(RelativeMIDIAddress rhs) const;
     269              : 
     270              :     /// @}
     271              : 
     272              :   public:
     273              :     /// @name   Member access
     274              :     /// @{
     275              : 
     276              :     /// Get the address [0, 127].
     277          400 :     constexpr uint8_t getAddress() const { return addresses.address; }
     278              : 
     279              :     /// Get the channel [Channel_1, Channel_16]
     280          216 :     constexpr Channel getChannel() const { return Channel {addresses.channel}; }
     281              :     /// Get the channel as an integer [0, 15]
     282           32 :     constexpr uint8_t getRawChannel() const { return addresses.channel; }
     283              : 
     284              :     /// Get the cable number [Cable_1, Cable_16].
     285          224 :     constexpr Cable getCableNumber() const {
     286          224 :         return Cable(addresses.cableNumber);
     287              :     }
     288              :     /// Get the cable number as an integer [0, 15].
     289           12 :     constexpr uint8_t getRawCableNumber() const {
     290           12 :         return addresses.cableNumber;
     291              :     }
     292              :     /// Get the channel and cable number.
     293            9 :     constexpr MIDIChannelCable getChannelCable() const { return {addresses}; }
     294              : 
     295              :     /// @}
     296              : 
     297              :   public:
     298              :     /// @name   Checks
     299              :     /// @{
     300              : 
     301              :     /// Check for equality: two addresses are equal if and only if they are both
     302              :     /// valid addresses and the MIDI address, MIDI channel and MIDI USB cable
     303              :     /// number are equal.
     304              :     /// @note   Invalid addresses are not equal nor inequal.
     305           32 :     constexpr bool operator==(MIDIAddress rhs) const {
     306           63 :         return this->addresses.valid && rhs.addresses.valid &&
     307           31 :                this->addresses.address == rhs.addresses.address &&
     308           92 :                this->addresses.channel == rhs.addresses.channel &&
     309           61 :                this->addresses.cableNumber == rhs.addresses.cableNumber;
     310              :     }
     311              : 
     312              :     /// Check for inequality: two addresses are not equal if and only if they
     313              :     /// are both valid addresses and have a MIDI address, MIDI channel or MIDI
     314              :     /// USB cable number that differs.
     315              :     /// @note   Invalid addresses are not equal nor inequal.
     316            8 :     constexpr bool operator!=(MIDIAddress rhs) const {
     317           15 :         return this->addresses.valid && rhs.addresses.valid &&
     318            7 :                !(this->addresses.address == rhs.addresses.address &&
     319            5 :                  this->addresses.channel == rhs.addresses.channel &&
     320           11 :                  this->addresses.cableNumber == rhs.addresses.cableNumber);
     321              :     }
     322              : 
     323              :     /// Check if the MIDI address is valid.
     324          247 :     constexpr bool isValid() const { return addresses.valid; }
     325              : 
     326              :     /// Check if the MIDI address is valid.
     327              :     /// @see    isValid
     328          134 :     constexpr explicit operator bool() const { return isValid(); }
     329              : 
     330              :     /// @}
     331              : 
     332              :   public:
     333              :     /// @name   Base functions for address pattern matching.
     334              :     /// @{
     335              : 
     336              :     /// Check if two addresses match (are equal).
     337           26 :     static bool matchSingle(MIDIAddress toMatch, MIDIAddress base) {
     338           26 :         return base == toMatch;
     339              :     }
     340              : 
     341              :     /// Check if an address falls within a range of MIDI addresses, starting
     342              :     /// with address `base`, with a given length.
     343              :     /// @retval true
     344              :     ///         `toMatch` and `base` are both valid addresses,
     345              :     ///         the MIDI address is within the given range, and the MIDI Channel
     346              :     ///         and MIDI USB Cable Number of `toMatch` and `base` are the same.
     347              :     /// @retval false
     348              :     ///         Otherwise.
     349              :     static bool matchAddressInRange(MIDIAddress toMatch, MIDIAddress base,
     350              :                                     uint8_t length);
     351              : 
     352              :     /// @}
     353              : 
     354              :   private:
     355              :     RawMIDIAddress addresses;
     356              : };
     357              : 
     358              : END_CS_NAMESPACE
        

Generated by: LCOV version 2.4-beta