diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html
index c61aaf8..8cf4b9a 100644
--- a/web/zebra-spaces.html
+++ b/web/zebra-spaces.html
@@ -1774,11 +1774,17 @@ class JitterBufferProcessor extends AudioWorkletProcessor {
this.maxSeconds = o.maxSeconds || (this.targetSeconds * 1.5);
this.targetSamples = Math.round(this.targetSeconds * sampleRate);
this.maxSamples = Math.round(this.maxSeconds * sampleRate);
+ /* re-arm only after this many consecutive empty blocks. 128 samples
+ * per block at 48 kHz = 2.67 ms; 100 blocks ≈ 267 ms of silence.
+ * brief upstream drains (a single empty process() tick) MUST NOT
+ * tear down playback, or a 4s re-buffer kicks in every time —
+ * which is what made the phone choppy. */
+ this.rearmThresholdBlocks = 100;
this.queue = [];
this.buffered = 0;
this.started = false;
+ this.emptyStreak = 0;
this.dropped = 0;
- this.starved = 0;
}
process(inputs, outputs){
const inBlk = inputs[0];
@@ -1800,21 +1806,28 @@ class JitterBufferProcessor extends AudioWorkletProcessor {
this.dropped += drop[0].length;
}
}
- /* lock onto the buffer once it fills; re-arm if we ever fully
- * drain so a brief upstream outage doesn't lock us into a
- * silent state */
+ /* lock onto the buffer once it fills. Do NOT un-lock on a single
+ * empty queue tick — that's what made the phone choppy: any
+ * 2.67ms drain forced a full 4s re-buffer. emptyStreak tracks
+ * sustained silence and only re-arms after ~267ms. */
if (!this.started && this.buffered >= this.targetSamples) this.started = true;
- else if (this.started && this.queue.length === 0) this.started = false;
if (this.started && this.queue.length > 0){
const head = this.queue.shift();
this.buffered -= head[0].length;
+ this.emptyStreak = 0;
for (let c = 0; c < nch; c++){
const srcCh = head[c] || head[0]; /* mono → stereo: dup L→R */
outBlk[c].set(srcCh.subarray(0, outBlk[c].length));
}
} else {
for (let c = 0; c < nch; c++) outBlk[c].fill(0);
- if (!this.started && this.buffered > 0) this.starved++;
+ if (this.started){
+ this.emptyStreak++;
+ if (this.emptyStreak >= this.rearmThresholdBlocks){
+ this.started = false;
+ this.emptyStreak = 0;
+ }
+ }
}
return true;
}
@@ -6098,8 +6111,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');