LCOV - code coverage report
Current view: top level - src/Def - MIDIAddress.hpp (source / functions) Hit Total Coverage
Test: ffed98f648fe78e7aa7bdd228474317d40dadbec Lines: 57 58 98.3 %
Date: 2022-05-28 15:22:59 Functions: 23 24 95.8 %
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         253 :     constexpr MIDIChannelCable(Channel channel, Cable cableNumber = CABLE_1)
      33         253 :         : 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          19 :     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          19 :     constexpr Cable getCableNumber() const {
      50          19 :         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          19 :     constexpr bool isValid() const { return addresses.valid; }
      85             : 
      86             :     /// Check if the MIDI address is valid.
      87             :     /// @see    isValid
      88          19 :     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             :   private:
     104             :     RawMIDIAddress addresses;
     105             : };
     106             : 
     107             : /// A class for saving an offset to a MIDI address.
     108             : /// It can be added to a MIDIAddress.
     109             : class RelativeMIDIAddress {
     110             :     friend class MIDIAddress;
     111             : 
     112             :   public:
     113           0 :     constexpr RelativeMIDIAddress() : addresses{0, 0, 0, 0} {}
     114          40 :     constexpr RelativeMIDIAddress(int deltaAddress, int deltaChannel = 0,
     115             :                                   int deltaCableNumber = 0)
     116          40 :         : addresses{
     117             :               1,
     118          40 :               (uint8_t)deltaAddress,
     119          40 :               (uint8_t)deltaChannel,
     120          40 :               (uint8_t)deltaCableNumber,
     121          40 :           } {}
     122             :     constexpr bool isValid() const { return addresses.valid; }
     123             : 
     124             :     /// Compound addition. Element-wise addition, result is valid if both
     125             :     /// operands were valid.
     126             :     RelativeMIDIAddress &operator+=(RelativeMIDIAddress that);
     127             : 
     128             :   private:
     129             :     RawMIDIAddress addresses;
     130             :     static_assert(((-1) & 0x7F) == 0x7F,
     131             :                   "Negative numbers must be two's complement");
     132             : };
     133             : 
     134             : /// A type-safe utility class for saving a MIDI address consisting of a 7-bit
     135             : /// address, a 4-bit channel, and a 4-bit cable number.
     136             : /// A MIDI address can be marked "invalid". The MIDI sending functions
     137             : /// (@ref MIDI_Sender) will never send messages addressed to invalid addresses.
     138             : ///
     139             : /// See @ref midi_md-midi-addresses "MIDI Tutorial: MIDI addresses" for a
     140             : /// tutorial on how to use MIDI addresses.
     141             : class MIDIAddress {
     142             :   public:
     143             :     /// @name   Constructors
     144             :     /// @{
     145             : 
     146             :     /// Default constructor, creates an invalid address.
     147             :     constexpr MIDIAddress()
     148             :         : addresses{
     149             :               0,
     150             :               0,
     151             :               0,
     152             :               0,
     153             :           } {}
     154             : 
     155             :     /** 
     156             :      * @brief Constructor.
     157             :      * 
     158             :      * @param   address 
     159             :      *          The 7-bit MIDI address. Depending on the message type, this can
     160             :      *          be the MIDI note number, the number of the MIDI Control Change
     161             :      *          Controller, etc.  
     162             :      *          Must be a number in the range [0, 127].
     163             :      * @param   channelCN 
     164             :      *          The MIDI Channel and the MIDI USB cable number.
     165             :      */
     166         234 :     constexpr MIDIAddress(int address, MIDIChannelCable channelCN)
     167         234 :         : addresses{
     168             :               1,
     169         234 :               (uint8_t)address,
     170         468 :               channelCN.getRawChannel(),
     171         468 :               channelCN.getRawCableNumber(),
     172         234 :           } {} // Deliberate overflow for negative numbers
     173             : 
     174             :     /** 
     175             :      * @brief Constructor.
     176             :      * 
     177             :      * @param   address 
     178             :      *          The 7-bit MIDI address.  
     179             :      *          Depending on the message type, this can be the MIDI note number,
     180             :      *          the number of the MIDI Control Change Controller, etc.  
     181             :      *          Must be a number in the range [0, 127].
     182             :      * @param   channel
     183             :      *          The MIDI Channel.  
     184             :      *          Use the constants @ref CHANNEL_1 through @ref CHANNEL_16.
     185             :      * @param   cableNumber 
     186             :      *          The MIDI USB cable number.  
     187             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     188             :      */
     189          82 :     constexpr MIDIAddress(int address, Channel channel = CHANNEL_1,
     190             :                           Cable cableNumber = CABLE_1)
     191          82 :         : addresses{
     192             :               1,
     193          82 :               (uint8_t)address,
     194         164 :               channel.getRaw(),
     195         164 :               cableNumber.getRaw(),
     196          82 :           } {} // Deliberate overflow for negative numbers
     197             : 
     198             :     /** 
     199             :      * @brief Constructor.
     200             :      * 
     201             :      * @param   address 
     202             :      *          The 7-bit MIDI address.  
     203             :      *          Depending on the message type, this can be the MIDI note number,
     204             :      *          the number of the MIDI Control Change Controller, etc.  
     205             :      *          Must be a number in the range [0, 127].
     206             :      * @param   cableNumber 
     207             :      *          The MIDI USB cable number.  
     208             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     209             :      */
     210             :     constexpr MIDIAddress(int address, Cable cableNumber)
     211             :         : addresses{
     212             :               1,
     213             :               (uint8_t)address,
     214             :               0,
     215             :               cableNumber.getRaw(),
     216             :           } {} // Deliberate overflow for negative numbers
     217             : 
     218             :     /** 
     219             :      * @brief Constructor.
     220             :      * 
     221             :      * @param   channel
     222             :      *          The MIDI Channel.  
     223             :      *          Use the constants @ref CHANNEL_1 through @ref CHANNEL_16.
     224             :      * @param   cableNumber 
     225             :      *          The MIDI USB cable number.  
     226             :      *          Use the constants @ref CABLE_1 through @ref CABLE_16.
     227             :      */
     228             :     constexpr MIDIAddress(Channel channel, Cable cableNumber = CABLE_1)
     229             :         : addresses{
     230             :               1,
     231             :               0,
     232             :               channel.getRaw(),
     233             :               cableNumber.getRaw(),
     234             :           } {}
     235             : 
     236             :     /** 
     237             :      * @brief Constructor.
     238             :      * 
     239             :      * @param   address 
     240             :      *          The MIDI Channel and the MIDI USB cable number.  
     241             :      */
     242          14 :     constexpr MIDIAddress(MIDIChannelCable address)
     243          14 :         : addresses(address.addresses) {}
     244             : 
     245             :     /// Return an invalid address.
     246             :     constexpr static MIDIAddress invalid() { return {}; }
     247             : 
     248             :     /// @}
     249             : 
     250             :   public:
     251             :     /// @name   Adding/subtracting offsets
     252             :     /// @{
     253             : 
     254             :     /// Add a relative offset to this address.
     255             :     MIDIAddress &operator+=(RelativeMIDIAddress rhs);
     256             : 
     257             :     /// Subtract a relative offset from this address.
     258             :     MIDIAddress &operator-=(RelativeMIDIAddress rhs);
     259             : 
     260             :     /// Add a relative offset.
     261             :     MIDIAddress operator+(RelativeMIDIAddress rhs) const;
     262             : 
     263             :     /// Subtract a relative offset.
     264             :     MIDIAddress operator-(RelativeMIDIAddress rhs) const;
     265             : 
     266             :     /// @}
     267             : 
     268             :   public:
     269             :     /// @name   Member access
     270             :     /// @{
     271             : 
     272             :     /// Get the address [0, 127].
     273         400 :     constexpr uint8_t getAddress() const { return addresses.address; }
     274             : 
     275             :     /// Get the channel [CHANNEL_1, CHANNEL_16]
     276         225 :     constexpr Channel getChannel() const { return Channel{addresses.channel}; }
     277             :     /// Get the channel as an integer [0, 15]
     278          32 :     constexpr uint8_t getRawChannel() const { return addresses.channel; }
     279             : 
     280             :     /// Get the cable number [CABLE_1, CABLE_16].
     281         233 :     constexpr Cable getCableNumber() const {
     282         233 :         return Cable(addresses.cableNumber);
     283             :     }
     284             :     /// Get the cable number as an integer [0, 15].
     285          12 :     constexpr uint8_t getRawCableNumber() const {
     286          12 :         return addresses.cableNumber;
     287             :     }
     288             : 
     289             :     /// @}
     290             : 
     291             :   public:
     292             :     /// @name   Checks
     293             :     /// @{
     294             : 
     295             :     /// Check for equality: two addresses are equal if and only if they are both
     296             :     /// valid addresses and the MIDI address, MIDI channel and MIDI USB cable
     297             :     /// number are equal.
     298             :     /// @note   Invalid addresses are not equal nor inequal.
     299          32 :     constexpr bool operator==(MIDIAddress rhs) const {
     300          63 :         return this->addresses.valid && rhs.addresses.valid &&
     301          31 :                this->addresses.address == rhs.addresses.address &&
     302          92 :                this->addresses.channel == rhs.addresses.channel &&
     303          61 :                this->addresses.cableNumber == rhs.addresses.cableNumber;
     304             :     }
     305             : 
     306             :     /// Check for inequality: two addresses are not equal if and only if they
     307             :     /// are both valid addresses and have a MIDI address, MIDI channel or MIDI
     308             :     /// USB cable number that differs.
     309             :     /// @note   Invalid addresses are not equal nor inequal.
     310           8 :     constexpr bool operator!=(MIDIAddress rhs) const {
     311          15 :         return this->addresses.valid && rhs.addresses.valid &&
     312           7 :                !(this->addresses.address == rhs.addresses.address &&
     313           5 :                  this->addresses.channel == rhs.addresses.channel &&
     314          11 :                  this->addresses.cableNumber == rhs.addresses.cableNumber);
     315             :     }
     316             : 
     317             :     /// Check if the MIDI address is valid.
     318         247 :     constexpr bool isValid() const { return addresses.valid; }
     319             : 
     320             :     /// Check if the MIDI address is valid.
     321             :     /// @see    isValid
     322         134 :     constexpr explicit operator bool() const { return isValid(); }
     323             : 
     324             :     /// @}
     325             : 
     326             :   public:
     327             :     /// @name   Base functions for address pattern matching.
     328             :     /// @{
     329             : 
     330             :     /// Check if two addresses match (are equal).
     331          26 :     static bool matchSingle(MIDIAddress toMatch, MIDIAddress base) {
     332          26 :         return base == toMatch;
     333             :     }
     334             : 
     335             :     /// Check if an address falls within a range of MIDI addresses, starting
     336             :     /// with address `base`, with a given length.
     337             :     /// @retval true
     338             :     ///         `toMatch` and `base` are both valid addresses,
     339             :     ///         the MIDI address is within the given range, and the MIDI Channel
     340             :     ///         and MIDI USB Cable Number of `toMatch` and `base` are the same.
     341             :     /// @retval false
     342             :     ///         Otherwise.
     343             :     static bool matchAddressInRange(MIDIAddress toMatch, MIDIAddress base,
     344             :                                     uint8_t length);
     345             : 
     346             :     /// @}
     347             : 
     348             :   private:
     349             :     RawMIDIAddress addresses;
     350             : };
     351             : 
     352             : END_CS_NAMESPACE

Generated by: LCOV version 1.15