LCOV - code coverage report
Current view: top level - src/Def - MIDIAddress.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 57 57 100.0 %
Date: 2020-09-08 17:44:46 Functions: 23 23 100.0 %
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             : struct __attribute__((packed)) RawMIDIAddress {
      12             :     bool valid : 1;
      13             :     uint8_t address : 7;
      14             :     uint8_t channel : 4;
      15             :     uint8_t cableNumber : 4;
      16             : };
      17             : 
      18             : /// A class for saving a MIDI channel and cable number.
      19             : class MIDIChannelCN {
      20             :     friend class MIDIAddress;
      21             : 
      22             :   public:
      23           2 :     constexpr MIDIChannelCN() : addresses{0, 0, 0, 0} {}
      24          40 :     constexpr MIDIChannelCN(Channel channel, Cable cableNumber = CABLE_1)
      25         120 :         : addresses{
      26             :               1,
      27             :               0,
      28          40 :               channel.getRaw(),
      29          40 :               cableNumber.getRaw(),
      30          40 :           } {}
      31             : 
      32             :     /// Get the channel [1, 16].
      33          16 :     constexpr Channel getChannel() const { return Channel{addresses.channel}; }
      34             :     /// Get the channel as an integer [0, 15].
      35          27 :     constexpr uint8_t getRawChannel() const { return addresses.channel; }
      36             : 
      37             :     /// Get the cable number [CABLE_1, CABLE_16].
      38          16 :     constexpr Cable getCableNumber() const {
      39          16 :         return Cable(addresses.cableNumber);
      40             :     }
      41             :     /// Get the cable number as an integer [0, 15].
      42          27 :     constexpr uint8_t getRawCableNumber() const {
      43          27 :         return addresses.cableNumber;
      44             :     }
      45             : 
      46             :     /// Check if the MIDI address is valid.
      47          16 :     constexpr bool isValid() const { return addresses.valid; }
      48             : 
      49             :     /// Check if the MIDI address is valid.
      50             :     /// @see    isValid
      51          16 :     constexpr explicit operator bool() const { return isValid(); }
      52             : 
      53             :     constexpr static MIDIChannelCN invalid() { return {}; }
      54             : 
      55             :   private:
      56             :     RawMIDIAddress addresses;
      57             : };
      58             : 
      59             : /// @deprecated
      60             : using MIDICNChannel [[deprecated("Use MIDIChannelCN instead")]] = MIDIChannelCN;
      61             : 
      62             : /// A class for saving an offset to a MIDI address.
      63             : /// It can be added to a MIDIAddress.
      64             : class RelativeMIDIAddress {
      65             :     friend class MIDIAddress;
      66             : 
      67             :   public:
      68           3 :     constexpr RelativeMIDIAddress() : addresses{0, 0, 0, 0} {}
      69          65 :     constexpr RelativeMIDIAddress(int deltaAddress, int deltaChannel = 0,
      70             :                                   int deltaCableNumber = 0)
      71         260 :         : addresses{
      72             :               1,
      73          65 :               (uint8_t)deltaAddress,
      74          65 :               (uint8_t)deltaChannel,
      75          65 :               (uint8_t)deltaCableNumber,
      76          65 :           } {}
      77             :     constexpr bool isValid() const { return addresses.valid; }
      78             : 
      79             :     /// Compound addition. Element-wise addition, result is valid if both
      80             :     /// operands were valid.
      81             :     RelativeMIDIAddress &operator+=(RelativeMIDIAddress that);
      82             : 
      83             :   private:
      84             :     RawMIDIAddress addresses;
      85             :     static_assert(((-1) & 0x7F) == 0x7F,
      86             :                   "Negative numbers must be two's complement");
      87             : };
      88             : 
      89             : /// A type-safe utility class for saving a MIDI address consisting of a 7-bit
      90             : /// address, a 4-bit channel, and a 4-bit cable number.
      91             : class MIDIAddress {
      92             :   public:
      93             :     /// @name   Constructors
      94             :     /// @{
      95             : 
      96             :     /// Default constructor, creates an invalid address.
      97           9 :     constexpr MIDIAddress()
      98           9 :         : addresses{
      99             :               0,
     100             :               0,
     101             :               0,
     102             :               0,
     103           9 :           } {}
     104             : 
     105             :     /** 
     106             :      * @brief Constructor.
     107             :      * 
     108             :      * @param   address 
     109             :      *          The 7-bit MIDI address. Depending on the message type, this can
     110             :      *          be the MIDI note number, the number of the MIDI Control Change
     111             :      *          Controller, etc.  
     112             :      *          Must be a number in the range [0, 127].
     113             :      * @param   channelCN 
     114             :      *          The MIDI Channel and the MIDI USB cable number.
     115             :      */
     116          27 :     constexpr MIDIAddress(int address, MIDIChannelCN channelCN)
     117         108 :         : addresses{
     118             :               1,
     119          27 :               (uint8_t)address,
     120          27 :               channelCN.getRawChannel(),
     121          27 :               channelCN.getRawCableNumber(),
     122          27 :           } {} // Deliberate overflow for negative numbers
     123             : 
     124             :     /** 
     125             :      * @brief Constructor.
     126             :      * 
     127             :      * @param   address 
     128             :      *          The 7-bit MIDI address.  
     129             :      *          Depending on the message type, this can be the MIDI note number,
     130             :      *          the number of the MIDI Control Change Controller, etc.  
     131             :      *          Must be a number in the range [0, 127].
     132             :      * @param   channel
     133             :      *          The MIDI Channel.  
     134             :      *          Use the constants @ref CHANNEL_1 through @ref CHANNEL_16.
     135             :      * @param   cableNumber 
     136             :      *          The MIDI USB cable number.  
     137             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     138             :      */
     139         217 :     constexpr MIDIAddress(int address, Channel channel = CHANNEL_1,
     140             :                           Cable cableNumber = CABLE_1)
     141         868 :         : addresses{
     142             :               1,
     143         217 :               (uint8_t)address,
     144         217 :               channel.getRaw(),
     145         217 :               cableNumber.getRaw(),
     146         217 :           } {} // Deliberate overflow for negative numbers
     147             : 
     148             :     /** 
     149             :      * @brief Constructor.
     150             :      * 
     151             :      * @param   address 
     152             :      *          The 7-bit MIDI address.  
     153             :      *          Depending on the message type, this can be the MIDI note number,
     154             :      *          the number of the MIDI Control Change Controller, etc.  
     155             :      *          Must be a number in the range [0, 127].
     156             :      * @param   cableNumber 
     157             :      *          The MIDI USB cable number.  
     158             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     159             :      */
     160             :     constexpr MIDIAddress(int address, Cable cableNumber)
     161             :         : addresses{
     162             :               1,
     163             :               (uint8_t)address,
     164             :               0,
     165             :               cableNumber.getRaw(),
     166             :           } {} // Deliberate overflow for negative numbers
     167             : 
     168             :     /** 
     169             :      * @brief Constructor.
     170             :      * 
     171             :      * @param   channel
     172             :      *          The MIDI Channel.  
     173             :      *          Use the constants @ref CHANNEL_1 through @ref CHANNEL_16.
     174             :      * @param   cableNumber 
     175             :      *          The MIDI USB cable number.  
     176             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     177             :      */
     178             :     constexpr MIDIAddress(Channel channel, Cable cableNumber = CABLE_1)
     179             :         : addresses{
     180             :               1,
     181             :               0,
     182             :               channel.getRaw(),
     183             :               cableNumber.getRaw(),
     184             :           } {}
     185             : 
     186             :     /** 
     187             :      * @brief Constructor.
     188             :      * 
     189             :      * @param   address 
     190             :      *          The MIDI Channel and the MIDI USB cable number.  
     191             :      */
     192          11 :     constexpr MIDIAddress(const MIDIChannelCN &address)
     193          11 :         : addresses(address.addresses) {}
     194             : 
     195             :     /// Return an invalid address.
     196             :     constexpr static MIDIAddress invalid() { return {}; }
     197             : 
     198             :     /// @}
     199             : 
     200             :   public:
     201             :     /// @name   Adding/subtracting offsets
     202             :     /// @{
     203             : 
     204             :     /// Add a relative offset to this address.
     205             :     MIDIAddress &operator+=(const RelativeMIDIAddress &rhs);
     206             : 
     207             :     /// Subtract a relative offset from this address.
     208             :     MIDIAddress &operator-=(const RelativeMIDIAddress &rhs);
     209             : 
     210             :     /// Add a relative offset.
     211             :     MIDIAddress operator+(const RelativeMIDIAddress &rhs) const;
     212             : 
     213             :     /// Subtract a relative offset.
     214             :     MIDIAddress operator-(const RelativeMIDIAddress &rhs) const;
     215             : 
     216             :     /// @}
     217             : 
     218             :   public:
     219             :     /// @name   Member access
     220             :     /// @{
     221             : 
     222             :     /// Get the address [0, 127].
     223         385 :     constexpr uint8_t getAddress() const { return addresses.address; }
     224             : 
     225             :     /// Get the channel [CHANNEL_1, CHANNEL_16]
     226         164 :     constexpr Channel getChannel() const { return Channel{addresses.channel}; }
     227             :     /// Get the channel as an integer [0, 15]
     228          32 :     constexpr uint8_t getRawChannel() const { return addresses.channel; }
     229             : 
     230             :     /// Get the cable number [CABLE_1, CABLE_16].
     231         174 :     constexpr Cable getCableNumber() const {
     232         174 :         return Cable(addresses.cableNumber);
     233             :     }
     234             :     /// Get the cable number as an integer [0, 15].
     235          12 :     constexpr uint8_t getRawCableNumber() const {
     236          12 :         return addresses.cableNumber;
     237             :     }
     238             : 
     239             :     /// @}
     240             : 
     241             :   public:
     242             :     /// @name   Checks
     243             :     /// @{
     244             : 
     245             :     /// Check for equality: two addresses are equal if and only if they are both
     246             :     /// valid addresses and the MIDI address, MIDI channel and MIDI USB cable
     247             :     /// number are equal.
     248             :     /// @note   Invalid addresses are not equal nor inequal.
     249          18 :     constexpr bool operator==(const MIDIAddress &rhs) const {
     250          35 :         return this->addresses.valid && rhs.addresses.valid &&
     251          17 :                this->addresses.address == rhs.addresses.address &&
     252          16 :                this->addresses.channel == rhs.addresses.channel &&
     253          15 :                this->addresses.cableNumber == rhs.addresses.cableNumber;
     254             :     }
     255             : 
     256             :     /// Check for inequality: two addresses are not equal if and only if they
     257             :     /// are both valid addresses and have a MIDI address, MIDI channel or MIDI
     258             :     /// USB cable number that differs.
     259             :     /// @note   Invalid addresses are not equal nor inequal.
     260           8 :     constexpr bool operator!=(const MIDIAddress &rhs) const {
     261          15 :         return this->addresses.valid && rhs.addresses.valid &&
     262          12 :                !(this->addresses.address == rhs.addresses.address &&
     263           5 :                  this->addresses.channel == rhs.addresses.channel &&
     264           3 :                  this->addresses.cableNumber == rhs.addresses.cableNumber);
     265             :     }
     266             : 
     267             :     /// Check if the MIDI address is valid.
     268         186 :     constexpr bool isValid() const { return addresses.valid; }
     269             : 
     270             :     /// Check if the MIDI address is valid.
     271             :     /// @see    isValid
     272         105 :     constexpr explicit operator bool() const { return isValid(); }
     273             : 
     274             :     /// @}
     275             : 
     276             :   public:
     277             :     /// @name   Base functions for address pattern matching
     278             :     /// @{
     279             : 
     280             :     /// Check if two addresses match (are equal).
     281             :     static bool matchSingle(const MIDIAddress &toMatch,
     282             :                             const MIDIAddress &base);
     283             : 
     284             :     /// Check if an address falls within a range of MIDI addresses, starting
     285             :     /// with address `base`, with a given length.
     286             :     /// @retval true
     287             :     ///         `toMatch` and `base` are both valid addresses,
     288             :     ///         the MIDI address is within the given range, and the MIDI Channel
     289             :     ///         and MIDI USB Cable Number of `toMatch` and `base` are the same.
     290             :     /// @retval false
     291             :     ///         Otherwise.
     292             :     static bool matchAddressInRange(const MIDIAddress &toMatch,
     293             :                                     const MIDIAddress &base, uint8_t length);
     294             : 
     295             :     /// @}
     296             : 
     297             :   private:
     298             :     RawMIDIAddress addresses;
     299             : };
     300             : 
     301             : /// @deprecated
     302             : using MIDICNChannelAddress [[deprecated("Use MIDIAddress instead")]] =
     303             :     MIDIAddress;
     304             : 
     305             : END_CS_NAMESPACE

Generated by: LCOV version 1.14-6-g40580cd