LCOV - code coverage report
Current view: top level - src/MIDI_Senders - RelativeCCSender.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 95.8 % 24 23
Test Date: 2026-06-06 17:44:35 Functions: 100.0 % 6 6
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 2.4-beta