1 /*
2   rakarrack - a guitar effects software
3 
4  Compressor.C  -  Compressor Effect
5  Based on artscompressor.cc by Matthias Kretz <kretz@kde.org>
6  Stefan Westerfeld <stefan@space.twc.de>
7 
8   Copyright (C) 2008-2010 Josep Andreu
9   Author: Josep Andreu
10 
11 	Patches:
12 	September 2009  Ryan Billing (a.k.a. Transmogrifox)
13 		--Modified DSP code to fix discontinuous gain change at threshold.
14 		--Improved automatic gain adjustment function
15 		--Improved handling of knee
16 		--Added support for user-adjustable knee
17 		--See inline comments
18 
19  This program is free software; you can redistribute it and/or modify
20  it under the terms of version 2 of the GNU General Public License
21  as published by the Free Software Foundation.
22 
23  This program is distributed in the hope that it will be useful,
24  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  GNU General Public License (version 2) for more details.
27 
28  You should have received a copy of the GNU General Public License
29  (version2)  along with this program; if not, write to the Free Software
30  Foundation,
31  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
32 
33 */
34 
35 #include <math.h>
36 #include "global.h"
37 #include "Compressor.h"
38 #define  MIN_GAIN  0.00001f        // -100dB  This will help prevent evaluation of denormal numbers
39 
Compressor(float * efxoutl_,float * efxoutr_,double samplerate)40 Compressor::Compressor (float * efxoutl_, float * efxoutr_, double samplerate)
41 {
42     efxoutl = efxoutl_;
43     efxoutr = efxoutr_;
44 
45     rvolume = 0.0f;
46     rvolume_db = 0.0f;
47     lvolume = 0.0f;
48     lvolume_db = 0.0f;
49     tthreshold = -24;
50     tratio = 4;
51     toutput = -10;
52     tatt = 20;
53     trel = 50;
54     a_out = 1;
55     stereo = 0;
56     tknee = 30;
57     rgain = 1.0f;
58     rgain_old = 1.0f;
59     lgain = 1.0f;
60     lgain_old = 1.0f;
61     lgain_t = 1.0f;
62     rgain_t = 1.0f;
63     ratio = 1.0;
64     kpct = 0.0f;
65     peak = 0;
66     lpeak = 0.0f;
67     rpeak = 0.0f;
68     rell = relr = attr = attl = 1.0f;
69 
70     ltimer = rtimer = 0;
71     hold = (int) (samplerate*0.0125);  //12.5ms
72     clipping = 0;
73     limit = 0;
74 
75     cSAMPLE_RATE = 1.0/samplerate;
76 }
77 
~Compressor()78 Compressor::~Compressor ()
79 {
80 }
81 
82 
83 void
cleanup()84 Compressor::cleanup ()
85 {
86 
87     lgain = rgain = 1.0f;
88     lgain_old = rgain_old = 1.0f;
89     rpeak = 0.0f;
90     lpeak = 0.0f;
91     limit = 0;
92     clipping = 0;
93 }
94 
95 
96 void
Compressor_Change(int np,int value)97 Compressor::Compressor_Change (int np, int value)
98 {
99 
100     switch (np) {
101 
102     case 1:
103         tthreshold = value;
104         thres_db = (float)tthreshold;    //implicit type cast int to float
105         break;
106 
107     case 2:
108         tratio = value;
109         ratio = (float)tratio;
110         break;
111 
112     case 3:
113         toutput = value;
114         break;
115 
116     case 4:
117         tatt = value;
118         att = cSAMPLE_RATE /(((float)value / 1000.0f) + cSAMPLE_RATE);
119         attr = att;
120         attl = att;
121         break;
122 
123     case 5:
124         trel = value;
125         rel = cSAMPLE_RATE /(((float)value / 1000.0f) + cSAMPLE_RATE);
126         rell = rel;
127         relr = rel;
128         break;
129 
130     case 6:
131         a_out = value;
132         break;
133 
134     case 7:
135         tknee = value;  //knee expressed a percentage of range between thresh and zero dB
136         kpct = (float)tknee/100.1f;
137         break;
138 
139     case 8:
140         stereo = value;
141         break;
142     case 9:
143         peak = value;
144         break;
145 
146 
147     }
148 
149     kratio = logf(ratio)/LOG_2;  //  Log base 2 relationship matches slope
150     knee = -kpct*thres_db;
151 
152     coeff_kratio = 1.0 / kratio;
153     coeff_ratio = 1.0 / ratio;
154     coeff_knee = 1.0 / knee;
155 
156     coeff_kk = knee * coeff_kratio;
157 
158 
159     thres_mx = thres_db + knee;  //This is the value of the input when the output is at t+k
160     makeup = -thres_db - knee/kratio + thres_mx/ratio;
161     makeuplin = dB2rap(makeup);
162     if (a_out)
163         outlevel = dB2rap((float)toutput) * makeuplin;
164     else
165         outlevel = dB2rap((float)toutput);
166 
167 }
168 
169 int
getpar(int np)170 Compressor::getpar (int np)
171 {
172 
173     switch (np)
174 
175     {
176 
177     case 1:
178         return (tthreshold);
179         break;
180     case 2:
181         return (tratio);
182         break;
183     case 3:
184         return (toutput);
185         break;
186     case 4:
187         return (tatt);
188         break;
189     case 5:
190         return (trel);
191         break;
192     case 6:
193         return (a_out);
194         break;
195     case 7:
196         return (tknee);
197         break;
198     case 8:
199         return (stereo);
200         break;
201     case 9:
202         return (peak);
203         break;
204     }
205 
206     return (0);
207 
208 }
209 
210 void
Compressor_Change_Preset(int dgui,int npreset)211 Compressor::Compressor_Change_Preset (int dgui, int npreset)
212 {
213 
214     const int PRESET_SIZE = 10;
215     const int NUM_PRESETS = 7;
216     int pdata[PRESET_SIZE];
217     int presets[NUM_PRESETS][PRESET_SIZE] = {
218         //2:1
219         {-30, 2, -6, 20, 120, 1, 0, 0, 0},
220         //4:1
221         {-26, 4, -8, 20, 120, 1, 10, 0, 0},
222         //8:1
223         {-24, 8, -12, 20, 35, 1, 30, 0, 0},
224         //Final Limiter
225         {-1, 15, 0, 5, 250, 0 ,0 ,1 ,1},
226         //HarmonicEnhancer
227         {-20, 15, -3, 5, 50, 0 ,0 ,1 ,1},
228         //Band CompBand
229         {-3, 2, 0, 5, 50, 1, 0, 1, 0},
230         //End CompBand
231         {-60, 2, 0, 10, 500, 1, 0, 1, 1},
232 
233 
234     };
235 
236     if((dgui)&&(npreset>2)) {
237         Fpre->ReadPreset(1,npreset-2,pdata);
238         for (int n = 1; n < PRESET_SIZE; n++)
239             Compressor_Change (n , pdata[n-1]);
240 
241     } else {
242         for (int n = 1; n < PRESET_SIZE; n++)
243             Compressor_Change (n , presets[npreset][n-1]);
244     }
245 
246 }
247 
248 
249 
250 void
out(float * efxoutl,float * efxoutr,uint32_t period)251 Compressor::out (float *efxoutl, float *efxoutr, uint32_t period)
252 {
253 
254     unsigned int i;
255 
256 
257     for (i = 0; i < period; i++) {
258         float rdelta = 0.0f;
259         float ldelta = 0.0f;
260 //Right Channel
261 
262         if(peak) {
263             if (rtimer > hold) {
264                 rpeak *= 0.9998f;   //The magic number corresponds to ~0.1s based on T/(RC + T),
265                 rtimer--;
266             }
267             if (ltimer > hold) {
268                 lpeak *= 0.9998f;	//leaky peak detector.
269                 ltimer --;  //keeps the timer from eventually exceeding max int & rolling over
270             }
271             ltimer++;
272             rtimer++;
273             if(rpeak<fabs(efxoutr[i])) {
274                 rpeak = fabs(efxoutr[i]);
275                 rtimer = 0;
276             }
277             if(lpeak<fabs(efxoutl[i])) {
278                 lpeak = fabs(efxoutl[i]);
279                 ltimer = 0;
280             }
281 
282             if(lpeak>20.0f) lpeak = 20.0f;
283             if(rpeak>20.0f) rpeak = 20.0f; //keeps limiter from getting locked up when signal levels go way out of bounds (like hundreds)
284 
285         } else {
286             rpeak = efxoutr[i];
287             lpeak = efxoutl[i];
288         }
289 
290         if(stereo) {
291             rdelta = fabsf (rpeak);
292             if(rvolume < 0.9f) {
293                 attr = att;
294                 relr = rel;
295             } else if (rvolume < 1.0f) {
296                 attr = att + ((1.0f - att)*(rvolume - 0.9f)*10.0f);	//dynamically change attack time for limiting mode
297                 relr = rel/(1.0f + (rvolume - 0.9f)*9.0f);  //release time gets longer when signal is above limiting
298             } else {
299                 attr = 1.0f;
300                 relr = rel*0.1f;
301             }
302 
303             if (rdelta > rvolume)
304                 rvolume = attr * rdelta + (1.0f - attr)*rvolume;
305             else
306                 rvolume = relr * rdelta + (1.0f - relr)*rvolume;
307 
308 
309             rvolume_db = rap2dB (rvolume);
310             if (rvolume_db < thres_db) {
311                 rgain = outlevel;
312             } else if (rvolume_db < thres_mx) {
313                 //Dynamic ratio that depends on volume.  As can be seen, ratio starts
314                 //at something negligibly larger than 1 once volume exceeds thres, and increases toward selected
315                 // ratio by the time it has reached thres_mx.  --Transmogrifox
316 
317                 eratio = 1.0f + (kratio-1.0f)*(rvolume_db-thres_db)* coeff_knee;
318                 rgain =   outlevel*dB2rap(thres_db + (rvolume_db-thres_db)/eratio - rvolume_db);
319             } else {
320                 rgain = outlevel*dB2rap(thres_db + coeff_kk + (rvolume_db-thres_mx)*coeff_ratio - rvolume_db);
321                 limit = 1;
322             }
323 
324             if ( rgain < MIN_GAIN) rgain = MIN_GAIN;
325             rgain_t = .4f * rgain + .6f * rgain_old;
326         };
327 
328 //Left Channel
329         if(stereo)  {
330             ldelta = fabsf (lpeak);
331         } else  {
332             ldelta = 0.5f*(fabsf (lpeak) + fabsf (rpeak));
333         };  //It's not as efficient to check twice, but it's small expense worth code clarity
334 
335         if(lvolume < 0.9f) {
336             attl = att;
337             rell = rel;
338         } else if (lvolume < 1.0f) {
339             attl = att + ((1.0f - att)*(lvolume - 0.9f)*10.0f);	//dynamically change attack time for limiting mode
340             rell = rel/(1.0f + (lvolume - 0.9f)*9.0f);  //release time gets longer when signal is above limiting
341         } else {
342             attl = 1.0f;
343             rell = rel*0.1f;
344         }
345 
346         if (ldelta > lvolume)
347             lvolume = attl * ldelta + (1.0f - attl)*lvolume;
348         else
349             lvolume = rell*ldelta + (1.0f - rell)*lvolume;
350 
351         lvolume_db = rap2dB (lvolume);
352 
353         if (lvolume_db < thres_db) {
354             lgain = outlevel;
355         } else if (lvolume_db < thres_mx) { //knee region
356             eratio = 1.0f + (kratio-1.0f)*(lvolume_db-thres_db)* coeff_knee;
357             lgain =   outlevel*dB2rap(thres_db + (lvolume_db-thres_db)/eratio - lvolume_db);
358         } else {
359             lgain = outlevel*dB2rap(thres_db + coeff_kk + (lvolume_db-thres_mx)*coeff_ratio - lvolume_db);
360             limit = 1;
361         }
362 
363 
364         if ( lgain < MIN_GAIN) lgain = MIN_GAIN;
365         lgain_t = .4f * lgain + .6f * lgain_old;
366 
367         if (stereo) {
368             efxoutl[i] *= lgain_t;
369             efxoutr[i] *= rgain_t;
370             rgain_old = rgain;
371             lgain_old = lgain;
372         } else {
373             efxoutl[i] *= lgain_t;
374             efxoutr[i] *= lgain_t;
375             lgain_old = lgain;
376         }
377 
378         if(peak) {
379             if(efxoutl[i]>0.999f) {            //output hard limiting
380                 efxoutl[i] = 0.999f;
381                 clipping = 1;
382             }
383             if(efxoutl[i]<-0.999f) {
384                 efxoutl[i] = -0.999f;
385                 clipping = 1;
386             }
387             if(efxoutr[i]>0.999f) {
388                 efxoutr[i] = 0.999f;
389                 clipping = 1;
390             }
391             if(efxoutr[i]<-0.999f) {
392                 efxoutr[i] = -0.999f;
393                 clipping = 1;
394             }
395             //highly probably there is a more elegant way to do that, but what the hey...
396         }
397     }
398 
399 }
400 
401