Control Surface  1.1.0
MIDI Control Surface library for Arduino
VU.hpp
Go to the documentation of this file.
1 #pragma once
2 
5 #include <AH/Math/MinMaxFix.hpp>
7 #include <string.h>
8 
10 
11 /**
12  * @brief An abstract interface for VU meters. To allow for both floating
13  * point values and integers, all values are integers under the hood.
14  *
15  * Using floats instead integers would be a strange choice as LED bar VU meters
16  * have discrete levels.
17  * Continuous "analog" VU meters can use or override the `getFloatValue()`
18  * method.
19  */
20 class IVU {
21  public:
22  IVU(uint8_t max) : max(max) {}
23  /** Return the VU meter value as an integer. */
24  virtual uint8_t getValue() = 0;
25  /** Return the overload status. */
26  virtual bool getOverload() = 0;
27  /** Get the VU meter value as a floating point number. */
28  virtual float getFloatValue() { return (float)getValue() / getMax(); }
29  /** Get the maximum value that this VU meter can return. */
30  uint8_t getMax() const { return max; }
31 
32  protected:
33  const uint8_t max;
34 };
35 
36 namespace MCU {
37 
38 /// VU Decay time constants
39 namespace VUDecay {
40 /// Don't decay automatically, hold the latest value until a new one is received.
41 constexpr unsigned int Hold = 0;
42 /// Decay one segment/block every 150 ms if no new values are received.
43 constexpr unsigned int Default = 150;
44 } // namespace VUDecay
45 
46 /// Empty callback for VU meters that does nothing.
48  template <class T>
49  void begin(const T &) {}
50  template <class T>
51  void update(const T &) {}
52 };
53 
54 /**
55  * @brief A MIDI input element that represents a Mackie Control Universal VU
56  * meter.
57  * This is a base class to both the Bankable and non-Bankable version.
58  *
59  * In the Mackie Control Universal protocol, VU meters are updated using Channel
60  * Pressure events.
61  * Each device (cable number) has eight VU meters for the eight tracks. Only
62  * MIDI channel 1 is used in the original protocol.
63  *
64  * The format of the MIDI message is as follows:
65  * `| 1101 cccc | 0hhh llll |`
66  *
67  * - `1101` (or `0xD`) is the status for Channel Pressure events
68  * - `cccc` is the MIDI channel
69  * - `hhh` is the track index [0-7]
70  * - `llll` is the level of the VU meter
71  *
72  * If the level is `0x0`, the meter is at 0%, if it's `0xC`, the meter is at
73  * 100%.
74  * `0xD` is an invalid value.
75  * `0xE` sets the overload indicator, and `0xF` clears the overload indicator.
76  */
77 template <uint8_t NumValues, class Callback>
79  protected:
80  VU_Base(uint8_t track, const MIDICNChannel &channelCN,
81  unsigned int decayTime, const Callback &callback)
82  : MIDIInputElementChannelPressure{{track - 1, channelCN}}, IVU(12),
84 
85  public:
86  /// Initialize
87  void begin() override { callback.begin(*this); }
88  /// Reset all values to zero
89  void reset() override {
90  values = {{}};
91  callback.update(*this);
92  }
93 
94  /// Return the VU meter value as an integer in [0, 12].
95  uint8_t getValue() override { return getValue(getSelection()); }
96  /// Return the overload status.
97  bool getOverload() override { return getOverload(getSelection()); }
98 
99  /// Update is called periodically, it decays the meter if the time is right.
100  void update() override {
101  if (decayTime && (millis() - prevDecayTime >= decayTime)) {
103  decay();
104  callback.update(*this);
105  }
106  }
107 
108  private:
109  /// Called when an incoming MIDI message matches this element
110  bool updateImpl(const ChannelMessageMatcher &midimsg,
111  const MIDICNChannelAddress &target) override {
112  uint8_t data = midimsg.data1 & 0x0F;
113  uint8_t index = getBankIndex(target);
114  switch (data) {
115  case 0xF: clearOverload(index); break;
116  case 0xE: setOverload(index); break;
117  case 0xD: break; // no meaning
118  default: setValue(index, data); break;
119  }
120  callback.update(*this);
121  return true;
122  }
123 
124  /// The address of the VU meter is the high nibble of the first (and only)
125  /// data byte.
127  getTarget(const ChannelMessageMatcher &midimsg) const override {
128  return {
129  int8_t(midimsg.data1 >> 4),
130  Channel(midimsg.channel),
131  midimsg.CN,
132  };
133  }
134 
135  void decay() {
136  for (uint8_t i = 0; i < NumValues; ++i)
137  if (getValue(i) > 0)
138  values[i]--;
139  }
140 
141  /// Get the active bank selection
142  virtual uint8_t getSelection() const { return 0; }
143 
144  /// Get the bank index from a MIDI address
145  virtual setting_t getBankIndex(const MIDICNChannelAddress &target) const {
146  (void)target;
147  return 0;
148  }
149 
150  /// Set the VU meter value.
151  void setValue(uint8_t index, uint8_t newValue) {
152  prevDecayTime = millis();
153  values[index] = newValue | (values[index] & 0xF0);
154  }
155 
156  /// Set the overload status.
157  void setOverload(uint8_t index) { values[index] |= 0xF0; }
158  /// Clear the overload status.
159  void clearOverload(uint8_t index) { values[index] &= 0x0F; }
160  /// Get the VU meter value from the raw value.
161  uint8_t getValue(uint8_t index) const { return values[index] & 0x0F; }
162  /// Get the overload status value from the raw value.
163  bool getOverload(uint8_t index) const { return values[index] & 0xF0; }
164 
165  private:
167  unsigned int decayTime;
168  unsigned long prevDecayTime = 0;
169 
170  public:
171  Callback callback;
172 };
173 
174 // -------------------------------------------------------------------------- //
175 
176 /**
177  * @brief A class for MIDI input elements that represent Mackie Control
178  * Universal VU meters. This version is generic to allow for custom
179  * callbacks.
180  * This version cannot be banked.
181  */
182 template <class Callback = VUEmptyCallback>
183 class GenericVU : public VU_Base<1, Callback> {
184  public:
185  /**
186  * @brief Construct a new GenericVU object.
187  *
188  * @param track
189  * The track of the VU meter. [1, 8]
190  * @param channelCN
191  * The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
192  * Number [0, 15].
193  * @param decayTime
194  * The time in milliseconds it takes for the value to decay one
195  * step.
196  * The MCU protocol uses 300 ms per division, and two steps
197  * per division, so the default is 150 ms per step.
198  * Some software doesn't work if the VU meter decays automatically,
199  * in that case, you can set the decay time to zero to disable
200  * the decay.
201  * @param callback
202  * The callback object that is update when the value changes.
203  * Used for displaying the value on a range of LEDs etc.
204  */
205  GenericVU(uint8_t track, const MIDICNChannel &channelCN,
206  unsigned int decayTime, const Callback &callback)
207  : VU_Base<1, Callback>{
208  track,
209  channelCN,
210  decayTime,
211  callback,
212  } {}
213 };
214 
215 /**
216  * @brief A class for MIDI input elements that represent Mackie Control
217  * Universal VU meters.
218  * This version cannot be banked.
219  *
220  * @ingroup MIDIInputElements
221  */
222 class VU : public GenericVU<> {
223  public:
224  /**
225  * @brief Construct a new VU object.
226  *
227  * @param track
228  * The track of the VU meter. [1, 8]
229  * @param channelCN
230  * The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
231  * Number [0, 15].
232  * @param decayTime
233  * The time in milliseconds it takes for the value to decay one
234  * step.
235  * The MCU protocol uses 300 ms per division, and two steps
236  * per division, so the default is 150 ms per step.
237  * Some software doesn't work if the VU meter decays automatically,
238  * in that case, you can set the decay time to zero to disable
239  * the decay.
240  */
241  VU(uint8_t track, const MIDICNChannel &channelCN,
242  unsigned int decayTime = VUDecay::Default)
243  : GenericVU<>{
244  track,
245  channelCN,
246  decayTime,
247  {},
248  } {}
249 
250  /**
251  * @brief Construct a new VU object.
252  *
253  * @param track
254  * The track of the VU meter. [1, 8]
255  * @param decayTime
256  * The time in milliseconds it takes for the value to decay one
257  * step.
258  * The MCU protocol uses 300 ms per division, and two steps
259  * per division, so the default is 150 ms per step.
260  * Some software doesn't work if the VU meter decays automatically,
261  * in that case, you can set the decay time to zero to disable
262  * the decay.
263  */
264  VU(uint8_t track, unsigned int decayTime = VUDecay::Default)
265  : GenericVU<>{
266  track,
267  CHANNEL_1,
268  decayTime,
269  {},
270  } {}
271 };
272 
273 // -------------------------------------------------------------------------- //
274 
275 namespace Bankable {
276 
277 /**
278  * @brief A class for MIDI input elements that represent Mackie Control
279  * Universal VU meters. This version is generic to allow for custom
280  * callbacks.
281  * This version can be banked.
282  *
283  * @tparam NumBanks
284  * The number of banks.
285  */
286 template <uint8_t NumBanks, class Callback = VUEmptyCallback>
287 class GenericVU : public VU_Base<NumBanks, Callback>,
288  public BankableMIDIInput<NumBanks> {
289  public:
290  /**
291  * @brief Construct a new Bankable VU object.
292  *
293  * @param config
294  * The bank configuration to use.
295  * @param track
296  * The track of the VU meter. [1, 8]
297  * @param channelCN
298  * The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
299  * Number [0, 15].
300  * @param decayTime
301  * The time in milliseconds it takes for the value to decay one
302  * step.
303  * The MCU protocol uses 300 ms per division, and two steps
304  * per division, so the default is 150 ms per step.
305  * Some software doesn't work if the VU meter decays automatically,
306  * in that case, you can set the decay time to zero to disable
307  * the decay.
308  * @param callback
309  * The callback object that is update when the value or bank
310  * changes.
311  * Used for displaying the value on a range of LEDs etc.
312  */
313  GenericVU(const BankConfig<NumBanks> &config, uint8_t track,
314  const MIDICNChannel &channelCN, unsigned int decayTime,
315  const Callback &callback)
316  : VU_Base<NumBanks, Callback>{
317  track,
318  channelCN,
319  decayTime,
320  callback,
321  },
322  BankableMIDIInput<NumBanks>{config} {}
323 
324  private:
325  setting_t getSelection() const override {
327  };
328 
329  uint8_t getBankIndex(const MIDICNChannelAddress &target) const override {
331  }
332 
333  /// Check if the address of the incoming MIDI message is in one of the banks
334  /// of this element.
335  bool match(const MIDICNChannelAddress &target) const override {
337  this->address);
338  }
339 
340  void onBankSettingChange() override { this->callback.update(*this); }
341 };
342 
343 /**
344  * @brief A class for MIDI input elements that represent Mackie Control
345  * Universal VU meters.
346  *
347  * @tparam NumBanks
348  * The number of banks.
349  *
350  * @ingroup BankableMIDIInputElements
351  */
352 template <uint8_t NumBanks>
353 class VU : public GenericVU<NumBanks> {
354  public:
355  /**
356  * @brief Construct a new Bankable VU object.
357  *
358  * @param config
359  * The bank configuration to use.
360  * @param track
361  * The track of the VU meter. [1, 8]
362  * @param channelCN
363  * The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
364  * Number [0, 15].
365  * @param decayTime
366  * The time in milliseconds it takes for the value to decay one
367  * step.
368  * The MCU protocol uses 300 ms per division, and two steps
369  * per division, so the default is 150 ms per step.
370  * Some software doesn't work if the VU meter decays automatically,
371  * in that case, you can set the decay time to zero to disable
372  * the decay.
373  */
374  VU(const BankConfig<NumBanks> &config, uint8_t track,
375  const MIDICNChannel &channelCN,
376  unsigned int decayTime = VUDecay::Default)
377  : GenericVU<NumBanks>{
378  config, track, channelCN, decayTime, {},
379  } {}
380 
381  /**
382  * @brief Construct a new Bankable VU object.
383  *
384  * @param config
385  * The bank configuration to use.
386  * @param track
387  * The track of the VU meter. [1, 8]
388  * @param decayTime
389  * The time in milliseconds it takes for the value to decay one
390  * step.
391  * The MCU protocol uses 300 ms per division, and two steps
392  * per division, so the default is 150 ms per step.
393  * Some software doesn't work if the VU meter decays automatically,
394  * in that case, you can set the decay time to zero to disable
395  * the decay.
396  */
397  VU(const BankConfig<NumBanks> &config, uint8_t track,
398  unsigned int decayTime = VUDecay::Default)
399  : GenericVU<NumBanks>{
400  config, track, CHANNEL_1, decayTime, {},
401  } {}
402 };
403 
404 } // namespace Bankable
405 
406 } // namespace MCU
407 
MCU::VU_Base::getSelection
virtual uint8_t getSelection() const
Get the active bank selection.
Definition: VU.hpp:142
MCU::VU::VU
VU(uint8_t track, const MIDICNChannel &channelCN, unsigned int decayTime=VUDecay::Default)
Construct a new VU object.
Definition: VU.hpp:241
ChannelMessageMatcher::CN
uint8_t CN
Definition: ChannelMessageMatcher.hpp:22
MCU::VU_Base::callback
Callback callback
Definition: VU.hpp:171
Channel
A type-safe class for MIDI channels.
Definition: Channel.hpp:13
MCU::VU_Base::update
void update() override
Update is called periodically, it decays the meter if the time is right.
Definition: VU.hpp:100
IVU::max
const uint8_t max
Definition: VU.hpp:33
IVU
An abstract interface for VU meters.
Definition: VU.hpp:20
MCU::VU_Base::setOverload
void setOverload(uint8_t index)
Set the overload status.
Definition: VU.hpp:157
IVU::getFloatValue
virtual float getFloatValue()
Get the VU meter value as a floating point number.
Definition: VU.hpp:28
Bankable
A namespace for MIDI elements that can be added to a Bank, to change their address or channel.
Definition: BankAddresses.hpp:7
MCU::VU_Base::begin
void begin() override
Initialize.
Definition: VU.hpp:87
MCU::VU_Base::clearOverload
void clearOverload(uint8_t index)
Clear the overload status.
Definition: VU.hpp:159
MCU::VU_Base::getOverload
bool getOverload(uint8_t index) const
Get the overload status value from the raw value.
Definition: VU.hpp:163
MCU::Bankable::GenericVU
A class for MIDI input elements that represent Mackie Control Universal VU meters.
Definition: VU.hpp:287
MCU::Bankable::GenericVU::onBankSettingChange
void onBankSettingChange() override
A function to be executed each time the bank setting changes.
Definition: VU.hpp:340
MCU::VU_Base::getTarget
MIDICNChannelAddress getTarget(const ChannelMessageMatcher &midimsg) const override
The address of the VU meter is the high nibble of the first (and only) data byte.
Definition: VU.hpp:127
IVU::getOverload
virtual bool getOverload()=0
Return the overload status.
MCU::VUEmptyCallback::update
void update(const T &)
Definition: VU.hpp:51
MCU
Names and note and controller numbers for the Mackie Control Universal (MCU) protocol.
Definition: LCDDisplay.hpp:10
MCU::VUEmptyCallback
Empty callback for VU meters that does nothing.
Definition: VU.hpp:47
IVU::getValue
virtual uint8_t getValue()=0
Return the VU meter value as an integer.
MCU::VU_Base::getBankIndex
virtual setting_t getBankIndex(const MIDICNChannelAddress &target) const
Get the bank index from a MIDI address.
Definition: VU.hpp:145
MCU::VU_Base::setValue
void setValue(uint8_t index, uint8_t newValue)
Set the VU meter value.
Definition: VU.hpp:151
ExtendedInputOutput.hpp
BEGIN_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:9
MCU::VUDecay::Default
constexpr unsigned int Default
Decay one segment/block every 150 ms if no new values are received.
Definition: VU.hpp:43
MCU::Bankable::VU
A class for MIDI input elements that represent Mackie Control Universal VU meters.
Definition: VU.hpp:353
MCU::VU_Base::getValue
uint8_t getValue(uint8_t index) const
Get the VU meter value from the raw value.
Definition: VU.hpp:161
MCU::Bankable::VU::VU
VU(const BankConfig< NumBanks > &config, uint8_t track, const MIDICNChannel &channelCN, unsigned int decayTime=VUDecay::Default)
Construct a new Bankable VU object.
Definition: VU.hpp:374
MCU::VU::VU
VU(uint8_t track, unsigned int decayTime=VUDecay::Default)
Construct a new VU object.
Definition: VU.hpp:264
ChannelMessageMatcher
Struct for easily matching MIDI messages.
Definition: ChannelMessageMatcher.hpp:10
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
MCU::VUDecay::Hold
constexpr unsigned int Hold
Don't decay automatically, hold the latest value until a new one is received.
Definition: VU.hpp:41
END_CS_NAMESPACE
#define END_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:10
MCU::VU
A class for MIDI input elements that represent Mackie Control Universal VU meters.
Definition: VU.hpp:222
MCU::VU_Base::decayTime
unsigned int decayTime
Definition: VU.hpp:167
MIDICNChannelAddress
A type-safe utility class for saving a MIDI address consisting of a 7-bit address,...
Definition: MIDICNChannelAddress.hpp:82
AH::Array
An array wrapper for easy copying, comparing, and iterating.
Definition: Array.hpp:36
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
IVU::IVU
IVU(uint8_t max)
Definition: VU.hpp:22
MIDIInputElementChannelPressure
Definition: MIDIInputElementChannelPressure.hpp:15
MCU::GenericVU
A class for MIDI input elements that represent Mackie Control Universal VU meters.
Definition: VU.hpp:183
MIDICNChannel
A class for saving a MIDI channel and cable number.
Definition: MIDICNChannelAddress.hpp:19
MCU::VU_Base::updateImpl
bool updateImpl(const ChannelMessageMatcher &midimsg, const MIDICNChannelAddress &target) override
Called when an incoming MIDI message matches this element.
Definition: VU.hpp:110
MCU::Bankable::GenericVU::GenericVU
GenericVU(const BankConfig< NumBanks > &config, uint8_t track, const MIDICNChannel &channelCN, unsigned int decayTime, const Callback &callback)
Construct a new Bankable VU object.
Definition: VU.hpp:313
MCU::VU_Base::getValue
uint8_t getValue() override
Return the VU meter value as an integer in [0, 12].
Definition: VU.hpp:95
ChannelMessageMatcher::channel
uint8_t channel
Definition: ChannelMessageMatcher.hpp:19
ChannelMessageMatcher::data1
uint8_t data1
Definition: ChannelMessageMatcher.hpp:20
MCU::VU_Base::reset
void reset() override
Reset all values to zero.
Definition: VU.hpp:89
MCU::GenericVU::GenericVU
GenericVU(uint8_t track, const MIDICNChannel &channelCN, unsigned int decayTime, const Callback &callback)
Construct a new GenericVU object.
Definition: VU.hpp:205
MCU::VUEmptyCallback::begin
void begin(const T &)
Definition: VU.hpp:49
BankableMIDIInput::getSelection
setting_t getSelection() const
Get the current bank setting.
Definition: BankableMIDIInput.hpp:59
MCU::VU_Base::values
Array< uint8_t, NumValues > values
Definition: VU.hpp:166
MCU::Bankable::GenericVU::getSelection
setting_t getSelection() const override
Get the active bank selection.
Definition: VU.hpp:325
setting_t
uint8_t setting_t
The type used for Selectors.
Definition: Def.hpp:50
MCU::VU_Base
A MIDI input element that represents a Mackie Control Universal VU meter.
Definition: VU.hpp:78
MCU::Bankable::GenericVU::match
bool match(const MIDICNChannelAddress &target) const override
Check if the address of the incoming MIDI message is in one of the banks of this element.
Definition: VU.hpp:335
BankableMIDIInput
A base class for all MIDIInputElements that can be banked.
Definition: Bank.hpp:13
MCU::VU_Base::getOverload
bool getOverload() override
Return the overload status.
Definition: VU.hpp:97
BankConfig
A struct for selecting the bank of BankableMIDIInputs and the bank type.
Definition: BankConfig.hpp:39
MCU::Bankable::GenericVU::getBankIndex
uint8_t getBankIndex(const MIDICNChannelAddress &target) const override
Get the bank index from a MIDI address.
Definition: VU.hpp:329
MIDIInputElement::address
const MIDICNChannelAddress address
Definition: MIDIInputElement.hpp:80
MinMaxFix.hpp
MCU::VU_Base::VU_Base
VU_Base(uint8_t track, const MIDICNChannel &channelCN, unsigned int decayTime, const Callback &callback)
Definition: VU.hpp:80
IVU::getMax
uint8_t getMax() const
Get the maximum value that this VU meter can return.
Definition: VU.hpp:30
CHANNEL_1
constexpr Channel CHANNEL_1
Definition: Channel.hpp:111
BankableMIDIInput.hpp
MIDIInputElementChannelPressure.hpp
MCU::Bankable::VU::VU
VU(const BankConfig< NumBanks > &config, uint8_t track, unsigned int decayTime=VUDecay::Default)
Construct a new Bankable VU object.
Definition: VU.hpp:397
MCU::VU_Base::prevDecayTime
unsigned long prevDecayTime
Definition: VU.hpp:168
MCU::VU_Base::decay
void decay()
Definition: VU.hpp:135