// Plays the blocks the audio worker renders. The page sends the worker's feed
// port across; every block taken off the queue is acknowledged so another one
// can be rendered. Blocks are planar: the left channel first, then the right.
class QuackPlayer extends AudioWorkletProcessor {
constructor() {
super();
this.blocks = [];
this.current = null;
this.pos = 0;
this.feed = null;
this.port.onmessage = ({ data }) => {
if (data?.type !== 'feed') return;
this.feed = data.feed;
this.feed.onmessage = ({ data: block }) => this.blocks.push(block);
};
}
process(_inputs, outputs) {
const [left, right] = outputs[0];
for (let i = 0; i < left.length; i++) {
if (!this.current || this.pos >= this.current.audio.length / 2) {
this.current = this.blocks.shift() ?? null;
this.pos = 0;
if (!this.current) {
// the worker fell behind (or is just starting)
left.fill(0, i);
right.fill(0, i);
break;
}
this.feed?.postMessage(0);
}
const frames = this.current.audio.length / 2;
left[i] = this.current.audio[this.pos];
right[i] = this.current.audio[frames + this.pos];
this.pos++;
}
return true;
}
}
registerProcessor('quack-audio-player', QuackPlayer);