@thi.ng/dsp
Version:
Composable signal generators, oscillators, filters, FFT, spectrum, windowing & related DSP utils
197 lines (196 loc) • 5.09 kB
JavaScript
import { unsupported } from "@thi.ng/errors/unsupported";
import { PI, SQRT2, SQRT2_2 } from "@thi.ng/math/api";
import { clamp05 } from "@thi.ng/math/interval";
import { AProc } from "./aproc.js";
import { dbMag } from "./convert.js";
const biquad = (type, fc, q, gain) => new Biquad(type, fc, q, gain);
const biquadLP = (fc, q) => new Biquad("lp", fc, q);
const biquadHP = (fc, q) => new Biquad("hp", fc, q);
const biquadBP = (fc, q) => new Biquad("bp", fc, q);
const biquadNotch = (fc, q) => new Biquad("notch", fc, q);
const biquadPeak = (fc, q, gain = 6) => new Biquad("peak", fc, q, gain);
const biquadLoShelf = (fc, gain = -6) => new Biquad("loshelf", fc, void 0, gain);
const biquadHiShelf = (fc, gain = -6) => new Biquad("hishelf", fc, void 0, gain);
class Biquad extends AProc {
constructor(_type, _freq, _q = SQRT2_2, _gain = 0) {
super(0);
this._type = _type;
this._freq = _freq;
this._q = _q;
this._gain = _gain;
this.reset();
this.calcCoeffs();
}
_a0;
_a1;
_a2;
_b1;
_b2;
_z1;
_z2;
reset() {
this._z1 = this._z2 = this._val = 0;
return this;
}
next(x) {
const out = x * this._a0 + this._z1;
this._z1 = x * this._a1 + this._z2 - this._b1 * out;
this._z2 = x * this._a2 - this._b2 * out;
return this._val = out;
}
freq() {
return this._freq;
}
q() {
return this._q;
}
gain() {
return this._gain;
}
set(fc, q, gain) {
this._freq = clamp05(fc);
this._q = q;
this._gain = gain;
this.calcCoeffs();
}
setFreq(fc) {
this._freq = clamp05(fc);
this.calcCoeffs();
}
setQ(q) {
this._q = q;
this.calcCoeffs();
}
setGain(g) {
this._gain = g;
this.calcCoeffs();
}
filterCoeffs() {
return {
zeroes: [this._a0, this._a1, this._a2],
poles: [1, this._b1, this._b2]
};
}
calcCoeffs() {
const k = Math.tan(PI * this._freq);
const k2 = k * k;
const k22 = 2 * (k2 - 1);
const kq = k / this._q;
const k2kqp1 = 1 + kq + k2;
const k2kqm1 = 1 - kq + k2;
const ksqrt2 = k * SQRT2;
const v = dbMag(Math.abs(this._gain));
const kvq = k * (v / this._q);
const ksqrt2v = k * Math.sqrt(2 * v);
let norm = 1 / k2kqp1;
switch (this._type) {
case "lp":
this._a0 = k2 * norm;
this._a1 = 2 * this._a0;
this._a2 = this._a0;
this._b1 = k22 * norm;
this._b2 = k2kqm1 * norm;
break;
case "hp":
this._a0 = norm;
this._a1 = -2 * this._a0;
this._a2 = this._a0;
this._b1 = k22 * norm;
this._b2 = k2kqm1 * norm;
break;
case "bp":
this._a0 = kq * norm;
this._a1 = 0;
this._a2 = -this._a0;
this._b1 = k22 * norm;
this._b2 = k2kqm1 * norm;
break;
case "notch":
this._a0 = (1 + k2) * norm;
this._a1 = k22 * norm;
this._a2 = this._a0;
this._b1 = this._a1;
this._b2 = k2kqm1 * norm;
break;
case "peak": {
const z1 = 1 + kvq + k2;
const z2 = 1 - kvq + k2;
if (this._gain >= 0) {
this._a0 = z1 * norm;
this._a1 = k22 * norm;
this._a2 = z2 * norm;
this._b1 = this._a1;
this._b2 = k2kqm1 * norm;
} else {
norm = 1 / z1;
this._a0 = k2kqp1 * norm;
this._a1 = k22 * norm;
this._a2 = k2kqm1 * norm;
this._b1 = this._a1;
this._b2 = z2 * norm;
}
break;
}
case "loshelf": {
const z1 = 1 + ksqrt2 + k2;
const z2 = 1 - ksqrt2 + k2;
const vk2 = v * k2;
const y1 = 1 + ksqrt2v + vk2;
const y2 = 1 - ksqrt2v + vk2;
const vk22 = 2 * (vk2 - 1);
if (this._gain >= 0) {
norm = 1 / z1;
this._a0 = y1 * norm;
this._a1 = vk22 * norm;
this._a2 = y2 * norm;
this._b1 = k22 * norm;
this._b2 = z2 * norm;
} else {
norm = 1 / y1;
this._a0 = z1 * norm;
this._a1 = k22 * norm;
this._a2 = z2 * norm;
this._b1 = vk22 * norm;
this._b2 = y2 * norm;
}
break;
}
case "hishelf": {
const z1 = 1 + ksqrt2 + k2;
const z2 = 1 - ksqrt2 + k2;
const y1 = v + ksqrt2v + k2;
const y2 = v - ksqrt2v + k2;
const vk2 = 2 * (k2 - v);
if (this._gain >= 0) {
norm = 1 / z1;
this._a0 = y1 * norm;
this._a1 = vk2 * norm;
this._a2 = y2 * norm;
this._b1 = k22 * norm;
this._b2 = z2 * norm;
} else {
norm = 1 / y1;
this._a0 = z1 * norm;
this._a1 = k22 * norm;
this._a2 = z2 * norm;
this._b1 = vk2 * norm;
this._b2 = y2 * norm;
}
break;
}
default:
unsupported(`invalid filter type: ${this._type}`);
}
}
}
export {
Biquad,
biquad,
biquadBP,
biquadHP,
biquadHiShelf,
biquadLP,
biquadLoShelf,
biquadNotch,
biquadPeak
};