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
|