LCOV - code coverage report
Current view: top level - src/MIDI_Senders - RelativeCCSender.hpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 23 24 95.8 %
Date: 2024-12-21 21:28:55 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <Control_Surface/Control_Surface_Class.hpp>
       4             : 
       5             : #include <AH/Arduino-Wrapper.h> // for constrain
       6             : 
       7             : BEGIN_CS_NAMESPACE
       8             : 
       9             : /// The encoding to use for relative control change value.
      10             : enum relativeCCmode {
      11             :     /**
      12             :      * @brief    Encode negative MIDI CC values as 7-bit two's complement.
      13             :      *
      14             :      * | Encoded  | Value | 
      15             :      * |---------:|------:|
      16             :      * | 000'0000 |  0    |
      17             :      * | 000'0001 | +1    |
      18             :      * | 011'1111 | +63   |
      19             :      * | 100'0000 | -64   |
      20             :      * | 100'0001 | -63   |
      21             :      * | 111'1111 | -1    |
      22             :      */
      23             :     TWOS_COMPLEMENT,
      24             :     /** 
      25             :      * @brief   Encode negative MIDI CC values by adding a fixed offset of 
      26             :      *          @f$ 2^6 = 64 @f$.
      27             :      *
      28             :      * | Encoded  | Value | 
      29             :      * |---------:|------:|
      30             :      * | 000'0000 | -64   |
      31             :      * | 000'0001 | -63   |
      32             :      * | 011'1111 | -1    |
      33             :      * | 100'0000 |  0    |
      34             :      * | 100'0001 | +1    |
      35             :      * | 111'1111 | +63   |
      36             :      */
      37             :     BINARY_OFFSET,
      38             :     /**
      39             :      * @brief   Encode negative MIDI CC values by using the most significant bit
      40             :      *          as a sign bit, and the six least significant bits as the 
      41             :      *          absolute value.
      42             :      *
      43             :      * | Encoded  | Value | 
      44             :      * |---------:|------:|
      45             :      * | 000'0000 | +0    |
      46             :      * | 000'0001 | +1    |
      47             :      * | 011'1111 | +63   |
      48             :      * | 100'0000 | -0    |
      49             :      * | 100'0001 | -1    |
      50             :      * | 111'1111 | -63   |
      51             :      */
      52             :     SIGN_MAGNITUDE,
      53             :     /**
      54             :      * @brief   Encode negative MIDI CC values by incrementing the address if 
      55             :      *          the number is negative, the MIDI value that's sent is always the
      56             :      *          absolute value of the relative delta.
      57             :      * 
      58             :      * For example, if the base address is 0x10, a delta value of +4 will be 
      59             :      * sent as a value of 4 to address 0x10, and a delta value of -8 will be 
      60             :      * sent as a value of 8 to address 0x11.
      61             :      */
      62             :     NEXT_ADDRESS = 4,
      63             :     /// First relative mode in Reaper.
      64             :     REAPER_RELATIVE_1 = TWOS_COMPLEMENT,
      65             :     /// Second relative mode in Reaper.
      66             :     REAPER_RELATIVE_2 = BINARY_OFFSET,
      67             :     /// Third relative mode in Reaper.
      68             :     REAPER_RELATIVE_3 = SIGN_MAGNITUDE,
      69             :     /// Relative mode in Tracktion.
      70             :     TRACKTION_RELATIVE = TWOS_COMPLEMENT,
      71             :     /// Relative mode used by the Mackie Control Universal protocol.
      72             :     MACKIE_CONTROL_RELATIVE = SIGN_MAGNITUDE,
      73             :     /// Korg KONTROL in Inc/Dec mode 1.
      74             :     KORG_KONTROL_INC_DEC_1 = NEXT_ADDRESS,
      75             : };
      76             : 
      77             : /**
      78             :  * @brief   Class that sends relative/incremental MIDI control change messages.
      79             :  *          This is often used for rotary encoders.
      80             :  * 
      81             :  * This requires explicit support from your DAW. You have to select relative
      82             :  * MIDI CC mode in the MIDI learn settings.
      83             :  * 
      84             :  * This class supports multiple different modes, see @ref relativeCCmode.
      85             :  * 
      86             :  * @ingroup MIDI_Senders
      87             :  */
      88             : class RelativeCCSender {
      89             :   public:
      90             :     /// Convert an 8-bit two's complement integer to a 7-bit two's complement
      91             :     /// integer.
      92          38 :     static uint8_t toTwosComplement7bit(int8_t value) { return value & 0x7F; }
      93             :     /// Convert an 8-bit two's complement integer to a 7-bit integer with a
      94             :     /// binary offset of 64. In other words, a value of 0 corresponds to -64,
      95             :     /// a value of 64 corresponds to 0, and a value of 127 corresponds to 63.
      96           9 :     static uint8_t toBinaryOffset7bit(int8_t value) { return value + 64; }
      97             :     /// Convert an 8-bit two's complement integer to 7-bit sign-magnitude
      98             :     /// format.
      99          11 :     static uint8_t toSignedMagnitude7bit(int8_t value) {
     100          11 :         uint8_t mask = value >> 7;
     101          11 :         uint8_t abs = (value + mask) ^ mask;
     102          11 :         uint8_t sign = mask & 0b01000000;
     103          11 :         return (abs & 0b00111111) | sign;
     104             :     }
     105             :     /// Convert an 8-bit two's complement integer to a 7-bit value to send over
     106             :     /// MIDI.
     107          64 :     static uint8_t mapRelativeCC(int8_t value) {
     108          64 :         switch (mode) {
     109          38 :             case TWOS_COMPLEMENT: return toTwosComplement7bit(value);
     110           9 :             case BINARY_OFFSET: return toBinaryOffset7bit(value);
     111          11 :             case SIGN_MAGNITUDE: return toSignedMagnitude7bit(value);
     112           6 :             case NEXT_ADDRESS: return value < 0 ? -value : value;
     113           0 :             default: return 0; // Keeps the compiler happy
     114             :         }
     115             :     }
     116             : 
     117             :     /// Send a relative CC message.
     118          31 :     static void send(long delta, MIDIAddress address) {
     119          31 :         if (delta < 0 && mode == NEXT_ADDRESS)
     120           1 :             address = address + 1;
     121          78 :         while (delta != 0) {
     122             :             // Constrain relative movement to +/-15 for
     123             :             // Mackie Control Universal compatibility
     124          47 :             long thisDelta = constrain(delta, -15, 15);
     125          47 :             uint8_t msgVal = mapRelativeCC(thisDelta);
     126             :             // send a Control Change MIDI event
     127          47 :             Control_Surface.sendControlChange(address, msgVal);
     128          47 :             delta -= thisDelta;
     129             :         }
     130          31 :     }
     131             : 
     132             :     /// Set the relative CC mode that's used. Requires the same setting on the
     133             :     /// receiving end, in your DAW, for example.
     134          22 :     static void setMode(relativeCCmode mode) { RelativeCCSender::mode = mode; }
     135             : 
     136             :   private:
     137             :     static relativeCCmode mode;
     138             : };
     139             : 
     140             : END_CS_NAMESPACE

Generated by: LCOV version 1.15