diff --git a/@applications/web/src/worklets/pcm-player.js b/@applications/web/src/worklets/pcm-player.js index 8e3b8f2..a3d5e97 100644 --- a/@applications/web/src/worklets/pcm-player.js +++ b/@applications/web/src/worklets/pcm-player.js @@ -21,8 +21,9 @@ */ const INPUT_SAMPLE_RATE = 22050; -/** Ring buffer capacity in samples (≈1.5 seconds at 22050Hz) */ -const RING_CAPACITY = 32768; // must be power of 2 +/** Ring buffer capacity in samples (≈5.95 seconds at 22050Hz). + * Must be a power of 2. Sized to hold a full TTS segment (typically 2–4s). */ +const RING_CAPACITY = 131072; // 2^17 class PcmPlayerProcessor extends AudioWorkletProcessor { constructor() { @@ -59,20 +60,25 @@ class PcmPlayerProcessor extends AudioWorkletProcessor { const int16 = new Int16Array(buffer); const incoming = int16.length; - // Check for overrun — drop oldest samples to make room + // If the incoming chunk is larger than the ring, keep only the last RING_CAPACITY samples + // (i.e. drop the oldest part of a very long chunk rather than corrupting state). + const srcOffset = incoming > RING_CAPACITY ? incoming - RING_CAPACITY : 0; + const writeCount = incoming - srcOffset; + + // Drop oldest buffered samples to make room — never go below zero const freeSpace = RING_CAPACITY - this._available; - if (incoming > freeSpace) { - const drop = incoming - freeSpace; + if (writeCount > freeSpace) { + const drop = Math.min(writeCount - freeSpace, this._available); this._readHead = (this._readHead + drop) & (RING_CAPACITY - 1); this._available -= drop; } // Write Int16 → Float32 into ring - for (let i = 0; i < incoming; i++) { - this._ring[this._writeHead] = int16[i] / 32768.0; + for (let i = 0; i < writeCount; i++) { + this._ring[this._writeHead] = int16[srcOffset + i] / 32768.0; this._writeHead = (this._writeHead + 1) & (RING_CAPACITY - 1); } - this._available += incoming; + this._available += writeCount; } /**