Spaces:
Paused
Paused
| class PCMProcessor extends AudioWorkletProcessor { | |
| constructor() { | |
| super(); | |
| this.buffer = new Float32Array(24000 * 30); // Pre-allocate buffer for ~30 seconds at 24kHz | |
| this.writeIndex = 0; | |
| this.readIndex = 0; | |
| this.pendingBytes = new Uint8Array(0); // Buffer for incomplete samples | |
| this.volume = 1.0; // Default volume (1.0 = 100%, 0.5 = 50%, etc.) | |
| this.port.onmessage = (event) => { | |
| if (event.data.pcmData) { | |
| // Combine any pending bytes with new data | |
| const newData = new Uint8Array(event.data.pcmData); | |
| const combined = new Uint8Array(this.pendingBytes.length + newData.length); | |
| combined.set(this.pendingBytes); | |
| combined.set(newData, this.pendingBytes.length); | |
| // Calculate how many complete 16-bit samples we have | |
| const completeSamples = Math.floor(combined.length / 2); | |
| const bytesToProcess = completeSamples * 2; | |
| if (completeSamples > 0) { | |
| // Process complete samples | |
| const int16Array = new Int16Array(combined.buffer.slice(0, bytesToProcess)); | |
| // Write directly to circular buffer | |
| for (let i = 0; i < int16Array.length; i++) { | |
| // Expand buffer if needed | |
| if (this.writeIndex >= this.buffer.length) { | |
| const newBuffer = new Float32Array(this.buffer.length * 2); | |
| // Copy existing data maintaining order | |
| let sourceIndex = this.readIndex; | |
| let targetIndex = 0; | |
| while (sourceIndex !== this.writeIndex) { | |
| newBuffer[targetIndex++] = this.buffer[sourceIndex]; | |
| sourceIndex = (sourceIndex + 1) % this.buffer.length; | |
| } | |
| this.buffer = newBuffer; | |
| this.readIndex = 0; | |
| this.writeIndex = targetIndex; | |
| } | |
| this.buffer[this.writeIndex] = int16Array[i] / 32768.0; // Convert 16-bit to float | |
| this.writeIndex = (this.writeIndex + 1) % this.buffer.length; | |
| } | |
| } | |
| // Store any remaining incomplete bytes | |
| if (combined.length > bytesToProcess) { | |
| this.pendingBytes = combined.slice(bytesToProcess); | |
| } else { | |
| this.pendingBytes = new Uint8Array(0); | |
| } | |
| } else if (event.data.volume !== undefined) { | |
| // Set volume (0.0 to 1.0, can go higher for amplification) | |
| this.volume = Math.max(0, event.data.volume); | |
| } | |
| }; | |
| } | |
| process(inputs, outputs, parameters) { | |
| const output = outputs[0]; | |
| if (output.length > 0 && this.readIndex !== this.writeIndex) { | |
| const channelData = output[0]; | |
| for (let i = 0; i < channelData.length && this.readIndex !== this.writeIndex; i++) { | |
| channelData[i] = this.buffer[this.readIndex] * this.volume; | |
| this.readIndex = (this.readIndex + 1) % this.buffer.length; | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| registerProcessor('pcm-processor', PCMProcessor); | |