Skip to content

Instantly share code, notes, and snippets.

@TF3RDL
Created October 19, 2024 21:48
Show Gist options
  • Save TF3RDL/040993f207fee042e80637966efb14c8 to your computer and use it in GitHub Desktop.
Save TF3RDL/040993f207fee042e80637966efb14c8 to your computer and use it in GitHub Desktop.
// Second-order IIR filter with arbitrary coefficient configurations
class IIRFilter {
constructor(a0, a1, a2, b1, b2) {
this.recalcCoeffs(a0, a1, a2, b1, b2);
}
recalcCoeffs(a0 = 0, a1 = 0, a2 = 0, b1 = 0, b2 = 0) {
this.a0 = a0;
this.a1 = a1;
this.a2 = a2;
this.b1 = b1;
this.b2 = b2;
this.z1 = 0;
this.z2 = 0;
}
process(x) {
const output = x * this.a0 + this.z1;
this.z1 = x * this.a1 + this.z2 - this.b1 * output;
this.z2 = x * this.a2 - this.b2 * output;
return output;
}
}
// K-weighting filter, used for LUFS metering
class KWeighting {
constructor(sampleRate) {
this.highpass = new IIRFilter();
this.highshelf = new IIRFilter();
this.recalcCoeffs(sampleRate);
}
recalcCoeffs(sampleRate = 44100) {
const hpFreq = 80,
hpQ = Math.SQRT2/2,
hsFreq = 1500,
hsGain = 10 ** (4/20),
k1 = Math.tan(Math.PI * hpFreq/sampleRate),
k2 = Math.tan(Math.PI * hsFreq/sampleRate),
norm1 = 1 / (1 + k1 / hpQ + k1 ** 2),
norm2 = 1 / (1 + Math.SQRT2 * k2 + k2 ** 2);
// highpass part
const hpA0 = norm1,
hpA1 = -hpA0 * 2,
hpA2 = norm1,
hpB1 = 2 * (k1 ** 2 - 1) * norm1,
hpB2 = (1 - k1 / hpQ + k1 ** 2) * norm1,
// highshelf part
hsA0 = (hsGain + Math.sqrt(hsGain*2) * k2 + k2 ** 2) * norm2,
hsA1 = 2 * (k2 ** 2 - hsGain) * norm2,
hsA2 = (hsGain - Math.sqrt(hsGain*2) * k2 + k2 ** 2) * norm2,
hsB1 = 2 * (k2 ** 2 - 1) * norm2,
hsB2 = (1 - Math.SQRT2 * k2 + k2 ** 2) * norm2;
this.highpass.recalcCoeffs(
hpA0,
hpA1,
hpA2,
hpB1,
hpB2
);
this.highshelf.recalcCoeffs(
hsA0,
hsA1,
hsA2,
hsB1,
hsB2
);
}
process(x) {
return this.highshelf.process(this.highpass.process(x));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment