LCOV - code coverage report
Current view: top level - src/Def - MIDIAddress.hpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 60 61 98.4 %
Date: 2024-12-21 21:28:55 Functions: 25 26 96.2 %
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 1.15