zebra-spaces: re-apply sink on every attach + per-tick ctx.sink telemetry (kill blanka-chrome sink drift)
After caa0548 (fixes A + B for ticket 0001), blanka-chrome was still
silent. Fresh telemetry pinned a third failure mode:
21:31:31 audio via AudioContext 47e5 sink=c38572ec… ← good
…leave + rejoin…
21:31:33 audio via AudioContext 47e5 sink=default ← drifted
No `audioCtx sink → c38572ec…` log line between the two attaches.
applySinkToAudioCtx was only invoked inside the `if (!audioCtx)` branch
in attachAudioStreamViaWorklet — a persistent audioCtx whose .sinkId
getter returned '' (system default) after a leave/rejoin never got
its sink re-applied, so every subsequent attach emitted to the system
default speaker instead of the device the user picked in the dropdown.
Fix: when audioCtx already exists AND speakerDeviceId is set AND
audioCtx.sinkId !== speakerDeviceId, call applySinkToAudioCtx() to
restore routing. Idempotent (same-sink call is a noop).
Telemetry: every 5s tick now includes `ctx.sink=…` and `want=…` when
the active sink doesn't match the picked one, so drift is visible
without grepping for the rare attach event.
Test: attach re-applies sink when audioCtx persists across leave/rejoin
and the picked sink drifted. Sandbox now exposes setSpeakerDeviceId so
the test can drive the picked-device path through the real
attachAudioStreamViaWorklet branch.
Ticket 0001 updated with the second-pass telemetry and fix (C).
This commit is contained in:
parent
b9e254172b
commit
4cedd85dc6
3 changed files with 115 additions and 6 deletions
|
|
@ -3281,6 +3281,19 @@ function attachAudioStreamViaWorklet(uuid, stream, targetSeconds){
|
|||
applySinkToAudioCtx();
|
||||
}
|
||||
catch(e){ logLine('err','audioCtx create: '+e.message); return false; }
|
||||
} else if (speakerDeviceId
|
||||
&& typeof audioCtx.setSinkId === 'function'
|
||||
&& audioCtx.sinkId !== speakerDeviceId){
|
||||
/* audioCtx survived a leave→re-enter but its sinkId drifted back
|
||||
* to '' (system default). Re-apply the picked sink so the worklet
|
||||
* keeps emitting to the device the user picked in the dropdown.
|
||||
* Fox 2026-06-07 blanka-chrome telemetry: first attach
|
||||
* `sink=c38572ec…`, a leave/rejoin later, next attach
|
||||
* `sink=default` and no `audioCtx sink → …` success line —
|
||||
* applySinkToAudioCtx only ran inside the `!audioCtx` branch above,
|
||||
* so a persistent audioCtx with reset sinkId stayed default
|
||||
* forever. Idempotent: same-sink call is a noop. */
|
||||
applySinkToAudioCtx();
|
||||
}
|
||||
if (audioCtx.state === 'suspended'){
|
||||
audioCtx.resume().catch(()=>{});
|
||||
|
|
@ -5830,6 +5843,23 @@ async function dumpTelemetry(){
|
|||
/* reset per-tick whisper counters */
|
||||
_whisperTick.sent = 0; _whisperTick.dropped = 0;
|
||||
_whisperTick.emitted = 0; _whisperTick.totalLatencyMs = 0;
|
||||
/* audioCtx sink visibility on EVERY tick — the one-shot 'sink=…' in
|
||||
* the attach log only captures the moment of attach; if the sink
|
||||
* drifts later (Chrome's audioCtx.sinkId getter can revert to '' on
|
||||
* device-changes, leave/rejoin, suspend/resume), this surfaces it
|
||||
* every 5s instead of needing to grep for the rare attach event.
|
||||
* Fox 2026-06-07 blanka-chrome: per-attach sink looked fine but the
|
||||
* silent-listener state persisted because the GETTER value drifted
|
||||
* between attaches. */
|
||||
if (audioCtx){
|
||||
const cs = (audioCtx.sinkId === undefined) ? 'n/a'
|
||||
: (audioCtx.sinkId || 'default');
|
||||
const want = speakerDeviceId || 'default';
|
||||
parts.push('ctx.sink=' + (cs.length > 12 ? cs.slice(0,8) : cs)
|
||||
+ (cs !== want && audioCtx.sinkId !== undefined
|
||||
? ' want=' + (want.length > 12 ? want.slice(0,8) : want)
|
||||
: ''));
|
||||
}
|
||||
/* receiver stats from sfuSubPC (the main listener path) */
|
||||
if (sfuSubPC && typeof sfuSubPC.getStats === 'function'){
|
||||
try {
|
||||
|
|
@ -8183,8 +8213,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-07</span><br>
|
||||
md5 <span class="stamp-md5">bb5c7dea19fcb19f88c479d1cb26f24f</span><br>
|
||||
sha256 <span class="stamp-sha">03e08e3499cf18fac3aec4b5dbeb808e6b3b6bceb5222ca560026c6d65864d38</span><br>
|
||||
md5 <span class="stamp-md5">1e77ac2d270d5b6b8697257a6d7f43cc</span><br>
|
||||
sha256 <span class="stamp-sha">6b76bd2486a0b366599cc8e22c1e11d51072e3bf09df3afed6f0208e5af8b0a9</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