Line data Source code
1 : #pragma once
2 :
3 : #include <Banks/BankableMIDIInput.hpp>
4 : #include <MIDI_Inputs/MIDIInputElementCC.hpp>
5 :
6 : BEGIN_CS_NAMESPACE
7 :
8 : namespace MCU {
9 :
10 : constexpr static uint8_t VPotRingAddress = 0x30;
11 :
12 2 : inline int8_t minimum(int8_t a, int8_t b) { return a > b ? b : a; }
13 2 : inline int8_t maximum(int8_t a, int8_t b) { return a < b ? b : a; }
14 :
15 : struct VPotEmptyCallback {
16 : VPotEmptyCallback() = default;
17 : template <class T>
18 0 : void begin(const T &) {}
19 : template <class T>
20 5 : void update(const T &) {}
21 : };
22 :
23 : /**
24 : * @todo I'm terrible at naming things.
25 : */
26 : class IVPotRing {
27 : protected:
28 5 : IVPotRing() = default;
29 :
30 : public:
31 : /// Return the position of the V-Pot ring. [0, 11]
32 15 : uint8_t getPosition() const { return getPosition(getValue()); }
33 : /// Return the status of the center LED of the V-Pot ring.
34 11 : bool getCenterLed() const { return getCenterLed(getValue()); }
35 : /// Return the mode of the V-Pot ring: 0 = single dot, 1 = boost/cut,
36 : /// 2 = wrap, 3 = spread
37 15 : uint8_t getMode() const { return getMode(getValue()); }
38 :
39 : /// Get the first segment that should be on.
40 2 : uint8_t getStartOn() const {
41 2 : int8_t position = getPosition();
42 2 : if (position == 0)
43 0 : return 0;
44 2 : int8_t value = position - 1;
45 2 : switch (getMode()) {
46 0 : case 0: return value;
47 2 : case 1: return minimum(value, 5);
48 0 : case 2: return 0;
49 0 : case 3: return maximum(5 - value, 0);
50 : // Shouldn't happen, just keeps the compiler happy:
51 0 : default: return 0;
52 : }
53 2 : }
54 :
55 : /// Get the first segment that should be off.
56 2 : uint8_t getStartOff() const {
57 2 : uint8_t value = getPosition();
58 2 : switch (getMode()) {
59 0 : case 0: return value;
60 2 : case 1: return maximum(value, 6);
61 0 : case 2: return value;
62 0 : case 3: return minimum(5 + value, 11);
63 : // Shouldn't happen, just keeps the compiler happy:
64 0 : default: return 0;
65 : }
66 2 : }
67 :
68 : private:
69 : virtual uint8_t getValue() const = 0;
70 :
71 : /// Extract the position from the raw value.
72 15 : static uint8_t getPosition(uint8_t value) {
73 15 : uint8_t position = value & 0x0F;
74 15 : return position < 0x0C ? position : 0x0B;
75 15 : }
76 : /// Extract the center LED state from the raw value.
77 11 : static bool getCenterLed(uint8_t value) { return value & 0x40; }
78 : /// Extract the mode from the raw value.
79 15 : static uint8_t getMode(uint8_t value) { return (value & 0x30) >> 4; }
80 : };
81 :
82 : template <uint8_t NumValues, class Callback>
83 5 : class VPotRing_Base : public MIDIInputElementCC, public IVPotRing {
84 : protected:
85 5 : VPotRing_Base(uint8_t track, const MIDIChannelCN &channelCN,
86 : const Callback &callback)
87 5 : : MIDIInputElementCC{{track + VPotRingAddress - 1, channelCN}},
88 20 : callback(callback) {}
89 :
90 : public:
91 : /// Initialize
92 1 : void begin() override { callback.begin(*this); }
93 :
94 : /// Reset all values to zero
95 0 : void reset() override {
96 : #ifdef VPOTRING_RESET
97 : values = {{}};
98 : callback.update(*this);
99 : #endif
100 0 : }
101 :
102 : protected:
103 : /** Make sure that the received value is valid and will not result in array
104 : * out of bounds conditions. */
105 7 : static uint8_t sanitizeValue(uint8_t value) {
106 7 : return (value & 0x0F) < 0x0C ? value : ((value & 0xF0) | 0xB);
107 : }
108 :
109 : private:
110 7 : bool updateImpl(const ChannelMessageMatcher &midimsg,
111 : const MIDIAddress &target) override {
112 7 : uint8_t index = getBankIndex(target);
113 7 : uint8_t value = sanitizeValue(midimsg.data2);
114 7 : values[index] = value;
115 7 : if (getSelection() == index)
116 3 : callback.update(*this);
117 7 : return true;
118 7 : }
119 :
120 41 : uint8_t getValue() const override { return values[getSelection()]; }
121 :
122 : /// Get the active bank selection
123 8 : virtual uint8_t getSelection() const { return 0; }
124 :
125 : /// Get the bank index from a MIDI address
126 2 : virtual setting_t getBankIndex(const MIDIAddress &target) const {
127 2 : (void)target;
128 2 : return 0;
129 : }
130 :
131 9 : Array<uint8_t, NumValues> values = {{}};
132 :
133 : public:
134 : Callback callback;
135 : };
136 :
137 : // -------------------------------------------------------------------------- //
138 :
139 : /**
140 : * @brief A class for MIDI input elements that represent Mackie Control
141 : * Universal V-Pots. This version is generic to allow for custom
142 : * callbacks.
143 : * This version cannot be banked.
144 : */
145 : template <class Callback = VPotEmptyCallback>
146 1 : class GenericVPotRing : public VPotRing_Base<1, Callback> {
147 : public:
148 1 : GenericVPotRing(uint8_t track, const MIDIChannelCN &channelCN,
149 : const Callback &callback)
150 1 : : VPotRing_Base<1, Callback>{track, channelCN, callback} {}
151 : };
152 :
153 : /**
154 : * @brief A class for MIDI input elements that represent Mackie Control
155 : * Universal V-Pots.
156 : * This version cannot be banked.
157 : * @ingroup MIDIInputElements
158 : */
159 1 : class VPotRing : public GenericVPotRing<> {
160 : public:
161 1 : VPotRing(uint8_t track, const MIDIChannelCN &channelCN = CHANNEL_1)
162 1 : : GenericVPotRing{track, channelCN, {}} {}
163 : };
164 :
165 : // -------------------------------------------------------------------------- //
166 :
167 : namespace Bankable {
168 :
169 : /**
170 : * @brief A class for MIDI input elements that represent Mackie Control
171 : * Universal V-Pots. This version is generic to allow for custom
172 : * callbacks.
173 : * This version can be banked.
174 : *
175 : * @tparam NumBanks
176 : * The number of banks.
177 : */
178 : template <uint8_t NumBanks, class Callback = VPotEmptyCallback>
179 4 : class GenericVPotRing : public VPotRing_Base<NumBanks, Callback>,
180 : public BankableMIDIInput<NumBanks> {
181 : public:
182 4 : GenericVPotRing(BankConfig<NumBanks> config, uint8_t track,
183 : const MIDIChannelCN &channelCN, const Callback &callback)
184 4 : : VPotRing_Base<NumBanks, Callback>{track, channelCN, callback},
185 8 : BankableMIDIInput<NumBanks>{config} {}
186 :
187 : private:
188 40 : setting_t getSelection() const override {
189 40 : return BankableMIDIInput<NumBanks>::getSelection();
190 : };
191 :
192 5 : uint8_t getBankIndex(const MIDIAddress &target) const override {
193 5 : return BankableMIDIInput<NumBanks>::getBankIndex(target, this->address);
194 : }
195 :
196 : /// Check if the address of the incoming MIDI message is in one of the banks
197 : /// of this element.
198 5 : bool match(const MIDIAddress &target) const override {
199 10 : return BankableMIDIInput<NumBanks>::matchBankable(target,
200 5 : this->address);
201 : }
202 :
203 4 : void onBankSettingChange() override { this->callback.update(*this); }
204 : };
205 :
206 : /**
207 : * @brief A class for MIDI input elements that represent Mackie Control
208 : * Universal V-Pots.
209 : * This version can be banked.
210 : *
211 : * @tparam NumBanks
212 : * The number of banks.
213 : */
214 : template <uint8_t NumBanks>
215 3 : class VPotRing : public GenericVPotRing<NumBanks> {
216 : public:
217 3 : VPotRing(BankConfig<NumBanks> config, uint8_t track,
218 : MIDIChannelCN channelCN = CHANNEL_1)
219 3 : : GenericVPotRing<NumBanks>{config, track, channelCN, {}} {}
220 : };
221 :
222 : } // namespace Bankable
223 :
224 : } // namespace MCU
225 :
226 : END_CS_NAMESPACE
|