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
|