Line data Source code
1 : /* ✔ */ 2 : 3 : #pragma once 4 : 5 : #include "Bank.hpp" 6 : #include "BankConfig.hpp" 7 : #include <Def/MIDIAddress.hpp> 8 : 9 : BEGIN_CS_NAMESPACE 10 : 11 : /// Class for keeping track of the active bank, and allows locking of the 12 : /// bank setting. 13 : /// 14 : /// @see @ref OutputBankableMIDIAddress 15 : class OutputBankableMIDIAddress_Base { 16 : protected: 17 : /** 18 : * @brief Constructor. 19 : * 20 : * @param bank 21 : * The bank to add this element to. 22 : */ 23 43 : OutputBankableMIDIAddress_Base(const OutputBank &bank) : bank(bank) {} 24 : 25 : public: 26 : /** 27 : * @brief Get the actual bank setting (no matter whether the element is 28 : * locked or not). 29 : */ 30 22 : setting_t getRawBankSetting() const { return bank.getSelection(); } 31 : 32 : /** 33 : * @brief Get the bank setting. 34 : * 35 : * If the element is locked, the bank setting from the moment it was locked 36 : * is returned. 37 : */ 38 44 : setting_t getSelection() const { 39 44 : return lockedSetting == Unlocked ? getRawBankSetting() : lockedSetting; 40 : } 41 : 42 : /** 43 : * @brief Lock the bank setting. 44 : * 45 : * As long as it's locked, `getSelection` will return the current setting, 46 : * independent from the actual bank setting. 47 : */ 48 14 : void lock() { 49 14 : if (lockedSetting == Unlocked) 50 14 : lockedSetting = getRawBankSetting(); 51 14 : } 52 : 53 : /** 54 : * @brief Unlock the bank setting. 55 : * 56 : * After unlocking, @ref getSelection will return the actual bank setting 57 : * again. 58 : */ 59 14 : void unlock() { lockedSetting = Unlocked; } 60 : 61 : protected: 62 : const OutputBank &bank; 63 : 64 : private: 65 : constexpr static setting_t Unlocked = NoSetting; 66 : setting_t lockedSetting = Unlocked; 67 : }; 68 : 69 : /** 70 : * @brief A base class for all MIDIOutputElement%s that can be banked. 71 : * 72 : * @note These elements don't have to be updated when the bank setting is 73 : * changed, because they poll the bank setting each time they send a 74 : * MIDI event. 75 : * They are not added to the bank, they just keep a reference to the 76 : * bank they're a part of. 77 : * 78 : * @note To prevent 'sticky' notes (i.e. a button is pressed, a note on is 79 : * sent, the bank is changed, the button is released, and the note off 80 : * is sent to a different address, causing the first note to keep on 81 : * playing indefinitely), there must be a way to lock the bank setting 82 : * while a note is playing. Then when it is no longer playing, the 83 : * bank setting is unlocked. 84 : */ 85 : class OutputBankableMIDIAddress : public OutputBankableMIDIAddress_Base { 86 : public: 87 : /** 88 : * @brief Create a new OutputBankableMIDIAddress object. 89 : * 90 : * @param bank 91 : * The bank to add this element to. 92 : * @param type 93 : * What address type to change (address, channel or cable number). 94 : */ 95 31 : OutputBankableMIDIAddress(const OutputBank &bank, BankType type) 96 31 : : OutputBankableMIDIAddress_Base(bank), type(type) {} 97 : 98 : /** 99 : * @brief Create a new OutputBankableMIDIAddress object. 100 : * 101 : * @param config 102 : * The bank and address type to change. 103 : * 104 : * @see OutputBankableMIDIAddress::OutputBankableMIDIAddress(Bank<N> &, BankType) 105 : */ 106 31 : OutputBankableMIDIAddress(BaseOutputBankConfig config) 107 31 : : OutputBankableMIDIAddress(config.bank, config.type) {} 108 : 109 : /** 110 : * @brief Get the offset relative to the base address. 111 : */ 112 36 : RelativeMIDIAddress getAddressOffset(setting_t bankindex) const { 113 36 : int8_t offset = bank.getOffsetOfSetting(bankindex); 114 36 : switch (type) { 115 36 : case ChangeAddress: return {offset, 0, 0}; 116 0 : case ChangeChannel: return {0, offset, 0}; 117 0 : case ChangeCable: return {0, 0, offset}; 118 0 : default: return {}; 119 : } 120 : } 121 : /** 122 : * @brief Get the offset relative to the base address for the active bank. 123 : */ 124 36 : RelativeMIDIAddress getAddressOffset() const { 125 36 : return getAddressOffset(getSelection()); 126 : } 127 : 128 : private: 129 : const BankType type; 130 : }; 131 : 132 : /** 133 : * @brief A base class for all MIDIOutputElement%s that can have one of many 134 : * addresses. 135 : * 136 : * The bank setting determines the address that's being used. 137 : * 138 : * @note To prevent 'sticky' notes (i.e. a button is pressed, a note on is 139 : * sent, the bank is changed, the button is released, and the note off 140 : * is sent to a different address, causing the first note to keep on 141 : * playing indefinitely), there must be a way to lock the bank setting 142 : * while a note is playing. Then when it is no longer playing, the 143 : * bank setting is unlocked. 144 : * 145 : * @invariant `getSelection` and `getRawBankSetting` always return a number in 146 : * the half-open interval 147 : * @f$ \left[0, \mathrm{bank.getNumberOfBanks()}\right) \cap 148 : * \mathbb{N} @f$. 149 : */ 150 : class ManyAddresses_Base : public OutputBankableMIDIAddress_Base { 151 : public: 152 : /** 153 : * @brief Constructor. 154 : * 155 : * @param bank 156 : * The bank to add this element to. 157 : * @tparam NumBanks 158 : * The number of bank settings @p bank has. 159 : */ 160 : template <uint8_t NumBanks> 161 12 : ManyAddresses_Base(const Bank<NumBanks> &bank) 162 12 : : OutputBankableMIDIAddress_Base(bank) {} 163 : }; 164 : 165 : END_CS_NAMESPACE