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 220 : for (BankSettingChangeCallback &e : inputBankables) 166 62 : e.onBankSettingChange(); 167 79 : } 168 : 169 : END_CS_NAMESPACE