Line data Source code
1 : /* ✔ */
2 :
3 : #pragma once
4 :
5 : #include <AH/Containers/LinkedList.hpp>
6 : #include <AH/Debug/Debug.hpp>
7 : #include <AH/Error/Error.hpp>
8 : #include <Selectors/Selectable.hpp>
9 :
10 : BEGIN_CS_NAMESPACE
11 :
12 : template <setting_t N>
13 : class BankableMIDIInput;
14 :
15 : /// A class for changing the address of BankableMIDIOutput%s.
16 : class OutputBank {
17 : public:
18 : /// Create a new OutputBank object.
19 : ///
20 : /// @param tracksPerBank
21 : /// The number of addresses/tracks to skip for each bank setting.
22 : /// Must be strictly positive.
23 : /// @param initialSelection
24 : /// The initial bank setting.
25 : /// @param selectionOffset
26 : /// The offset added to the bank setting before computing the
27 : /// actual address offset.
28 45 : OutputBank(uint8_t tracksPerBank = 1, setting_t initialSelection = 0,
29 : int8_t selectionOffset = 0)
30 45 : : tracksPerBank(tracksPerBank), bankSetting(initialSelection),
31 45 : selectionOffset(selectionOffset) {
32 45 : if (tracksPerBank == 0)
33 1 : FATAL_ERROR(F("A Bank must have a non-zero number of tracks."),
34 : 0x4573);
35 44 : }
36 :
37 : /// Select the given bank setting.
38 : ///
39 : /// @param setting
40 : /// The new setting to select (zero-based).
41 90 : void select(setting_t setting) { bankSetting = setting; }
42 :
43 : /// Get the current bank setting (zero-based).
44 331 : setting_t getSelection() const { return bankSetting; }
45 : /// Get the offset of the bank setting.
46 144 : int8_t getSelectionOffset() const { return selectionOffset; }
47 :
48 : /// Get the number of tracks per bank.
49 : /// This is the number of addresses/tracks to skip for each bank setting.
50 147 : uint8_t getTracksPerBank() const { return tracksPerBank; }
51 :
52 : /// The same as @ref getOffset, but for a given setting.
53 60 : int8_t getOffsetOfSetting(setting_t s) const {
54 60 : return (s + getSelectionOffset()) * getTracksPerBank();
55 : }
56 : /// Get the address offset (number of banks times the index of the selected
57 : /// bank after applying the offset)
58 24 : int8_t getOffset() const { return getOffsetOfSetting(getSelection()); }
59 :
60 : private:
61 : uint8_t tracksPerBank;
62 : setting_t bankSetting;
63 : int8_t selectionOffset;
64 : };
65 :
66 : /// Callback class for Bankable objects that need to be notified when the
67 : /// active setting of their Bank changes.
68 : class BankSettingChangeCallback
69 : : public DoublyLinkable<BankSettingChangeCallback> {
70 : template <setting_t N>
71 : friend class Bank;
72 :
73 : private:
74 : /// A function to be executed each time the bank setting changes.
75 : /// Think of an LED that indicates whether a track is muted or not. If this
76 : /// LED is bankable, let's say with 4 tracks per bank, 2 banks, and a base
77 : /// address of 3, then this LED object keeps the state of tracks 3 and 7.
78 : /// When the bank setting is 0, the LED displays the state of track 3, when
79 : /// the bank setting is 1, the LED displays the state of track 7.
80 : /// To know when to update the LED, this callback is used.
81 0 : virtual void onBankSettingChange() {}
82 : };
83 :
84 : /// A class that groups @ref BankableMIDIOutputElements and
85 : /// @ref BankableMIDIInputElements, and allows the user to change the addresses
86 : /// of these elements.
87 : ///
88 : /// @see FAQ: @ref faq-change-address-runtime
89 : /// @see FAQ: @ref faq-banks
90 : ///
91 : /// @tparam NumBanks
92 : /// The number of banks.
93 : template <setting_t NumBanks>
94 : class Bank : public Selectable<NumBanks>, public OutputBank {
95 :
96 : public:
97 : /// Construct a new Bank object.
98 : ///
99 : /// @param tracksPerBank
100 : /// The number of addresses/tracks to skip for each bank setting.
101 : /// Must be strictly positive.
102 : /// @param initialSelection
103 : /// The initial bank setting.
104 : /// @param selectionOffset
105 : /// The offset added to the bank setting before computing the
106 : /// actual address offset.
107 28 : Bank(uint8_t tracksPerBank = 1, setting_t initialSelection = 0,
108 : int8_t selectionOffset = 0)
109 : : Selectable<NumBanks>(initialSelection),
110 28 : OutputBank(tracksPerBank, initialSelection, selectionOffset) {}
111 :
112 : /// Select the given bank setting.
113 : ///
114 : /// All Bankable MIDI Input elements that were added to this bank will be
115 : /// updated.
116 : ///
117 : /// @param bankSetting
118 : /// The new setting to select.
119 : void select(setting_t bankSetting) override;
120 :
121 : /// Get the number of banks.
122 : constexpr static uint8_t getNumberOfBanks() { return NumBanks; }
123 :
124 : public:
125 : /// Add a Bankable MIDI Input Element to the bank.
126 : ///
127 : /// @param bankable
128 : /// The MIDI Input Element to be added.
129 : void add(BankSettingChangeCallback *bankable);
130 :
131 : /// Remove a Bankable MIDI Input Element from the bank.
132 : ///
133 : /// @param bankable
134 : /// The MIDI Input Element to be removed.
135 : void remove(BankSettingChangeCallback *bankable);
136 :
137 : private:
138 : /// A linked list of all Bankable MIDI Input Elements that have been added
139 : /// to this bank, and that should be updated when the bank setting changes.
140 : /// The list is updated automatically when Bankable MIDI Input Elements are
141 : /// created or destroyed.
142 : DoublyLinkedList<BankSettingChangeCallback> inputBankables;
143 : };
144 :
145 : END_CS_NAMESPACE
146 :
147 : // ---------------------------- Implementations ----------------------------- //
148 :
149 : BEGIN_CS_NAMESPACE
150 :
151 : template <setting_t NumBanks>
152 17 : void Bank<NumBanks>::add(BankSettingChangeCallback *bankable) {
153 17 : inputBankables.append(bankable);
154 17 : }
155 :
156 : template <setting_t NumBanks>
157 17 : void Bank<NumBanks>::remove(BankSettingChangeCallback *bankable) {
158 17 : inputBankables.remove(bankable);
159 17 : }
160 :
161 : template <setting_t NumBanks>
162 80 : void Bank<NumBanks>::select(setting_t bankSetting) {
163 80 : bankSetting = this->validateSetting(bankSetting);
164 79 : OutputBank::select(bankSetting);
165 141 : for (BankSettingChangeCallback &e : inputBankables)
166 62 : e.onBankSettingChange();
167 79 : }
168 :
169 : END_CS_NAMESPACE
|