conversational-webgpu / src /play-worklet.js
Xenova's picture
Xenova HF Staff
Delete public (#1)
022e8a2 verified
raw
history blame
2.06 kB
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,
);
};