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
|