1 /*
2 * MIDI PBToCC plugin based on DISTRHO Plugin Framework (DPF)
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Copyright (C) 2019 Christopher Arndt <info@chrisarndt.de>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27 #include "PluginMIDIPBToCC.hpp"
28
29 START_NAMESPACE_DISTRHO
30
31 // -----------------------------------------------------------------------
32
PluginMIDIPBToCC()33 PluginMIDIPBToCC::PluginMIDIPBToCC()
34 : Plugin(paramCount, presetCount, 0) // 0 states
35 {
36 loadProgram(0);
37 }
38
39 // -----------------------------------------------------------------------
40 // Init
41
initParameter(uint32_t index,Parameter & parameter)42 void PluginMIDIPBToCC::initParameter(uint32_t index, Parameter& parameter) {
43 if (index >= paramCount)
44 return;
45
46 parameter.hints = kParameterIsAutomable | kParameterIsInteger;
47 parameter.ranges.def = 0;
48 parameter.ranges.min = 0;
49 parameter.ranges.max = 127;
50
51 switch (index) {
52 case paramFilterChannel:
53 parameter.name = "Filter Channel";
54 parameter.symbol = "channelf";
55 parameter.ranges.def = 0;
56 parameter.ranges.min = 0;
57 parameter.ranges.max = 16;
58 parameter.enumValues.count = 17;
59 parameter.enumValues.restrictedMode = true;
60 {
61 ParameterEnumerationValue* const channels = new ParameterEnumerationValue[17];
62 parameter.enumValues.values = channels;
63 channels[0].label = "Any";
64 channels[0].value = 0;
65 channels[1].label = "Channel 1";
66 channels[1].value = 1;
67 channels[2].label = "Channel 2";
68 channels[2].value = 2;
69 channels[3].label = "Channel 3";
70 channels[3].value = 3;
71 channels[4].label = "Channel 4";
72 channels[4].value = 4;
73 channels[5].label = "Channel 5";
74 channels[5].value = 5;
75 channels[6].label = "Channel 6";
76 channels[6].value = 6;
77 channels[7].label = "Channel 7";
78 channels[7].value = 7;
79 channels[8].label = "Channel 8";
80 channels[8].value = 8;
81 channels[9].label = "Channel 9";
82 channels[9].value = 9;
83 channels[10].label = "Channel 10";
84 channels[10].value = 10;
85 channels[11].label = "Channel 11";
86 channels[11].value = 11;
87 channels[12].label = "Channel 12";
88 channels[12].value = 12;
89 channels[13].label = "Channel 13";
90 channels[13].value = 13;
91 channels[14].label = "Channel 14";
92 channels[14].value = 14;
93 channels[15].label = "Channel 15";
94 channels[15].value = 15;
95 channels[16].label = "Channel 16";
96 channels[16].value = 16;
97 }
98 break;
99 case paramKeepOriginal:
100 parameter.name = "Keep original PB events";
101 parameter.shortName = "Keep PB";
102 parameter.symbol = "keep_original";
103 parameter.hints |= kParameterIsBoolean;
104 parameter.ranges.max = 1;
105 break;
106 case paramPBMin:
107 parameter.name = "PB min. value";
108 parameter.symbol = "pb_min";
109 parameter.ranges.def = -8192;
110 parameter.ranges.min = -8192;
111 parameter.ranges.max = 8191;
112 break;
113 case paramPBMax:
114 parameter.name = "PB max. value";
115 parameter.symbol = "pb_max";
116 parameter.ranges.def = 8192;
117 parameter.ranges.min = -8192;
118 parameter.ranges.max = 8191;
119 break;
120 case paramCC1:
121 parameter.name = "Pos. PB -> CC A";
122 parameter.symbol = "cc1";
123 parameter.ranges.def = 1;
124 break;
125 case paramCC1Min:
126 parameter.name = "CC A min. value";
127 parameter.symbol = "cc1_min";
128 break;
129 case paramCC1Max:
130 parameter.name = "CC A max. value";
131 parameter.symbol = "cc1_max";
132 parameter.ranges.def = 127;
133 break;
134 case paramCC2:
135 parameter.name = "Neg. PB -> CC B";
136 parameter.symbol = "cc2";
137 parameter.ranges.def = 1;
138 break;
139 case paramCC2Min:
140 parameter.name = "CC B min. value";
141 parameter.symbol = "cc2_min";
142 break;
143 case paramCC2Max:
144 parameter.name = "CC B max. value";
145 parameter.symbol = "cc2_max";
146 parameter.ranges.def = 127;
147 break;
148 }
149 }
150
151 /**
152 Set the name of the program @a index.
153 This function will be called once, shortly after the plugin is created.
154 */
initProgramName(uint32_t index,String & programName)155 void PluginMIDIPBToCC::initProgramName(uint32_t index, String& programName) {
156 if (index < presetCount) {
157 programName = factoryPresets[index].name;
158 }
159 }
160
161 // -----------------------------------------------------------------------
162 // Internal data
163
164 /**
165 Optional callback to inform the plugin about a sample rate change.
166 */
sampleRateChanged(double newSampleRate)167 void PluginMIDIPBToCC::sampleRateChanged(double newSampleRate) {
168 (void) newSampleRate;
169 }
170
171 /**
172 Get the current value of a parameter.
173 */
getParameterValue(uint32_t index) const174 float PluginMIDIPBToCC::getParameterValue(uint32_t index) const {
175 return fParams[index];
176 }
177
178 /**
179 Change a parameter value.
180 */
setParameterValue(uint32_t index,float value)181 void PluginMIDIPBToCC::setParameterValue(uint32_t index, float value) {
182 switch (index) {
183 case paramFilterChannel:
184 fParams[index] = CLAMP(value, 0.0f, 16.0f);
185 filterChannel = (int8_t) fParams[index] - 1;
186 break;
187 case paramKeepOriginal:
188 fParams[index] = CLAMP(value, 0.0f, 1.0f);
189 break;
190 case paramPBMin:
191 case paramPBMax:
192 fParams[index] = CLAMP(value, -8192.0f, 8191.0f);
193 break;
194 case paramCC1:
195 case paramCC1Min:
196 case paramCC1Max:
197 case paramCC2:
198 case paramCC2Min:
199 case paramCC2Max:
200 fParams[index] = CLAMP(value, 0.0f, 127.0f);
201 break;
202 }
203 }
204
205 /**
206 Load a program.
207 The host may call this function from any context,
208 including realtime processing.
209 */
loadProgram(uint32_t index)210 void PluginMIDIPBToCC::loadProgram(uint32_t index) {
211 if (index < presetCount) {
212 for (int i=0; i < paramCount; i++) {
213 setParameterValue(i, factoryPresets[index].params[i]);
214 }
215
216 }
217 }
218
219 // -----------------------------------------------------------------------
220 // Process
221
activate()222 void PluginMIDIPBToCC::activate() {
223 // plugin is activated
224 }
225
226
run(const float **,float **,uint32_t,const MidiEvent * events,uint32_t eventCount)227 void PluginMIDIPBToCC::run(const float**, float**, uint32_t,
228 const MidiEvent* events, uint32_t eventCount) {
229 bool pass;
230 uint8_t chan;
231 int16_t pb_value,
232 pb_min = (uint16_t) fParams[paramPBMin],
233 pb_max = (uint16_t) fParams[paramPBMax];
234 struct MidiEvent cc_event;
235
236 for (uint32_t i=0; i<eventCount; ++i) {
237 pass = true;
238
239 if ((events[i].data[0] & 0xF0) != MIDI_PITCH_BEND) {
240 writeMidiEvent(events[i]);
241 continue;
242 }
243
244 chan = events[i].data[0] & 0x0F;
245
246 if (filterChannel == -1 || chan == filterChannel) {
247 pb_value = (((events[i].data[2] & 0x7f) << 7) | (events[i].data[1] & 0x7f)) - 8192;
248
249 if (IN_RANGE(pb_value, pb_min, pb_max)) {
250 pass = (bool) fParams[paramKeepOriginal];
251 cc_event.frame = events[i].frame;
252 cc_event.size = 3;
253 cc_event.data[0] = MIDI_CONTROL_CHANGE | chan;
254
255 if (pb_value >= 0) {
256 cc_event.data[1] = (uint8_t) fParams[paramCC1];
257
258 if (pb_min <= pb_max)
259 cc_event.data[2] = ((uint8_t) MAP(pb_value, 0, pb_max, fParams[paramCC1Min], fParams[paramCC1Max])) & 0x7f;
260 else
261 cc_event.data[2] = ((uint8_t) MAP(pb_value, pb_min, 8191, fParams[paramCC2Min], fParams[paramCC1Max])) & 0x7f;
262 }
263 else {
264 cc_event.data[1] = (uint8_t) fParams[paramCC2];
265
266 if (pb_min <= pb_max)
267 cc_event.data[2] = ((uint8_t) MAP(pb_value, -1, pb_min, fParams[paramCC2Min], fParams[paramCC2Max])) & 0x7f;
268 else
269 cc_event.data[2] = ((uint8_t) MAP(pb_value, pb_max, -8192, fParams[paramCC2Min], fParams[paramCC2Max])) & 0x7f;
270 }
271
272 writeMidiEvent(cc_event);
273 }
274 }
275
276 if (pass) writeMidiEvent(events[i]);
277 }
278 }
279
280 // -----------------------------------------------------------------------
281
createPlugin()282 Plugin* createPlugin() {
283 return new PluginMIDIPBToCC();
284 }
285
286 // -----------------------------------------------------------------------
287
288 END_NAMESPACE_DISTRHO
289