jevstrudel.git / packages / superdough / dspworklet.mjs
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}