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