1 /*
2  * Carla Native Plugins
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
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.
9  *
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.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaNative.h"
19 
20 #include "CarlaDefines.h"
21 #include "CarlaMIDI.h"
22 
23 #include <math.h>
24 #include <stdlib.h>
25 
26 // -----------------------------------------------------------------------
27 
28 typedef enum {
29     PARAM_MODE = 0,
30     PARAM_SPEED,
31     PARAM_MULTIPLIER,
32     PARAM_BASE_START,
33     PARAM_LFO_OUT,
34     PARAM_COUNT
35 } LfoParams;
36 
37 typedef struct {
38     const  NativeHostDescriptor* host;
39     int    mode;
40     double speed;
41     float  multiplier;
42     float  baseStart;
43     float  value;
44 } LfoHandle;
45 
46 // -----------------------------------------------------------------------
47 
lfo_instantiate(const NativeHostDescriptor * host)48 static NativePluginHandle lfo_instantiate(const NativeHostDescriptor* host)
49 {
50     LfoHandle* const handle = (LfoHandle*)malloc(sizeof(LfoHandle));
51 
52     if (handle == NULL)
53         return NULL;
54 
55     handle->host       = host;
56     handle->mode       = 1;
57     handle->speed      = 1.0f;
58     handle->multiplier = 1.0f;
59     handle->baseStart  = 0.0f;
60     handle->value      = 0.0f;
61     return handle;
62 }
63 
64 #define handlePtr ((LfoHandle*)handle)
65 
lfo_cleanup(NativePluginHandle handle)66 static void lfo_cleanup(NativePluginHandle handle)
67 {
68     free(handlePtr);
69 }
70 
lfo_get_parameter_count(NativePluginHandle handle)71 static uint32_t lfo_get_parameter_count(NativePluginHandle handle)
72 {
73     return PARAM_COUNT;
74 
75     // unused
76     (void)handle;
77 }
78 
lfo_get_parameter_info(NativePluginHandle handle,uint32_t index)79 static const NativeParameter* lfo_get_parameter_info(NativePluginHandle handle, uint32_t index)
80 {
81     if (index > PARAM_COUNT)
82         return NULL;
83 
84     static NativeParameter param;
85     static NativeParameterScalePoint paramModes[5];
86 
87     param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
88     param.scalePointCount = 0;
89     param.scalePoints     = NULL;
90 
91     paramModes[0].label = "Triangle";
92     paramModes[1].label = "Sawtooth";
93     paramModes[2].label = "Sawtooth (inverted)";
94     paramModes[3].label = "Sine (TODO)";
95     paramModes[4].label = "Square";
96 
97     paramModes[0].value = 1.0f;
98     paramModes[1].value = 2.0f;
99     paramModes[2].value = 3.0f;
100     paramModes[3].value = 4.0f;
101     paramModes[4].value = 5.0f;
102 
103     switch (index)
104     {
105     case PARAM_MODE:
106         param.name   = "Mode";
107         param.unit   = NULL;
108         param.hints |= NATIVE_PARAMETER_IS_INTEGER|NATIVE_PARAMETER_USES_SCALEPOINTS;
109         param.ranges.def = 1.0f;
110         param.ranges.min = 1.0f;
111         param.ranges.max = 5.0f;
112         param.ranges.step = 1.0f;
113         param.ranges.stepSmall = 1.0f;
114         param.ranges.stepLarge = 1.0f;
115         param.scalePointCount = 5;
116         param.scalePoints = paramModes;
117         break;
118     case PARAM_SPEED:
119         param.name = "Speed";
120         param.unit = "(coef)";
121         param.ranges.def = 1.0f;
122         param.ranges.min = 0.01f;
123         param.ranges.max = 2048.0f;
124         param.ranges.step = 0.25f;
125         param.ranges.stepSmall = 0.1f;
126         param.ranges.stepLarge = 0.5f;
127         break;
128     case PARAM_MULTIPLIER:
129         param.name = "Multiplier";
130         param.unit = "(coef)";
131         param.ranges.def = 1.0f;
132         param.ranges.min = 0.01f;
133         param.ranges.max = 2.0f;
134         param.ranges.step = 0.01f;
135         param.ranges.stepSmall = 0.0001f;
136         param.ranges.stepLarge = 0.1f;
137         break;
138     case PARAM_BASE_START:
139         param.name = "Start value";
140         param.unit = NULL;
141         param.ranges.def = 0.0f;
142         param.ranges.min = -1.0f;
143         param.ranges.max = 1.0f;
144         param.ranges.step = 0.01f;
145         param.ranges.stepSmall = 0.0001f;
146         param.ranges.stepLarge = 0.1f;
147         break;
148     case PARAM_LFO_OUT:
149         param.name   = "LFO Out";
150         param.unit   = NULL;
151         param.hints |= NATIVE_PARAMETER_IS_OUTPUT;
152         param.ranges.def = 0.0f;
153         param.ranges.min = 0.0f;
154         param.ranges.max = 1.0f;
155         param.ranges.step = 0.01f;
156         param.ranges.stepSmall = 0.0001f;
157         param.ranges.stepLarge = 0.1f;
158         break;
159     }
160 
161     return &param;
162 
163     // unused
164     (void)handle;
165 }
166 
lfo_get_parameter_value(NativePluginHandle handle,uint32_t index)167 static float lfo_get_parameter_value(NativePluginHandle handle, uint32_t index)
168 {
169     switch (index)
170     {
171     case PARAM_MODE:
172         return (float)handlePtr->mode;
173     case PARAM_SPEED:
174         return (float)handlePtr->speed;
175     case PARAM_MULTIPLIER:
176         return handlePtr->multiplier;
177     case PARAM_BASE_START:
178         return handlePtr->baseStart;
179     case PARAM_LFO_OUT:
180         return handlePtr->value;
181     default:
182         return 0.0f;
183     }
184 }
185 
lfo_set_parameter_value(NativePluginHandle handle,uint32_t index,float value)186 static void lfo_set_parameter_value(NativePluginHandle handle, uint32_t index, float value)
187 {
188     switch (index)
189     {
190     case PARAM_MODE:
191         handlePtr->mode = (int)value;
192         break;
193     case PARAM_SPEED:
194         handlePtr->speed = value;
195         break;
196     case PARAM_MULTIPLIER:
197         handlePtr->multiplier = value;
198         break;
199     case PARAM_BASE_START:
200         handlePtr->baseStart = value;
201         break;
202     case PARAM_LFO_OUT:
203         handlePtr->value = value;
204         break;
205     }
206 }
207 
208 // FIXME for v3.0, use const for the input buffer
lfo_process(NativePluginHandle handle,float ** inBuffer,float ** outBuffer,uint32_t frames,const NativeMidiEvent * midiEvents,uint32_t midiEventCount)209 static void lfo_process(NativePluginHandle handle,
210                         float** inBuffer, float** outBuffer, uint32_t frames,
211                         const NativeMidiEvent* midiEvents, uint32_t midiEventCount)
212 {
213     const NativeHostDescriptor* const host     = handlePtr->host;
214     const NativeTimeInfo*       const timeInfo = host->get_time_info(host->handle);
215 
216     if (! timeInfo->playing)
217        return;
218 
219     const double bpm        = timeInfo->bbt.valid ? timeInfo->bbt.beatsPerMinute : 120.0;
220     const double sampleRate = host->get_sample_rate(host->handle);
221 
222     const double speedRate  = handlePtr->speed/(bpm/60.0/sampleRate);
223     const uint   speedRatei = (uint)speedRate;
224 
225     double value = 0.0;
226 
227     switch (handlePtr->mode)
228     {
229     case 1: // Triangle
230         value = fabs(1.0-(double)(timeInfo->frame % speedRatei)/(speedRate/2.0));
231         break;
232     case 2: // Sawtooth
233         value = (double)(timeInfo->frame % speedRatei)/speedRate;
234         break;
235     case 3: // Sawtooth (inverted)
236         value = 1.0 - (double)(timeInfo->frame % speedRatei)/speedRate;
237         break;
238     case 4: // Sine -- TODO!
239         value = 0.0;
240         break;
241     case 5: // Square
242         value = (timeInfo->frame % speedRatei <= speedRatei/2) ? 1.0 : 0.0;
243         break;
244     }
245 
246     value *= (double)handlePtr->multiplier;
247     value += (double)handlePtr->baseStart;
248 
249     if (value <= 0.0)
250         handlePtr->value = 0.0f;
251     else if (value >= 1.0)
252         handlePtr->value = 1.0f;
253     else
254         handlePtr->value = (float)value;
255 
256     return;
257 
258     // unused
259     (void)inBuffer;
260     (void)outBuffer;
261     (void)frames;
262     (void)midiEvents;
263     (void)midiEventCount;
264 }
265 
266 #undef handlePtr
267 
268 // -----------------------------------------------------------------------
269 
270 static const NativePluginDescriptor lfoDesc = {
271     .category  = NATIVE_PLUGIN_CATEGORY_UTILITY,
272     .hints     = NATIVE_PLUGIN_IS_RTSAFE,
273     .supports  = NATIVE_PLUGIN_SUPPORTS_NOTHING,
274     .audioIns  = 0,
275     .audioOuts = 0,
276     .cvIns     = 0,
277     .cvOuts    = 0,
278     .midiIns   = 0,
279     .midiOuts  = 0,
280     .paramIns  = PARAM_COUNT-1,
281     .paramOuts = 1,
282     .name      = "LFO",
283     .label     = "lfo",
284     .maker     = "falkTX",
285     .copyright = "GNU GPL v2+",
286 
287     .instantiate = lfo_instantiate,
288     .cleanup     = lfo_cleanup,
289 
290     .get_parameter_count = lfo_get_parameter_count,
291     .get_parameter_info  = lfo_get_parameter_info,
292     .get_parameter_value = lfo_get_parameter_value,
293 
294     .get_midi_program_count = NULL,
295     .get_midi_program_info  = NULL,
296 
297     .set_parameter_value = lfo_set_parameter_value,
298     .set_midi_program    = NULL,
299     .set_custom_data     = NULL,
300 
301     .ui_show = NULL,
302     .ui_idle = NULL,
303 
304     .ui_set_parameter_value = NULL,
305     .ui_set_midi_program    = NULL,
306     .ui_set_custom_data     = NULL,
307 
308     .activate   = NULL,
309     .deactivate = NULL,
310     .process    = lfo_process,
311 
312     .get_state = NULL,
313     .set_state = NULL,
314 
315     .dispatcher = NULL
316 };
317 
318 // -----------------------------------------------------------------------
319 
320 void carla_register_native_plugin_lfo(void);
321 
carla_register_native_plugin_lfo(void)322 void carla_register_native_plugin_lfo(void)
323 {
324     carla_register_native_plugin(&lfoDesc);
325 }
326 
327 // -----------------------------------------------------------------------
328