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 18 : 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 22 : return (abs & 0b00111111) | sign; 104 11 : } 105 : /// Convert an 8-bit two's complement integer to a 7-bit value to send over 106 : /// MIDI. 107 44 : static uint8_t mapRelativeCC(int8_t value) { 108 44 : switch (mode) { 109 18 : 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 44 : } 116 : 117 : /// Send a relative CC message. 118 13 : static void send(long delta, MIDIAddress address) { 119 13 : if (delta < 0 && mode == NEXT_ADDRESS) 120 1 : address = address + 1; 121 40 : while (delta != 0) { 122 : // Constrain relative movement to +/-15 for 123 : // Mackie Control Universal compatibility 124 27 : long thisDelta = constrain(delta, -15, 15); 125 27 : uint8_t msgVal = mapRelativeCC(thisDelta); 126 : // send a Control Change MIDI event 127 27 : Control_Surface.sendCC(address, msgVal); 128 27 : delta -= thisDelta; 129 27 : } 130 13 : } 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 19 : static void setMode(relativeCCmode mode) { RelativeCCSender::mode = mode; } 135 : 136 : private: 137 : static relativeCCmode mode; 138 : }; 139 : 140 : END_CS_NAMESPACE