1 /*
2  * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.media.sound;
27 
28 /**
29  * Infinite impulse response (IIR) filter class.
30  *
31  * The filters where implemented and adapted using algorithms from musicdsp.org
32  * archive: 1-RC and C filter, Simple 2-pole LP LP and HP filter, biquad,
33  * tweaked butterworth RBJ Audio-EQ-Cookbook, EQ filter kookbook
34  *
35  * @author Karl Helgason
36  */
37 public final class SoftFilter {
38 
39     public static final int FILTERTYPE_LP6 = 0x00;
40     public static final int FILTERTYPE_LP12 = 0x01;
41     public static final int FILTERTYPE_HP12 = 0x11;
42     public static final int FILTERTYPE_BP12 = 0x21;
43     public static final int FILTERTYPE_NP12 = 0x31;
44     public static final int FILTERTYPE_LP24 = 0x03;
45     public static final int FILTERTYPE_HP24 = 0x13;
46 
47     //
48     // 0x0 = 1st-order, 6 dB/oct
49     // 0x1 = 2nd-order, 12 dB/oct
50     // 0x2 = 3rd-order, 18 dB/oct
51     // 0x3 = 4th-order, 24 db/oct
52     //
53     // 0x00 = LP, Low Pass Filter
54     // 0x10 = HP, High Pass Filter
55     // 0x20 = BP, Band Pass Filter
56     // 0x30 = NP, Notch or Band Elimination Filter
57     //
58     private int filtertype = FILTERTYPE_LP6;
59     private final float samplerate;
60     private float x1;
61     private float x2;
62     private float y1;
63     private float y2;
64     private float xx1;
65     private float xx2;
66     private float yy1;
67     private float yy2;
68     private float a0;
69     private float a1;
70     private float a2;
71     private float b1;
72     private float b2;
73     private float q;
74     private float gain = 1;
75     private float wet = 0;
76     private float last_wet = 0;
77     private float last_a0;
78     private float last_a1;
79     private float last_a2;
80     private float last_b1;
81     private float last_b2;
82     private float last_q;
83     private float last_gain;
84     private boolean last_set = false;
85     private double cutoff = 44100;
86     private double resonancedB = 0;
87     private boolean dirty = true;
88 
SoftFilter(float samplerate)89     public SoftFilter(float samplerate) {
90         this.samplerate = samplerate;
91         dirty = true;
92     }
93 
setFrequency(double cent)94     public void setFrequency(double cent) {
95         if (cutoff == cent)
96             return;
97         cutoff = cent;
98         dirty = true;
99     }
100 
setResonance(double db)101     public void setResonance(double db) {
102         if (resonancedB == db)
103             return;
104         resonancedB = db;
105         dirty = true;
106     }
107 
reset()108     public void reset() {
109         dirty = true;
110         last_set = false;
111         x1 = 0;
112         x2 = 0;
113         y1 = 0;
114         y2 = 0;
115         xx1 = 0;
116         xx2 = 0;
117         yy1 = 0;
118         yy2 = 0;
119         wet = 0.0f;
120         gain = 1.0f;
121         a0 = 0;
122         a1 = 0;
123         a2 = 0;
124         b1 = 0;
125         b2 = 0;
126     }
127 
setFilterType(int filtertype)128     public void setFilterType(int filtertype) {
129         this.filtertype = filtertype;
130     }
131 
processAudio(SoftAudioBuffer sbuffer)132     public void processAudio(SoftAudioBuffer sbuffer) {
133         if (filtertype == FILTERTYPE_LP6)
134             filter1(sbuffer);
135         if (filtertype == FILTERTYPE_LP12)
136             filter2(sbuffer);
137         if (filtertype == FILTERTYPE_HP12)
138             filter2(sbuffer);
139         if (filtertype == FILTERTYPE_BP12)
140             filter2(sbuffer);
141         if (filtertype == FILTERTYPE_NP12)
142             filter2(sbuffer);
143         if (filtertype == FILTERTYPE_LP24)
144             filter4(sbuffer);
145         if (filtertype == FILTERTYPE_HP24)
146             filter4(sbuffer);
147     }
148 
filter4(SoftAudioBuffer sbuffer)149     public void filter4(SoftAudioBuffer sbuffer) {
150 
151         float[] buffer = sbuffer.array();
152 
153         if (dirty) {
154             filter2calc();
155             dirty = false;
156         }
157         if (!last_set) {
158             last_a0 = a0;
159             last_a1 = a1;
160             last_a2 = a2;
161             last_b1 = b1;
162             last_b2 = b2;
163             last_gain = gain;
164             last_wet = wet;
165             last_set = true;
166         }
167 
168         if (wet > 0 || last_wet > 0) {
169 
170             int len = buffer.length;
171             float a0 = this.last_a0;
172             float a1 = this.last_a1;
173             float a2 = this.last_a2;
174             float b1 = this.last_b1;
175             float b2 = this.last_b2;
176             float gain = this.last_gain;
177             float wet = this.last_wet;
178             float a0_delta = (this.a0 - this.last_a0) / len;
179             float a1_delta = (this.a1 - this.last_a1) / len;
180             float a2_delta = (this.a2 - this.last_a2) / len;
181             float b1_delta = (this.b1 - this.last_b1) / len;
182             float b2_delta = (this.b2 - this.last_b2) / len;
183             float gain_delta = (this.gain - this.last_gain) / len;
184             float wet_delta = (this.wet - this.last_wet) / len;
185             float x1 = this.x1;
186             float x2 = this.x2;
187             float y1 = this.y1;
188             float y2 = this.y2;
189             float xx1 = this.xx1;
190             float xx2 = this.xx2;
191             float yy1 = this.yy1;
192             float yy2 = this.yy2;
193 
194             if (wet_delta != 0) {
195                 for (int i = 0; i < len; i++) {
196                     a0 += a0_delta;
197                     a1 += a1_delta;
198                     a2 += a2_delta;
199                     b1 += b1_delta;
200                     b2 += b2_delta;
201                     gain += gain_delta;
202                     wet += wet_delta;
203                     float x = buffer[i];
204                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
205                     float xx = (y * gain) * wet + (x) * (1 - wet);
206                     x2 = x1;
207                     x1 = x;
208                     y2 = y1;
209                     y1 = y;
210                     float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
211                     buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
212                     xx2 = xx1;
213                     xx1 = xx;
214                     yy2 = yy1;
215                     yy1 = yy;
216                 }
217             } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
218                     && b1_delta == 0 && b2_delta == 0) {
219                 for (int i = 0; i < len; i++) {
220                     float x = buffer[i];
221                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
222                     float xx = (y * gain) * wet + (x) * (1 - wet);
223                     x2 = x1;
224                     x1 = x;
225                     y2 = y1;
226                     y1 = y;
227                     float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
228                     buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
229                     xx2 = xx1;
230                     xx1 = xx;
231                     yy2 = yy1;
232                     yy1 = yy;
233                 }
234             } else {
235                 for (int i = 0; i < len; i++) {
236                     a0 += a0_delta;
237                     a1 += a1_delta;
238                     a2 += a2_delta;
239                     b1 += b1_delta;
240                     b2 += b2_delta;
241                     gain += gain_delta;
242                     float x = buffer[i];
243                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
244                     float xx = (y * gain) * wet + (x) * (1 - wet);
245                     x2 = x1;
246                     x1 = x;
247                     y2 = y1;
248                     y1 = y;
249                     float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
250                     buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
251                     xx2 = xx1;
252                     xx1 = xx;
253                     yy2 = yy1;
254                     yy1 = yy;
255                 }
256             }
257 
258             if (Math.abs(x1) < 1.0E-8)
259                 x1 = 0;
260             if (Math.abs(x2) < 1.0E-8)
261                 x2 = 0;
262             if (Math.abs(y1) < 1.0E-8)
263                 y1 = 0;
264             if (Math.abs(y2) < 1.0E-8)
265                 y2 = 0;
266             this.x1 = x1;
267             this.x2 = x2;
268             this.y1 = y1;
269             this.y2 = y2;
270             this.xx1 = xx1;
271             this.xx2 = xx2;
272             this.yy1 = yy1;
273             this.yy2 = yy2;
274         }
275 
276         this.last_a0 = this.a0;
277         this.last_a1 = this.a1;
278         this.last_a2 = this.a2;
279         this.last_b1 = this.b1;
280         this.last_b2 = this.b2;
281         this.last_gain = this.gain;
282         this.last_wet = this.wet;
283 
284     }
285 
sinh(double x)286     private double sinh(double x) {
287         return (Math.exp(x) - Math.exp(-x)) * 0.5;
288     }
289 
filter2calc()290     public void filter2calc() {
291 
292         double resonancedB = this.resonancedB;
293         if (resonancedB < 0)
294             resonancedB = 0;    // Negative dB are illegal.
295         if (resonancedB > 30)
296             resonancedB = 30;   // At least 22.5 dB is needed.
297         if (filtertype == FILTERTYPE_LP24 || filtertype == FILTERTYPE_HP24)
298             resonancedB *= 0.6;
299 
300         if (filtertype == FILTERTYPE_BP12) {
301             wet = 1;
302             double r = (cutoff / samplerate);
303             if (r > 0.45)
304                 r = 0.45;
305 
306             double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
307 
308             double omega = 2 * Math.PI * r;
309             double cs = Math.cos(omega);
310             double sn = Math.sin(omega);
311             double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn * 2));
312 
313             double b0 = alpha;
314             double b1 = 0;
315             double b2 = -alpha;
316             double a0 = 1 + alpha;
317             double a1 = -2 * cs;
318             double a2 = 1 - alpha;
319 
320             double cf = 1.0 / a0;
321             this.b1 = (float) (a1 * cf);
322             this.b2 = (float) (a2 * cf);
323             this.a0 = (float) (b0 * cf);
324             this.a1 = (float) (b1 * cf);
325             this.a2 = (float) (b2 * cf);
326         }
327 
328         if (filtertype == FILTERTYPE_NP12) {
329             wet = 1;
330             double r = (cutoff / samplerate);
331             if (r > 0.45)
332                 r = 0.45;
333 
334             double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
335 
336             double omega = 2 * Math.PI * r;
337             double cs = Math.cos(omega);
338             double sn = Math.sin(omega);
339             double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn*2));
340 
341             double b0 = 1;
342             double b1 = -2 * cs;
343             double b2 = 1;
344             double a0 = 1 + alpha;
345             double a1 = -2 * cs;
346             double a2 = 1 - alpha;
347 
348             double cf = 1.0 / a0;
349             this.b1 = (float)(a1 * cf);
350             this.b2 = (float)(a2 * cf);
351             this.a0 = (float)(b0 * cf);
352             this.a1 = (float)(b1 * cf);
353             this.a2 = (float)(b2 * cf);
354         }
355 
356         if (filtertype == FILTERTYPE_LP12 || filtertype == FILTERTYPE_LP24) {
357             double r = (cutoff / samplerate);
358             if (r > 0.45) {
359                 if (wet == 0) {
360                     if (resonancedB < 0.00001)
361                         wet = 0.0f;
362                     else
363                         wet = 1.0f;
364                 }
365                 r = 0.45;
366             } else
367                 wet = 1.0f;
368 
369             double c = 1.0 / (Math.tan(Math.PI * r));
370             double csq = c * c;
371             double resonance = Math.pow(10.0, -(resonancedB / 20));
372             double q = Math.sqrt(2.0f) * resonance;
373             double a0 = 1.0 / (1.0 + (q * c) + (csq));
374             double a1 = 2.0 * a0;
375             double a2 = a0;
376             double b1 = (2.0 * a0) * (1.0 - csq);
377             double b2 = a0 * (1.0 - (q * c) + csq);
378 
379             this.a0 = (float)a0;
380             this.a1 = (float)a1;
381             this.a2 = (float)a2;
382             this.b1 = (float)b1;
383             this.b2 = (float)b2;
384 
385         }
386 
387         if (filtertype == FILTERTYPE_HP12 || filtertype == FILTERTYPE_HP24) {
388             double r = (cutoff / samplerate);
389             if (r > 0.45)
390                 r = 0.45;
391             if (r < 0.0001)
392                 r = 0.0001;
393             wet = 1.0f;
394             double c = (Math.tan(Math.PI * (r)));
395             double csq = c * c;
396             double resonance = Math.pow(10.0, -(resonancedB / 20));
397             double q = Math.sqrt(2.0f) * resonance;
398             double a0 = 1.0 / (1.0 + (q * c) + (csq));
399             double a1 = -2.0 * a0;
400             double a2 = a0;
401             double b1 = (2.0 * a0) * (csq - 1.0);
402             double b2 = a0 * (1.0 - (q * c) + csq);
403 
404             this.a0 = (float)a0;
405             this.a1 = (float)a1;
406             this.a2 = (float)a2;
407             this.b1 = (float)b1;
408             this.b2 = (float)b2;
409 
410         }
411 
412     }
413 
filter2(SoftAudioBuffer sbuffer)414     public void filter2(SoftAudioBuffer sbuffer) {
415 
416         float[] buffer = sbuffer.array();
417 
418         if (dirty) {
419             filter2calc();
420             dirty = false;
421         }
422         if (!last_set) {
423             last_a0 = a0;
424             last_a1 = a1;
425             last_a2 = a2;
426             last_b1 = b1;
427             last_b2 = b2;
428             last_q = q;
429             last_gain = gain;
430             last_wet = wet;
431             last_set = true;
432         }
433 
434         if (wet > 0 || last_wet > 0) {
435 
436             int len = buffer.length;
437             float a0 = this.last_a0;
438             float a1 = this.last_a1;
439             float a2 = this.last_a2;
440             float b1 = this.last_b1;
441             float b2 = this.last_b2;
442             float gain = this.last_gain;
443             float wet = this.last_wet;
444             float a0_delta = (this.a0 - this.last_a0) / len;
445             float a1_delta = (this.a1 - this.last_a1) / len;
446             float a2_delta = (this.a2 - this.last_a2) / len;
447             float b1_delta = (this.b1 - this.last_b1) / len;
448             float b2_delta = (this.b2 - this.last_b2) / len;
449             float gain_delta = (this.gain - this.last_gain) / len;
450             float wet_delta = (this.wet - this.last_wet) / len;
451             float x1 = this.x1;
452             float x2 = this.x2;
453             float y1 = this.y1;
454             float y2 = this.y2;
455 
456             if (wet_delta != 0) {
457                 for (int i = 0; i < len; i++) {
458                     a0 += a0_delta;
459                     a1 += a1_delta;
460                     a2 += a2_delta;
461                     b1 += b1_delta;
462                     b2 += b2_delta;
463                     gain += gain_delta;
464                     wet += wet_delta;
465                     float x = buffer[i];
466                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
467                     buffer[i] = (y * gain) * wet + (x) * (1 - wet);
468                     x2 = x1;
469                     x1 = x;
470                     y2 = y1;
471                     y1 = y;
472                 }
473             } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
474                     && b1_delta == 0 && b2_delta == 0) {
475                 for (int i = 0; i < len; i++) {
476                     float x = buffer[i];
477                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
478                     buffer[i] = y * gain;
479                     x2 = x1;
480                     x1 = x;
481                     y2 = y1;
482                     y1 = y;
483                 }
484             } else {
485                 for (int i = 0; i < len; i++) {
486                     a0 += a0_delta;
487                     a1 += a1_delta;
488                     a2 += a2_delta;
489                     b1 += b1_delta;
490                     b2 += b2_delta;
491                     gain += gain_delta;
492                     float x = buffer[i];
493                     float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
494                     buffer[i] = y * gain;
495                     x2 = x1;
496                     x1 = x;
497                     y2 = y1;
498                     y1 = y;
499                 }
500             }
501 
502             if (Math.abs(x1) < 1.0E-8)
503                 x1 = 0;
504             if (Math.abs(x2) < 1.0E-8)
505                 x2 = 0;
506             if (Math.abs(y1) < 1.0E-8)
507                 y1 = 0;
508             if (Math.abs(y2) < 1.0E-8)
509                 y2 = 0;
510             this.x1 = x1;
511             this.x2 = x2;
512             this.y1 = y1;
513             this.y2 = y2;
514         }
515 
516         this.last_a0 = this.a0;
517         this.last_a1 = this.a1;
518         this.last_a2 = this.a2;
519         this.last_b1 = this.b1;
520         this.last_b2 = this.b2;
521         this.last_q = this.q;
522         this.last_gain = this.gain;
523         this.last_wet = this.wet;
524 
525     }
526 
filter1calc()527     public void filter1calc() {
528         if (cutoff < 120)
529             cutoff = 120;
530         double c = (7.0 / 6.0) * Math.PI * 2 * cutoff / samplerate;
531         if (c > 1)
532             c = 1;
533         a0 = (float)(Math.sqrt(1 - Math.cos(c)) * Math.sqrt(0.5 * Math.PI));
534         if (resonancedB < 0)
535             resonancedB = 0;
536         if (resonancedB > 20)
537             resonancedB = 20;
538         q = (float)(Math.sqrt(0.5) * Math.pow(10.0, -(resonancedB / 20)));
539         gain = (float)Math.pow(10, -((resonancedB)) / 40.0);
540         if (wet == 0.0f)
541             if (resonancedB > 0.00001 || c < 0.9999999)
542                 wet = 1.0f;
543     }
544 
filter1(SoftAudioBuffer sbuffer)545     public void filter1(SoftAudioBuffer sbuffer) {
546 
547         if (dirty) {
548             filter1calc();
549             dirty = false;
550         }
551         if (!last_set) {
552             last_a0 = a0;
553             last_q = q;
554             last_gain = gain;
555             last_wet = wet;
556             last_set = true;
557         }
558 
559         if (wet > 0 || last_wet > 0) {
560 
561             float[] buffer = sbuffer.array();
562             int len = buffer.length;
563             float a0 = this.last_a0;
564             float q = this.last_q;
565             float gain = this.last_gain;
566             float wet = this.last_wet;
567             float a0_delta = (this.a0 - this.last_a0) / len;
568             float q_delta = (this.q - this.last_q) / len;
569             float gain_delta = (this.gain - this.last_gain) / len;
570             float wet_delta = (this.wet - this.last_wet) / len;
571             float y2 = this.y2;
572             float y1 = this.y1;
573 
574             if (wet_delta != 0) {
575                 for (int i = 0; i < len; i++) {
576                     a0 += a0_delta;
577                     q += q_delta;
578                     gain += gain_delta;
579                     wet += wet_delta;
580                     float ga0 = (1 - q * a0);
581                     y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
582                     y2 = ga0 * y2 + (a0) * y1;
583                     buffer[i] = y2 * gain * wet + buffer[i] * (1 - wet);
584                 }
585             } else if (a0_delta == 0 && q_delta == 0) {
586                 float ga0 = (1 - q * a0);
587                 for (int i = 0; i < len; i++) {
588                     y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
589                     y2 = ga0 * y2 + (a0) * y1;
590                     buffer[i] = y2 * gain;
591                 }
592             } else {
593                 for (int i = 0; i < len; i++) {
594                     a0 += a0_delta;
595                     q += q_delta;
596                     gain += gain_delta;
597                     float ga0 = (1 - q * a0);
598                     y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
599                     y2 = ga0 * y2 + (a0) * y1;
600                     buffer[i] = y2 * gain;
601                 }
602             }
603 
604             if (Math.abs(y2) < 1.0E-8)
605                 y2 = 0;
606             if (Math.abs(y1) < 1.0E-8)
607                 y1 = 0;
608             this.y2 = y2;
609             this.y1 = y1;
610         }
611 
612         this.last_a0 = this.a0;
613         this.last_q = this.q;
614         this.last_gain = this.gain;
615         this.last_wet = this.wet;
616     }
617 }
618