V42+V42A: fix seam click khi loop master FX — worklet wrap crossfade (tail->head) + native WebAudio loop cho bypass (het click beat 0); dip bypass 5ms; PDC playhead freeze/snap

This commit is contained in:
2026-08-28 11:35:32 +07:00
parent 9ccc28d211
commit fa61712ecc
10 changed files with 690 additions and 108 deletions
+17
View File
@@ -1123,6 +1123,16 @@ async def ws_fx_realtime(websocket: WebSocket, session_id: str):
# tới worklet kịp khi main-thread stall chặn cả 2 hướng (fade nằm trong
# browser queue, tới sau khi worklet đã cạn buffer → click vẫn còn), và
# fade-in sau stall tạo dip âm lượng định kỳ = vibrato/wow&flutter.
# V34 diag: pump gap log - bridge khong san xuat block (SHM rong) > 30ms
# => fxrt_pump.log (phan biet bridge stall vs Python pump stall).
_plog = os.environ.get("FXRT_PUMP_LOG")
if not _plog:
_ad = os.environ.get("APPDATA")
_plog = os.path.join(_ad, "SonicForgeDAW", "logs", "fxrt_pump.log") if _ad else ""
_pf = open(_plog, "a", buffering=1) if _plog else None
_pump_last = None
if _pf is not None:
_pf.write("%d pump_start\n" % int(_time.time() * 1000))
_last_lat = None
try:
while True:
@@ -1147,6 +1157,13 @@ async def ws_fx_realtime(websocket: WebSocket, session_id: str):
# time.sleep dùng high-res waitable timer -> poll đúng 2ms.
await asyncio.to_thread(_time.sleep, 0.002)
continue
if _pf is not None:
_now = _time.time()
if _pump_last is not None:
_gap = (_now - _pump_last) * 1000.0
if _gap > 30.0:
_pf.write("%d pump_gap %.0f n=%d\n" % (int(_now * 1000), _gap, len(blks)))
_pump_last = _now
for _blk in blks:
await websocket.send_bytes(_blk)
except Exception: