829N/A/*
6321N/A * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
829N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
829N/A *
829N/A * This code is free software; you can redistribute it and/or modify it
829N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
829N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
829N/A *
829N/A * This code is distributed in the hope that it will be useful, but WITHOUT
829N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
829N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
829N/A * version 2 for more details (a copy is included in the LICENSE file that
829N/A * accompanied this code).
829N/A *
829N/A * You should have received a copy of the GNU General Public License version
829N/A * 2 along with this work; if not, write to the Free Software Foundation,
829N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
829N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
829N/A */
829N/Apackage com.sun.media.sound;
829N/A
829N/A/**
829N/A * Infinite impulse response (IIR) filter class.
829N/A *
829N/A * The filters where implemented and adapted using algorithms from musicdsp.org
829N/A * archive: 1-RC and C filter, Simple 2-pole LP LP and HP filter, biquad,
829N/A * tweaked butterworth RBJ Audio-EQ-Cookbook, EQ filter kookbook
829N/A *
829N/A * @author Karl Helgason
829N/A */
6321N/Apublic final class SoftFilter {
829N/A
829N/A public final static int FILTERTYPE_LP6 = 0x00;
829N/A public final static int FILTERTYPE_LP12 = 0x01;
829N/A public final static int FILTERTYPE_HP12 = 0x11;
829N/A public final static int FILTERTYPE_BP12 = 0x21;
829N/A public final static int FILTERTYPE_NP12 = 0x31;
829N/A public final static int FILTERTYPE_LP24 = 0x03;
829N/A public final static int FILTERTYPE_HP24 = 0x13;
829N/A
829N/A //
829N/A // 0x0 = 1st-order, 6 dB/oct
829N/A // 0x1 = 2nd-order, 12 dB/oct
829N/A // 0x2 = 3rd-order, 18 dB/oct
829N/A // 0x3 = 4th-order, 24 db/oct
829N/A //
829N/A // 0x00 = LP, Low Pass Filter
829N/A // 0x10 = HP, High Pass Filter
829N/A // 0x20 = BP, Band Pass Filter
829N/A // 0x30 = NP, Notch or Band Elimination Filter
829N/A //
829N/A private int filtertype = FILTERTYPE_LP6;
6321N/A private final float samplerate;
829N/A private float x1;
829N/A private float x2;
829N/A private float y1;
829N/A private float y2;
829N/A private float xx1;
829N/A private float xx2;
829N/A private float yy1;
829N/A private float yy2;
829N/A private float a0;
829N/A private float a1;
829N/A private float a2;
829N/A private float b1;
829N/A private float b2;
829N/A private float q;
829N/A private float gain = 1;
829N/A private float wet = 0;
829N/A private float last_wet = 0;
829N/A private float last_a0;
829N/A private float last_a1;
829N/A private float last_a2;
829N/A private float last_b1;
829N/A private float last_b2;
829N/A private float last_q;
829N/A private float last_gain;
829N/A private boolean last_set = false;
829N/A private double cutoff = 44100;
829N/A private double resonancedB = 0;
829N/A private boolean dirty = true;
829N/A
829N/A public SoftFilter(float samplerate) {
829N/A this.samplerate = samplerate;
829N/A dirty = true;
829N/A }
829N/A
829N/A public void setFrequency(double cent) {
829N/A if (cutoff == cent)
829N/A return;
829N/A cutoff = cent;
829N/A dirty = true;
829N/A }
829N/A
829N/A public void setResonance(double db) {
829N/A if (resonancedB == db)
829N/A return;
829N/A resonancedB = db;
829N/A dirty = true;
829N/A }
829N/A
829N/A public void reset() {
829N/A dirty = true;
829N/A last_set = false;
829N/A x1 = 0;
829N/A x2 = 0;
829N/A y1 = 0;
829N/A y2 = 0;
829N/A xx1 = 0;
829N/A xx2 = 0;
829N/A yy1 = 0;
829N/A yy2 = 0;
829N/A wet = 0.0f;
829N/A gain = 1.0f;
829N/A a0 = 0;
829N/A a1 = 0;
829N/A a2 = 0;
829N/A b1 = 0;
829N/A b2 = 0;
829N/A }
829N/A
829N/A public void setFilterType(int filtertype) {
829N/A this.filtertype = filtertype;
829N/A }
829N/A
829N/A public void processAudio(SoftAudioBuffer sbuffer) {
829N/A if (filtertype == FILTERTYPE_LP6)
829N/A filter1(sbuffer);
829N/A if (filtertype == FILTERTYPE_LP12)
829N/A filter2(sbuffer);
829N/A if (filtertype == FILTERTYPE_HP12)
829N/A filter2(sbuffer);
829N/A if (filtertype == FILTERTYPE_BP12)
829N/A filter2(sbuffer);
829N/A if (filtertype == FILTERTYPE_NP12)
829N/A filter2(sbuffer);
829N/A if (filtertype == FILTERTYPE_LP24)
829N/A filter4(sbuffer);
829N/A if (filtertype == FILTERTYPE_HP24)
829N/A filter4(sbuffer);
829N/A }
829N/A
829N/A public void filter4(SoftAudioBuffer sbuffer) {
829N/A
829N/A float[] buffer = sbuffer.array();
829N/A
829N/A if (dirty) {
829N/A filter2calc();
829N/A dirty = false;
829N/A }
829N/A if (!last_set) {
829N/A last_a0 = a0;
829N/A last_a1 = a1;
829N/A last_a2 = a2;
829N/A last_b1 = b1;
829N/A last_b2 = b2;
829N/A last_gain = gain;
829N/A last_wet = wet;
829N/A last_set = true;
829N/A }
829N/A
829N/A if (wet > 0 || last_wet > 0) {
829N/A
829N/A int len = buffer.length;
829N/A float a0 = this.last_a0;
829N/A float a1 = this.last_a1;
829N/A float a2 = this.last_a2;
829N/A float b1 = this.last_b1;
829N/A float b2 = this.last_b2;
829N/A float gain = this.last_gain;
829N/A float wet = this.last_wet;
829N/A float a0_delta = (this.a0 - this.last_a0) / len;
829N/A float a1_delta = (this.a1 - this.last_a1) / len;
829N/A float a2_delta = (this.a2 - this.last_a2) / len;
829N/A float b1_delta = (this.b1 - this.last_b1) / len;
829N/A float b2_delta = (this.b2 - this.last_b2) / len;
829N/A float gain_delta = (this.gain - this.last_gain) / len;
829N/A float wet_delta = (this.wet - this.last_wet) / len;
829N/A float x1 = this.x1;
829N/A float x2 = this.x2;
829N/A float y1 = this.y1;
829N/A float y2 = this.y2;
829N/A float xx1 = this.xx1;
829N/A float xx2 = this.xx2;
829N/A float yy1 = this.yy1;
829N/A float yy2 = this.yy2;
829N/A
829N/A if (wet_delta != 0) {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A a1 += a1_delta;
829N/A a2 += a2_delta;
829N/A b1 += b1_delta;
829N/A b2 += b2_delta;
829N/A gain += gain_delta;
829N/A wet += wet_delta;
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A float xx = (y * gain) * wet + (x) * (1 - wet);
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
829N/A buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
829N/A xx2 = xx1;
829N/A xx1 = xx;
829N/A yy2 = yy1;
829N/A yy1 = yy;
829N/A }
829N/A } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
829N/A && b1_delta == 0 && b2_delta == 0) {
829N/A for (int i = 0; i < len; i++) {
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A float xx = (y * gain) * wet + (x) * (1 - wet);
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
829N/A buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
829N/A xx2 = xx1;
829N/A xx1 = xx;
829N/A yy2 = yy1;
829N/A yy1 = yy;
829N/A }
829N/A } else {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A a1 += a1_delta;
829N/A a2 += a2_delta;
829N/A b1 += b1_delta;
829N/A b2 += b2_delta;
829N/A gain += gain_delta;
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A float xx = (y * gain) * wet + (x) * (1 - wet);
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A float yy = (a0*xx + a1*xx1 + a2*xx2 - b1*yy1 - b2*yy2);
829N/A buffer[i] = (yy * gain) * wet + (xx) * (1 - wet);
829N/A xx2 = xx1;
829N/A xx1 = xx;
829N/A yy2 = yy1;
829N/A yy1 = yy;
829N/A }
829N/A }
829N/A
829N/A if (Math.abs(x1) < 1.0E-8)
829N/A x1 = 0;
829N/A if (Math.abs(x2) < 1.0E-8)
829N/A x2 = 0;
829N/A if (Math.abs(y1) < 1.0E-8)
829N/A y1 = 0;
829N/A if (Math.abs(y2) < 1.0E-8)
829N/A y2 = 0;
829N/A this.x1 = x1;
829N/A this.x2 = x2;
829N/A this.y1 = y1;
829N/A this.y2 = y2;
829N/A this.xx1 = xx1;
829N/A this.xx2 = xx2;
829N/A this.yy1 = yy1;
829N/A this.yy2 = yy2;
829N/A }
829N/A
829N/A this.last_a0 = this.a0;
829N/A this.last_a1 = this.a1;
829N/A this.last_a2 = this.a2;
829N/A this.last_b1 = this.b1;
829N/A this.last_b2 = this.b2;
829N/A this.last_gain = this.gain;
829N/A this.last_wet = this.wet;
829N/A
829N/A }
829N/A
829N/A private double sinh(double x) {
829N/A return (Math.exp(x) - Math.exp(-x)) * 0.5;
829N/A }
829N/A
829N/A public void filter2calc() {
829N/A
829N/A double resonancedB = this.resonancedB;
829N/A if (resonancedB < 0)
829N/A resonancedB = 0; // Negative dB are illegal.
829N/A if (resonancedB > 30)
829N/A resonancedB = 30; // At least 22.5 dB is needed.
829N/A if (filtertype == FILTERTYPE_LP24 || filtertype == FILTERTYPE_HP24)
829N/A resonancedB *= 0.6;
829N/A
829N/A if (filtertype == FILTERTYPE_BP12) {
829N/A wet = 1;
829N/A double r = (cutoff / samplerate);
829N/A if (r > 0.45)
829N/A r = 0.45;
829N/A
829N/A double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
829N/A
829N/A double omega = 2 * Math.PI * r;
829N/A double cs = Math.cos(omega);
829N/A double sn = Math.sin(omega);
829N/A double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn * 2));
829N/A
829N/A double b0 = alpha;
829N/A double b1 = 0;
829N/A double b2 = -alpha;
829N/A double a0 = 1 + alpha;
829N/A double a1 = -2 * cs;
829N/A double a2 = 1 - alpha;
829N/A
829N/A double cf = 1.0 / a0;
829N/A this.b1 = (float) (a1 * cf);
829N/A this.b2 = (float) (a2 * cf);
829N/A this.a0 = (float) (b0 * cf);
829N/A this.a1 = (float) (b1 * cf);
829N/A this.a2 = (float) (b2 * cf);
829N/A }
829N/A
829N/A if (filtertype == FILTERTYPE_NP12) {
829N/A wet = 1;
829N/A double r = (cutoff / samplerate);
829N/A if (r > 0.45)
829N/A r = 0.45;
829N/A
829N/A double bandwidth = Math.PI * Math.pow(10.0, -(resonancedB / 20));
829N/A
829N/A double omega = 2 * Math.PI * r;
829N/A double cs = Math.cos(omega);
829N/A double sn = Math.sin(omega);
829N/A double alpha = sn * sinh((Math.log(2)*bandwidth*omega) / (sn*2));
829N/A
829N/A double b0 = 1;
829N/A double b1 = -2 * cs;
829N/A double b2 = 1;
829N/A double a0 = 1 + alpha;
829N/A double a1 = -2 * cs;
829N/A double a2 = 1 - alpha;
829N/A
829N/A double cf = 1.0 / a0;
829N/A this.b1 = (float)(a1 * cf);
829N/A this.b2 = (float)(a2 * cf);
829N/A this.a0 = (float)(b0 * cf);
829N/A this.a1 = (float)(b1 * cf);
829N/A this.a2 = (float)(b2 * cf);
829N/A }
829N/A
829N/A if (filtertype == FILTERTYPE_LP12 || filtertype == FILTERTYPE_LP24) {
829N/A double r = (cutoff / samplerate);
829N/A if (r > 0.45) {
829N/A if (wet == 0) {
829N/A if (resonancedB < 0.00001)
829N/A wet = 0.0f;
829N/A else
829N/A wet = 1.0f;
829N/A }
829N/A r = 0.45;
829N/A } else
829N/A wet = 1.0f;
829N/A
829N/A double c = 1.0 / (Math.tan(Math.PI * r));
829N/A double csq = c * c;
829N/A double resonance = Math.pow(10.0, -(resonancedB / 20));
829N/A double q = Math.sqrt(2.0f) * resonance;
829N/A double a0 = 1.0 / (1.0 + (q * c) + (csq));
829N/A double a1 = 2.0 * a0;
829N/A double a2 = a0;
829N/A double b1 = (2.0 * a0) * (1.0 - csq);
829N/A double b2 = a0 * (1.0 - (q * c) + csq);
829N/A
829N/A this.a0 = (float)a0;
829N/A this.a1 = (float)a1;
829N/A this.a2 = (float)a2;
829N/A this.b1 = (float)b1;
829N/A this.b2 = (float)b2;
829N/A
829N/A }
829N/A
829N/A if (filtertype == FILTERTYPE_HP12 || filtertype == FILTERTYPE_HP24) {
829N/A double r = (cutoff / samplerate);
829N/A if (r > 0.45)
829N/A r = 0.45;
829N/A if (r < 0.0001)
829N/A r = 0.0001;
829N/A wet = 1.0f;
829N/A double c = (Math.tan(Math.PI * (r)));
829N/A double csq = c * c;
829N/A double resonance = Math.pow(10.0, -(resonancedB / 20));
829N/A double q = Math.sqrt(2.0f) * resonance;
829N/A double a0 = 1.0 / (1.0 + (q * c) + (csq));
829N/A double a1 = -2.0 * a0;
829N/A double a2 = a0;
829N/A double b1 = (2.0 * a0) * (csq - 1.0);
829N/A double b2 = a0 * (1.0 - (q * c) + csq);
829N/A
829N/A this.a0 = (float)a0;
829N/A this.a1 = (float)a1;
829N/A this.a2 = (float)a2;
829N/A this.b1 = (float)b1;
829N/A this.b2 = (float)b2;
829N/A
829N/A }
829N/A
829N/A }
829N/A
829N/A public void filter2(SoftAudioBuffer sbuffer) {
829N/A
829N/A float[] buffer = sbuffer.array();
829N/A
829N/A if (dirty) {
829N/A filter2calc();
829N/A dirty = false;
829N/A }
829N/A if (!last_set) {
829N/A last_a0 = a0;
829N/A last_a1 = a1;
829N/A last_a2 = a2;
829N/A last_b1 = b1;
829N/A last_b2 = b2;
829N/A last_q = q;
829N/A last_gain = gain;
829N/A last_wet = wet;
829N/A last_set = true;
829N/A }
829N/A
829N/A if (wet > 0 || last_wet > 0) {
829N/A
829N/A int len = buffer.length;
829N/A float a0 = this.last_a0;
829N/A float a1 = this.last_a1;
829N/A float a2 = this.last_a2;
829N/A float b1 = this.last_b1;
829N/A float b2 = this.last_b2;
829N/A float gain = this.last_gain;
829N/A float wet = this.last_wet;
829N/A float a0_delta = (this.a0 - this.last_a0) / len;
829N/A float a1_delta = (this.a1 - this.last_a1) / len;
829N/A float a2_delta = (this.a2 - this.last_a2) / len;
829N/A float b1_delta = (this.b1 - this.last_b1) / len;
829N/A float b2_delta = (this.b2 - this.last_b2) / len;
829N/A float gain_delta = (this.gain - this.last_gain) / len;
829N/A float wet_delta = (this.wet - this.last_wet) / len;
829N/A float x1 = this.x1;
829N/A float x2 = this.x2;
829N/A float y1 = this.y1;
829N/A float y2 = this.y2;
829N/A
829N/A if (wet_delta != 0) {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A a1 += a1_delta;
829N/A a2 += a2_delta;
829N/A b1 += b1_delta;
829N/A b2 += b2_delta;
829N/A gain += gain_delta;
829N/A wet += wet_delta;
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A buffer[i] = (y * gain) * wet + (x) * (1 - wet);
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A }
829N/A } else if (a0_delta == 0 && a1_delta == 0 && a2_delta == 0
829N/A && b1_delta == 0 && b2_delta == 0) {
829N/A for (int i = 0; i < len; i++) {
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A buffer[i] = y * gain;
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A }
829N/A } else {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A a1 += a1_delta;
829N/A a2 += a2_delta;
829N/A b1 += b1_delta;
829N/A b2 += b2_delta;
829N/A gain += gain_delta;
829N/A float x = buffer[i];
829N/A float y = (a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2);
829N/A buffer[i] = y * gain;
829N/A x2 = x1;
829N/A x1 = x;
829N/A y2 = y1;
829N/A y1 = y;
829N/A }
829N/A }
829N/A
829N/A if (Math.abs(x1) < 1.0E-8)
829N/A x1 = 0;
829N/A if (Math.abs(x2) < 1.0E-8)
829N/A x2 = 0;
829N/A if (Math.abs(y1) < 1.0E-8)
829N/A y1 = 0;
829N/A if (Math.abs(y2) < 1.0E-8)
829N/A y2 = 0;
829N/A this.x1 = x1;
829N/A this.x2 = x2;
829N/A this.y1 = y1;
829N/A this.y2 = y2;
829N/A }
829N/A
829N/A this.last_a0 = this.a0;
829N/A this.last_a1 = this.a1;
829N/A this.last_a2 = this.a2;
829N/A this.last_b1 = this.b1;
829N/A this.last_b2 = this.b2;
829N/A this.last_q = this.q;
829N/A this.last_gain = this.gain;
829N/A this.last_wet = this.wet;
829N/A
829N/A }
829N/A
829N/A public void filter1calc() {
829N/A if (cutoff < 120)
829N/A cutoff = 120;
829N/A double c = (7.0 / 6.0) * Math.PI * 2 * cutoff / samplerate;
829N/A if (c > 1)
829N/A c = 1;
829N/A a0 = (float)(Math.sqrt(1 - Math.cos(c)) * Math.sqrt(0.5 * Math.PI));
829N/A if (resonancedB < 0)
829N/A resonancedB = 0;
829N/A if (resonancedB > 20)
829N/A resonancedB = 20;
829N/A q = (float)(Math.sqrt(0.5) * Math.pow(10.0, -(resonancedB / 20)));
829N/A gain = (float)Math.pow(10, -((resonancedB)) / 40.0);
829N/A if (wet == 0.0f)
829N/A if (resonancedB > 0.00001 || c < 0.9999999)
829N/A wet = 1.0f;
829N/A }
829N/A
829N/A public void filter1(SoftAudioBuffer sbuffer) {
829N/A
829N/A if (dirty) {
829N/A filter1calc();
829N/A dirty = false;
829N/A }
829N/A if (!last_set) {
829N/A last_a0 = a0;
829N/A last_q = q;
829N/A last_gain = gain;
829N/A last_wet = wet;
829N/A last_set = true;
829N/A }
829N/A
829N/A if (wet > 0 || last_wet > 0) {
829N/A
1170N/A float[] buffer = sbuffer.array();
829N/A int len = buffer.length;
829N/A float a0 = this.last_a0;
829N/A float q = this.last_q;
829N/A float gain = this.last_gain;
829N/A float wet = this.last_wet;
829N/A float a0_delta = (this.a0 - this.last_a0) / len;
829N/A float q_delta = (this.q - this.last_q) / len;
829N/A float gain_delta = (this.gain - this.last_gain) / len;
829N/A float wet_delta = (this.wet - this.last_wet) / len;
829N/A float y2 = this.y2;
829N/A float y1 = this.y1;
829N/A
829N/A if (wet_delta != 0) {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A q += q_delta;
829N/A gain += gain_delta;
829N/A wet += wet_delta;
1170N/A float ga0 = (1 - q * a0);
1170N/A y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
1170N/A y2 = ga0 * y2 + (a0) * y1;
829N/A buffer[i] = y2 * gain * wet + buffer[i] * (1 - wet);
829N/A }
829N/A } else if (a0_delta == 0 && q_delta == 0) {
1170N/A float ga0 = (1 - q * a0);
829N/A for (int i = 0; i < len; i++) {
1170N/A y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
1170N/A y2 = ga0 * y2 + (a0) * y1;
829N/A buffer[i] = y2 * gain;
829N/A }
829N/A } else {
829N/A for (int i = 0; i < len; i++) {
829N/A a0 += a0_delta;
829N/A q += q_delta;
829N/A gain += gain_delta;
1170N/A float ga0 = (1 - q * a0);
1170N/A y1 = ga0 * y1 + (a0) * (buffer[i] - y2);
1170N/A y2 = ga0 * y2 + (a0) * y1;
829N/A buffer[i] = y2 * gain;
829N/A }
829N/A }
829N/A
829N/A if (Math.abs(y2) < 1.0E-8)
829N/A y2 = 0;
829N/A if (Math.abs(y1) < 1.0E-8)
829N/A y1 = 0;
829N/A this.y2 = y2;
829N/A this.y1 = y1;
829N/A }
829N/A
829N/A this.last_a0 = this.a0;
829N/A this.last_q = this.q;
829N/A this.last_gain = this.gain;
829N/A this.last_wet = this.wet;
829N/A }
829N/A}