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
|