Spaces:
Running
Running
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; | |
// Remove the chunk if fully consumed. | |
if (this.currentChunkOffset >= currentChunk.length) { | |
this.bufferQueue.shift(); | |
this.currentChunkOffset = 0; | |
} | |
} else { | |
// If no data is available, fill the rest of the buffer with silence. | |
channel.fill(0, outputIndex); | |
outputIndex = numSamples; | |
} | |
} | |
return true; | |
} | |
} | |
registerProcessor( | |
"buffered-audio-worklet-processor", | |
BufferedAudioWorkletProcessor, | |
); | |
}; | |