From 6e26f3975165ff4430b2079242937be351c40317 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:34:31 +0200 Subject: [PATCH 1/3] add dough function for raw dsp --- packages/superdough/dspworklet.mjs | 80 ++++++++++++++++++++++++++++++ packages/superdough/index.mjs | 1 + website/src/repl/Repl.jsx | 7 ++- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/superdough/dspworklet.mjs diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs new file mode 100644 index 00000000..ac51c8fc --- /dev/null +++ b/packages/superdough/dspworklet.mjs @@ -0,0 +1,80 @@ +import { Pattern } from '@strudel.cycles/core'; +import { getAudioContext } from './superdough.mjs'; + +let worklet; +export async function dspWorklet(ac, code) { + const name = `dsp-worklet-${Date.now()}`; + const workletCode = `${code} +let __q = []; // trigger queue +class MyProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.t = 0; + this.stopped = false; + this.port.onmessage = (e) => { + if(e.data==='stop') { + this.stopped = true; + } else if(e.data?.dough) { + const deadline = e.data.time-currentTime; + __q.push(e.data) + } else { + msg?.(e.data) + } + }; + } + process(inputs, outputs, parameters) { + const output = outputs[0]; + if(__q.length) { + __q = __q.filter((el) => { + const deadline = el.time-currentTime; + return deadline>0 ? true : trigger(el.dough) + }) + } + for (let i = 0; i < output[0].length; i++) { + const out = dsp(this.t / sampleRate); + output.forEach((channel) => { + channel[i] = out; + }); + this.t++; + } + return !this.stopped; + } +} +registerProcessor('${name}', MyProcessor); +`; + const base64String = btoa(workletCode); + const dataURL = `data:text/javascript;base64,${base64String}`; + await ac.audioWorklet.addModule(dataURL); + const node = new AudioWorkletNode(ac, name); + const stop = () => node.port.postMessage('stop'); + return { node, stop }; +} +const stop = () => { + if (worklet) { + worklet?.stop(); + worklet?.node?.disconnect(); + } +}; + +if (typeof window !== 'undefined') { + window.addEventListener('message', (e) => { + if (e.data === 'strudel-stop') { + stop(); + } else if (e.data?.dough) { + worklet?.node.port.postMessage(e.data); + } + }); +} + +export const dough = async (code) => { + const ac = getAudioContext(); + stop(); + worklet = await dspWorklet(ac, code); + worklet.node.connect(ac.destination); +}; + +Pattern.prototype.dough = function () { + return this.onTrigger((t, hap) => { + window.postMessage({ time: t, dough: hap.value }); + }, 1); +}; diff --git a/packages/superdough/index.mjs b/packages/superdough/index.mjs index e5d4498b..3247c5b4 100644 --- a/packages/superdough/index.mjs +++ b/packages/superdough/index.mjs @@ -10,3 +10,4 @@ export * from './helpers.mjs'; export * from './synth.mjs'; export * from './zzfx.mjs'; export * from './logger.mjs'; +export * from './dspworklet.mjs'; diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 173bb455..fe32e741 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -149,7 +149,12 @@ export function Repl({ embedded = false }) { onEvalError: (err) => { setPending(false); }, - onToggle: (play) => !play && cleanupDraw(false), + onToggle: (play) => { + if (!play) { + cleanupDraw(false); + window.postMessage('strudel-stop'); + } + }, drawContext, // drawTime: [0, 6], paintOptions, From 52c01abbe9e62abc18820143bfea0569e2c7d31a Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:39:03 +0200 Subject: [PATCH 2/3] encapsulate .dough --- packages/superdough/dspworklet.mjs | 9 +++------ packages/webaudio/webaudio.mjs | 6 +++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index ac51c8fc..2a77de53 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -1,4 +1,3 @@ -import { Pattern } from '@strudel.cycles/core'; import { getAudioContext } from './superdough.mjs'; let worklet; @@ -73,8 +72,6 @@ export const dough = async (code) => { worklet.node.connect(ac.destination); }; -Pattern.prototype.dough = function () { - return this.onTrigger((t, hap) => { - window.postMessage({ time: t, dough: hap.value }); - }, 1); -}; +export function doughTrigger(t, hap, currentTime, duration, cps) { + window.postMessage({ time: t, dough: hap.value, currentTime, duration, cps }); +} diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 8b32a90c..fb4a3d7d 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th */ import * as strudel from '@strudel.cycles/core'; -import { superdough, getAudioContext, setLogger } from 'superdough'; +import { superdough, getAudioContext, setLogger, doughTrigger } from 'superdough'; const { Pattern, logger } = strudel; setLogger(logger); @@ -35,3 +35,7 @@ export function webaudioScheduler(options = {}) { onTrigger: strudel.getTrigger({ defaultOutput, getTime }), }); } + +Pattern.prototype.dough = function () { + return this.onTrigger(doughTrigger, 1); +}; From 7078e20200bcbcc98e61de795604fc174d9495e3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 25 Sep 2023 22:56:44 +0200 Subject: [PATCH 3/3] less garbage --- packages/superdough/dspworklet.mjs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/superdough/dspworklet.mjs b/packages/superdough/dspworklet.mjs index 2a77de53..1c109452 100644 --- a/packages/superdough/dspworklet.mjs +++ b/packages/superdough/dspworklet.mjs @@ -24,10 +24,13 @@ class MyProcessor extends AudioWorkletProcessor { process(inputs, outputs, parameters) { const output = outputs[0]; if(__q.length) { - __q = __q.filter((el) => { - const deadline = el.time-currentTime; - return deadline>0 ? true : trigger(el.dough) - }) + for(let i=0;i<__q.length;++i) { + const deadline = __q[i].time-currentTime; + if(deadline<=0) { + trigger(__q[i].dough) + __q.splice(i,1) + } + } } for (let i = 0; i < output[0].length; i++) { const out = dsp(this.t / sampleRate);