From d49c9dce4fccb5df67e60577352a7bcb997cfc49 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 17 May 2024 00:27:34 -0400 Subject: [PATCH 01/15] working --- packages/superdough/superdough.mjs | 2 + packages/superdough/worklets.mjs | 64 ++++++++++++++++++++++++++++++ website/src/repl/panel/Forms.jsx | 5 +-- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c0ba96e0..cd9fcfeb 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -471,6 +471,8 @@ export const superdough = async (value, t, hapDuration) => { shape !== undefined && chain.push(getWorklet(ac, 'shape-processor', { shape, postgain: shapevol })); distort !== undefined && chain.push(getWorklet(ac, 'distort-processor', { distort, postgain: distortvol })); + chain.push(getWorklet(ac, 'ladder-processor', { cutoff: 500, q: 1 })); + compressorThreshold !== undefined && chain.push( getCompressor(ac, compressorThreshold, compressorRatio, compressorKnee, compressorAttack, compressorRelease), diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index a1f524ca..152e51d6 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -106,6 +106,70 @@ class ShapeProcessor extends AudioWorkletProcessor { } registerProcessor('shape-processor', ShapeProcessor); +function fast_tanh(x) { + const x2 = x * x; + return (x * (27.0 + x2)) / (27.0 + 9.0 * x2); +} +const _PI = 3.14159265359; + +class LadderProcessor extends AudioWorkletProcessor { + static get parameterDescriptors() { + return [ + { name: 'cutoff', defaultValue: 500 }, + { name: 'q', defaultValue: 1 }, + ]; + } + + constructor() { + super(); + this.started = false; + this.p0 = 0; + this.p1 = 0; + this.p2 = 0; + this.p3 = 0; + this.p32 = 0; + this.p33 = 0; + this.p34 = 0; + } + + process(inputs, outputs, parameters) { + const input = inputs[0]; + const output = outputs[0]; + + const hasInput = !(input[0] === undefined); + if (this.started && !hasInput) { + return false; + } + this.started = hasInput; + + const resonance = parameters.q[0]; + let cutoff = parameters.cutoff[0]; + cutoff = (cutoff * 2 * _PI) / sampleRate; + cutoff = cutoff > 1 ? 1 : cutoff; + + const k = resonance * 4; + + for (let n = 0; n < blockSize; n++) { + for (let i = 0; i < input.length; i++) { + const out = this.p3 * 0.360891 + this.p32 * 0.41729 + this.p33 * 0.177896 + this.p34 * 0.0439725; + + this.p34 = this.p33; + this.p33 = this.p32; + this.p32 = this.p3; + + this.p0 += (fast_tanh(input[i][n] - k * out) - fast_tanh(this.p0)) * cutoff; + this.p1 += (fast_tanh(this.p0) - fast_tanh(this.p1)) * cutoff; + this.p2 += (fast_tanh(this.p1) - fast_tanh(this.p2)) * cutoff; + this.p3 += (fast_tanh(this.p2) - fast_tanh(this.p3)) * cutoff; + + output[i][n] = out; + } + } + return true; + } +} +registerProcessor('ladder-processor', LadderProcessor); + class DistortProcessor extends AudioWorkletProcessor { static get parameterDescriptors() { return [ diff --git a/website/src/repl/panel/Forms.jsx b/website/src/repl/panel/Forms.jsx index c5b22002..b9172e91 100644 --- a/website/src/repl/panel/Forms.jsx +++ b/website/src/repl/panel/Forms.jsx @@ -2,16 +2,13 @@ import cx from '@src/cx.mjs'; export function ButtonGroup({ value, onChange, items }) { return ( -
+
{Object.entries(items).map(([key, label], i, arr) => (