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