Files
bruitiste/public/worklets/svf-processor.js
2025-10-06 02:16:23 +02:00

104 lines
2.3 KiB
JavaScript

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)