LCOV - code coverage report
Current view: top level - src/MIDI_Inputs/MCU - VU.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 93 95 97.9 %
Date: 2020-09-08 17:44:46 Functions: 76 116 65.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AH/Hardware/ExtendedInputOutput/ExtendedInputOutput.hpp>
       4             : #include <AH/Math/MinMaxFix.hpp>
       5             : #include <Banks/BankableMIDIInput.hpp>
       6             : #include <MIDI_Inputs/MIDIInputElementChannelPressure.hpp>
       7             : #include <string.h>
       8             : 
       9             : BEGIN_CS_NAMESPACE
      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          12 :     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           1 :     virtual float getFloatValue() { return (float)getValue() / getMax(); }
      29             :     /** Get the maximum value that this VU meter can return. */
      30           1 :     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.
      47             : struct VUEmptyCallback {
      48             :     template <class T>
      49           0 :     void begin(const T &) {}
      50             :     template <class T>
      51          29 :     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>
      78          12 : class VU_Base : public MIDIInputElementChannelPressure, public IVU {
      79             :   protected:
      80          12 :     VU_Base(uint8_t track, const MIDIChannelCN &channelCN,
      81             :             unsigned int decayTime, const Callback &callback)
      82          24 :         : MIDIInputElementChannelPressure{{track - 1, channelCN}}, IVU(12),
      83          48 :           decayTime(decayTime), callback(callback) {}
      84             : 
      85             :   public:
      86             :     /// Initialize
      87           1 :     void begin() override { callback.begin(*this); }
      88             :     /// Reset all values to zero
      89           1 :     void reset() override {
      90           1 :         values = {{}};
      91           1 :         callback.update(*this);
      92           1 :     }
      93             : 
      94             :     /// Return the VU meter value as an integer in [0, 12].
      95          25 :     uint8_t getValue() override { return getValue(getSelection()); }
      96             :     /// Return the overload status.
      97           9 :     bool getOverload() override { return getOverload(getSelection()); }
      98             : 
      99             :     /// Update is called periodically, it decays the meter if the time is right.
     100           1 :     void update() override {
     101           1 :         if (decayTime && (millis() - prevDecayTime >= decayTime)) {
     102           1 :             prevDecayTime += decayTime;
     103           1 :             decay();
     104           1 :             callback.update(*this);
     105           1 :         }
     106           1 :     }
     107             : 
     108             :   private:
     109             :     /// Called when an incoming MIDI message matches this element
     110          19 :     bool updateImpl(const ChannelMessageMatcher &midimsg,
     111             :                     const MIDIAddress &target) override {
     112          19 :         uint8_t data = midimsg.data1 & 0x0F;
     113          19 :         uint8_t index = getBankIndex(target);
     114          19 :         switch (data) {
     115           3 :             case 0xF: clearOverload(index); break;
     116           4 :             case 0xE: setOverload(index); break;
     117           0 :             case 0xD: break; // no meaning
     118          12 :             default: setValue(index, data); break;
     119             :         }
     120          19 :         callback.update(*this);
     121          19 :         return true;
     122          19 :     }
     123             : 
     124             :     /// The address of the VU meter is the high nibble of the first (and only)
     125             :     /// data byte.
     126          19 :     MIDIAddress getTarget(const ChannelMessageMatcher &midimsg) const override {
     127          19 :         return {
     128          19 :             int8_t(midimsg.data1 >> 4),
     129          19 :             Channel(midimsg.channel),
     130          19 :             Cable(midimsg.CN),
     131             :         };
     132             :     }
     133             : 
     134           1 :     void decay() {
     135           2 :         for (uint8_t i = 0; i < NumValues; ++i)
     136           2 :             if (getValue(i) > 0)
     137           1 :                 values[i]--;
     138           1 :     }
     139             : 
     140             :     /// Get the active bank selection
     141          14 :     virtual uint8_t getSelection() const { return 0; }
     142             : 
     143             :     /// Get the bank index from a MIDI address
     144          10 :     virtual setting_t getBankIndex(const MIDIAddress &target) const {
     145          10 :         (void)target;
     146          10 :         return 0;
     147             :     }
     148             : 
     149             :     /// Set the VU meter value.
     150          12 :     void setValue(uint8_t index, uint8_t newValue) {
     151          12 :         prevDecayTime = millis();
     152          12 :         values[index] = newValue | (values[index] & 0xF0);
     153          12 :     }
     154             : 
     155             :     /// Set the overload status.
     156           4 :     void setOverload(uint8_t index) { values[index] |= 0xF0; }
     157             :     /// Clear the overload status.
     158           3 :     void clearOverload(uint8_t index) { values[index] &= 0x0F; }
     159             :     /// Get the VU meter value from the raw value.
     160          26 :     uint8_t getValue(uint8_t index) const { return values[index] & 0x0F; }
     161             :     /// Get the overload status value from the raw value.
     162           9 :     bool getOverload(uint8_t index) const { return values[index] & 0xF0; }
     163             : 
     164             :   private:
     165          19 :     Array<uint8_t, NumValues> values = {{}};
     166             :     unsigned int decayTime;
     167          12 :     unsigned long prevDecayTime = 0;
     168             : 
     169             :   public:
     170             :     Callback callback;
     171             : };
     172             : 
     173             : // -------------------------------------------------------------------------- //
     174             : 
     175             : /** 
     176             :  * @brief   A class for MIDI input elements that represent Mackie Control
     177             :  *          Universal VU meters. This version is generic to allow for custom 
     178             :  *          callbacks.  
     179             :  *          This version cannot be banked.
     180             :  */
     181             : template <class Callback = VUEmptyCallback>
     182           7 : class GenericVU : public VU_Base<1, Callback> {
     183             :   public:
     184             :     /** 
     185             :      * @brief   Construct a new GenericVU object.
     186             :      * 
     187             :      * @param   track
     188             :      *          The track of the VU meter. [1, 8]
     189             :      * @param   channelCN
     190             :      *          The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
     191             :      *          Number [CABLE_1, CABLE_16].
     192             :      * @param   decayTime
     193             :      *          The time in milliseconds it takes for the value to decay one
     194             :      *          step.  
     195             :      *          The MCU protocol uses 300 ms per division, and two steps
     196             :      *          per division, so the default is 150 ms per step.  
     197             :      *          Some software doesn't work if the VU meter decays automatically, 
     198             :      *          in that case, you can set the decay time to zero to disable 
     199             :      *          the decay.
     200             :      * @param   callback
     201             :      *          The callback object that is update when the value changes.
     202             :      *          Used for displaying the value on a range of LEDs etc.
     203             :      */
     204           7 :     GenericVU(uint8_t track, const MIDIChannelCN &channelCN,
     205             :               unsigned int decayTime, const Callback &callback)
     206           7 :         : VU_Base<1, Callback>{
     207           7 :               track,
     208           7 :               channelCN,
     209           7 :               decayTime,
     210           7 :               callback,
     211          14 :           } {}
     212             : };
     213             : 
     214             : /** 
     215             :  * @brief   A class for MIDI input elements that represent Mackie Control
     216             :  *          Universal VU meters.  
     217             :  *          This version cannot be banked.
     218             :  * 
     219             :  * @ingroup MIDIInputElements
     220             :  */
     221           7 : class VU : public GenericVU<> {
     222             :   public:
     223             :     /** 
     224             :      * @brief   Construct a new VU object.
     225             :      * 
     226             :      * @param   track
     227             :      *          The track of the VU meter. [1, 8]
     228             :      * @param   channelCN
     229             :      *          The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
     230             :      *          Number [CABLE_1, CABLE_16].
     231             :      * @param   decayTime
     232             :      *          The time in milliseconds it takes for the value to decay one
     233             :      *          step.  
     234             :      *          The MCU protocol uses 300 ms per division, and two steps
     235             :      *          per division, so the default is 150 ms per step.  
     236             :      *          Some software doesn't work if the VU meter decays automatically, 
     237             :      *          in that case, you can set the decay time to zero to disable 
     238             :      *          the decay.
     239             :      */
     240           7 :     VU(uint8_t track, const MIDIChannelCN &channelCN,
     241             :        unsigned int decayTime = VUDecay::Default)
     242           7 :         : GenericVU<>{
     243           7 :               track,
     244           7 :               channelCN,
     245           7 :               decayTime,
     246           7 :               {},
     247          14 :           } {}
     248             : 
     249             :     /** 
     250             :      * @brief   Construct a new VU object.
     251             :      * 
     252             :      * @param   track
     253             :      *          The track of the VU meter. [1, 8]
     254             :      * @param   decayTime
     255             :      *          The time in milliseconds it takes for the value to decay one
     256             :      *          step.  
     257             :      *          The MCU protocol uses 300 ms per division, and two steps
     258             :      *          per division, so the default is 150 ms per step.  
     259             :      *          Some software doesn't work if the VU meter decays automatically,
     260             :      *          in that case, you can set the decay time to zero to disable 
     261             :      *          the decay.
     262             :      */
     263             :     VU(uint8_t track, unsigned int decayTime = VUDecay::Default)
     264             :         : GenericVU<>{
     265             :               track,
     266             :               CHANNEL_1,
     267             :               decayTime,
     268             :               {},
     269             :           } {}
     270             : };
     271             : 
     272             : // -------------------------------------------------------------------------- //
     273             : 
     274             : namespace Bankable {
     275             : 
     276             : /** 
     277             :  * @brief   A class for MIDI input elements that represent Mackie Control
     278             :  *          Universal VU meters.  This version is generic to allow for custom 
     279             :  *          callbacks.  
     280             :  *          This version can be banked.
     281             :  * 
     282             :  * @tparam  NumBanks
     283             :  *          The number of banks.
     284             :  */
     285             : template <uint8_t NumBanks, class Callback = VUEmptyCallback>
     286           5 : class GenericVU : public VU_Base<NumBanks, Callback>,
     287             :                   public BankableMIDIInput<NumBanks> {
     288             :   public:
     289             :     /** 
     290             :      * @brief   Construct a new Bankable VU object.
     291             :      * 
     292             :      * @param   config
     293             :      *          The bank configuration to use.
     294             :      * @param   track
     295             :      *          The track of the VU meter. [1, 8]
     296             :      * @param   channelCN
     297             :      *          The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
     298             :      *          Number [CABLE_1, CABLE_16].
     299             :      * @param   decayTime
     300             :      *          The time in milliseconds it takes for the value to decay one
     301             :      *          step.  
     302             :      *          The MCU protocol uses 300 ms per division, and two steps
     303             :      *          per division, so the default is 150 ms per step.  
     304             :      *          Some software doesn't work if the VU meter decays automatically, 
     305             :      *          in that case, you can set the decay time to zero to disable 
     306             :      *          the decay.
     307             :      * @param   callback
     308             :      *          The callback object that is update when the value or bank 
     309             :      *          changes.  
     310             :      *          Used for displaying the value on a range of LEDs etc.
     311             :      */
     312           5 :     GenericVU(BankConfig<NumBanks> config, uint8_t track,
     313             :               const MIDIChannelCN &channelCN, unsigned int decayTime,
     314             :               const Callback &callback)
     315           5 :         : VU_Base<NumBanks, Callback>{
     316           5 :             track, 
     317           5 :             channelCN, 
     318           5 :             decayTime, 
     319           5 :             callback,
     320             :         },
     321          10 :         BankableMIDIInput<NumBanks>{config} {}
     322             : 
     323             :   private:
     324          20 :     setting_t getSelection() const override {
     325          20 :         return BankableMIDIInput<NumBanks>::getSelection();
     326             :     };
     327             : 
     328           9 :     uint8_t getBankIndex(const MIDIAddress &target) const override {
     329           9 :         return BankableMIDIInput<NumBanks>::getBankIndex(target, this->address);
     330             :     }
     331             : 
     332             :     /// Check if the address of the incoming MIDI message is in one of the banks
     333             :     /// of this element.
     334           9 :     bool match(const MIDIAddress &target) const override {
     335          18 :         return BankableMIDIInput<NumBanks>::matchBankable(target,
     336           9 :                                                           this->address);
     337             :     }
     338             : 
     339          12 :     void onBankSettingChange() override { this->callback.update(*this); }
     340             : };
     341             : 
     342             : /**
     343             :  * @brief   A class for MIDI input elements that represent Mackie Control
     344             :  *          Universal VU meters.  
     345             :  * 
     346             :  * @tparam  NumBanks 
     347             :  *          The number of banks.
     348             :  * 
     349             :  * @ingroup BankableMIDIInputElements
     350             :  */
     351             : template <uint8_t NumBanks>
     352           4 : class VU : public GenericVU<NumBanks> {
     353             :   public:
     354             :     /** 
     355             :      * @brief   Construct a new Bankable VU object.
     356             :      * 
     357             :      * @param   config
     358             :      *          The bank configuration to use.
     359             :      * @param   track
     360             :      *          The track of the VU meter. [1, 8]
     361             :      * @param   channelCN
     362             :      *          The MIDI channel [CHANNEL_1, CHANNEL_16] and optional Cable
     363             :      *          Number [CABLE_1, CABLE_16].
     364             :      * @param   decayTime
     365             :      *          The time in milliseconds it takes for the value to decay one
     366             :      *          step.  
     367             :      *          The MCU protocol uses 300 ms per division, and two steps
     368             :      *          per division, so the default is 150 ms per step.  
     369             :      *          Some software doesn't work if the VU meter decays automatically, 
     370             :      *          in that case, you can set the decay time to zero to disable 
     371             :      *          the decay.
     372             :      */
     373           4 :     VU(BankConfig<NumBanks> config, uint8_t track,
     374             :        const MIDIChannelCN &channelCN,
     375             :        unsigned int decayTime = VUDecay::Default)
     376           4 :         : GenericVU<NumBanks>{
     377           4 :               config, track, channelCN, decayTime, {},
     378           8 :           } {}
     379             : 
     380             :     /** 
     381             :      * @brief   Construct a new Bankable VU object.
     382             :      * 
     383             :      * @param   config
     384             :      *          The bank configuration to use.
     385             :      * @param   track
     386             :      *          The track of the VU meter. [1, 8]
     387             :      * @param   decayTime
     388             :      *          The time in milliseconds it takes for the value to decay one
     389             :      *          step.  
     390             :      *          The MCU protocol uses 300 ms per division, and two steps
     391             :      *          per division, so the default is 150 ms per step.  
     392             :      *          Some software doesn't work if the VU meter decays automatically, 
     393             :      *          in that case, you can set the decay time to zero to disable 
     394             :      *          the decay.
     395             :      */
     396             :     VU(BankConfig<NumBanks> config, uint8_t track,
     397             :        unsigned int decayTime = VUDecay::Default)
     398             :         : GenericVU<NumBanks>{
     399             :               config, track, CHANNEL_1, decayTime, {},
     400             :           } {}
     401             : };
     402             : 
     403             : } // namespace Bankable
     404             : 
     405             : } // namespace MCU
     406             : 
     407             : END_CS_NAMESPACE

Generated by: LCOV version 1.14-6-g40580cd