LCOV - code coverage report
Current view: top level - src/MIDI_Inputs/MCU - VU.hpp (source / functions) Hit Total Coverage
Test: 90a1b9beff85a60dc6ebcea034a947a845e56960 Lines: 91 93 97.8 %
Date: 2019-11-30 15:53:32 Functions: 76 116 65.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <Banks/BankableMIDIInput.hpp>
       4             : #include <AH/Hardware/ExtendedInputOutput/ExtendedInputOutput.hpp>
       5             : #include <AH/Math/MinMaxFix.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 MIDICNChannel &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 MIDICNChannelAddress &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             :     }
     123             : 
     124             :     /// The address of the VU meter is the high nibble of the first (and only)
     125             :     /// data byte.
     126             :     MIDICNChannelAddress
     127          19 :     getTarget(const ChannelMessageMatcher &midimsg) const override {
     128          19 :         return {
     129          19 :             int8_t(midimsg.data1 >> 4),
     130          19 :             Channel(midimsg.channel),
     131          19 :             midimsg.CN,
     132             :         };
     133             :     }
     134             : 
     135           1 :     void decay() {
     136           2 :         for (uint8_t i = 0; i < NumValues; ++i)
     137           2 :             if (getValue(i) > 0)
     138           1 :                 values[i]--;
     139           1 :     }
     140             : 
     141             :     /// Get the active bank selection
     142          14 :     virtual uint8_t getSelection() const { return 0; }
     143             : 
     144             :     /// Get the bank index from a MIDI address
     145          10 :     virtual setting_t getBankIndex(const MIDICNChannelAddress &target) const {
     146          10 :         (void)target;
     147          10 :         return 0;
     148             :     }
     149             : 
     150             :     /// Set the VU meter value.
     151          12 :     void setValue(uint8_t index, uint8_t newValue) {
     152          12 :         prevDecayTime = millis();
     153          12 :         values[index] = newValue | (values[index] & 0xF0);
     154          12 :     }
     155             : 
     156             :     /// Set the overload status.
     157           4 :     void setOverload(uint8_t index) { values[index] |= 0xF0; }
     158             :     /// Clear the overload status.
     159           3 :     void clearOverload(uint8_t index) { values[index] &= 0x0F; }
     160             :     /// Get the VU meter value from the raw value.
     161          26 :     uint8_t getValue(uint8_t index) const { return values[index] & 0x0F; }
     162             :     /// Get the overload status value from the raw value.
     163           9 :     bool getOverload(uint8_t index) const { return values[index] & 0xF0; }
     164             : 
     165             :   private:
     166          19 :     Array<uint8_t, NumValues> values = {{}};
     167             :     unsigned int decayTime;
     168          12 :     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           7 : 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           7 :     GenericVU(uint8_t track, const MIDICNChannel &channelCN,
     206             :               unsigned int decayTime, const Callback &callback)
     207           7 :         : VU_Base<1, Callback>{
     208           7 :               track,
     209           7 :               channelCN,
     210           7 :               decayTime,
     211           7 :               callback,
     212          14 :           } {}
     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           7 : 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           7 :     VU(uint8_t track, const MIDICNChannel &channelCN,
     242             :        unsigned int decayTime = VUDecay::Default)
     243           7 :         : GenericVU<>{
     244           7 :               track,
     245           7 :               channelCN,
     246           7 :               decayTime,
     247             :               {},
     248          14 :           } {}
     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           5 : 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           5 :     GenericVU(const BankConfig<NumBanks> &config, uint8_t track,
     314             :               const MIDICNChannel &channelCN, unsigned int decayTime,
     315             :               const Callback &callback)
     316           5 :         : VU_Base<NumBanks, Callback>{
     317           5 :             track, 
     318           5 :             channelCN, 
     319           5 :             decayTime, 
     320           5 :             callback,
     321             :         },
     322          10 :         BankableMIDIInput<NumBanks>{config} {}
     323             : 
     324             :   private:
     325          20 :     setting_t getSelection() const override {
     326          20 :         return BankableMIDIInput<NumBanks>::getSelection();
     327             :     };
     328             : 
     329           9 :     uint8_t getBankIndex(const MIDICNChannelAddress &target) const override {
     330           9 :         return BankableMIDIInput<NumBanks>::getBankIndex(target, this->address);
     331             :     }
     332             : 
     333             :     /// Check if the address of the incoming MIDI message is in one of the banks
     334             :     /// of this element.
     335           9 :     bool match(const MIDICNChannelAddress &target) const override {
     336          18 :         return BankableMIDIInput<NumBanks>::matchBankable(target,
     337           9 :                                                           this->address);
     338             :     }
     339             : 
     340          12 :     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           4 : 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           4 :     VU(const BankConfig<NumBanks> &config, uint8_t track,
     375             :        const MIDICNChannel &channelCN,
     376             :        unsigned int decayTime = VUDecay::Default)
     377           4 :         : GenericVU<NumBanks>{
     378           4 :               config, track, channelCN, decayTime, {},
     379           8 :           } {}
     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             : 
     408             : END_CS_NAMESPACE

Generated by: LCOV version 1.14-5-g4ff2ed6