fix(fx-rt): het crackle master FX - engine gui block ASAP + worklet FILL buffer 8

_pump_output bo pace realtime (truoc: moi block send cach nhau block_sec lam
client queue ~1 -> jitter bridge/WS/GC -> queue can -> zero-fill crackle).
Gio gui het burst ASAP; worklet sf-fx-realtime them FILL=8: chi phat khi
outQueue >= 8 block, underrun -> re-buffer (im lang cho du FILL) thay vi
zero-fill lien tuc. Cap 32 giu nguyen. PDC: FX_RT_ROUNDTRIP_SAMPLES 1792->3840
(RTT ~7 block + FILL 8 block = 15 block @48k) de dry-bypass path khong lech
pha voi mastered. Cache-buster v=202608212401 (precompiled + worklet).
Rebuild PyInstaller + copy 3 noi deploy. Verify: probe crackle3 25s Ozone
rebuffer_events=0 (zero_fill 12 block = startup fill), picker UI PASS (59 VST).
This commit is contained in:
2026-08-23 12:50:11 +07:00
parent 9bb9a234b0
commit 6edfe1db32
5 changed files with 56 additions and 44 deletions
+8 -11
View File
@@ -1095,17 +1095,14 @@ async def ws_fx_realtime(websocket: WebSocket, session_id: str):
if not blks:
await asyncio.sleep(0.002)
continue
# Pace theo deadline (không sleep cố định sau mỗi send): thời
# gian send_bytes (WS → browser) CỘNG sleep cố định làm chu kì
# mỗi block > block_sec → engine drain chậm hơn realtime
# (đo: outRead 131/s thay vì 187.5/s → worklet zero-fill crackle).
# Gửi mỗi block cách nhau đúng block_sec kể từ mốc t0.
_t0 = _loop.time()
for _i, blk in enumerate(blks):
await websocket.send_bytes(blk)
_dt = _t0 + (_i + 1) * block_sec - _loop.time()
if _dt > 0:
await asyncio.sleep(_dt)
# Gửi ASAP (không pace theo realtime): bridge sản xuất burst
# theo batch 4 → gửi hết ngay → CLIENT tích lũy buffer
# (worklet FILL) hấp thụ jitter bridge/WS. Pace đúng realtime
# trước đây làm client queue ≈1 → bất kỳ jitter (bridge process
# VST biến thiên, GC, WS) → queue cạn → zero-fill crackle.
# Worklet cap 32 block chặn overflow khi bridge nhanh hơn.
for _blk in blks:
await websocket.send_bytes(_blk)
except Exception:
pass
pump_task = asyncio.create_task(_pump_output())