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 1 : inline int8_t minimum(int8_t a, int8_t b) { return a > b ? b : a; }
13 1 : 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 3 : 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 12 : uint8_t getPosition() const { return getPosition(getValue()); }
33 : /// Return the status of the center LED of the V-Pot ring.
34 10 : 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 12 : uint8_t getMode() const { return getMode(getValue()); }
38 :
39 : /// Get the first segment that should be on.
40 1 : uint8_t getStartOn() const {
41 1 : int8_t position = getPosition();
42 1 : if (position == 0)
43 0 : return 0;
44 1 : int8_t value = position - 1;
45 1 : switch (getMode()) {
46 0 : case 0: return value;
47 1 : 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 1 : }
54 :
55 : /// Get the first segment that should be off.
56 1 : uint8_t getStartOff() const {
57 1 : uint8_t value = getPosition();
58 1 : switch (getMode()) {
59 0 : case 0: return value;
60 1 : 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 1 : }
67 :
68 : private:
69 : virtual uint8_t getValue() const = 0;
70 :
71 : /// Extract the position from the raw value.
72 12 : static uint8_t getPosition(uint8_t value) {
73 12 : uint8_t position = value & 0x0F;
74 12 : return position < 0x0C ? position : 0x0B;
75 : }
76 : /// Extract the center LED state from the raw value.
77 10 : static bool getCenterLed(uint8_t value) { return value & 0x40; }
78 : /// Extract the mode from the raw value.
79 12 : 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 MIDICNChannel &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 6 : static uint8_t sanitizeValue(uint8_t value) {
106 6 : return (value & 0x0F) < 0x0C ? value : ((value & 0xF0) | 0xB);
107 : }
108 :
109 : private:
110 6 : bool updateImpl(const ChannelMessageMatcher &midimsg,
111 : const MIDICNChannelAddress &target) override {
112 6 : uint8_t index = getBankIndex(target);
113 6 : uint8_t value = sanitizeValue(midimsg.data2);
114 6 : values[index] = value;
115 6 : return true;
116 : }
117 :
118 34 : uint8_t getValue() const override { return values[getSelection()]; }
119 :
120 : /// Get the active bank selection
121 6 : virtual uint8_t getSelection() const { return 0; }
122 :
123 : /// Get the bank index from a MIDI address
124 2 : virtual setting_t getBankIndex(const MIDICNChannelAddress &target) const {
125 2 : (void)target;
126 2 : return 0;
127 : }
128 :
129 9 : Array<uint8_t, NumValues> values = {{}};
130 :
131 : public:
132 : Callback callback;
133 : };
134 :
135 : // -------------------------------------------------------------------------- //
136 :
137 : /**
138 : * @brief A class for MIDI input elements that represent Mackie Control
139 : * Universal V-Pots. This version is generic to allow for custom
140 : * callbacks.
141 : * This version cannot be banked.
142 : */
143 : template <class Callback = VPotEmptyCallback>
144 1 : class GenericVPotRing : public VPotRing_Base<1, Callback> {
145 : public:
146 1 : GenericVPotRing(uint8_t track, const MIDICNChannel &channelCN,
147 : const Callback &callback)
148 1 : : VPotRing_Base<1, Callback>{track, channelCN, callback} {}
149 : };
150 :
151 : /**
152 : * @brief A class for MIDI input elements that represent Mackie Control
153 : * Universal V-Pots.
154 : * This version cannot be banked.
155 : * @ingroup MIDIInputElements
156 : */
157 1 : class VPotRing : public GenericVPotRing<> {
158 : public:
159 1 : VPotRing(uint8_t track, const MIDICNChannel &channelCN = CHANNEL_1)
160 1 : : GenericVPotRing{track, channelCN, {}} {}
161 : };
162 :
163 : // -------------------------------------------------------------------------- //
164 :
165 : namespace Bankable {
166 :
167 : /**
168 : * @brief A class for MIDI input elements that represent Mackie Control
169 : * Universal V-Pots. This version is generic to allow for custom
170 : * callbacks.
171 : * This version can be banked.
172 : *
173 : * @tparam NumBanks
174 : * The number of banks.
175 : */
176 : template <uint8_t NumBanks, class Callback = VPotEmptyCallback>
177 4 : class GenericVPotRing : public VPotRing_Base<NumBanks, Callback>,
178 : public BankableMIDIInput<NumBanks> {
179 : public:
180 4 : GenericVPotRing(const BankConfig<NumBanks> &config, uint8_t track,
181 : const MIDICNChannel &channelCN, const Callback &callback)
182 4 : : VPotRing_Base<NumBanks, Callback>{track, channelCN, callback},
183 8 : BankableMIDIInput<NumBanks>{config} {}
184 :
185 : private:
186 28 : setting_t getSelection() const override {
187 28 : return BankableMIDIInput<NumBanks>::getSelection();
188 : };
189 :
190 4 : uint8_t getBankIndex(const MIDICNChannelAddress &target) const override {
191 4 : return BankableMIDIInput<NumBanks>::getBankIndex(target, this->address);
192 : }
193 :
194 : /// Check if the address of the incoming MIDI message is in one of the banks
195 : /// of this element.
196 4 : bool match(const MIDICNChannelAddress &target) const override {
197 8 : return BankableMIDIInput<NumBanks>::matchBankable(target,
198 4 : this->address);
199 : }
200 :
201 4 : void onBankSettingChange() override { this->callback.update(*this); }
202 : };
203 :
204 : /**
205 : * @brief A class for MIDI input elements that represent Mackie Control
206 : * Universal V-Pots.
207 : * This version can be banked.
208 : *
209 : * @tparam NumBanks
210 : * The number of banks.
211 : */
212 : template <uint8_t NumBanks>
213 3 : class VPotRing : public GenericVPotRing<NumBanks> {
214 : public:
215 3 : VPotRing(BankConfig<NumBanks> config, uint8_t track,
216 : MIDICNChannel channelCN = CHANNEL_1)
217 3 : : GenericVPotRing<NumBanks>{config, track, channelCN, {}} {}
218 : };
219 :
220 : } // namespace Bankable
221 :
222 : } // namespace MCU
223 :
224 : END_CS_NAMESPACE
|