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)) RawMIDICNChannelAddress {
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 MIDICNChannel {
20 : friend class MIDICNChannelAddress;
21 :
22 : public:
23 2 : constexpr MIDICNChannel() : addresses{0, 0, 0, 0} {}
24 40 : constexpr MIDICNChannel(Channel channel, int cableNumber = 0)
25 120 : : addresses{
26 : 1,
27 : 0,
28 40 : (uint8_t)channel.getRaw(),
29 40 : (uint8_t)cableNumber,
30 40 : } {}
31 :
32 : /// Get the channel [1, 16].
33 : constexpr Channel getChannel() const {
34 : return Channel{int8_t(addresses.channel)};
35 : }
36 :
37 : /// Get the channel as an integer [0, 15].
38 43 : constexpr uint8_t getRawChannel() const { return addresses.channel; }
39 :
40 : /// Get the cable number [0, 15].
41 43 : constexpr uint8_t getCableNumber() const { return addresses.cableNumber; }
42 :
43 : /// Check if the MIDI address is valid.
44 16 : constexpr bool isValid() const { return addresses.valid; }
45 :
46 : /// Check if the MIDI address is valid.
47 : /// @see isValid
48 16 : constexpr explicit operator bool() const { return isValid(); }
49 :
50 : constexpr static MIDICNChannel invalid() { return {}; }
51 :
52 : private:
53 : RawMIDICNChannelAddress addresses;
54 : };
55 :
56 : /// A class for saving an offset to a MIDI address.
57 : /// It can be added to a MIDICNChannelAddress.
58 : class RelativeMIDICNChannelAddress {
59 : friend class MIDICNChannelAddress;
60 :
61 : public:
62 3 : constexpr RelativeMIDICNChannelAddress() : addresses{0, 0, 0, 0} {}
63 48 : constexpr RelativeMIDICNChannelAddress(int deltaAddress,
64 : int deltaChannel = 0,
65 : int deltaCableNumber = 0)
66 192 : : addresses{
67 : 1,
68 48 : (uint8_t)deltaAddress,
69 48 : (uint8_t)deltaChannel,
70 48 : (uint8_t)deltaCableNumber,
71 48 : } {}
72 91 : constexpr bool isValid() const { return addresses.valid; }
73 :
74 : private:
75 : RawMIDICNChannelAddress addresses;
76 : static_assert(((-1) & 0x7F) == 0x7F,
77 : "Negative numbers must be two's complement");
78 : };
79 :
80 : /// A type-safe utility class for saving a MIDI address consisting of a 7-bit
81 : /// address, a 4-bit channel, and a 4-bit cable number.
82 : class MIDICNChannelAddress {
83 : public:
84 9 : constexpr MIDICNChannelAddress()
85 9 : : addresses{
86 : 0,
87 : 0,
88 : 0,
89 : 0,
90 9 : } {}
91 27 : constexpr MIDICNChannelAddress(int address, MIDICNChannel channelCN)
92 108 : : addresses{
93 : 1,
94 27 : (uint8_t)address,
95 27 : channelCN.getRawChannel(),
96 27 : channelCN.getCableNumber(),
97 27 : } {}
98 209 : constexpr MIDICNChannelAddress(int address, Channel channel = CHANNEL_1,
99 : int cableNumber = 0x0)
100 836 : : addresses{
101 : 1,
102 209 : (uint8_t)address,
103 209 : (uint8_t)channel.getRaw(),
104 209 : (uint8_t)cableNumber,
105 209 : } {} // Deliberate overflow for negative numbers
106 12 : constexpr MIDICNChannelAddress(Channel channel, int cableNumber = 0x0)
107 36 : : addresses{
108 : 1,
109 : 0,
110 12 : (uint8_t)channel.getRaw(),
111 12 : (uint8_t)cableNumber,
112 12 : } {} // Deliberate overflow for negative numbers
113 11 : constexpr MIDICNChannelAddress(const MIDICNChannel &address)
114 11 : : addresses(address.addresses) {}
115 :
116 : MIDICNChannelAddress &operator+=(const RelativeMIDICNChannelAddress &rhs);
117 :
118 : MIDICNChannelAddress &operator-=(const RelativeMIDICNChannelAddress &rhs);
119 :
120 : MIDICNChannelAddress
121 : operator+(const RelativeMIDICNChannelAddress &rhs) const;
122 :
123 : MIDICNChannelAddress
124 : operator-(const RelativeMIDICNChannelAddress &rhs) const;
125 :
126 18 : constexpr bool operator==(const MIDICNChannelAddress &rhs) const {
127 35 : return this->addresses.valid && rhs.addresses.valid &&
128 17 : this->addresses.address == rhs.addresses.address &&
129 16 : this->addresses.channel == rhs.addresses.channel &&
130 15 : this->addresses.cableNumber == rhs.addresses.cableNumber;
131 : }
132 :
133 8 : constexpr bool operator!=(const MIDICNChannelAddress &rhs) const {
134 15 : return this->addresses.valid && rhs.addresses.valid &&
135 12 : !(this->addresses.address == rhs.addresses.address &&
136 5 : this->addresses.channel == rhs.addresses.channel &&
137 3 : this->addresses.cableNumber == rhs.addresses.cableNumber);
138 : }
139 :
140 : /// Get the address [0, 127].
141 378 : constexpr uint8_t getAddress() const { return addresses.address; }
142 :
143 : /// Get the channel [CHANNEL_1, CHANNEL_16]
144 69 : constexpr Channel getChannel() const {
145 69 : return Channel{int8_t(addresses.channel)};
146 : }
147 : /// Get the channel [0, 15]
148 128 : constexpr uint8_t getRawChannel() const { return addresses.channel; }
149 :
150 : /// Get the cable number [0, 15]
151 187 : constexpr uint8_t getCableNumber() const { return addresses.cableNumber; }
152 :
153 : /// Check if the MIDI address is valid.
154 280 : constexpr bool isValid() const { return addresses.valid; }
155 :
156 : /// Check if the MIDI address is valid.
157 : /// @see isValid
158 96 : constexpr explicit operator bool() const { return isValid(); }
159 :
160 : /// Check if two addresses match.
161 : static bool matchSingle(const MIDICNChannelAddress &toMatch,
162 : const MIDICNChannelAddress &base);
163 :
164 : /// Check if an address falls within a range of addresses, starting with
165 : /// address `base`, with a given length.
166 : static bool matchAddressInRange(const MIDICNChannelAddress &toMatch,
167 : const MIDICNChannelAddress &base,
168 : uint8_t length);
169 :
170 : constexpr static MIDICNChannelAddress invalid() { return {}; }
171 :
172 : private:
173 : RawMIDICNChannelAddress addresses;
174 : };
175 :
176 : END_CS_NAMESPACE
|