class SVFProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ { name: 'frequency', defaultValue: 1000, minValue: 20, maxValue: 20000, automationRate: 'k-rate' }, { name: 'resonance', defaultValue: 0.707, minValue: 0.05, maxValue: 10, automationRate: 'k-rate' } ] } constructor() { super() this.mode = 'lowpass' this.ic1eq = 0 this.ic2eq = 0 this.port.onmessage = (event) => { const { type, value } = event.data if (type === 'mode') { this.mode = value } } } process(inputs, outputs, parameters) { const input = inputs[0] const output = outputs[0] if (input.length === 0 || output.length === 0) { return true } const inputChannel = input[0] const outputChannel = output[0] const frequency = parameters.frequency const resonance = parameters.resonance const sampleRate = globalThis.sampleRate || 44100 const isFreqArray = frequency.length > 1 const isResArray = resonance.length > 1 for (let i = 0; i < inputChannel.length; i++) { const freq = isFreqArray ? frequency[i] : frequency[0] const res = isResArray ? resonance[i] : resonance[0] const g = Math.tan(Math.PI * Math.min(freq, sampleRate * 0.49) / sampleRate) const k = 1 / Math.max(0.05, res) const inputSample = inputChannel[i] const a1 = 1 / (1 + g * (g + k)) const a2 = g * a1 const a3 = g * a2 const v3 = inputSample - this.ic2eq const v1 = a1 * this.ic1eq + a2 * v3 const v2 = this.ic2eq + a2 * this.ic1eq + a3 * v3 this.ic1eq = 2 * v1 - this.ic1eq this.ic2eq = 2 * v2 - this.ic2eq const lp = v2 const bp = v1 const hp = inputSample - k * v1 - v2 const notch = inputSample - k * v1 let outSample switch (this.mode) { case 'lowpass': outSample = lp break case 'highpass': outSample = hp break case 'bandpass': outSample = bp break case 'notch': outSample = notch break default: outSample = lp } outputChannel[i] = outSample } return true } } registerProcessor('svf-processor', SVFProcessor)