1 /***************************************************************************
2 * Copyright (C) 2011 by Pere Ràfols Soler *
3 * sapista2@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) 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 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 /***************************************************************************
22 This file is the implementation of the DYN plugin
23 This plugin is inside the Sapista Plugins Bundle
24 This file implements functionalities for diferent dynamic plugins
25 ****************************************************************************/
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30
31 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
32 #include "dsp/db.h"
33 #include "dsp/fastmath.h"
34 #include "dsp/vu.h"
35 #include "dsp/filter.h"
36
37 #define @Plugin_Is_Dynamics_Compressor@
38 #define USE_EQ10Q_FAST_MATH
39 #define NUM_CHANNELS @Dyn_Channels_Count@
40
41 #define DYN_URI @Dyn_Uri@
42 #define PORT_OUTPUT_L 0
43 #define PORT_INPUT_L 1
44 #define PORT_KEY_LISTEN 2
45 #define PORT_THRESHOLD 3
46 #define PORT_ATACK 4
47 #define PORT_HOLD_MAKEUP 5
48 #define PORT_DECAY 6
49 #define PORT_RATIO 7
50 #define PORT_HPFFREQ 8
51 #define PORT_LPFFREQ 9
52 #define PORT_GAIN 10
53 #define PORT_INVU 11
54 #define PORT_GAINREDUCTION 12
55 #define PORT_KNEE 13
56 #define PORT_DRY_WET 14
57
58 #ifdef PLUGIN_IS_COMPRESSOR
59 #define PORT_FEEDBACK 15
60 #define PORT_COMP_MODE 16
61 #define PORT_PUNCH 17
62 #define PORT_OUTPUT_R 18
63 #define PORT_INPUT_R 19
64
65 #else
66 #ifdef PLUGIN_IS_COMPRESSOR_WITH_SC
67 #define PORT_SC_ACTIVE 15
68 #define PORT_COMP_MODE 16
69 #define PORT_PUNCH 17
70
71 #if NUM_CHANNELS==1
72 #define PORT_SC_AUDIO_IN 18
73 #else
74 #define PORT_OUTPUT_R 18
75 #define PORT_INPUT_R 19
76 #define PORT_SC_AUDIO_IN 20
77 #endif
78
79 #else
80 #define PORT_RANGE 15
81 #define PORT_OUTPUT_R 16
82 #define PORT_INPUT_R 17
83 #endif
84 #endif
85
86 #define K_BIAS_GAIN 0.0891250938133745507219174442070652730762958526611328125f //A trick to change the dinamic range of the fast_db2lin10 method for gate plugin
87 typedef struct {
88 //Plugin ports
89 float *key_listen;
90 float *threshold;
91 float *attack;
92 float *hold_makeup; //Hold for gate makeup for compressor/expander
93 float *decay;
94 float *ratio;
95 float *output[NUM_CHANNELS];
96 float *gainreduction;
97 const float *input[NUM_CHANNELS];
98 float *hpffreq;
99 float *lpffreq;
100 float *ingain;
101 float *fVuIn;
102 float *drywet;
103 float *knee;
104 #ifdef PLUGIN_IS_COMPRESSOR
105 float *feedback;
106 float *compressor_mode;
107 #else
108 #ifdef PLUGIN_IS_COMPRESSOR_WITH_SC
109 float *sidechain_active;
110 float *compressor_mode;
111 const float *input_sidechain;
112 #else
113 float *range;
114 #endif
115 #endif
116
117 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
118 float *punch;
119 float g_error0;
120 #endif
121
122 //Plugin Internal data
123 float sample_rate;
124 float g;
125
126 int hold_count;
127 Vu *InputVu[1];
128 float detector_vu;
129 float noise;
130 Filter *LPF_fil, *HPF_fil;
131 Buffers LPF_buf, HPF_buf;
132 float *LutLog10;
133 double PGAOut_L;
134 #if NUM_CHANNELS == 2
135 double PGAOut_R;
136 #endif
137 } Dynamics;
138
cleanupDyn(LV2_Handle instance)139 static void cleanupDyn(LV2_Handle instance)
140 {
141 Dynamics *plugin = (Dynamics *)instance;
142 free(plugin->LutLog10);
143 VuClean(plugin->InputVu[0]);
144 FilterClean(plugin->HPF_fil);
145 FilterClean(plugin->LPF_fil);
146 free(instance);
147 }
148
connectPortDyn(LV2_Handle instance,uint32_t port,void * data)149 static void connectPortDyn(LV2_Handle instance, uint32_t port, void *data)
150 {
151 Dynamics *plugin = (Dynamics *)instance;
152 switch (port) {
153 case PORT_KEY_LISTEN:
154 plugin->key_listen = (float*)data;
155 break;
156 case PORT_THRESHOLD:
157 plugin->threshold = (float*)data;
158 break;
159 case PORT_ATACK:
160 plugin->attack = (float*)data;
161 break;
162 case PORT_HOLD_MAKEUP:
163 plugin->hold_makeup = (float*)data;
164 break;
165 case PORT_DECAY:
166 plugin->decay = (float*)data;
167 break;
168 case PORT_RATIO:
169 plugin->ratio = (float*)data;
170 break;
171 case PORT_INPUT_L:
172 plugin->input[0] = (const float*)data;
173 break;
174 case PORT_OUTPUT_L:
175 plugin->output[0] = (float*)data;
176 break;
177 case PORT_GAINREDUCTION:
178 plugin->gainreduction = (float*)data;
179 break;
180 case PORT_HPFFREQ:
181 plugin->hpffreq = (float*)data;
182 break;
183 case PORT_LPFFREQ:
184 plugin->lpffreq = (float*)data;
185 break;
186 case PORT_GAIN:
187 plugin->ingain = (float*)data;
188 break;
189 case PORT_INVU:
190 plugin->fVuIn=(float*)data;
191 break;
192
193 case PORT_DRY_WET:
194 plugin->drywet = (float*)data;
195 break;
196
197 case PORT_KNEE:
198 plugin->knee = (float*)data;
199 break;
200
201 #ifdef PLUGIN_IS_COMPRESSOR
202 case PORT_FEEDBACK:
203 plugin->feedback = (float*)data;
204 break;
205 case PORT_COMP_MODE:
206 plugin->compressor_mode = (float*)data;
207 break;
208
209 case PORT_PUNCH:
210 plugin->punch = (float*)data;
211 break;
212
213 #else
214 #ifdef PLUGIN_IS_COMPRESSOR_WITH_SC
215 case PORT_SC_ACTIVE:
216 plugin->sidechain_active = (float*)data;
217 break;
218 case PORT_COMP_MODE:
219 plugin->compressor_mode = (float*)data;
220 break;
221 case PORT_SC_AUDIO_IN:
222 plugin->input_sidechain = (const float*)data;
223 break;
224 case PORT_PUNCH:
225 plugin->punch = (float*)data;
226 break;
227
228 #else
229 case PORT_RANGE:
230 plugin->range = (float*)data;
231 break;
232 #endif
233 #endif
234
235 #if NUM_CHANNELS == 2
236 case PORT_INPUT_R:
237 plugin->input[1] = (const float*)data;
238 break;
239 case PORT_OUTPUT_R:
240 plugin->output[1] = (float*)data;
241 break;
242 #endif
243 }
244 }
245
instantiateDyn(const LV2_Descriptor * descriptor,double s_rate,const char * path,const LV2_Feature * const * features)246 static LV2_Handle instantiateDyn(const LV2_Descriptor *descriptor, double s_rate, const char *path, const LV2_Feature *const * features)
247 {
248 Dynamics *plugin_data = (Dynamics *)malloc(sizeof(Dynamics));
249 plugin_data->LutLog10 = GenerateLog10LUT();
250 plugin_data->sample_rate = s_rate;
251 plugin_data->hold_count = 1000000;
252
253 #ifdef PLUGIN_IS_GATE
254 plugin_data->g = 0.0f;
255 #endif
256 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
257 plugin_data->g = 1.0f;
258 plugin_data->g_error0 = 0.0f;
259 #endif
260
261 plugin_data->InputVu[0] = VuInit(s_rate);
262 plugin_data->detector_vu = 0.0f;
263 plugin_data->noise = 0.0001; //the noise to get the GR VU workin in GUI
264 plugin_data->HPF_fil = FilterInit(s_rate);
265 plugin_data->LPF_fil = FilterInit(s_rate);
266 plugin_data->PGAOut_L = 0.0;
267 #if NUM_CHANNELS == 2
268 plugin_data->PGAOut_R = 0.0;
269 #endif
270 flushBuffers(&plugin_data->LPF_buf);
271 flushBuffers(&plugin_data->HPF_buf);
272 return (LV2_Handle)plugin_data;
273 }
274
275 #define DENORMAL_TO_ZERO_FLOAT(x) if (fabs(x) < (1e-30)) x = 0.f; //Min float is 1.1754943e-38
runDyn(LV2_Handle instance,uint32_t sample_count)276 static void runDyn(LV2_Handle instance, uint32_t sample_count)
277 {
278 Dynamics *plugin_data = (Dynamics *)instance;
279
280 const float attack = *(plugin_data->attack);
281 const float decay = *(plugin_data->decay);
282 const float hpffreq = *(plugin_data->hpffreq);
283 const float lpffreq = *(plugin_data->lpffreq);
284 const float KeyListen = *(plugin_data->key_listen);
285 const float InputGain = dB2Lin(*(plugin_data->ingain));
286 const float DryWet = *(plugin_data->drywet);
287 float gr_meter = 1.0f;
288 const float ratio = *(plugin_data->ratio);
289 const float threshold = *(plugin_data->threshold);
290 const float knee = *(plugin_data->knee);
291
292 //Read ports (gate)
293 #ifdef PLUGIN_IS_GATE
294 const float range = *(plugin_data->range);
295 const float hold = *(plugin_data->hold_makeup);
296 const float threshold_lin = Fast_dB2Lin10(*(plugin_data->threshold));
297 #endif
298
299 //Read ports (compressor/expander)
300 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
301 const float makeup = dB2Lin(*(plugin_data->hold_makeup));
302 const float punch = *(plugin_data->punch);
303 #else
304 const float makeup = 1.0f;
305 #endif
306
307 #ifdef PLUGIN_IS_COMPRESSOR
308 const double FeedBack = (double)*(plugin_data->feedback);
309 const int bIsOptoCompressor = *(plugin_data->compressor_mode) > 0.5 ? 1 : 0;
310 const double SideChainActive = 0.0;
311 #endif
312
313 #ifdef PLUGIN_IS_COMPRESSOR_WITH_SC
314 const double SideChainActive = (double)*(plugin_data->sidechain_active);
315 const double FeedBack = 0.0;
316 const int bIsOptoCompressor = *(plugin_data->compressor_mode) > 0.5 ? 1 : 0;
317 #endif
318
319 //Plguin data
320 float input_detector;
321 float sample_rate = plugin_data->sample_rate;
322 float g = plugin_data->g;
323 int hold_count = plugin_data->hold_count;
324 float x_dB, y_dB;
325 float knee_range;
326
327 //Processor vars (only for gate)
328 #ifdef PLUGIN_IS_GATE
329 const float range_lin = pow(10, range * 0.05);
330 const int hold_max = (int)round(hold * sample_rate * 0.001f);
331 const float Kac = pow((range_lin/(1.0f-range_lin))*((1.0f-0.9f)/0.9f), 1.0f/(attack*0.001f*sample_rate)); //Attack constant for S curve in gate
332 #endif
333
334 //Processor vars (only COMPRESSOR)
335 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
336 float g_error = 0.0f;
337 float scl = 0.0f;
338 const float range_lin = pow(10, -12 * 0.05); //By various tests -12 dB is the optimal setting for the release time of a opto-compressor
339 const float Kdc = pow((range_lin/(1.0f-range_lin))*((1.0f-0.6f)/0.6f), 1.0f/(decay*0.001f*sample_rate)); //Decay constant for S curve in compressor
340 #endif
341
342 //Processor vars common
343 const float ac = exp(-6.0f/(attack * sample_rate * 0.001f)); //Attack constant in compressor
344 const float dc = exp(-2.0f/(decay * sample_rate * 0.001f)); //Decay constant
345 float detector_vu = plugin_data->detector_vu;
346 float gain_reduction = 0.0f;
347 float input_filtered = 0.0f;
348 double dToFiltersChain = 0.0;
349 float input_preL;
350 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
351 float input_sc = 0.0;
352 #endif
353
354 #if NUM_CHANNELS == 2
355 float input_preR;
356 #endif
357 for (uint32_t i = 0; i < sample_count; ++i)
358 {
359 //Compute filter coeficients
360 if(hpffreq != plugin_data->HPF_fil->freq)
361 {
362 calcCoefs(plugin_data->HPF_fil, 0.0, hpffreq, 0.75, F_HPF_ORDER_2, 1.0);
363 }
364 if(lpffreq != plugin_data->LPF_fil->freq)
365 {
366 calcCoefs(plugin_data->LPF_fil, 0.0, lpffreq, 0.75, F_LPF_ORDER_2, 1.0);
367 }
368
369 //Input gain
370 input_preL = plugin_data->input[0][i] * InputGain;
371 #ifdef PLUGIN_IS_COMPRESSOR_WITH_SC
372 input_sc = plugin_data->input_sidechain[i];
373 #endif
374 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
375 dToFiltersChain = ((double)input_preL * (1.0 - FeedBack) + plugin_data->PGAOut_L*FeedBack)*(1.0 - SideChainActive) + (double)input_sc * SideChainActive;
376 #else
377 dToFiltersChain = (double)input_preL;
378 #endif
379 #if NUM_CHANNELS == 2
380 input_preR = plugin_data->input[1][i] * InputGain;
381 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
382 dToFiltersChain = (((double)(input_preL + input_preR)*(1.0 - FeedBack) + (plugin_data->PGAOut_L + plugin_data->PGAOut_R)*FeedBack )*0.5)*(1.0 - SideChainActive) + (double)input_sc * SideChainActive;
383 #else
384 dToFiltersChain = (double)(input_preL + input_preR)*0.5;
385 #endif
386 #endif
387
388
389 //Apply Filters
390 computeFilter(plugin_data->LPF_fil, &plugin_data->LPF_buf, &dToFiltersChain);
391 computeFilter(plugin_data->HPF_fil, &plugin_data->HPF_buf, &dToFiltersChain);
392 input_filtered = (float)dToFiltersChain;
393
394 //Detector signal used for Threshold VU-meter in both gate and compressor, also used in expander hold
395 input_detector = fabs(input_filtered); //This is the detector signal filtered
396
397 //Thresholding and gain computer
398 #ifdef USE_EQ10Q_FAST_MATH
399 x_dB = 20.0f * fastLog10((int*)(&input_filtered), plugin_data->LutLog10);
400 #else
401 x_dB = 20.0f*log10(fabs(input_filtered) + 0.00001f); //Add -100dB constant to avoid zero crozing
402 #endif
403 knee_range = 2.0f*(x_dB - threshold);
404
405 //Sample to Input TH-Vumeter
406 if(input_detector >= detector_vu)
407 {
408 detector_vu = input_detector - (input_detector - detector_vu)*ac;
409 }
410 else
411 {
412 detector_vu = input_detector - (input_detector - detector_vu)*dc;
413 }
414 SetSample(plugin_data->InputVu[0], detector_vu);
415
416 //===================== GATE CODE ================================
417 #ifdef PLUGIN_IS_GATE
418 if (knee_range < -knee)
419 {
420 //Under Threshold
421 y_dB = threshold + (x_dB - threshold)*ratio;
422 }
423 else if(knee_range >= knee )
424 {
425 //Over Threshold
426 y_dB = x_dB;
427 }
428 else
429 {
430 //On Knee
431 y_dB = x_dB + ((1.0 - ratio)*(x_dB - threshold - knee/2)*(x_dB - threshold - knee/2))/(2*knee);
432 }
433 if( y_dB < x_dB + range )
434 {
435 y_dB = x_dB + range;
436 }
437
438 //Linear gain computing
439 #ifdef USE_EQ10Q_FAST_MATH
440 gain_reduction = K_BIAS_GAIN*Fast_dB2Lin10(y_dB - x_dB + 22); //22dB bias compensated with K_BIAS_GAIN linear multiplication. This allows a good response of Fast_dB2Lin10 at -90 dB range
441 #else
442 gain_reduction = pow(10.0f, 0.05f*(y_dB - x_dB));
443 #endif
444
445 //Ballistics and peak detector
446 hold_count = input_detector > threshold_lin ? 0 : hold_count;
447 if(gain_reduction > g)
448 {
449 //Gate/Expander OFF (Opening)
450 g = 1.0f/(1.0f + Kac*((1.0f - g)/(g + 1e-8f)));
451 gain_reduction = g > gain_reduction ? gain_reduction : g;
452 }
453 else
454 {
455 //Gate/Expander ON (Closeing)
456 if(hold_count > hold_max)
457 {
458 gain_reduction = gain_reduction - (gain_reduction - g)*dc; //Log-Curve release
459 }
460 else
461 {
462 //Holding...
463 gain_reduction = g;
464 }
465 }
466 hold_count++;
467
468 //-----------------------------------------------------------
469 #endif
470 //===================== END OF GATE CODE =========================
471
472 //=================== COMPRESSOR CODE ============================
473 #if defined(PLUGIN_IS_COMPRESSOR) || defined(PLUGIN_IS_COMPRESSOR_WITH_SC)
474 if (knee_range < -knee)
475 {
476 //Under Threshold
477 y_dB = x_dB;
478 }
479 else if(knee_range >= knee )
480 {
481 //Over Threshold
482 y_dB = threshold + (x_dB - threshold)/ratio;
483 }
484 else
485 {
486 //On Knee
487 y_dB = x_dB + ((1.0f/ratio -1.0f)*(x_dB - threshold + knee/2.0f)*(x_dB - threshold + knee/2.0f))/(2.0f*knee);
488 }
489
490 //Linear gain computing
491 #ifdef USE_EQ10Q_FAST_MATH
492 gain_reduction = Fast_dB2Lin8(y_dB - x_dB);
493 #else
494 gain_reduction = pow(10.0f, 0.05f*(y_dB - x_dB));
495 #endif
496
497 //Ballistics and peak detector
498 if(gain_reduction > g)
499 {
500
501 //Compressor OFF
502 if(bIsOptoCompressor)
503 {
504 gain_reduction = 1.0f/(1.0f + Kdc*((1.0f - g)/(g + 1e-8f))); //S-Curve release
505 }
506 else
507 {
508 gain_reduction = 1.0f - (1.0f - g)*dc; //Log-Curve release
509 }
510 }
511 else
512 {
513 //Compressor ON
514 g_error = gain_reduction - g;
515 scl = punch*(g_error*plugin_data->g_error0)/((1.0f-gain_reduction + 1e-8f)*(1.0f-gain_reduction + 1e-8f));
516 gain_reduction = (gain_reduction - (g_error*ac))*(1.0f- scl) + scl*g;
517 plugin_data->g_error0 = g_error;
518 }
519 #endif
520 //=================== END OF COMPRESSOR CODE ======================
521
522 DENORMAL_TO_ZERO_FLOAT(gain_reduction);
523 g = gain_reduction;
524 gr_meter = gain_reduction < gr_meter ? gain_reduction : gr_meter;
525
526 plugin_data->PGAOut_L = (double)input_preL * gain_reduction;
527 plugin_data->output[0][i] = input_filtered*(KeyListen) + (input_preL*(1.0f - DryWet) + makeup*(float)plugin_data->PGAOut_L*DryWet)*(1-KeyListen);
528
529 #if NUM_CHANNELS == 2
530 plugin_data->PGAOut_R = (double)input_preR * gain_reduction;
531 plugin_data->output[1][i] = input_filtered*(KeyListen) + (input_preR*(1.0f - DryWet) + makeup*(float)plugin_data->PGAOut_R*DryWet)*(1-KeyListen);
532 #endif
533
534 }
535 plugin_data->g = g;
536 plugin_data->hold_count = hold_count;
537 plugin_data->noise *= -1.0;
538 plugin_data->detector_vu = detector_vu;
539 *(plugin_data->gainreduction) = 1.0/gr_meter + plugin_data->noise; // + ((float)(rand() % 100)/100.0);; //OK esta en lineal
540 *(plugin_data->fVuIn) = ComputeVu(plugin_data->InputVu[0], sample_count);
541 }
542
543 static const LV2_Descriptor dynDescriptor = {
544 DYN_URI,
545 instantiateDyn,
546 connectPortDyn,
547 NULL,
548 runDyn,
549 NULL,
550 cleanupDyn,
551 NULL
552 };
553
554 LV2_SYMBOL_EXPORT
lv2_descriptor(uint32_t index)555 const LV2_Descriptor *lv2_descriptor(uint32_t index)
556 {
557 switch (index) {
558 case 0:
559 return &dynDescriptor;
560 default:
561 return NULL;
562 }
563 }
564