zebra-spaces: worklet sticky-started — tolerate brief drains, don't re-buffer 4s on every hiccup
Fox 2026-06-04: "delay does seem to be about 4 secs but still very chappy." Buffer IS holding 4s and emitting — that part works — but every transient empty-queue tick (a single 2.67 ms drain) was setting started=false, which forced a full 4-second re-fill before emit resumed. So a 50 ms network jitter on the upstream caused a 4 s silence on the listener. That's the chop. Fix: track consecutive empty-queue blocks. Only re-arm (started=false) after rearmThresholdBlocks (100 = ~267 ms) of sustained silence. Brief drains emit silence-fill but keep started=true so playback resumes the instant new samples arrive. Listener hears at most ~267 ms of dead air on each drain — almost certainly Opus PLC will mask far shorter ones. Long outages (>267 ms with no samples) still re-buffer to 4 s — that case isn't this bug, it's a real upstream death where a fresh cushion is correct.
This commit is contained in:
parent
6302e9978a
commit
fd2d927b5e
1 changed files with 21 additions and 8 deletions
|
|
@ -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');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-04</span><br>
|
||||
md5 <span class="stamp-md5">545d1105afc28939ffd80b0ab81b3181</span><br>
|
||||
sha256 <span class="stamp-sha">1dbf38c720551b0dd746ca2467a1e9b936fe08dca1291de0a392381f6b3417d6</span><br>
|
||||
md5 <span class="stamp-md5">40b0bf67548bfa739a92c09566384731</span><br>
|
||||
sha256 <span class="stamp-sha">498399317ffdf93d814ccc98e4e5f413b249e91ccfa15e7d7767a81f34e31eb1</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue