Line data Source code
1 : #pragma once 2 : 3 : #include <Control_Surface/Control_Surface_Class.hpp> 4 : 5 : #include <Arduino.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 : /// First relative mode in Reaper. 54 : REAPER_RELATIVE_1 = TWOS_COMPLEMENT, 55 : /// Second relative mode in Reaper. 56 : REAPER_RELATIVE_2 = BINARY_OFFSET, 57 : /// Third relative mode in Reaper. 58 : REAPER_RELATIVE_3 = SIGN_MAGNITUDE, 59 : /// Relative mode in Tracktion. 60 : TRACKTION_RELATIVE = TWOS_COMPLEMENT, 61 : /// Relative mode used by the Mackie Control Universal protocol. 62 : MACKIE_CONTROL_RELATIVE = SIGN_MAGNITUDE, 63 : }; 64 : 65 : class RelativeCCSender { 66 : public: 67 : /** Convert an 8-bit two's complement integer to a 7-bit two's complement 68 : * integer. */ 69 18 : static uint8_t toTwosComplement7bit(int8_t value) { return value & 0x7F; } 70 : /** Convert an 8-bit two's complement integer to a 7-bit integer with a 71 : * binary offset of 64. In other words, a value of 0 corresponds to -64, 72 : * a value of 64 corresponds to 0, and a value of 127 corresponds to 63. */ 73 9 : static uint8_t toBinaryOffset7bit(int8_t value) { return value + 64; } 74 : /** Convert an 8-bit two's complement integer to 7-bit sign-magnitude 75 : * format. */ 76 8 : static uint8_t toSignedMagnitude7bit(int8_t value) { 77 8 : uint8_t mask = value >> 7; 78 8 : uint8_t abs = (value + mask) ^ mask; 79 8 : uint8_t sign = mask & 0b01000000; 80 8 : return (abs & 0b00111111) | sign; 81 : } 82 : /** Convert an 8-bit two's complement integer to a 7-bit value to send over 83 : * MIDI. */ 84 35 : static uint8_t mapRelativeCC(int8_t value) { 85 35 : switch (mode) { 86 18 : case TWOS_COMPLEMENT: return toTwosComplement7bit(value); 87 9 : case BINARY_OFFSET: return toBinaryOffset7bit(value); 88 8 : case SIGN_MAGNITUDE: return toSignedMagnitude7bit(value); 89 0 : default: return 0; // Keeps the compiler happy 90 : } 91 35 : } 92 : 93 10 : static void send(long delta, MIDICNChannelAddress address) { 94 28 : while (delta != 0) { 95 : // Constrain relative movement to +/-15 for 96 : // Mackie Control Universal compatibility 97 18 : long thisDelta = constrain(delta, -15, 15); 98 18 : uint8_t msgVal = mapRelativeCC(thisDelta); 99 : // send a Control Change MIDI event 100 18 : Control_Surface.MIDI().sendCC(address, msgVal); 101 18 : delta -= thisDelta; 102 : } 103 10 : } 104 : 105 13 : static void setMode(relativeCCmode mode) { RelativeCCSender::mode = mode; } 106 : 107 : private: 108 : static relativeCCmode mode; 109 : }; 110 : 111 : END_CS_NAMESPACE