|
class BufferedAudioWorkletProcessor extends AudioWorkletProcessor { |
|
constructor() { |
|
super(); |
|
this.bufferQueue = []; |
|
this.currentChunkOffset = 0; |
|
this.hadData = false; |
|
|
|
this.port.onmessage = (event) => { |
|
const data = event.data; |
|
if (data instanceof Float32Array) { |
|
this.hadData = true; |
|
this.bufferQueue.push(data); |
|
} else if (data === "stop") { |
|
this.bufferQueue = []; |
|
this.currentChunkOffset = 0; |
|
} |
|
}; |
|
} |
|
|
|
process(inputs, outputs) { |
|
const channel = outputs[0][0]; |
|
if (!channel) return true; |
|
|
|
const numSamples = channel.length; |
|
let outputIndex = 0; |
|
|
|
if (this.hadData && this.bufferQueue.length === 0) { |
|
this.port.postMessage({ type: "playback_ended" }); |
|
this.hadData = false; |
|
} |
|
|
|
while (outputIndex < numSamples) { |
|
if (this.bufferQueue.length > 0) { |
|
const currentChunk = this.bufferQueue[0]; |
|
const remainingSamples = currentChunk.length - this.currentChunkOffset; |
|
const samplesToCopy = Math.min(remainingSamples, numSamples - outputIndex); |
|
|
|
channel.set( |
|
currentChunk.subarray(this.currentChunkOffset, this.currentChunkOffset + samplesToCopy), |
|
outputIndex, |
|
); |
|
|
|
this.currentChunkOffset += samplesToCopy; |
|
outputIndex += samplesToCopy; |
|
|
|
|
|
if (this.currentChunkOffset >= currentChunk.length) { |
|
|
|
this.bufferQueue.shift(); |
|
this.currentChunkOffset = 0; |
|
this.port.postMessage({ type: "next_chunk" }); |
|
} |
|
} else { |
|
|
|
channel.fill(0, outputIndex); |
|
outputIndex = numSamples; |
|
} |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
registerProcessor("buffered-audio-worklet-processor", BufferedAudioWorkletProcessor); |
|
|