Files

3.4 KiB
Raw Permalink Blame History

G3 — 1 clock: PDC latency thật + playhead engine-derived (kết quả)

Ngày: 2026-08-28. Plan: JUCE_LATENCY_PLAN.md G3. Option A — 1 clock.

Nội dung

  • Bridge báo processed samples qua lat ring slot đặc biệt 0xFFFFFFFE (JuceFxLoop.cpp, mỗi block sau ++processed): samples = processed*n (n=curBlock). Python _lat_drain tách slot này riêng → self.processed (KHÔNG vào latencies dict — tổng chain không đổi). Thêm get_processed().
  • WS latency message thêm processed (plugins.py): gửi khi processed đổi (mỗi block ~5.33ms), bỏ gate chỉ theo total — total chỉ đổi khi chain đổi.
  • Playhead engine-derived (app.jsx): khi FX active và có anchor, position = startOffset + (processed - anchor - latencyTotal)/sr (đã trừ latency → bám vị trí mẫu NGHE được; pre-roll tự giữ tại startOffset qua clamp). Anchor = raw processed tại play/wrap start (fxRtAnchorProcessed gọi từ __fxRtPlayheadFreeze, mọi play/resume path đều đi qua). Bỏ freeze/snap khi active; fallback wall-clock khi không active. Áp dụng 4 chỗ: bridge position block, sub-tab _ph, sub-tab wallTime, main branch _playheadSec → setCurrentTime.
  • Worklet nhận latency thật qua port message {type:'latency', samples}; clear_queue('loop'): wrapFadeBlocks = outQueue.length + ceil(latencySamples/BLOCK) — align processed-tail, crossfade đúng seam end→start kể cả khi FX latency (pureComp 2108 mẫu) còn in-flight trong bridge.

Verify (đã chạy)

  1. Build: cmake --build native_bridge/build_juce --config Release --target juce_fx_bridge OK. Copy src-tauri/binaries/juce_fx_bridge-x86_64-pc-windows-msvc.exe, SHA256 khớp bản build (07621b81...).
  2. Probe SHM thật (native_bridge/debug/g3_probe_check.py, JUCE bridge qua SF_JUCE_FX_BRIDGE_PATH, chain pureComp, SR 48k, 400 block ~2.1s):
    • latencies dict KHÔNG đổi: {0: 2108} trước/sau — processed tách riêng không làm sai tổng chain.
    • processed tiến 0 → 102400 (= 400 block × 256 mẫu) — bridge báo đúng.
  3. Cú pháp: python -m py_compile fx_realtime.py + plugins.py OK; node --check worklet OK; babel (node_modules) parse app.jsx OK (node thuần không hiểu JSX — dùng babel, exit 0).
  4. Fake session (object.new, không shm): get_processed() → None, get_latencies() → {0: 2108} — path không-SHM an toàn.

Done-when trạng thái

  • ✅ processed tách riêng, tổng latency chain không đổi (probe)
  • ✅ playhead công thức engine-derived đã cài đủ 4 chỗ; freeze/snap vô hiệu khi active (code path)
  • ⏳ Chưa verify bằng tai: 100-pass loop không click với crossfade tối thiểu; playhead bám vị trí mẫu trong app thật (cần user chạy GUI + nghe).

Ghi chú

  • Probe check script giữ ở native_bridge/debug/g3_probe_check.py (chạy lại: python native_bridge/debug/g3_probe_check.py).
  • BufferError lúc thoát (cannot close exported pointers) là quirk teardown shared_memory Python 3.13, không ảnh hưởng kết quả.
  • Latency WS ~5.33ms/msg = 187 msg/s — nhỏ so với pump audio; nếu cần tiết kiệm sau này: batch processed 4 block/lần (giảm 4×) — ponytail: chỉ làm khi có bằng chứng quá tải.