Line data Source code
1 : #pragma once
2 :
3 : #include <Banks/Bank.hpp>
4 : #include <Banks/BankConfig.hpp>
5 : #include <Def/MIDIAddress.hpp>
6 :
7 : BEGIN_CS_NAMESPACE
8 :
9 : namespace BankableMIDIMatcherHelpers {
10 :
11 : /**
12 : * @brief Check whether a given address is within a range of given length
13 : * starting from the given base address.
14 : *
15 : * @param tgt
16 : * The address to check
17 : * @param base
18 : * The base address, start of the range.
19 : * @param length
20 : * The length of the range.
21 : */
22 10 : inline bool inRange(uint8_t tgt, uint8_t base, uint8_t length) {
23 10 : return (base <= tgt) && (tgt - base < length);
24 : }
25 :
26 : /**
27 : * @brief Check if the given address is part of the bank relative to the
28 : * base address.
29 : *
30 : * Consider the following example:
31 : * A Bank with 4 tracks per bank (T), 2 bank settings (N),
32 : * and a base address of 3.
33 : *
34 : * ~~~
35 : * 0 1 2 3 4 5 6 7 8 9 10 11 12 ...
36 : * ☐ ☐ ☐ ☒ ☐ ☐ ☐ ☒ ☐ ☐ ☐ ☐ ☐ ...
37 : * ~~~
38 : *
39 : * Addresses before the base adddress are not matched (0, 1, 2).
40 : * Addresses after N * T are not matched (8, 9, 10, 11, 12).
41 : * Addresses with a distance to the base address that is not a multiple of N
42 : * are not matched (4, 5, 6).
43 : *
44 : * @param tgt
45 : * The address to check.
46 : * @param base
47 : * The base address (the address of bank setting 0).
48 : * @param bank
49 : * The bank to match the address in.
50 : *
51 : * @note Equivalent to `matchBankableInRange(toMatch, base, 1)`.
52 : */
53 : template <uint8_t BankSize>
54 26 : bool matchBankable(uint8_t tgt, uint8_t base, const Bank<BankSize> &bank) {
55 26 : const uint8_t N = BankSize;
56 26 : const uint8_t B = bank.getTracksPerBank();
57 26 : const int8_t F = bank.getSelectionOffset();
58 26 : const uint8_t m = tgt;
59 26 : const uint8_t b = base;
60 26 : uint8_t diff = m - b - F * B;
61 51 : return m >= b + F * B && //
62 50 : diff < N * B && //
63 50 : diff % B == 0;
64 : }
65 :
66 : /// @see @ref matchBankableInRange(MIDIAddress,MIDIAddress,BaseBankConfig<BankSize>,uint8_t)
67 : template <uint8_t BankSize>
68 18 : bool matchBankableInRange(uint8_t tgt, uint8_t base, const Bank<BankSize> &bank,
69 : uint8_t rangeLen) {
70 18 : const uint8_t R = rangeLen;
71 18 : const uint8_t N = BankSize;
72 18 : const uint8_t B = bank.getTracksPerBank();
73 18 : const int8_t F = bank.getSelectionOffset();
74 18 : const uint8_t m = tgt;
75 18 : const uint8_t b = base;
76 18 : uint8_t diff = m - b - F * B;
77 32 : return m >= b + F * B && //
78 30 : diff < N * B && //
79 30 : diff % B < R;
80 : }
81 :
82 : /// @see @ref getRangeIndex(MIDIAddress,MIDIAddress,BaseBankConfig<BankSize>)
83 : template <uint8_t BankSize>
84 8 : uint8_t getRangeIndex(uint8_t tgt, uint8_t base, const Bank<BankSize> &bank) {
85 8 : const uint8_t B = bank.getTracksPerBank();
86 8 : const int8_t F = bank.getSelectionOffset();
87 8 : const uint8_t m = tgt;
88 8 : const uint8_t b = base;
89 8 : uint8_t diff = m - b - F * B;
90 8 : return diff % B;
91 : }
92 :
93 : /// @see @ref getBankIndex(MIDIAddress,MIDIAddress,BaseBankConfig<BankSize>)
94 : template <uint8_t BankSize>
95 32 : uint8_t getBankIndex(uint8_t tgt, uint8_t base, const Bank<BankSize> &bank) {
96 32 : const uint8_t B = bank.getTracksPerBank();
97 32 : const int8_t F = bank.getSelectionOffset();
98 32 : const uint8_t m = tgt;
99 32 : const uint8_t b = base;
100 32 : uint8_t diff = m - b - F * B;
101 32 : return diff / B;
102 : }
103 :
104 : /**
105 : * @brief Check whether a given address is part of the bank relative to
106 : * the base address.
107 : *
108 : * @param tgt
109 : * The address to check.
110 : * @param base
111 : * The base address (the address of bank setting 0).
112 : * @param config
113 : * The bank configuration.
114 : */
115 : template <uint8_t BankSize>
116 20 : bool matchBankable(MIDIAddress tgt, MIDIAddress base,
117 : BaseBankConfig<BankSize> config) {
118 20 : if (!tgt.isValid() || !base.isValid())
119 0 : return false;
120 20 : switch (config.type) {
121 14 : case BankType::ChangeAddress: {
122 98 : return tgt.getChannel() == base.getChannel() &&
123 98 : tgt.getCableNumber() == base.getCableNumber() &&
124 14 : matchBankable(tgt.getAddress(), base.getAddress(),
125 28 : config.bank);
126 : }
127 3 : case BankType::ChangeChannel: {
128 12 : return tgt.getAddress() == base.getAddress() &&
129 21 : tgt.getCableNumber() == base.getCableNumber() &&
130 3 : matchBankable(tgt.getRawChannel(), base.getRawChannel(),
131 6 : config.bank);
132 : }
133 3 : case BankType::ChangeCable: {
134 12 : return tgt.getAddress() == base.getAddress() &&
135 21 : tgt.getChannel() == base.getChannel() &&
136 3 : matchBankable(tgt.getRawCableNumber(),
137 6 : base.getRawCableNumber(), config.bank);
138 : }
139 : default: return false; // LCOV_EXCL_LINE
140 : }
141 : }
142 :
143 : /**
144 : * @brief Check whether a given address is part of the bank relative to
145 : * the base address and within a range with a given length.
146 : *
147 : * @param tgt
148 : * The address to check.
149 : * @param base
150 : * The base address (the address of bank setting 0).
151 : * @param config
152 : * The bank configuration.
153 : * @param length
154 : * The length of the range.
155 : */
156 : template <uint8_t BankSize>
157 30 : bool matchBankableInRange(MIDIAddress tgt, MIDIAddress base,
158 : BaseBankConfig<BankSize> config, uint8_t length) {
159 30 : if (!tgt.isValid() || !base.isValid())
160 0 : return false;
161 30 : switch (config.type) {
162 20 : case ChangeAddress:
163 134 : return tgt.getChannel() == base.getChannel() &&
164 134 : tgt.getCableNumber() == base.getCableNumber() &&
165 18 : matchBankableInRange(tgt.getAddress(), base.getAddress(),
166 38 : config.bank, length);
167 10 : case ChangeChannel:
168 28 : return inRange(tgt.getAddress(), base.getAddress(), length) &&
169 54 : tgt.getCableNumber() == base.getCableNumber() &&
170 6 : matchBankable(tgt.getRawChannel(), base.getRawChannel(),
171 16 : config.bank);
172 0 : case ChangeCable:
173 0 : return inRange(tgt.getAddress(), base.getAddress(), length) &&
174 0 : tgt.getChannel() == base.getChannel() &&
175 0 : matchBankable(tgt.getRawCableNumber(),
176 0 : base.getRawCableNumber(), config.bank);
177 0 : default: return false;
178 : }
179 : }
180 :
181 : /**
182 : * @brief Calculate the bank setting of a given MIDI address, relative to
183 : * a base address.
184 : *
185 : * @param target
186 : * The MIDI address to calculate the bank setting of.
187 : * @param base
188 : * The base address to compare it to (the address of bank setting 0).
189 : * @param config
190 : * The bank configuration to determine the index.
191 : */
192 : template <uint8_t BankSize>
193 32 : uint8_t getBankIndex(MIDIAddress target, MIDIAddress base,
194 : BaseBankConfig<BankSize> config) {
195 32 : switch (config.type) {
196 22 : case BankType::ChangeAddress:
197 22 : return getBankIndex(target.getAddress(), base.getAddress(),
198 44 : config.bank);
199 7 : case BankType::ChangeChannel:
200 7 : return getBankIndex(target.getRawChannel(), base.getRawChannel(),
201 14 : config.bank);
202 3 : case BankType::ChangeCable:
203 3 : return getBankIndex(target.getRawCableNumber(),
204 6 : base.getRawCableNumber(), config.bank);
205 : default: return 0; // LCOV_EXCL_LINE
206 : }
207 : }
208 :
209 : /**
210 : * @brief Calculate the index in the address range of a given MIDI address,
211 : * relative to a base address.
212 : *
213 : * @param tgt
214 : * The MIDI address to calculate the bank setting of.
215 : * @param base
216 : * The base address to compare it to (beginning of the range for bank
217 : * setting 0).
218 : * @param config
219 : * The bank configuration to determine the index.
220 : */
221 : template <uint8_t BankSize>
222 12 : uint8_t getRangeIndex(MIDIAddress tgt, MIDIAddress base,
223 : BaseBankConfig<BankSize> config) {
224 12 : return config.type == BankType::ChangeAddress
225 8 : ? getRangeIndex(tgt.getAddress(), base.getAddress(), config.bank)
226 20 : : tgt.getAddress() - base.getAddress();
227 : }
228 :
229 : } // namespace BankableMIDIMatcherHelpers
230 :
231 : END_CS_NAMESPACE
|