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"
27 // -----------------------------------------------------------------------
29 #define NUM_NOTESBUFFER 8
40 // keep track of active notes
41 uint8_t activeNotesList
[NUM_NOTESBUFFER
];
42 uint8_t reTriggerBuffer
[NUM_NOTESBUFFER
];
45 uint8_t activeVelocity
;
53 float params
[PARAM_COUNT
];
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
));
87 memset(handle
->params
, 0, sizeof(float)*PARAM_COUNT
);
95 #define handlePtr ((Midi2CvHandle*)handle)
97 static void midi2cv_cleanup(NativePluginHandle handle
)
102 static uint32_t midi2cv_get_parameter_count(NativePluginHandle handle
)
110 static const NativeParameter
* midi2cv_get_parameter_info(NativePluginHandle handle
, uint32_t index
)
112 if (index
> PARAM_COUNT
)
115 static NativeParameter param
;
117 param
.hints
= NATIVE_PARAMETER_IS_ENABLED
|NATIVE_PARAMETER_IS_AUTOMATABLE
;
119 param
.scalePointCount
= 0;
120 param
.scalePoints
= NULL
;
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
;
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
;
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
;
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
;
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
)
187 static NativePortRange npr
;
211 static const char* midi2cv_get_buffer_port_name(NativePluginHandle handle
, uint32_t index
, bool isOutput
)
232 static void midi2cv_activate(NativePluginHandle handle
)
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)
260 const uint8_t* const mdata
= midiEvent
->data
;
261 const uint8_t status
= MIDI_GET_STATUS_FROM_DATA(mdata
);
264 bool emptySlot
= false;
265 int notesIndex
= NUM_NOTESBUFFER
- 1;
266 bool noteFound
= false;
270 case MIDI_STATUS_NOTE_ON
:
271 while (!emptySlot
&& storeN
< NUM_NOTESBUFFER
)
273 if (handlePtr
->activeNotesList
[storeN
] == 200)
275 handlePtr
->activeNotesList
[storeN
] = mdata
[1];
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];
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];
308 case MIDI_STATUS_CONTROL_CHANGE
:
309 if (mdata
[1] == MIDI_CONTROL_ALL_NOTES_OFF
)
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;
324 if (active_notes_found
)
326 set_status(handlePtr
, 1);
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;
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
,
364 .cvOuts
= 3, // pitch, velocity, gate
367 .paramIns
= PARAM_COUNT
,
369 .name
= "MIDI to CV",
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
,
394 .ui_set_parameter_value
= NULL
,
395 .ui_set_midi_program
= NULL
,
396 .ui_set_custom_data
= NULL
,
398 .activate
= midi2cv_activate
,
400 .process
= midi2cv_process
,
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 // -----------------------------------------------------------------------