UNPKG

@thi.ng/dsp

Version:

Composable signal generators, oscillators, filters, FFT, spectrum, windowing & related DSP utils

197 lines (196 loc) 5.09 kB
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 };