1 /*
2  * ZaMultiCompX2 Stereo multiband compressor
3  * Copyright (C) 2014  Damien Zammit <damien@zamaudio.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 (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  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "ZaMultiCompX2Plugin.hpp"
19 
20 START_NAMESPACE_DISTRHO
21 
22 // -----------------------------------------------------------------------
23 
24 ZaMultiCompX2Plugin::ZaMultiCompX2Plugin()
25     : Plugin(paramCount, 2, 0)
26 {
27     // set default values
28     loadProgram(0);
29 }
30 
31 // -----------------------------------------------------------------------
32 // Init
33 
34 void ZaMultiCompX2Plugin::initParameter(uint32_t index, Parameter& parameter)
35 {
36     switch (index)
37     {
38     case paramAttack1:
39         parameter.hints      = kParameterIsAutomable;
40         parameter.name       = "Attack1";
41         parameter.symbol     = "att1";
42         parameter.unit       = "ms";
43         parameter.ranges.def = 10.0f;
44         parameter.ranges.min = 0.1f;
45         parameter.ranges.max = 100.0f;
46         break;
47     case paramAttack2:
48         parameter.hints      = kParameterIsAutomable;
49         parameter.name       = "Attack2";
50         parameter.symbol     = "att2";
51         parameter.unit       = "ms";
52         parameter.ranges.def = 10.0f;
53         parameter.ranges.min = 0.1f;
54         parameter.ranges.max = 100.0f;
55         break;
56     case paramAttack3:
57         parameter.hints      = kParameterIsAutomable;
58         parameter.name       = "Attack3";
59         parameter.symbol     = "att3";
60         parameter.unit       = "ms";
61         parameter.ranges.def = 10.0f;
62         parameter.ranges.min = 0.1f;
63         parameter.ranges.max = 100.0f;
64         break;
65     case paramRelease1:
66         parameter.hints      = kParameterIsAutomable;
67         parameter.name       = "Release1";
68         parameter.symbol     = "rel1";
69         parameter.unit       = "ms";
70         parameter.ranges.def = 80.0f;
71         parameter.ranges.min = 1.0f;
72         parameter.ranges.max = 500.0f;
73         break;
74     case paramRelease2:
75         parameter.hints      = kParameterIsAutomable;
76         parameter.name       = "Release2";
77         parameter.symbol     = "rel2";
78         parameter.unit       = "ms";
79         parameter.ranges.def = 80.0f;
80         parameter.ranges.min = 1.0f;
81         parameter.ranges.max = 500.0f;
82         break;
83     case paramRelease3:
84         parameter.hints      = kParameterIsAutomable;
85         parameter.name       = "Release3";
86         parameter.symbol     = "rel3";
87         parameter.unit       = "ms";
88         parameter.ranges.def = 80.0f;
89         parameter.ranges.min = 1.0f;
90         parameter.ranges.max = 500.0f;
91         break;
92     case paramKnee1:
93         parameter.hints      = kParameterIsAutomable;
94         parameter.name       = "Knee1";
95         parameter.symbol     = "kn1";
96         parameter.unit       = "dB";
97         parameter.ranges.def = 0.0f;
98         parameter.ranges.min = 0.0f;
99         parameter.ranges.max = 8.0f;
100         break;
101     case paramKnee2:
102         parameter.hints      = kParameterIsAutomable;
103         parameter.name       = "Knee2";
104         parameter.symbol     = "kn2";
105         parameter.unit       = "dB";
106         parameter.ranges.def = 0.0f;
107         parameter.ranges.min = 0.0f;
108         parameter.ranges.max = 8.0f;
109         break;
110     case paramKnee3:
111         parameter.hints      = kParameterIsAutomable;
112         parameter.name       = "Knee3";
113         parameter.symbol     = "kn3";
114         parameter.unit       = "dB";
115         parameter.ranges.def = 0.0f;
116         parameter.ranges.min = 0.0f;
117         parameter.ranges.max = 8.0f;
118         break;
119     case paramRatio1:
120         parameter.hints      = kParameterIsAutomable | kParameterIsLogarithmic;
121         parameter.name       = "Ratio1";
122         parameter.symbol     = "rat1";
123         parameter.unit       = " ";
124         parameter.ranges.def = 4.0f;
125         parameter.ranges.min = 1.0f;
126         parameter.ranges.max = 20.0f;
127         break;
128     case paramRatio2:
129         parameter.hints      = kParameterIsAutomable | kParameterIsLogarithmic;
130         parameter.name       = "Ratio2";
131         parameter.symbol     = "rat2";
132         parameter.unit       = " ";
133         parameter.ranges.def = 4.0f;
134         parameter.ranges.min = 1.0f;
135         parameter.ranges.max = 20.0f;
136         break;
137     case paramRatio3:
138         parameter.hints      = kParameterIsAutomable | kParameterIsLogarithmic;
139         parameter.name       = "Ratio3";
140         parameter.symbol     = "rat3";
141         parameter.unit       = " ";
142         parameter.ranges.def = 4.0f;
143         parameter.ranges.min = 1.0f;
144         parameter.ranges.max = 20.0f;
145         break;
146     case paramThresh1:
147         parameter.hints      = kParameterIsAutomable;
148         parameter.name       = "Threshold 1";
149         parameter.symbol     = "thr1";
150         parameter.unit       = "dB";
151         parameter.ranges.def = -20.0f;
152         parameter.ranges.min = -60.0f;
153         parameter.ranges.max = 0.0f;
154         break;
155     case paramThresh2:
156         parameter.hints      = kParameterIsAutomable;
157         parameter.name       = "Threshold 2";
158         parameter.symbol     = "thr2";
159         parameter.unit       = "dB";
160         parameter.ranges.def = -18.0f;
161         parameter.ranges.min = -60.0f;
162         parameter.ranges.max = 0.0f;
163         break;
164     case paramThresh3:
165         parameter.hints      = kParameterIsAutomable;
166         parameter.name       = "Threshold 3";
167         parameter.symbol     = "thr3";
168         parameter.unit       = "dB";
169         parameter.ranges.def = -16.0f;
170         parameter.ranges.min = -60.0f;
171         parameter.ranges.max = 0.0f;
172         break;
173     case paramMakeup1:
174         parameter.hints      = kParameterIsAutomable;
175         parameter.name       = "Makeup 1";
176         parameter.symbol     = "mak1";
177         parameter.unit       = "dB";
178         parameter.ranges.def = 0.0f;
179         parameter.ranges.min = 0.0f;
180         parameter.ranges.max = 30.0f;
181         break;
182     case paramMakeup2:
183         parameter.hints      = kParameterIsAutomable;
184         parameter.name       = "Makeup 2";
185         parameter.symbol     = "mak2";
186         parameter.unit       = "dB";
187         parameter.ranges.def = 0.0f;
188         parameter.ranges.min = 0.0f;
189         parameter.ranges.max = 30.0f;
190         break;
191     case paramMakeup3:
192         parameter.hints      = kParameterIsAutomable;
193         parameter.name       = "Makeup 3";
194         parameter.symbol     = "mak3";
195         parameter.unit       = "dB";
196         parameter.ranges.def = 0.0f;
197         parameter.ranges.min = 0.0f;
198         parameter.ranges.max = 30.0f;
199         break;
200     case paramGainR1:
201         parameter.hints      = kParameterIsAutomable | kParameterIsOutput;
202         parameter.name       = "Gain Reduction 1";
203         parameter.symbol     = "gr1";
204         parameter.unit       = "dB";
205         parameter.ranges.def = 0.0f;
206         parameter.ranges.min = 0.0f;
207         parameter.ranges.max = 20.0f;
208         break;
209     case paramGainR2:
210         parameter.hints      = kParameterIsAutomable | kParameterIsOutput;
211         parameter.name       = "Gain Reduction 2";
212         parameter.symbol     = "gr2";
213         parameter.unit       = "dB";
214         parameter.ranges.def = 0.0f;
215         parameter.ranges.min = 0.0f;
216         parameter.ranges.max = 20.0f;
217         break;
218     case paramGainR3:
219         parameter.hints      = kParameterIsAutomable | kParameterIsOutput;
220         parameter.name       = "Gain Reduction 3";
221         parameter.symbol     = "gr3";
222         parameter.unit       = "dB";
223         parameter.ranges.def = 0.0f;
224         parameter.ranges.min = 0.0f;
225         parameter.ranges.max = 20.0f;
226         break;
227     case paramXover1:
228         parameter.hints      = kParameterIsAutomable | kParameterIsLogarithmic;
229         parameter.name       = "Crossover freq 1";
230         parameter.symbol     = "xover1";
231         parameter.unit       = "Hz";
232         parameter.ranges.def = 160.0f;
233         parameter.ranges.min = 20.0f;
234         parameter.ranges.max = 1400.0f;
235         break;
236     case paramXover2:
237         parameter.hints      = kParameterIsAutomable | kParameterIsLogarithmic;
238         parameter.name       = "Crossover freq 2";
239         parameter.symbol     = "xover2";
240         parameter.unit       = "Hz";
241         parameter.ranges.def = 1400.0f;
242         parameter.ranges.min = 1400.0f;
243         parameter.ranges.max = 14000.0f;
244         break;
245     case paramToggle1:
246         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
247         parameter.name       = "ZamComp 1 ON";
248         parameter.symbol     = "toggle1";
249         parameter.unit       = " ";
250         parameter.ranges.def = 0.0f;
251         parameter.ranges.min = 0.0f;
252         parameter.ranges.max = 1.0f;
253         break;
254     case paramToggle2:
255         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
256         parameter.name       = "ZamComp 2 ON";
257         parameter.symbol     = "toggle2";
258         parameter.unit       = " ";
259         parameter.ranges.def = 0.0f;
260         parameter.ranges.min = 0.0f;
261         parameter.ranges.max = 1.0f;
262         break;
263     case paramToggle3:
264         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
265         parameter.name       = "ZamComp 3 ON";
266         parameter.symbol     = "toggle3";
267         parameter.unit       = " ";
268         parameter.ranges.def = 0.0f;
269         parameter.ranges.min = 0.0f;
270         parameter.ranges.max = 1.0f;
271         break;
272     case paramListen1:
273         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
274         parameter.name       = "Listen 1";
275         parameter.symbol     = "listen1";
276         parameter.unit       = " ";
277         parameter.ranges.def = 0.0f;
278         parameter.ranges.min = 0.0f;
279         parameter.ranges.max = 1.0f;
280         break;
281     case paramListen2:
282         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
283         parameter.name       = "Listen 2";
284         parameter.symbol     = "listen2";
285         parameter.unit       = " ";
286         parameter.ranges.def = 0.0f;
287         parameter.ranges.min = 0.0f;
288         parameter.ranges.max = 1.0f;
289         break;
290     case paramListen3:
291         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
292         parameter.name       = "Listen 3";
293         parameter.symbol     = "listen3";
294         parameter.unit       = " ";
295         parameter.ranges.def = 0.0f;
296         parameter.ranges.min = 0.0f;
297         parameter.ranges.max = 1.0f;
298         break;
299     case paramGlobalGain:
300         parameter.hints      = kParameterIsAutomable;
301         parameter.name       = "Master Trim";
302         parameter.symbol     = "globalgain";
303         parameter.unit       = "dB";
304         parameter.ranges.def = 0.0f;
305         parameter.ranges.min = -12.0f;
306         parameter.ranges.max = 12.0f;
307         break;
308     case paramStereoDet:
309         parameter.hints      = kParameterIsAutomable | kParameterIsBoolean;
310         parameter.name       = "Detection (MAX/avg)";
311         parameter.symbol     = "stereodet";
312         parameter.unit       = " ";
313         parameter.ranges.def = 1.0f;
314         parameter.ranges.min = 0.0f;
315         parameter.ranges.max = 1.0f;
316         break;
317     case paramOutputLevelL:
318         parameter.hints      = kParameterIsOutput;
319         parameter.name       = "Output Left";
320         parameter.symbol     = "outl";
321         parameter.unit       = "dB";
322         parameter.ranges.def = -45.0f;
323         parameter.ranges.min = -45.0f;
324         parameter.ranges.max = 20.0f;
325         break;
326     case paramOutputLevelR:
327         parameter.hints      = kParameterIsOutput;
328         parameter.name       = "Output Right";
329         parameter.symbol     = "outr";
330         parameter.unit       = "dB";
331         parameter.ranges.def = -45.0f;
332         parameter.ranges.min = -45.0f;
333         parameter.ranges.max = 20.0f;
334         break;
335     case paramOutputLevelLow:
336         parameter.hints      = kParameterIsOutput;
337         parameter.name       = "Output low";
338         parameter.symbol     = "outlo";
339         parameter.unit       = "dB";
340         parameter.ranges.def = -45.0f;
341         parameter.ranges.min = -45.0f;
342         parameter.ranges.max = 20.0f;
343         break;
344     case paramOutputLevelMed:
345         parameter.hints      = kParameterIsOutput;
346         parameter.name       = "Output medium";
347         parameter.symbol     = "outmed";
348         parameter.unit       = "dB";
349         parameter.ranges.def = -45.0f;
350         parameter.ranges.min = -45.0f;
351         parameter.ranges.max = 20.0f;
352         break;
353     case paramOutputLevelHigh:
354         parameter.hints      = kParameterIsOutput;
355         parameter.name       = "Output high";
356         parameter.symbol     = "outhi";
357         parameter.unit       = "dB";
358         parameter.ranges.def = -45.0f;
359         parameter.ranges.min = -45.0f;
360         parameter.ranges.max = 20.0f;
361         break;
362     }
363 }
364 
365 void ZaMultiCompX2Plugin::initProgramName(uint32_t index, String& programName)
366 {
367 	switch(index) {
368 	case 0:
369 		programName = "Zero";
370 		break;
371 	case 1:
372 		programName = "Presence";
373 		break;
374 	}
375 }
376 
377 void ZaMultiCompX2Plugin::loadProgram(uint32_t index)
378 {
379 	switch(index) {
380 	case 0:
381 		attack[0] = 10.0;
382 		attack[1] = 10.0;
383 		attack[2] = 10.0;
384 		release[0] = 80.0;
385 		release[1] = 80.0;
386 		release[2] = 80.0;
387 		knee[0] = 0.0;
388 		knee[1] = 0.0;
389 		knee[2] = 0.0;
390 		ratio[0] = 4.0;
391 		ratio[1] = 4.0;
392 		ratio[2] = 4.0;
393 		thresdb[0] = -20.0;
394 		thresdb[1] = -18.0;
395 		thresdb[2] = -16.0;
396 		makeup[0] = 0.0;
397 		makeup[1] = 0.0;
398 		makeup[2] = 0.0;
399 		xover1 = 160.0;
400 		xover2 = 1400.0;
401 		gainr[0] = 0.0;
402 		gainr[1] = 0.0;
403 		gainr[2] = 0.0;
404 		toggle[0] = 0.0;
405 		toggle[1] = 0.0;
406 		toggle[2] = 0.0;
407 		listen[0] = 0.0;
408 		listen[1] = 0.0;
409 		listen[2] = 0.0;
410 		stereodet = 1.0;
411 		globalgain = 0.0;
412 		outl = -45.0;
413 		outr = -45.0;
414 		outlevel[0] = -45.0;
415 		outlevel[1] = -45.0;
416 		outlevel[2] = -45.0;
417 		break;
418 	case 1:
419 		attack[0] = 10.0;
420 		attack[1] = 10.0;
421 		attack[2] = 10.0;
422 		release[0] = 200.0;
423 		release[1] = 200.0;
424 		release[2] = 200.0;
425 		knee[0] = 3.0;
426 		knee[1] = 3.0;
427 		knee[2] = 3.0;
428 		ratio[0] = 5.0;
429 		ratio[1] = 5.0;
430 		ratio[2] = 5.0;
431 		thresdb[0] = -20.0;
432 		thresdb[1] = -18.0;
433 		thresdb[2] = -16.0;
434 		makeup[0] = 9.0;
435 		makeup[1] = 6.0;
436 		makeup[2] = 6.0;
437 		xover1 = 160.0;
438 		xover2 = 1400.0;
439 		gainr[0] = 0.0;
440 		gainr[1] = 0.0;
441 		gainr[2] = 0.0;
442 		toggle[0] = 1.0;
443 		toggle[1] = 1.0;
444 		toggle[2] = 1.0;
445 		listen[0] = 0.0;
446 		listen[1] = 0.0;
447 		listen[2] = 0.0;
448 		stereodet = 1.0;
449 		globalgain = 0.0;
450 		outl = -45.0;
451 		outr = -45.0;
452 		outlevel[0] = -45.0;
453 		outlevel[1] = -45.0;
454 		outlevel[2] = -45.0;
455 		break;
456 	}
457     /* Default variable values */
458     maxL = 0.f;
459     maxR = 0.f;
460 
461     /* reset filter values */
462     activate();
463 }
464 
465 // -----------------------------------------------------------------------
466 // Internal data
467 
468 float ZaMultiCompX2Plugin::getParameterValue(uint32_t index) const
469 {
470     switch (index)
471     {
472     case paramAttack1:
473         return attack[0];
474         break;
475     case paramAttack2:
476         return attack[1];
477         break;
478     case paramAttack3:
479         return attack[2];
480         break;
481     case paramRelease1:
482         return release[0];
483         break;
484     case paramRelease2:
485         return release[1];
486         break;
487     case paramRelease3:
488         return release[2];
489         break;
490     case paramKnee1:
491         return knee[0];
492         break;
493     case paramKnee2:
494         return knee[1];
495         break;
496     case paramKnee3:
497         return knee[2];
498         break;
499     case paramRatio1:
500         return ratio[0];
501         break;
502     case paramRatio2:
503         return ratio[1];
504         break;
505     case paramRatio3:
506         return ratio[2];
507         break;
508     case paramThresh1:
509         return thresdb[0];
510         break;
511     case paramThresh2:
512         return thresdb[1];
513         break;
514     case paramThresh3:
515         return thresdb[2];
516         break;
517     case paramMakeup1:
518         return makeup[0];
519         break;
520     case paramMakeup2:
521         return makeup[1];
522         break;
523     case paramMakeup3:
524         return makeup[2];
525         break;
526     case paramGainR1:
527         return gainr[0];
528         break;
529     case paramGainR2:
530         return gainr[1];
531         break;
532     case paramGainR3:
533         return gainr[2];
534         break;
535     case paramXover1:
536         return xover1;
537         break;
538     case paramXover2:
539         return xover2;
540         break;
541     case paramToggle1:
542         return toggle[0];
543         break;
544     case paramToggle2:
545         return toggle[1];
546         break;
547     case paramToggle3:
548         return toggle[2];
549         break;
550     case paramListen1:
551         return listen[0];
552         break;
553     case paramListen2:
554         return listen[1];
555         break;
556     case paramListen3:
557         return listen[2];
558         break;
559     case paramGlobalGain:
560         return globalgain;
561         break;
562     case paramStereoDet:
563         return stereodet;
564         break;
565     case paramOutputLevelL:
566         return outl;
567         break;
568     case paramOutputLevelR:
569         return outr;
570         break;
571     case paramOutputLevelLow:
572         return outlevel[0];
573         break;
574     case paramOutputLevelMed:
575         return outlevel[1];
576         break;
577     case paramOutputLevelHigh:
578         return outlevel[2];
579         break;
580     default:
581         return 0.0f;
582     }
583 }
584 
585 void ZaMultiCompX2Plugin::setParameterValue(uint32_t index, float value)
586 {
587     switch (index)
588     {
589     case paramAttack1:
590         attack[0] = value;
591         break;
592     case paramAttack2:
593         attack[1] = value;
594         break;
595     case paramAttack3:
596         attack[2] = value;
597         break;
598     case paramRelease1:
599         release[0] = value;
600         break;
601     case paramRelease2:
602         release[1] = value;
603         break;
604     case paramRelease3:
605         release[2] = value;
606         break;
607     case paramKnee1:
608         knee[0] = value;
609         break;
610     case paramKnee2:
611         knee[1] = value;
612         break;
613     case paramKnee3:
614         knee[2] = value;
615         break;
616     case paramRatio1:
617         ratio[0] = value;
618         break;
619     case paramRatio2:
620         ratio[1] = value;
621         break;
622     case paramRatio3:
623         ratio[2] = value;
624         break;
625     case paramThresh1:
626         thresdb[0] = value;
627         break;
628     case paramThresh2:
629         thresdb[1] = value;
630         break;
631     case paramThresh3:
632         thresdb[2] = value;
633         break;
634     case paramMakeup1:
635         makeup[0] = value;
636         break;
637     case paramMakeup2:
638         makeup[1] = value;
639         break;
640     case paramMakeup3:
641         makeup[2] = value;
642         break;
643     case paramGainR1:
644         gainr[0] = value;
645         break;
646     case paramGainR2:
647         gainr[1] = value;
648         break;
649     case paramGainR3:
650         gainr[2] = value;
651         break;
652     case paramXover1:
653         xover1 = value;
654         break;
655     case paramXover2:
656         xover2 = value;
657         break;
658     case paramToggle1:
659         toggle[0] = value;
660         if (value == 0.f)
661 	    gainr[0] = 0.f;
662         break;
663     case paramToggle2:
664         toggle[1] = value;
665         if (value == 0.f)
666 	    gainr[1] = 0.f;
667         break;
668     case paramToggle3:
669         toggle[2] = value;
670         if (value == 0.f)
671 	    gainr[2] = 0.f;
672         break;
673     case paramListen1:
674         listen[0] = value;
675         if (value == 0.f)
676 	    gainr[0] = 0.f;
677         break;
678     case paramListen2:
679         listen[1] = value;
680         if (value == 0.f)
681 	    gainr[1] = 0.f;
682         break;
683     case paramListen3:
684         listen[2] = value;
685         if (value == 0.f)
686 	    gainr[2] = 0.f;
687         break;
688     case paramGlobalGain:
689         globalgain = value;
690         break;
691     case paramStereoDet:
692         stereodet = value;
693         break;
694     case paramOutputLevelL:
695         outl = value;
696         break;
697     case paramOutputLevelR:
698         outr = value;
699         break;
700     case paramOutputLevelLow:
701         outlevel[0] = value;
702         break;
703     case paramOutputLevelMed:
704         outlevel[1] = value;
705         break;
706     case paramOutputLevelHigh:
707         outlevel[2] = value;
708         break;
709     }
710 }
711 
712 // -----------------------------------------------------------------------
713 // Process
714 
715 void ZaMultiCompX2Plugin::activate()
716 {
717         int i,j;
718         for (i = 0; i < MAX_COMP; i++)
719         	for (j = 0; j < 2; j++)
720                 	old_yl[j][i]=old_y1[j][i]=old_yg[j][i]=0.f;
721 
722 	for (j = 0; j < 2; j++)
723 		old_ll[j]=old_l1[j]=0.f;
724 
725 	for (j = 0; j < 2; j++) {
726 		for (i = 0; i < MAX_FILT; i++) {
727 			simper[j][i].k = 0.f;
728 			simper[j][i].g = 0.f;
729 			simper[j][i].s[0] = 0.f;
730 			simper[j][i].s[1] = 0.f;
731 		}
732         }
733 	maxL = maxR = 0.f;
734 	pos[0] = 0;
735 	pos[1] = 0;
736 	pos[2] = 0;
737 
738 	oldxover1 = 0.f;
739 	oldxover2 = 0.f;
740 }
741 
742 /*
743  * A. Simper's filters
744  * https://cytomic.com/files/dsp/SvfInputMixing.pdf
745  */
746 
747 void ZaMultiCompX2Plugin::linear_svf_set_xover(struct linear_svf *self, float sample_rate, float cutoff, float resonance)
748 {
749 	double w;
750 
751 	self->k = 2. - 2. * resonance;
752 	w = M_PI * cutoff / sample_rate;
753 	self->g = tan(w);
754 }
755 
756 void ZaMultiCompX2Plugin::linear_svf_reset(struct linear_svf *self)
757 {
758 	self->s[0] = self->s[1] = 0.f;
759 }
760 
761 float ZaMultiCompX2Plugin::run_linear_svf_xover(struct linear_svf *self, float in, float mixlow, float mixhigh)
762 {
763 	double v[3];
764 	double g = self->g;
765 	double k = self->k;
766 	double s0 = self->s[0];
767 	double s1 = self->s[1];
768 	double g2 = g*g;
769 	double vhigh = in * mixhigh;
770 	double vband = in * 0.75;
771 	double vlow = in * mixlow;
772 
773 	v[0] = in;
774 	v[1] = -1. / (1. + g2 + g*k) * (-s0 + g*s1 - g*k*s0 + g2*vband + g*vhigh - g*vlow - g2*k*vlow);
775 	v[2] = -1. / (1. + g2 + g*k) * (-g*s0 - s1 - g*vband + g2*vhigh + g*k*vhigh - g2*vlow);
776 	self->s[0] = 2. * v[1] - s0;
777 	self->s[1] = 2. * v[2] - s1;
778 
779 	return (float)(vhigh + v[2]);
780 }
781 
782 void ZaMultiCompX2Plugin::calc_lr4(float f, int i)
783 {
784 	float srate = getSampleRate();
785 
786 	linear_svf_set_xover(&simper[0][i], srate, f, 0.25);
787 	linear_svf_set_xover(&simper[1][i], srate, f, 0.25);
788 }
789 
790 void ZaMultiCompX2Plugin::run_lr4(int i, float in, float *outlo, float *outhi)
791 {
792 	*outlo = run_linear_svf_xover(&simper[0][i], in, 1., 0.);
793 	*outhi = run_linear_svf_xover(&simper[1][i], in, 0., 1.);
794 }
795 
796 void ZaMultiCompX2Plugin::run_comp(int k, float inL, float inR, float *outL, float *outR)
797 {
798 	float srate = getSampleRate();
799         float width = (6.f * knee[k]) + 0.01;
800         float attack_coeff = exp(-1000.f/(attack[k] * srate));
801         float release_coeff = exp(-1000.f/(release[k] * srate));
802 	int stereolink = (stereodet > 0.5f) ? STEREOLINK_MAX : STEREOLINK_AVERAGE;
803 	float checkwidth = 0.f;
804         float cdb=0.f;
805         float Lgain = 1.f;
806         float Rgain = 1.f;
807         float Lxg, Lyg;
808         float Rxg, Ryg;
809         float Lxl, Lyl;
810         float Rxl, Ryl;
811 
812         Lyg = Ryg = 0.f;
813 	inL = sanitize_denormal(inL);
814 	inR = sanitize_denormal(inR);
815         Lxg = (inL==0.f) ? -160.f : to_dB(fabsf(inL));
816         Rxg = (inR==0.f) ? -160.f : to_dB(fabsf(inR));
817         Lxg = sanitize_denormal(Lxg);
818         Rxg = sanitize_denormal(Rxg);
819 
820 	checkwidth = 2.f*fabsf(Lxg-thresdb[k]);
821 	if (2.f*(Lxg-thresdb[k]) < -width) {
822 		Lyg = Lxg;
823 	} else if (checkwidth <= width) {
824 		Lyg = Lxg + (1.f/ratio[k]-1.f)*(Lxg-thresdb[k]+width/2.f)*(Lxg-thresdb[k]+width/2.f)/(2.f*width);
825 		Lyg = sanitize_denormal(Lyg);
826 	} else if (2.f*(Lxg-thresdb[k]) > width) {
827 		Lyg = thresdb[k] + (Lxg-thresdb[k])/ratio[k];
828 		Lyg = sanitize_denormal(Lyg);
829 	}
830 
831 	checkwidth = 2.f*fabsf(Rxg-thresdb[k]);
832 	if (2.f*(Rxg-thresdb[k]) < -width) {
833 		Ryg = Rxg;
834 	} else if (checkwidth <= width) {
835 		Ryg = Rxg + (1.f/ratio[k]-1.f)*(Rxg-thresdb[k]+width/2.f)*(Rxg-thresdb[k]+width/2.f)/(2.f*width);
836 		Ryg = sanitize_denormal(Ryg);
837 	} else if (2.f*(Rxg-thresdb[k]) > width) {
838 		Ryg = thresdb[k] + (Rxg-thresdb[k])/ratio[k];
839 		Ryg = sanitize_denormal(Ryg);
840 	}
841 
842         if (stereolink == STEREOLINK_MAX) {
843                 Lxl = Rxl = fmaxf(Lxg - Lyg, Rxg - Ryg);
844         } else {
845                 Lxl = Rxl = (Lxg - Lyg + Rxg - Ryg) / 2.f;
846         }
847 
848         old_yl[0][k] = sanitize_denormal(old_yl[0][k]);
849         old_yl[1][k] = sanitize_denormal(old_yl[1][k]);
850 
851 
852 	if (Lxl < old_yl[0][k]) {
853 		Lyl = release_coeff * old_yl[0][k] + (1.f-release_coeff)*Lxl;
854 	} else if (Lxl > old_yl[0][k]) {
855 		Lyl = attack_coeff * old_yl[0][k]+(1.f-attack_coeff)*Lxl;
856 	} else {
857 		Lyl = Lxl;
858 	}
859         Lyl = sanitize_denormal(Lyl);
860 
861         cdb = -Lyl;
862         Lgain = from_dB(cdb);
863 
864 	if (Rxl < old_yl[1][k]) {
865 		Ryl = release_coeff * old_yl[1][k] + (1.f-release_coeff)*Rxl;
866 	} else if (Rxl > old_yl[1][k]) {
867 		Ryl = attack_coeff * old_yl[1][k]+(1.f-attack_coeff)*Rxl;
868 	} else {
869 		Ryl = Rxl;
870 	}
871         Ryl = sanitize_denormal(Ryl);
872 
873         cdb = -Ryl;
874         Rgain = from_dB(cdb);
875 
876         if (stereolink == STEREOLINK_MAX)
877 		gainr[k] = fmaxf(Lyl, Ryl);
878 	else
879         	gainr[k] = (Lyl + Ryl) / 2.f;
880 
881 	*outL = inL * Lgain;
882 	*outR = inR * Rgain;
883 
884         old_yl[0][k] = Lyl;
885         old_yl[1][k] = Ryl;
886         old_yg[0][k] = Lyg;
887         old_yg[1][k] = Ryg;
888 }
889 
890 void ZaMultiCompX2Plugin::pushsample(float sample, int k)
891 {
892 	const float rate = 2. * M_PI * 5.;
893 	float lpf = rate / (rate + getSampleRate());
894 
895 	average[k] += lpf * (sample*sample - average[k]);
896 }
897 
898 void ZaMultiCompX2Plugin::run(const float** inputs, float** outputs, uint32_t frames)
899 {
900 	float maxxL = 0.;
901 	float maxxR = 0.;
902 	uint32_t i;
903 
904         int tog1 = (toggle[0] > 0.5f) ? 1 : 0;
905         int tog2 = (toggle[1] > 0.5f) ? 1 : 0;
906         int tog3 = (toggle[2] > 0.5f) ? 1 : 0;
907 
908         int listen1 = (listen[0] > 0.5f) ? 1 : 0;
909         int listen2 = (listen[1] > 0.5f) ? 1 : 0;
910         int listen3 = (listen[2] > 0.5f) ? 1 : 0;
911 
912         if (oldxover1 != xover1) {
913 		calc_lr4(xover1, 0);
914 		calc_lr4(xover1, 1);
915 		oldxover1 = xover1;
916 	}
917 
918         if (oldxover2 != xover2) {
919 		calc_lr4(xover2, 2);
920 		calc_lr4(xover2, 3);
921 		oldxover2 = xover2;
922 	}
923 
924         for (i = 0; i < frames; ++i) {
925                 float tmp1[2], tmp2[2], tmp3[2];
926 		float fil1[2], fil2[2], fil3[2], fil4[2];
927 		float outL[MAX_COMP+1] = {0.f};
928 		float outR[MAX_COMP+1] = {0.f};
929 		float inl = sanitize_denormal(inputs[0][i]);
930 		float inr = sanitize_denormal(inputs[1][i]);
931 		inl = (fabsf(inl) < DANGER) ? inl : 0.f;
932 		inr = (fabsf(inr) < DANGER) ? inr : 0.f;
933 
934 		int listenmode = 0;
935 
936 		// Interleaved channel processing
937                 run_lr4(0, inl, &fil1[0], &fil2[0]);
938                 run_lr4(1, inr, &fil1[1], &fil2[1]);
939                 run_lr4(2, fil2[0], &fil3[0], &fil4[0]);
940                 run_lr4(3, fil2[1], &fil3[1], &fil4[1]);
941 
942 		pushsample(std::max(fil1[0], fil1[1]), 0);
943 		outlevel[0] = sqrt(average[0]);
944 		outlevel[0] = (outlevel[0] == 0.f) ? -45.0 : to_dB(outlevel[0]);
945 		if (tog1)
946 			run_comp(0, fil1[0], fil1[1], &outL[0], &outR[0]);
947 
948 		tmp1[0] = tog1 ? outL[0] * from_dB(makeup[0]) : fil1[0];
949                 tmp1[1] = tog1 ? outR[0] * from_dB(makeup[0]) : fil1[1];
950 
951 		pushsample(std::max(fil3[0], fil3[1]), 1);
952 		outlevel[1] = sqrt(average[1]);
953 		outlevel[1] = (outlevel[1] == 0.f) ? -45.0 : to_dB(outlevel[1]);
954 		if (tog2)
955 			run_comp(1, fil3[0], fil3[1], &outL[1], &outR[1]);
956 
957                 tmp2[0] = tog2 ? outL[1] * from_dB(makeup[1]) : fil3[0];
958                 tmp2[1] = tog2 ? outR[1] * from_dB(makeup[1]) : fil3[1];
959 
960 		pushsample(std::max(fil4[0], fil4[1]), 2);
961 		outlevel[2] = sqrt(average[2]);
962 		outlevel[2] = (outlevel[2] == 0.f) ? -45.0 : to_dB(outlevel[2]);
963 		if (tog3)
964 			run_comp(2, fil4[0], fil4[1], &outL[2], &outR[2]);
965 
966                 tmp3[0] = tog3 ? outL[2] * from_dB(makeup[2]) : fil4[0];
967                 tmp3[1] = tog3 ? outR[2] * from_dB(makeup[2]) : fil4[1];
968 
969 		outputs[0][i] = outputs[1][i] = 0.f;
970 		if (listen1) {
971 			listenmode = 1;
972 			outputs[0][i] += outL[0] * tog1*from_dB(makeup[0])
973 					+ (1.-tog1) * tmp1[0];
974 			outputs[1][i] += outR[0] * tog1*from_dB(makeup[0])
975 					+ (1.-tog1) * tmp1[1];
976 		}
977 		if (listen2) {
978 			listenmode = 1;
979 			outputs[0][i] += outL[1] * tog2*from_dB(makeup[1])
980 					+ (1.-tog2) * tmp2[0];
981 			outputs[1][i] += outR[1] * tog2*from_dB(makeup[1])
982 					+ (1.-tog2) * tmp2[1];
983 		}
984 		if (listen3) {
985 			listenmode = 1;
986 			outputs[0][i] += outL[2] * tog3*from_dB(makeup[2])
987 					+ (1.-tog3) * tmp3[0];
988 			outputs[1][i] += outR[2] * tog3*from_dB(makeup[2])
989 					+ (1.-tog3) * tmp3[1];
990 		}
991 		if (!listenmode) {
992 			outputs[0][i] = tmp1[0] + tmp2[0] + tmp3[0];
993 			outputs[1][i] = tmp1[1] + tmp2[1] + tmp3[1];
994 		}
995                 outputs[0][i] = sanitize_denormal(outputs[0][i]);
996                 outputs[1][i] = sanitize_denormal(outputs[1][i]);
997                 outputs[0][i] *= from_dB(globalgain);
998                 outputs[1][i] *= from_dB(globalgain);
999 
1000 		maxxL = (fabsf(outputs[0][i]) > maxxL) ? fabsf(outputs[0][i]) : sanitize_denormal(maxxL);
1001 		maxxR = (fabsf(outputs[1][i]) > maxxR) ? fabsf(outputs[1][i]) : sanitize_denormal(maxxR);
1002         }
1003 	outl = (maxxL == 0.f) ? -160.f : to_dB(maxxL);
1004 	outr = (maxxR == 0.f) ? -160.f : to_dB(maxxR);
1005 }
1006 
1007 // -----------------------------------------------------------------------
1008 
1009 Plugin* createPlugin()
1010 {
1011     return new ZaMultiCompX2Plugin();
1012 }
1013 
1014 // -----------------------------------------------------------------------
1015 
1016 END_NAMESPACE_DISTRHO
1017