Files
RemSound/lab-classic-after.txt
T
EdnunpandClaude Opus 5 dfdc243f03 Latency slider: one slider now means one value, and a move arrives in seconds
Two bugs behind the 2026-08-14 field report ("the latency slider does nothing" — a WASAPI-only
machine, auto-tune off, a clap test showing no change either way, while Ed's own two-slider rig
audibly works). Both found by MEASURING, not reasoning: --latency-lab drives the real playout
with realtime-paced sender/device shapes and records actual buffered depth once a second.

1. The slider wrote to a value nothing read. ReconcileReplicasLocked tags every session with an
   OUTPUT LANE (WasapiLane/AsioLane — never Mixed while a device is ticked), and the render path
   resolved its target from that lane. But MainForm.MaxLatencyBoxRoute sends the single slider to
   RenderRoute.Mixed in EVERY non-BothIndependent mode. So in classic modes the slider — and the
   auto-tune with it — updated a value the audio never consumed, leaving the real target on
   LaneLatency's 30 ms default for the whole session; raise AND lower equally inert (lower also
   matched on route, so it skipped every playing session). Only BothIndependent worked, because
   there the slider writes WasapiLane, which IS what its sessions read. That's precisely "works on
   mine, dead on his". Fix: the lane tag says which DEVICE a session renders through, not which
   knob governs it. One slider now means ONE value (sharedLatency) read by every session whatever
   its lane; two sliders only in BothIndependent, seeded from the shared value on entry so audio
   doesn't jump. Arming also uses the session's own route target (matters in BothIndependent).
   Measured, classic path: -0.15ms/s (STALLED) -> +2.07ms/s (GROWS).

2. Even correctly wired, a raise crawled. Steady-state depth feedback is capped at 0.3% (~3ms of
   catch-up per second) AND only recomputed at the 10s drift-window boundary, so a 400 ms raise
   took over two minutes and still read as a dead slider (Ed: "it needs to change the value
   quickly not over minutes"). A raise can't be met by dropping audio, only by banking it, so a
   DELIBERATE move (the hard setter — never the auto-tune's soft one, leaving its parked descent
   behaviour untouched) now engages a fast approach: 5% bias, recomputed 5x/sec, self-clearing
   within 15 ms of target. Audible glide, no gap, no click — the user asked for the change and
   hears it happen. Measured: 49ms -> 324ms within 10s, then holds.

Gate: new step pins the invariant fast and deterministically — what the slider sets IS what the
session's route reads, a lower reaches the session, and two-slider mode keeps its lanes separate.
74/74 + relay. --latency-lab (six scenarios) / --latency-lab classic (the faithful app path) stay
in the tree as the measuring harness.

NOT released. Test build for the reporting user: D:\Dropbox\remsound-latency-slider-test.zip

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:01:19 +01:00

19 lines
757 B
Plaintext

RemSound latency lab - measuring depth-target convergence with the shipped playout code.
Raise under test: 30ms -> 330ms. Expected growth at full depth-bias: ~3ms/s.
--- T3 classic app path: WASAPI lane active, slider drives Mixed ---
session route after arrival: WasapiLane (slider writes to: Mixed)
settled at slider 30ms: buffered=70ms
t+10s buffered= 48ms
t+20s buffered= 66ms
t+30s buffered= 114ms
t+40s buffered= 125ms
t+50s buffered= 165ms
t+60s buffered= 194ms
RAISE via slider: 70ms -> 194ms in 60s = 2.07ms/s GROWS (slider works)
LOWER via slider: back to 30ms -> buffered=48ms after 3s
=== SUMMARY ===
T3 classic app path: WASAPI lane active, slider drives Mixed: raise 2.07ms/s [GROWS (slider works)], lower -> 48ms