From 2a925573f329e400bb85dd61560bd2008ed05cb6 Mon Sep 17 00:00:00 2001 From: danigb Date: Sun, 8 Sep 2024 20:34:36 +0200 Subject: [PATCH] feat: expose parameters --- packages/chorus/README.md | 2 +- packages/chorus/src/index.ts | 12 ++++++-- packages/chorus/src/processor.ts | 2 +- packages/chorus/src/worklet.ts | 38 ++++++++++++++---------- site/content/docs/(effects)/chorus-t.mdx | 4 +++ site/content/docs/(effects)/chorus.mdx | 13 ++++++-- site/examples/ChorusExample.tsx | 23 ++++++++++++++ 7 files changed, 72 insertions(+), 22 deletions(-) diff --git a/packages/chorus/README.md b/packages/chorus/README.md index 251fe7f..73bbb83 100644 --- a/packages/chorus/README.md +++ b/packages/chorus/README.md @@ -1,5 +1,5 @@ # @synthlet/chorus -> Chorus mono effect written in Faust and ported to the web +> Chorus effect written in Faust and ported to the web Part of [Synthlet](https://github.com/danigb/synthlet) diff --git a/packages/chorus/src/index.ts b/packages/chorus/src/index.ts index 39e4951..9425664 100644 --- a/packages/chorus/src/index.ts +++ b/packages/chorus/src/index.ts @@ -8,18 +8,24 @@ import { PROCESSOR } from "./processor"; export const registerChorusWorklet = createRegistrar("CHORUS", PROCESSOR); export type ChorusInputs = { - trigger: ParamInput; + delay: ParamInput; + rate: ParamInput; + depth: ParamInput; + deviation: ParamInput; }; export type ChorusWorkletNode = AudioWorkletNode & { - trigger: AudioParam; + delay: AudioParam; + rate: AudioParam; + depth: AudioParam; + deviation: AudioParam; dispose(): void; }; export const Chorus = createWorkletConstructor( { processorName: "ChorusProcessor", - paramNames: ["trigger"], + paramNames: ["delay", "rate", "depth", "deviation"], workletOptions: () => ({ numberOfInputs: 1, numberOfOutputs: 1, diff --git a/packages/chorus/src/processor.ts b/packages/chorus/src/processor.ts index 65e2b6b..74ffb48 100644 --- a/packages/chorus/src/processor.ts +++ b/packages/chorus/src/processor.ts @@ -1 +1 @@ -export const PROCESSOR = `"use strict";(()=>{function pt(f){let l=new Float32Array(65536),a=new Float32Array(65536),i=f,r=new Float32Array(2),t=new Float32Array(2),m=new Float32Array(2),p=new Float32Array(2),c=new Float32Array(2),h=new Float32Array(2),T=new Float32Array(2),n=new Float32Array(2),u=new Float32Array(2),x=new Float32Array(2),y=new Float32Array(2),o=new Float32Array(8192),e=0,w=new Int32Array(2),M=Math.min(192e3,Math.max(1,i)),I=Math.exp(-(44.12234/M)),D=1-I,ct=.33333334/M,Tt=1/M,ut=.14285715/M,xt=.5/M,yt=.25/M,At=.16666667/M,wt=.125/M,E=.5,G=.5,O=.5,L=.5;st(l,St),st(a,Vt);function Ft(F,k,v){let P=D*E,Ct=4.096*G,gt=625e-7*O,bt=D*L;for(let A=0;A>>0,t[1]=t[0],h[1]=h[0],n[1]=n[0],T[1]=T[0],u[1]=u[0],x[1]=x[0],y[1]=y[0],m[1]=m[0],p[1]=p[0],c[1]=c[0]}}function Rt(F,k,v,P){E=F,G=k,O=v,L=P}return{update:Rt,compute:Ft}}var St=f=>Math.cos(958738e-10*f),Vt=f=>Math.sin(958738e-10*f);function st(f,l){if(f.length!==65536)throw new Error("Table must be 65536 samples long");let a=0,i=0,r=0,t=0;for(let m=0;m<65536;m++)a=1,r=(i+t)%65536,f[m]=Math.cos(958738e-10*r),i=a,t=r;return f}var B=class extends AudioWorkletProcessor{r;g;constructor(){super(),this.r=!0;let{compute:l}=pt(sampleRate);this.g=l,this.port.onmessage=a=>{switch(a.data.type){case"DISPOSE":this.r=!1;break}}}process(l,a,i){if(l[0].length===0)return this.r;let r=l[0][0],t=a[0][0],m=a[0][1];return this.g(r,t,m),this.r}static get parameterDescriptors(){return[["trigger",0,0,1]].map(([l,a,i,r])=>({name:l,defaultValue:a,minValue:i,maxValue:r,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",B);})();`; +export const PROCESSOR = `"use strict";(()=>{function pt(i){let l=new Float32Array(65536),a=new Float32Array(65536),h=i,r=new Float32Array(2),t=new Float32Array(2),m=new Float32Array(2),p=new Float32Array(2),c=new Float32Array(2),n=new Float32Array(2),u=new Float32Array(2),f=new Float32Array(2),T=new Float32Array(2),y=new Float32Array(2),x=new Float32Array(2),o=new Float32Array(8192),e=0,w=new Int32Array(2),M=Math.min(192e3,Math.max(1,h)),v=Math.exp(-(44.12234/M)),D=1-v,ct=.33333334/M,ut=1/M,Tt=.14285715/M,yt=.5/M,xt=.25/M,At=.16666667/M,wt=.125/M,E=.5,G=.5,O=.5,L=.5;st(l,St),st(a,Vt);function Ft(F,I,k){let P=D*E,Rt=4.096*G,Ct=625e-7*O,bt=D*L;for(let A=0;A>>0,t[1]=t[0],n[1]=n[0],f[1]=f[0],u[1]=u[0],T[1]=T[0],y[1]=y[0],x[1]=x[0],m[1]=m[0],p[1]=p[0],c[1]=c[0]}}function dt(F,I,k,P){E=F,G=I,O=k,L=P}return{update:dt,compute:Ft}}var St=i=>Math.cos(958738e-10*i),Vt=i=>Math.sin(958738e-10*i);function st(i,l){if(i.length!==65536)throw new Error("Table must be 65536 samples long");let a=0,h=0,r=0,t=0;for(let m=0;m<65536;m++)a=1,r=(h+t)%65536,i[m]=Math.cos(958738e-10*r),h=a,t=r;return i}var B=class extends AudioWorkletProcessor{r;u;g;constructor(){super(),this.r=!0;let{compute:l,update:a}=pt(sampleRate);this.u=a,this.g=l,this.port.onmessage=h=>{switch(h.data.type){case"DISPOSE":this.r=!1;break}}}process(l,a,h){if(l[0].length===0)return this.r;this.u(h.delay[0],h.rate[0],h.depth[0],h.deviation[0]);let r=l[0][0],t=a[0][0],m=a[0][1];return this.g(r,t,m),this.r}static get parameterDescriptors(){return[["delay",.5,0,1],["rate",.5,0,1],["depth",.5,0,1],["deviation",.5,0,1]].map(([l,a,h,r])=>({name:l,defaultValue:a,minValue:h,maxValue:r,automationRate:"k-rate"}))}};registerProcessor("ChorusProcessor",B);})();`; diff --git a/packages/chorus/src/worklet.ts b/packages/chorus/src/worklet.ts index eed3c64..b9fb653 100644 --- a/packages/chorus/src/worklet.ts +++ b/packages/chorus/src/worklet.ts @@ -2,12 +2,14 @@ import { createChorus } from "./dsp"; export class ChorusProcessor extends AudioWorkletProcessor { r: boolean; // running + u: ReturnType["update"]; g: ReturnType["compute"]; constructor() { super(); this.r = true; - const { compute } = createChorus(sampleRate); + const { compute, update } = createChorus(sampleRate); + this.u = update; this.g = compute; this.port.onmessage = (event) => { switch (event.data.type) { @@ -18,12 +20,15 @@ export class ChorusProcessor extends AudioWorkletProcessor { }; } - process( - inputs: Float32Array[][], - outputs: Float32Array[][], - parameters: any - ) { + process(inputs: Float32Array[][], outputs: Float32Array[][], params: any) { if (inputs[0].length === 0) return this.r; + + this.u( + params.delay[0], + params.rate[0], + params.depth[0], + params.deviation[0] + ); const input = inputs[0][0]; const outputL = outputs[0][0]; const outputR = outputs[0][1]; @@ -32,15 +37,18 @@ export class ChorusProcessor extends AudioWorkletProcessor { } static get parameterDescriptors() { - return [["trigger", 0, 0, 1]].map( - ([name, defaultValue, minValue, maxValue]) => ({ - name, - defaultValue, - minValue, - maxValue, - automationRate: "k-rate", - }) - ); + return [ + ["delay", 0.5, 0, 1], + ["rate", 0.5, 0, 1], + ["depth", 0.5, 0, 1], + ["deviation", 0.5, 0, 1], + ].map(([name, defaultValue, minValue, maxValue]) => ({ + name, + defaultValue, + minValue, + maxValue, + automationRate: "k-rate", + })); } } diff --git a/site/content/docs/(effects)/chorus-t.mdx b/site/content/docs/(effects)/chorus-t.mdx index 68b06d0..ae90a45 100644 --- a/site/content/docs/(effects)/chorus-t.mdx +++ b/site/content/docs/(effects)/chorus-t.mdx @@ -5,6 +5,10 @@ description: A chorus effect based on the TAL-Noisemaker one A Chorus effect based on the TAL-Noisemaker that in turn models a Roland Juno-60 chorus. + + This module is WIP. + + ```ts import { ChorusT } from "synthlet"; diff --git a/site/content/docs/(effects)/chorus.mdx b/site/content/docs/(effects)/chorus.mdx index a16f141..8bbf29c 100644 --- a/site/content/docs/(effects)/chorus.mdx +++ b/site/content/docs/(effects)/chorus.mdx @@ -5,6 +5,8 @@ description: A chorus effect implemented in Faust import ChorusExample from "../../../examples/ChorusExample"; +This is a mono-to-stereo chorus effect implemented in Faust and ported to the web. + ```ts import { Chorus } from "synthlet"; @@ -16,10 +18,17 @@ const chorus = Chorus(audioContext, { }); osc.connect(chorus).connect(audioContext.destination); - -chorus.lfoRate1.value = 0.2; ``` ## Parameters + +- `delay`: The delay time [0, 1] +- `rate`: The modulation rate [0, 1] +- `depth`: The modulation depth [0, 1] +- `deviation`: The modulation deviation [0, 1] + +## References + +- [Faust original code](https://github.com/grame-cncm/faust/blob/master-dev/examples/SAM/chorus/chorus.dsp) diff --git a/site/examples/ChorusExample.tsx b/site/examples/ChorusExample.tsx index c934f6a..26a98ba 100644 --- a/site/examples/ChorusExample.tsx +++ b/site/examples/ChorusExample.tsx @@ -3,6 +3,7 @@ import { AdsrAmp, Oscillator, Param } from "synthlet"; import { Chorus } from "../../packages/chorus/src"; import { ExamplePane, GateButton } from "./components/ExamplePane"; +import { Slider } from "./components/Slider"; import { useSynth } from "./useSynth"; function ChorusSynth(context: AudioContext) { @@ -22,6 +23,28 @@ function Example() { return ( <> +
+ + + + +
);