Control Surface  1.1.0
MIDI Control Surface library for Arduino
BankableMIDIInput.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "Bank.hpp"
4 #include "BankConfig.hpp"
6 #include <AH/Debug/Debug.hpp>
8 
10 
11 /**
12  * @brief A base class for all MIDIInputElement%s that can be banked.
13  *
14  * @note These elements must be updated when the bank setting is changed, so
15  * they are added to a linked list of the bank.
16  *
17  * @tparam N
18  * The number of banks.
19  */
20 template <setting_t N>
21 class BankableMIDIInput : public DoublyLinkable<BankableMIDIInput<N>> {
22  friend class Bank<N>;
23 
24  protected:
25  /**
26  * @brief Create a new BankableMIDIInput object, and add it to the bank.
27  *
28  * @param bank
29  * The bank to add this element to.
30  * @param type
31  * What address type to change (address, channel or cable number).
32  */
34  bank.add(this);
35  }
36 
37  /**
38  * @brief Create a new BankableMIDIInput object, and add it to the bank.
39  *
40  * @param config
41  * The bank and address type to change.
42  *
43  * @see BankableMIDIInput::BankableMIDIInput(Bank<N> &, BankType)
44  */
46  : BankableMIDIInput<N>(config.bank, config.type) {}
47 
48  public:
49  /**
50  * @brief Destructor: remove element from the bank.
51  */
52  virtual ~BankableMIDIInput() { bank.remove(this); }
53 
54  /**
55  * @brief Get the current bank setting.
56  *
57  * @see Bank<N>::getSelection()
58  */
59  setting_t getSelection() const { return bank.getSelection(); }
60 
61  /**
62  * @brief Calculate the bank setting of a given MIDI address, relative to
63  * a base address.
64  *
65  * @param target
66  * The MIDI address to calculate the bank setting of.
67  * @param base
68  * The base address to compare it to (the address of bank setting
69  * 0).
70  */
71  uint8_t getBankIndex(const MIDICNChannelAddress &target,
72  const MIDICNChannelAddress &base) const {
73  switch (type) {
74  case CHANGE_ADDRESS:
75  return (target.getAddress() - base.getAddress()) /
76  bank.getTracksPerBank();
77  case CHANGE_CHANNEL:
78  return (target.getRawChannel() - base.getRawChannel()) /
79  bank.getTracksPerBank();
80  case CHANGE_CABLENB:
81  return (target.getCableNumber() - base.getCableNumber()) /
82  bank.getTracksPerBank();
83  default: return 0;
84  }
85  }
86 
87  protected:
88  /**
89  * @brief Check if the given address is part of the bank relative to the
90  * base address.
91  *
92  * Consider the following example:
93  * A Bank with 4 tracks per bank (T), 2 bank settings (N),
94  * and a base address of 3.
95  *
96  * ```
97  * 0 1 2 3 4 5 6 7 8 9 10 11 12 ...
98  * F F F T F F F T F F F F F ...
99  * ```
100  *
101  * Addresses before the base adddress are not matched (0, 1, 2).
102  * Addresses after N * T are not matched (8, 9, 10, 11, 12).
103  * Addresses with a distance to the base address that is not a multiple of N
104  * are not matched (4, 5, 6).
105  *
106  * @param toMatch
107  * The address to check.
108  * @param base
109  * The base address (the address of bank setting 0).
110  *
111  * @note Equivalent to `matchBankableInRange(toMatch, base, 1)`.
112  */
113  bool matchBankable(uint8_t toMatch, uint8_t base) const {
114  uint8_t diff = toMatch - base;
115  return toMatch >= base && diff < N * bank.getTracksPerBank() &&
116  diff % bank.getTracksPerBank() == 0;
117  }
118 
119  /**
120  * @brief Check if the given address is part of the bank relative to the
121  * base address.
122  *
123  * @todo This is very hard to explain without a specific example ...
124  *
125  * @param toMatch
126  * The address to check.
127  * @param base
128  * The base address (the address of bank setting 0).
129  * @param length
130  * The length of the range.
131  */
132  bool matchBankableInRange(uint8_t toMatch, uint8_t base,
133  uint8_t length) const {
134  uint8_t diff = toMatch - base;
135  return toMatch >= base && diff < N * bank.getTracksPerBank() &&
136  diff % bank.getTracksPerBank() < length;
137  }
138 
139  /**
140  * @brief If matchBankableAddressInRange returned true, get the index of
141  * the message in the range.
142  */
144  MIDICNChannelAddress base) const {
145  uint8_t diff = target.getAddress() - base.getAddress();
146  if (type == CHANGE_ADDRESS)
147  diff %= bank.getTracksPerBank();
148  return diff;
149  }
150 
151  /**
152  * @brief Check whether a given address is within a range of given length
153  * starting from the given base address.
154  *
155  * @param toMatch
156  * The address to check
157  * @param base
158  * The base address, start of the range.
159  * @param length
160  * The length of the range.
161  */
162  static bool inRange(uint8_t toMatch, uint8_t base, uint8_t length) {
163  return (base <= toMatch) && (toMatch - base < length);
164  }
165 
166  /**
167  * @brief Check whether a given address is part of the bank relative to
168  * the base address.
169  *
170  * @param toMatch
171  * The address to check.
172  * @param base
173  * The base address (the address of bank setting 0).
174  */
175  bool matchBankable(const MIDICNChannelAddress &toMatch,
176  const MIDICNChannelAddress &base) const {
177  if (!toMatch.isValid() || !base.isValid())
178  return false;
179  switch (type) {
180  case CHANGE_ADDRESS: {
181  return toMatch.getChannel() == base.getChannel() &&
182  toMatch.getCableNumber() == base.getCableNumber() &&
183  matchBankable(toMatch.getAddress(), base.getAddress());
184  }
185  case CHANGE_CHANNEL: {
186  return toMatch.getAddress() == base.getAddress() &&
187  toMatch.getCableNumber() == base.getCableNumber() &&
188  matchBankable(toMatch.getRawChannel(),
189  base.getRawChannel());
190  }
191  case CHANGE_CABLENB: {
192  return toMatch.getAddress() == base.getAddress() &&
193  toMatch.getChannel() == base.getChannel() &&
194  matchBankable(toMatch.getCableNumber(),
195  base.getCableNumber());
196  }
197  default: return false;
198  }
199  }
200 
201  /**
202  * @brief Check whether a given address is part of the bank relative to
203  * the base address and within a range with a given length.
204  *
205  * @param toMatch
206  * The address to check.
207  * @param base
208  * The base address (the address of bank setting 0).
209  * @param length
210  * The length of the range.
211  */
213  const MIDICNChannelAddress &base,
214  uint8_t length) const {
215  if (!toMatch.isValid() || !base.isValid())
216  return false;
217  switch (type) {
218  case CHANGE_ADDRESS: {
219  return toMatch.getChannel() == base.getChannel() &&
220  toMatch.getCableNumber() == base.getCableNumber() &&
222  base.getAddress(), length);
223  }
224  case CHANGE_CHANNEL: {
225  return inRange(toMatch.getAddress(), base.getAddress(),
226  length) &&
227  toMatch.getCableNumber() == base.getCableNumber() &&
228  matchBankable(toMatch.getChannel(), base.getChannel());
229  }
230  case CHANGE_CABLENB: {
231  return inRange(toMatch.getAddress(), base.getAddress(),
232  length) &&
233  toMatch.getChannel() == base.getChannel() &&
234  matchBankable(toMatch.getCableNumber(),
235  base.getCableNumber());
236  }
237  default: return false;
238  }
239  }
240 
241  private:
243  const BankType type;
244 
245  /**
246  * @brief A function to be executed each time the bank setting changes.
247  *
248  * Think of an LED that indicates whether a track is muted or not. If this
249  * LED is bankable, let's say with 4 tracks per bank, 2 banks, and a base
250  * address of 3, then this LED object keeps the state of tracks 3 and 7.
251  * When the bank setting is 0, the LED displays the state of track 3, when
252  * the bank setting is 1, the LED displays the state of track 7.
253  * To know when to update the LED, this callback is used.
254  */
255  virtual void onBankSettingChange() {}
256 };
257 
BankableMIDIInput::matchBankableInRange
bool matchBankableInRange(uint8_t toMatch, uint8_t base, uint8_t length) const
Check if the given address is part of the bank relative to the base address.
Definition: BankableMIDIInput.hpp:132
Bank
A class that groups Bankable BankableMIDIOutputs and BankableMIDIInputs, and allows the user to chang...
Definition: Bank.hpp:77
MIDICNChannelAddress::getCableNumber
constexpr uint8_t getCableNumber() const
Get the cable number [0, 15].
Definition: MIDICNChannelAddress.hpp:151
BankConfig.hpp
MIDICNChannelAddress::isValid
constexpr bool isValid() const
Check if the MIDI address is valid.
Definition: MIDICNChannelAddress.hpp:154
BankableMIDIInput::inRange
static bool inRange(uint8_t toMatch, uint8_t base, uint8_t length)
Check whether a given address is within a range of given length starting from the given base address.
Definition: BankableMIDIInput.hpp:162
BankableMIDIInput::type
const BankType type
Definition: BankableMIDIInput.hpp:243
BankableMIDIInput::matchBankableAddressInRange
bool matchBankableAddressInRange(const MIDICNChannelAddress &toMatch, const MIDICNChannelAddress &base, uint8_t length) const
Check whether a given address is part of the bank relative to the base address and within a range wit...
Definition: BankableMIDIInput.hpp:212
MIDICNChannelAddress.hpp
BEGIN_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:9
BankableMIDIInput::getBankIndex
uint8_t getBankIndex(const MIDICNChannelAddress &target, const MIDICNChannelAddress &base) const
Calculate the bank setting of a given MIDI address, relative to a base address.
Definition: BankableMIDIInput.hpp:71
BankableMIDIInput::onBankSettingChange
virtual void onBankSettingChange()
A function to be executed each time the bank setting changes.
Definition: BankableMIDIInput.hpp:255
END_CS_NAMESPACE
#define END_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:10
LinkedList.hpp
MIDICNChannelAddress
A type-safe utility class for saving a MIDI address consisting of a 7-bit address,...
Definition: MIDICNChannelAddress.hpp:82
BankableMIDIInput::matchBankable
bool matchBankable(uint8_t toMatch, uint8_t base) const
Check if the given address is part of the bank relative to the base address.
Definition: BankableMIDIInput.hpp:113
CHANGE_CHANNEL
Change the offset of the channel number of the element.
Definition: BankConfig.hpp:21
BankableMIDIInput::~BankableMIDIInput
virtual ~BankableMIDIInput()
Destructor: remove element from the bank.
Definition: BankableMIDIInput.hpp:52
MIDICNChannelAddress::getAddress
constexpr uint8_t getAddress() const
Get the address [0, 127].
Definition: MIDICNChannelAddress.hpp:141
CHANGE_CABLENB
Change the offset of the cable number of the element (experimental).
Definition: BankConfig.hpp:27
BankType
BankType
An enumeration of the different bank types.
Definition: BankConfig.hpp:12
BankableMIDIInput::BankableMIDIInput
BankableMIDIInput(const BankConfig< N > &config)
Create a new BankableMIDIInput object, and add it to the bank.
Definition: BankableMIDIInput.hpp:45
BankableMIDIInput::getSelection
setting_t getSelection() const
Get the current bank setting.
Definition: BankableMIDIInput.hpp:59
setting_t
uint8_t setting_t
The type used for Selectors.
Definition: Def.hpp:50
DoublyLinkable
A class that can be inherited from to allow inserting into a DoublyLinkedList.
Definition: LinkedList.hpp:302
Bank.hpp
Debug.hpp
BankableMIDIInput
A base class for all MIDIInputElements that can be banked.
Definition: Bank.hpp:13
MIDICNChannelAddress::getRawChannel
constexpr uint8_t getRawChannel() const
Get the channel [0, 15].
Definition: MIDICNChannelAddress.hpp:148
BankConfig
A struct for selecting the bank of BankableMIDIInputs and the bank type.
Definition: BankConfig.hpp:39
CHANGE_ADDRESS
Change the offset of the address (i.e.
Definition: BankConfig.hpp:17
BankableMIDIInput::matchBankable
bool matchBankable(const MIDICNChannelAddress &toMatch, const MIDICNChannelAddress &base) const
Check whether a given address is part of the bank relative to the base address.
Definition: BankableMIDIInput.hpp:175
MIDICNChannelAddress::getChannel
constexpr Channel getChannel() const
Get the channel [CHANNEL_1, CHANNEL_16].
Definition: MIDICNChannelAddress.hpp:144
BankableMIDIInput::bank
Bank< N > & bank
Definition: BankableMIDIInput.hpp:242
BankableMIDIInput::getRangeIndex
uint8_t getRangeIndex(MIDICNChannelAddress target, MIDICNChannelAddress base) const
If matchBankableAddressInRange returned true, get the index of the message in the range.
Definition: BankableMIDIInput.hpp:143
BankableMIDIInput::BankableMIDIInput
BankableMIDIInput(Bank< N > &bank, BankType type)
Create a new BankableMIDIInput object, and add it to the bank.
Definition: BankableMIDIInput.hpp:33