Repository navigation
fix(watch): keep the audio decoder across a numeric delay change - #4809
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The audio decoder gated on `sync.in.delay === "instant"` by tracking the whole delay signal, so every numeric step (100 to 120 ms, or "auto" to a number) resubscribed, rebuilt the decoder, and truncated the ring on the handover: an audible gap. Sync now derives `out.instant`, and the decoder and Sync's own instant checks read that, so only a switch to or from "instant" reruns them. Numeric changes still reach the ring through `setLatency`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Quest outcome: implemented as planned; Open decision for the maintainer: whether Left as a draft for review. (Written by Claude Opus 5.5) |
|
Maintainer decision: (Written by Claude Opus 5.5) |
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 5 minutes. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 7279c57.
[P2] Preserve the immediate instant-mode behavior of Sync.wait (js/watch/src/sync.ts:255). out.instant starts false and #runInstant runs on a microtask, so const sync = new Sync({ delay: "instant" }); await sync.wait(Time.Milli(0)); now rejects with "reference not set; call received() first"; the base resolves. A same-turn switch into instant after reset has the same stale-predicate problem. Keep synchronous input peeks in wait (they do not subscribe or trigger decoder rebuilds), or otherwise make its mode check immediately current, and add a regression test without a preliminary flush.
Direction: deriving a deduplicated mode signal for the decoder effects is a focused fix, and numeric latency still flows through #runLatency. Retain that approach while preserving wait's synchronous mode semantics. No wire change; the public Getter addition matches the recorded maintainer decision.
Verification: inspected all eight changed files and the relevant decoder, Sync, and signal scheduling code. An isolated Node reproduction using base/head Sync and the repository's Signal/Time implementations, with import paths adapted, resolves on base and rejects on head. Full Bun tests, just check, and real-browser audio playback were not run.
(Written by OpenAI)
out.instant updates on a microtask, so a same-turn switch to "instant" (or a Sync constructed instant) left wait reading a stale predicate. Peek the input in wait instead; the decoder effects keep the deduplicated signal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automated review — head Small, well-targeted fix for #4777. Deriving The quest cleanup is complete: there are no remaining links to Blocking None. Non-blocking
Cross-PR
Verdict: MERGE (once CI is green) This is an automated review, not the maintainer's decision |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automated follow-up review — head This push is a one-line change in Blocking None. Still open (non-blocking)
Verdict: MERGE (once CI is green) This is an automated review, not the maintainer's decision |
|
Merge summary:
(Written by Claude Opus 5.5) |
Closes #4777
Problem
Changing
@moq/watch's numeric delay (100 to 120 ms, or"auto"to a number) resubscribed audio, rebuilt the decoder, and truncated the ring: an audible gap. The audio decoder readeffect.get(sync.in.delay) === "instant", which tracks the whole signal, so every numeric step reran#runDecoderand#runEnabled, and the replacement subscription's handover truncated the ring.Approach
Syncderivesout.instant(one owner for the predicate). The audio decoder and Sync's own#runMaxAgeand#runDelayread it, so only a switch to or from"instant"reruns them.waitand its#sleepstill check and wake onin.delaydirectly, sinceout.instantlags a same-turn change until effects flush (regression test included). Numeric changes still reach the ring through#runLatency'ssetLatency.Delaydoc, which said the owner must turn audio off; the decoder already does.js/watch/src/audio/decoder.test.tsdrives a realDecoderwith fake WebAudio/WebCodecs globals and a fed container consumer: a numeric change and"auto"to a number keep one subscription, one codec, and no ring truncate/reset (both fail onmainwith 2 subscriptions); a switch to"instant"resets the ring, and back rebuilds with one handover truncate.Impact
@moq/watch: addsSync.out.instant, a read-onlyGetter<boolean>. Additive, no breaking change. The maintainer decided it stays public.Alternatives
"instant"check acrossSyncand the decoder;out.instantkeeps one owner.Follow-ups
🤖 Generated with Claude Code
(Written by Claude Opus 5.5)