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 : /// @copydoc getAddressOffset(setting_t) const 122 36 : RelativeMIDIAddress getAddressOffset() const { 123 36 : return getAddressOffset(getSelection()); 124 : } 125 : 126 : private: 127 : const BankType type; 128 : }; 129 : 130 : /** 131 : * @brief A base class for all MIDIOutputElement%s that can have one of many 132 : * addresses. 133 : * 134 : * The bank setting determines the address that's being used. 135 : * 136 : * @note To prevent 'sticky' notes (i.e. a button is pressed, a note on is 137 : * sent, the bank is changed, the button is released, and the note off 138 : * is sent to a different address, causing the first note to keep on 139 : * playing indefinitely), there must be a way to lock the bank setting 140 : * while a note is playing. Then when it is no longer playing, the 141 : * bank setting is unlocked. 142 : * 143 : * @invariant `getSelection` and `getRawBankSetting` always return a number in 144 : * the half-open interval 145 : * @f$ \left[0, \mathrm{bank.getNumberOfBanks()}\right) \cap 146 : * \mathbb{N} @f$. 147 : */ 148 : class ManyAddresses_Base : public OutputBankableMIDIAddress_Base { 149 : public: 150 : /** 151 : * @brief Constructor. 152 : * 153 : * @param bank 154 : * The bank to add this element to. 155 : * @tparam NumBanks 156 : * The number of bank settings @p bank has. 157 : */ 158 : template <uint8_t NumBanks> 159 12 : ManyAddresses_Base(const Bank<NumBanks> &bank) 160 12 : : OutputBankableMIDIAddress_Base(bank) {} 161 : }; 162 : 163 : END_CS_NAMESPACE