| 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, |
| ); |
| }; |
|
|