|
export default () => { |
|
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; |
|
} |
|
} else { |
|
|
|
channel.fill(0, outputIndex); |
|
outputIndex = numSamples; |
|
} |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
registerProcessor( |
|
"buffered-audio-worklet-processor", |
|
BufferedAudioWorkletProcessor, |
|
); |
|
}; |
|
|