1import { getAudioContext } from './audioContext.mjs'; 2 3let worklet; 4export async function dspWorklet(ac, code) { 5 const name = `dsp-worklet-${Date.now()}`; 6 const workletCode = `${code} 7let __q = []; // trigger queue 8class MyProcessor extends AudioWorkletProcessor { 9 constructor() { 10 super(); 11 this.t = 0; 12 this.stopped = false; 13 this.port.onmessage = (e) => { 14 if(e.data==='stop') { 15 this.stopped = true; 16 } else if(e.data?.dough) { 17 __q.push(e.data) 18 } else { 19 msg?.(e.data) 20 } 21 }; 22 } 23 process(inputs, outputs, parameters) { 24 const output = outputs[0]; 25 if(__q.length) { 26 for(let i=0;i<__q.length;++i) { 27 const deadline = __q[i].time-currentTime; 28 if(deadline<=0) { 29 trigger(__q[i].dough) 30 __q.splice(i,1) 31 } 32 } 33 } 34 for (let i = 0; i < output[0].length; i++) { 35 const out = dsp(this.t / sampleRate); 36 output.forEach((channel) => { 37 channel[i] = out; 38 }); 39 this.t++; 40 } 41 return !this.stopped; 42 } 43} 44registerProcessor('${name}', MyProcessor); 45`; 46 const base64String = btoa(workletCode); 47 const dataURL = `data:text/javascript;base64,${base64String}`; 48 await ac.audioWorklet.addModule(dataURL); 49 const node = new AudioWorkletNode(ac, name); 50 const stop = () => node.port.postMessage('stop'); 51 return { node, stop }; 52} 53const stop = () => { 54 if (worklet) { 55 worklet?.stop(); 56 worklet?.node?.disconnect(); 57 } 58}; 59 60if (typeof window !== 'undefined') { 61 window.addEventListener('message', (e) => { 62 if (e.data === 'strudel-stop') { 63 stop(); 64 } else if (e.data?.dough) { 65 worklet?.node.port.postMessage(e.data); 66 } 67 }); 68} 69 70export const rawdsp = async (code) => { 71 const ac = getAudioContext(); 72 stop(); 73 worklet = await dspWorklet(ac, code); 74 worklet.node.connect(ac.destination); 75}; 76 77export function rawdspTrigger(hap, currentTime, cps, targetTime) { 78 window.postMessage({ time: targetTime, dough: hap.value, currentTime, duration: hap.duration, cps }); 79}