fix(engine): giam jitter bridge timer va fade-out khi transport stall

- RealtimeFxLoop: timeBeginPeriod(1) + sleep_until thay Sleep(1) -> loopMax 32.9->24.8ms
- plugins.py _pump_output: stall-fade 8 block gain 1->0 khi gap > 5.5*block_sec (~29ms),
  burst send, fade-in 0->1 khi resume; poll dung asyncio.to_thread(time.sleep) vi asyncio.sleep
  bi cap 15.6ms tren Win11 (IOCP) du timeBeginPeriod
- fx_realtime.py: stats dem publish/drop/stale/read cho diagnostics
- rebuild bridge binary
This commit is contained in:
2026-08-24 10:40:21 +07:00
parent 708e7de32b
commit 21130858d6
5 changed files with 135 additions and 14 deletions
+10
View File
@@ -128,6 +128,9 @@ class FxRealtimeSession:
# Phase 2.8: C++ consume qua SHM control ring.
self.pending_params = {} # {slot_idx: {key: value}}
self.latencies = {} # {slot_idx: samples}
# Diagnostics (FXRT crackle hunt): drop/publish counters.
self.stats = {'publish': 0, 'drop_batch': 0, 'drop_blocks': 0,
'stale_flush': 0, 'read_blocks': 0, 'read_empty': 0}
self._spawn()
# ── process ─────────────────────────────────────────────────────────────
@@ -266,13 +269,16 @@ class FxRealtimeSession:
self._in_pending = []
self._in_pending_t0 = None
avail = h.in_slots - (h.in_write - h.in_read)
self.stats['publish'] += len(frames)
if avail <= 0:
h.in_read += len(frames) # ring full → drop cả batch
self.stats['drop_batch'] += len(frames)
return
n = min(len(frames), avail)
drop = len(frames) - n
if drop > 0:
h.in_read += drop # drop frame cũ nhất
self.stats['drop_blocks'] += drop
for i in range(n):
L, R = frames[i + drop]
slot = (h.in_write + i) & (h.in_slots - 1)
@@ -287,6 +293,7 @@ class FxRealtimeSession:
burst) → không kẹt latency. Gọi từ _pump_output mỗi vòng."""
with self._lock:
if self._in_pending and self._in_pending_t0 is not None and time.time() - self._in_pending_t0 > timeout:
self.stats['stale_flush'] += 1
self._flush_input_locked()
def read_output(self):
@@ -303,6 +310,9 @@ class FxRealtimeSession:
inter[1::2] = self._np[br:br + self.block_size]
out.append(inter.tobytes())
h.out_read += 1
self.stats['read_blocks'] += len(out)
if not out:
self.stats['read_empty'] += 1
return out
def heartbeat_age(self):