Line data Source code
1 : #pragma once
2 :
3 : #include <Banks/BankableMIDIInput.hpp>
4 : #include <MIDI_Inputs/MIDIInputElementCC.hpp>
5 : #include <MIDI_Inputs/MIDIInputElementNote.hpp>
6 :
7 : BEGIN_CS_NAMESPACE
8 :
9 : /**
10 : * @brief Interface for NoteCCValue objects: provides getters for the
11 : * velocity or controller values.
12 : */
13 : class INoteCCValue {
14 : protected:
15 12 : INoteCCValue(uint8_t rangeLength) : rangeLength{rangeLength} {}
16 :
17 : public:
18 : /// Get the length of the range of note/CC addresses.
19 6 : uint8_t length() const { return rangeLength; }
20 : /// Get the velocity or controller value for the given index in the range.
21 : virtual uint8_t getValue(uint8_t index) const = 0;
22 : /// Get the velocity or controller value of the first or only note or
23 : /// controller.
24 15 : uint8_t getValue() const { return getValue(0); }
25 :
26 : private:
27 : uint8_t rangeLength;
28 : };
29 :
30 : /**
31 : * @brief A callback for NoteCCRange that doesn't do anything.
32 : */
33 : class NoteCCRangeEmptyCallback {
34 : public:
35 9 : void begin(const INoteCCValue &) {}
36 13 : void update(const INoteCCValue &, uint8_t) {}
37 30 : void updateAll(const INoteCCValue &) {}
38 : };
39 :
40 : /**
41 : * @brief A callback for NoteCCRange with an action that can be implemented
42 : * by the user.
43 : *
44 : * Provides default implementations for `begin` and `updateAll`.
45 : */
46 3 : struct SimpleNoteCCValueCallback {
47 : protected:
48 3 : SimpleNoteCCValueCallback() = default;
49 :
50 : public:
51 : /// Initialize: called once
52 0 : virtual void begin(const INoteCCValue &) {}
53 : /// Update the given index: called when a new message is received for this
54 : /// index
55 : virtual void update(const INoteCCValue ¬eccval, uint8_t index) = 0;
56 : /// Update all values: called when a bank change causes all values to
57 : /// (possibly) change, or when the entire range is reset to zero.
58 3 : virtual void updateAll(const INoteCCValue ¬eccval) {
59 6 : for (uint8_t i = 0; i < noteccval.length(); ++i)
60 3 : update(noteccval, i);
61 3 : }
62 : };
63 :
64 : // -------------------------------------------------------------------------- //
65 :
66 : /**
67 : * @brief Base class for all other classes that listen for incoming MIDI Note
68 : * or Control Change messages and saves their values.
69 : *
70 : * Can listen to a range of addresses or a single address.
71 : */
72 : template <class MIDIInput_t, uint8_t RangeLen, uint8_t NumBanks, class Callback>
73 12 : class NoteCCRange : public MIDIInput_t, public INoteCCValue {
74 : public:
75 12 : NoteCCRange(MIDIAddress address, const Callback &callback)
76 24 : : MIDIInput_t{address}, INoteCCValue{RangeLen}, callback(callback) {}
77 :
78 : /// @todo check index bounds
79 138 : uint8_t getValue(uint8_t index) const final override {
80 138 : return values[getSelection()][index];
81 : }
82 : using INoteCCValue::getValue;
83 :
84 : /// Initialize
85 12 : void begin() override { callback.begin(*this); }
86 : /// Reset all values to zero
87 2 : void reset() override {
88 2 : values = {{}};
89 2 : callback.updateAll(*this);
90 2 : }
91 :
92 : private:
93 : // Called when a MIDI message comes in, and if that message has been matched
94 : // by the `match` method.
95 24 : bool updateImpl(const ChannelMessageMatcher &midimsg,
96 : const MIDIAddress &target) override {
97 : // Compute the offsets/indices in the range and the bank.
98 : // E.g. if the start address of the range is 10 and the incoming message
99 : // is on address 15, the range index will be 5 = 15 - 10.
100 : // Very similar for banks, but in that case there are 3 possible
101 : // addresses (note/controller number, MIDI channel and MIDI cable).
102 24 : uint8_t bankIndex = this->getBankIndex(target);
103 24 : uint8_t rangeIndex = this->getRangeIndex(target);
104 : // extract the velocity or controller value from the message
105 24 : uint8_t value = getValueFromMIDIMessage(midimsg);
106 : // save the value
107 24 : values[bankIndex][rangeIndex] = value;
108 : // if the bank that the message belongs to is the active bank,
109 : // update the display
110 24 : if (bankIndex == this->getSelection())
111 20 : callback.update(*this, rangeIndex);
112 : // event was handled successfully
113 24 : return true;
114 24 : }
115 :
116 : /// Extract the "value" from a MIDI Note or Control Change message.
117 : /// For Note On and Control Change, this is simply the second data byte,
118 : /// for Note Off, it's zero.
119 : static uint8_t
120 24 : getValueFromMIDIMessage(const ChannelMessageMatcher &midimsg) {
121 24 : return midimsg.type == MIDIMessageType::NOTE_OFF ? 0 : midimsg.data2;
122 : }
123 :
124 : /// Get the active bank selection.
125 46 : virtual uint8_t getSelection() const { return 0; }
126 :
127 : /// Get the bank index from a MIDI address.
128 16 : virtual setting_t getBankIndex(MIDIAddress target) const {
129 : // Default implementation for non-bankable version (bank is always 0)
130 : (void)target;
131 16 : return 0;
132 : }
133 :
134 : /// Get the index of the given MIDI address in the range
135 16 : virtual uint8_t getRangeIndex(MIDIAddress target) const {
136 : // Default implementation for non-bankable version (base address of the
137 : // range is fixed)
138 16 : return target.getAddress() - this->address.getAddress();
139 : }
140 :
141 : /// A 2D array for saving all values of the range, for all banks.
142 16 : Array<Array<uint8_t, RangeLen>, NumBanks> values = {{}};
143 :
144 : public:
145 : /// Callback that is called when a value in the active bank changes.
146 : Callback callback;
147 : /// Get the length of the range.
148 : constexpr static uint8_t length() { return RangeLen; }
149 : };
150 :
151 : // -------------------------------------------------------------------------- //
152 :
153 : template <class MIDIInput_t, uint8_t RangeLen,
154 : class Callback = NoteCCRangeEmptyCallback>
155 8 : class GenericNoteCCRange
156 : : public NoteCCRange<MIDIInput_t, RangeLen, 1, Callback> {
157 : public:
158 8 : GenericNoteCCRange(MIDIAddress address, const Callback &callback)
159 8 : : NoteCCRange<MIDIInput_t, RangeLen, 1, Callback>{address, callback} {}
160 :
161 : private:
162 : /// Check if the address of the incoming MIDI message is within the range
163 : /// of addresses of this element.
164 16 : bool match(const MIDIAddress &target) const override {
165 16 : return MIDIAddress::matchAddressInRange( //
166 16 : target, this->address, RangeLen);
167 : }
168 : };
169 :
170 : template <uint8_t RangeLen, class Callback = NoteCCRangeEmptyCallback>
171 : using GenericNoteRange =
172 : GenericNoteCCRange<MIDIInputElementNote, RangeLen, Callback>;
173 :
174 : template <uint8_t RangeLen, class Callback = NoteCCRangeEmptyCallback>
175 : using GenericCCRange =
176 : GenericNoteCCRange<MIDIInputElementCC, RangeLen, Callback>;
177 :
178 : template <class Callback = NoteCCRangeEmptyCallback>
179 : using GenericNoteValue = GenericNoteCCRange<MIDIInputElementNote, 1, Callback>;
180 :
181 : template <class Callback = NoteCCRangeEmptyCallback>
182 : using GenericCCValue = GenericNoteCCRange<MIDIInputElementCC, 1, Callback>;
183 :
184 : /**
185 : * @brief MIDI Input Element that listens to a range of notes and saves their
186 : * velocity values.
187 : *
188 : * @ingroup MIDIInputElements
189 : * @tparam RangeLen
190 : * The length of the range of notes to listen to.
191 : */
192 : template <uint8_t RangeLen>
193 1 : class NoteRange : public GenericNoteRange<RangeLen> {
194 : public:
195 1 : NoteRange(MIDIAddress address)
196 1 : : GenericNoteRange<RangeLen>{address, {}} {}
197 : };
198 :
199 : /**
200 : * @brief MIDI Input Element that listens to a single note and saves its
201 : * velocity value.
202 : *
203 : * @ingroup MIDIInputElements
204 : */
205 4 : class NoteValue : public GenericNoteValue<> {
206 : public:
207 4 : NoteValue(MIDIAddress address) : GenericNoteValue<>{address, {}} {}
208 : };
209 : using MIDINote[[deprecated("Use NoteValue instead")]] = NoteValue;
210 :
211 : /**
212 : * @brief MIDI Input Element that listens to a range of controllers
213 : * and saves their values.
214 : *
215 : * @ingroup MIDIInputElements
216 : * @tparam RangeLen
217 : * The length of the range of controllers to listen to.
218 : */
219 : template <uint8_t RangeLen>
220 : class CCRange : public GenericCCRange<RangeLen> {
221 : public:
222 : CCRange(MIDIAddress address)
223 : : GenericCCRange<RangeLen>{address, {}} {}
224 : };
225 :
226 : /**
227 : * @brief MIDI Input Element that listens to a single controller and saves its
228 : * value.
229 : *
230 : * @ingroup MIDIInputElements
231 : */
232 : class CCValue : public GenericCCValue<> {
233 : public:
234 : CCValue(MIDIAddress address) : GenericCCValue<>{address, {}} {}
235 : };
236 :
237 : // -------------------------------------------------------------------------- //
238 :
239 : namespace Bankable {
240 :
241 : /// @tparam RangeLen
242 : /// The length of the range.
243 : /// @tparam NumBanks
244 : /// The size of the bank.
245 : template <class MIDIInput_t, uint8_t RangeLen, uint8_t NumBanks,
246 : class Callback = NoteCCRangeEmptyCallback>
247 4 : class GenericNoteCCRange
248 : : public NoteCCRange<MIDIInput_t, RangeLen, NumBanks, Callback>,
249 : public BankableMIDIInput<NumBanks> {
250 : public:
251 4 : GenericNoteCCRange(BankConfig<NumBanks> config,
252 : MIDIAddress address,
253 : const Callback &callback)
254 4 : : NoteCCRange<MIDIInput_t, RangeLen, NumBanks, Callback>{
255 4 : address,
256 4 : callback,
257 8 : }, BankableMIDIInput<NumBanks>{config} {}
258 :
259 : private:
260 : /// Check if the address of the incoming MIDI message is within the range
261 : /// of addresses and in one of the banks of this element.
262 20 : bool match(const MIDIAddress &target) const override {
263 40 : return BankableMIDIInput<NumBanks>::matchBankableAddressInRange(
264 20 : target, this->address, RangeLen);
265 : }
266 :
267 116 : setting_t getSelection() const override {
268 116 : return BankableMIDIInput<NumBanks>::getSelection();
269 : };
270 :
271 8 : uint8_t getBankIndex(MIDIAddress target) const override {
272 8 : return BankableMIDIInput<NumBanks>::getBankIndex(target, this->address);
273 : }
274 :
275 8 : uint8_t getRangeIndex(MIDIAddress target) const override {
276 16 : return BankableMIDIInput<NumBanks>::getRangeIndex(target,
277 8 : this->address);
278 : }
279 :
280 28 : void onBankSettingChange() override { this->callback.updateAll(*this); }
281 : };
282 :
283 : template <uint8_t RangeLen, uint8_t NumBanks,
284 : class Callback = NoteCCRangeEmptyCallback>
285 : using GenericNoteRange =
286 : GenericNoteCCRange<MIDIInputElementNote, RangeLen, NumBanks, Callback>;
287 :
288 : template <uint8_t RangeLen, uint8_t NumBanks,
289 : class Callback = NoteCCRangeEmptyCallback>
290 : using GenericCCRange =
291 : GenericNoteCCRange<MIDIInputElementCC, RangeLen, NumBanks, Callback>;
292 :
293 : template <uint8_t NumBanks, class Callback = NoteCCRangeEmptyCallback>
294 : using GenericNoteValue =
295 : GenericNoteCCRange<MIDIInputElementNote, 1, NumBanks, Callback>;
296 :
297 : template <uint8_t NumBanks, class Callback = NoteCCRangeEmptyCallback>
298 : using GenericCCValue =
299 : GenericNoteCCRange<MIDIInputElementCC, 1, NumBanks, Callback>;
300 :
301 : /**
302 : * @brief MIDI Input Element that listens to a range of notes and saves their
303 : * velocity values.
304 : *
305 : * @ingroup BankableMIDIInputElements
306 : * @tparam RangeLen
307 : * The length of the range of notes to listen to.
308 : * @tparam NumBanks
309 : * The size of the bank.
310 : */
311 : template <uint8_t RangeLen, uint8_t NumBanks>
312 4 : class NoteRange : public GenericNoteRange<RangeLen, NumBanks> {
313 : public:
314 4 : NoteRange(BankConfig<NumBanks> config, MIDIAddress address)
315 4 : : GenericNoteRange<RangeLen, NumBanks>{config, address, {}} {}
316 : };
317 :
318 : /**
319 : * @brief MIDI Input Element that listens to a single note and saves its
320 : * value.
321 : *
322 : * @ingroup BankableMIDIInputElements
323 : * @tparam NumBanks
324 : * The size of the bank.
325 : */
326 : template <uint8_t NumBanks>
327 : class NoteValue : public GenericNoteValue<NumBanks> {
328 : public:
329 : NoteValue(BankConfig<NumBanks> config, MIDIAddress address)
330 : : GenericNoteValue<NumBanks>{config, address, {}} {}
331 : };
332 :
333 : /// Deprecated.
334 : /// @see Bankable::NoteValue
335 : template <uint8_t NumBanks>
336 : using MIDINote[[deprecated("Use NoteValue instead")]] = NoteValue<NumBanks>;
337 :
338 : /**
339 : * @brief MIDI Input Element that listens to a range of controllers and saves
340 : * their values.
341 : *
342 : * @ingroup BankableMIDIInputElements
343 : * @tparam RangeLen
344 : * The length of the range of controllers to listen to.
345 : * @tparam NumBanks
346 : * The size of the bank.
347 : */
348 : template <uint8_t RangeLen, uint8_t NumBanks>
349 : class CCRange : public GenericCCRange<RangeLen, NumBanks> {
350 : public:
351 : CCRange(BankConfig<NumBanks> config, MIDIAddress address)
352 : : GenericCCRange<RangeLen, NumBanks>{config, address, {}} {}
353 : };
354 :
355 : /**
356 : * @brief MIDI Input Element that listens to a single controller and saves its
357 : * value.
358 : *
359 : * @ingroup BankableMIDIInputElements
360 : * @tparam NumBanks
361 : * The size of the bank.
362 : */
363 : template <uint8_t NumBanks>
364 : class CCValue : public GenericCCValue<NumBanks> {
365 : public:
366 : CCValue(BankConfig<NumBanks> config, MIDIAddress address)
367 : : GenericCCValue<NumBanks>{config, address, {}} {}
368 : };
369 :
370 : } // namespace Bankable
371 :
372 : END_CS_NAMESPACE
|