Update CI version
[carla.git] / source / native-plugins / midi-to-cv.c
bloba1063af058292e3184d673ff30c1026435fa9d70
1 /*
2 * Carla Native Plugins
3 * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 /* This plugin code is based on MOD Devices' midi-to-cv-mono by Bram Giesen and Jarno Verheesen
21 #include "CarlaNative.h"
22 #include "CarlaMIDI.h"
24 #include <stdlib.h>
25 #include <string.h>
27 // -----------------------------------------------------------------------
29 #define NUM_NOTESBUFFER 8
31 typedef enum {
32 PARAM_OCTAVE = 0,
33 PARAM_SEMITONE,
34 PARAM_CENT,
35 PARAM_RETRIGGER,
36 PARAM_COUNT
37 } Midi2CvParams;
39 typedef struct {
40 // keep track of active notes
41 uint8_t activeNotesList[NUM_NOTESBUFFER];
42 uint8_t reTriggerBuffer[NUM_NOTESBUFFER];
43 uint8_t triggerIndex;
44 uint8_t activeNotes;
45 uint8_t activeVelocity;
46 uint8_t reTriggered;
47 size_t notesIndex;
48 bool activePorts;
50 // other stuff
51 bool triggerState;
52 int notesPressed;
53 float params[PARAM_COUNT];
55 } Midi2CvHandle;
57 static void panic(Midi2CvHandle* const handle)
59 memset(handle->activeNotesList, 200, sizeof(uint8_t)*NUM_NOTESBUFFER);
60 memset(handle->reTriggerBuffer, 0, sizeof(uint8_t)*NUM_NOTESBUFFER);
61 handle->triggerIndex = 0;
62 handle->reTriggered = 200;
63 handle->activeNotes = 0;
64 handle->activeVelocity = 0;
65 handle->activePorts = false;
66 handle->notesPressed = 0;
67 handle->notesIndex = 0;
68 handle->triggerState = false;
71 static void set_status(Midi2CvHandle* const handle, int status)
73 handle->activePorts = status;
74 handle->triggerState = status;
77 // -----------------------------------------------------------------------
79 static NativePluginHandle midi2cv_instantiate(const NativeHostDescriptor* host)
81 Midi2CvHandle* const handle = (Midi2CvHandle*)malloc(sizeof(Midi2CvHandle));
83 if (handle == NULL)
84 return NULL;
86 panic(handle);
87 memset(handle->params, 0, sizeof(float)*PARAM_COUNT);
89 return handle;
91 // unused
92 (void)host;
95 #define handlePtr ((Midi2CvHandle*)handle)
97 static void midi2cv_cleanup(NativePluginHandle handle)
99 free(handlePtr);
102 static uint32_t midi2cv_get_parameter_count(NativePluginHandle handle)
104 return PARAM_COUNT;
106 // unused
107 (void)handle;
110 static const NativeParameter* midi2cv_get_parameter_info(NativePluginHandle handle, uint32_t index)
112 if (index > PARAM_COUNT)
113 return NULL;
115 static NativeParameter param;
117 param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMATABLE;
118 param.unit = NULL;
119 param.scalePointCount = 0;
120 param.scalePoints = NULL;
122 switch (index)
124 case PARAM_OCTAVE:
125 param.name = "Octave";
126 param.hints |= NATIVE_PARAMETER_IS_INTEGER;
127 param.ranges.def = 0.0f;
128 param.ranges.min = -3.0f;
129 param.ranges.max = 3.0f;
130 param.ranges.step = 1.0f;
131 param.ranges.stepSmall = 1.0f;
132 param.ranges.stepLarge = 1.0f;
133 break;
134 case PARAM_SEMITONE:
135 param.name = "Semitone";
136 param.hints |= NATIVE_PARAMETER_IS_INTEGER;
137 param.ranges.def = 0.0f;
138 param.ranges.min = -12.0f;
139 param.ranges.max = 12.0f;
140 param.ranges.step = 1.0f;
141 param.ranges.stepSmall = 1.0f;
142 param.ranges.stepLarge = 6.0f;
143 break;
144 case PARAM_CENT:
145 param.name = "Cent";
146 param.hints |= NATIVE_PARAMETER_IS_INTEGER;
147 param.ranges.def = 0.0f;
148 param.ranges.min = -100.0f;
149 param.ranges.max = 100.0f;
150 param.ranges.step = 10.0f;
151 param.ranges.stepSmall = 1.0f;
152 param.ranges.stepLarge = 50.0f;
153 break;
154 case PARAM_RETRIGGER:
155 param.name = "Retrigger";
156 param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
157 param.ranges.def = 0.0f;
158 param.ranges.min = 0.0f;
159 param.ranges.max = 1.0f;
160 param.ranges.step = 1.0f;
161 param.ranges.stepSmall = 1.0f;
162 param.ranges.stepLarge = 1.0f;
163 break;
166 return &param;
168 // unused
169 (void)handle;
172 static float midi2cv_get_parameter_value(NativePluginHandle handle, uint32_t index)
174 return handlePtr->params[index];
177 static void midi2cv_set_parameter_value(NativePluginHandle handle, uint32_t index, float value)
179 handlePtr->params[index] = value;
182 static const NativePortRange* midi2cv_get_buffer_port_range(NativePluginHandle handle, uint32_t index, bool isOutput)
184 if (! isOutput)
185 return NULL;
187 static NativePortRange npr;
189 switch (index)
191 case 0:
192 npr.minimum = 0.0f;
193 npr.maximum = 9.0f;
194 return &npr;
195 case 1:
196 npr.minimum = 0.0f;
197 npr.maximum = 10.5f;
198 return &npr;
199 case 2:
200 npr.minimum = 0.0f;
201 npr.maximum = 10.0f;
202 return &npr;
203 default:
204 return NULL;
207 // unused
208 (void)handle;
211 static const char* midi2cv_get_buffer_port_name(NativePluginHandle handle, uint32_t index, bool isOutput)
213 if (! isOutput)
214 return NULL;
216 switch (index)
218 case 0:
219 return "Pitch";
220 case 1:
221 return "Velocity";
222 case 2:
223 return "Gate";
224 default:
225 return NULL;
228 // unused
229 (void)handle;
232 static void midi2cv_activate(NativePluginHandle handle)
234 panic(handlePtr);
237 // FIXME for v3.0, use const for the input buffer
238 static void midi2cv_process(NativePluginHandle handle,
239 float** inBuffer, float** outBuffer, uint32_t frames,
240 const NativeMidiEvent* midiEvents, uint32_t midiEventCount)
242 float* const pitch = outBuffer[0];
243 float* const velocity = outBuffer[1];
244 float* const trigger = outBuffer[2];
246 const float oC = handlePtr->params[PARAM_OCTAVE];
247 const float sC = handlePtr->params[PARAM_SEMITONE];
248 const float cC = handlePtr->params[PARAM_CENT];
249 const bool rC = handlePtr->params[PARAM_RETRIGGER] > 0.5f;
251 bool retrigger = true;
253 for (uint32_t i=0; i < midiEventCount; ++i)
255 const NativeMidiEvent* const midiEvent = &midiEvents[i];
257 if (midiEvent->size <= 1 || midiEvent->size > 3)
258 continue;
260 const uint8_t* const mdata = midiEvent->data;
261 const uint8_t status = MIDI_GET_STATUS_FROM_DATA(mdata);
263 int storeN = 0;
264 bool emptySlot = false;
265 int notesIndex = NUM_NOTESBUFFER - 1;
266 bool noteFound = false;
268 switch (status)
270 case MIDI_STATUS_NOTE_ON:
271 while (!emptySlot && storeN < NUM_NOTESBUFFER)
273 if (handlePtr->activeNotesList[storeN] == 200)
275 handlePtr->activeNotesList[storeN] = mdata[1];
276 emptySlot = true;
278 storeN++;
280 handlePtr->activeNotes = mdata[1];
281 handlePtr->activeVelocity = mdata[2];
282 handlePtr->triggerIndex = (handlePtr->triggerIndex + 1U) % 8U;
283 handlePtr->reTriggerBuffer[handlePtr->triggerIndex] = 1U;
284 handlePtr->reTriggered = mdata[1];
285 break;
287 case MIDI_STATUS_NOTE_OFF:
288 handlePtr->notesPressed--;
289 for (int n = 0; n < NUM_NOTESBUFFER; ++n)
290 if (mdata[1] == handlePtr->activeNotesList[n])
291 handlePtr->activeNotesList[n] = 200;
293 while (!noteFound && notesIndex >= 0)
295 if (handlePtr->activeNotesList[notesIndex] < 200)
297 handlePtr->activeNotes = handlePtr->activeNotesList[notesIndex];
298 if(retrigger && handlePtr->activeNotes != handlePtr->reTriggered)
300 handlePtr->reTriggered = mdata[1];
302 noteFound = true;
304 notesIndex--;
306 break;
308 case MIDI_STATUS_CONTROL_CHANGE:
309 if (mdata[1] == MIDI_CONTROL_ALL_NOTES_OFF)
310 panic(handlePtr);
311 break;
315 int checked_note = 0;
316 bool active_notes_found = false;
317 while (checked_note < NUM_NOTESBUFFER && ! active_notes_found)
319 if (handlePtr->activeNotesList[checked_note] != 200)
320 active_notes_found = true;
321 checked_note++;
324 if (active_notes_found)
326 set_status(handlePtr, 1);
328 else
330 set_status(handlePtr, 0);
331 handlePtr->activeVelocity = 0;
334 for (uint32_t i=0; i<frames; ++i)
336 pitch[i] = (0.0f + (float)((oC) + (sC/12.0f)+(cC/1200.0f)) + ((float)handlePtr->activeNotes * 1/12.0f));
337 velocity[i] = (0.0f + ((float)handlePtr->activeVelocity * 1/12.0f));
338 trigger[i] = ((handlePtr->triggerState == true) ? 10.0f : 0.0f);
340 if (handlePtr->reTriggerBuffer[handlePtr->triggerIndex] == 1 && rC)
342 handlePtr->reTriggerBuffer[handlePtr->triggerIndex] = 0;
343 trigger[i] = 0.0f;
347 return;
349 // unused
350 (void)inBuffer;
353 #undef handlePtr
355 // -----------------------------------------------------------------------
357 static const NativePluginDescriptor midi2cvDesc = {
358 .category = NATIVE_PLUGIN_CATEGORY_UTILITY,
359 .hints = NATIVE_PLUGIN_IS_RTSAFE|NATIVE_PLUGIN_USES_CONTROL_VOLTAGE,
360 .supports = NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF,
361 .audioIns = 0,
362 .audioOuts = 0,
363 .cvIns = 0,
364 .cvOuts = 3, // pitch, velocity, gate
365 .midiIns = 1,
366 .midiOuts = 0,
367 .paramIns = PARAM_COUNT,
368 .paramOuts = 0,
369 .name = "MIDI to CV",
370 .label = "midi2cv",
371 .maker = "falkTX, Bram Giesen, Jarno Verheesen",
372 .copyright = "GNU GPL v2+",
374 .instantiate = midi2cv_instantiate,
375 .cleanup = midi2cv_cleanup,
377 .get_parameter_count = midi2cv_get_parameter_count,
378 .get_parameter_info = midi2cv_get_parameter_info,
379 .get_parameter_value = midi2cv_get_parameter_value,
381 .get_midi_program_count = NULL,
382 .get_midi_program_info = NULL,
384 .set_parameter_value = midi2cv_set_parameter_value,
385 .set_midi_program = NULL,
386 .set_custom_data = NULL,
388 .get_buffer_port_name = midi2cv_get_buffer_port_name,
389 .get_buffer_port_range = midi2cv_get_buffer_port_range,
391 .ui_show = NULL,
392 .ui_idle = NULL,
394 .ui_set_parameter_value = NULL,
395 .ui_set_midi_program = NULL,
396 .ui_set_custom_data = NULL,
398 .activate = midi2cv_activate,
399 .deactivate = NULL,
400 .process = midi2cv_process,
402 .get_state = NULL,
403 .set_state = NULL,
405 .dispatcher = NULL,
407 .render_inline_display = NULL
410 // -----------------------------------------------------------------------
412 void carla_register_native_plugin_midi2cv(void);
414 void carla_register_native_plugin_midi2cv(void)
416 carla_register_native_plugin(&midi2cvDesc);
419 // -----------------------------------------------------------------------