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