V24: fix master-chain crackle, 1s lag, solo bypass
- worklet: FILL 48->24 (PDC 7936 dung), flush protocol khi resume sau interruption
(clear queue + fade-in/crossfade chi voi block moi), drop block stale
- engine fx_realtime: _flush_input_locked NO-DROP (ring day -> giu pending, cap 2048),
flush_all() dong bo in/out khi worklet resume
- app.jsx: WS flush_done + flushing guard, fallback timer 500ms
- plugins.py: xu ly {cmd:flush} -> flush_done
- index.html: bump v param
This commit is contained in:
+31
-11
@@ -130,7 +130,8 @@ class FxRealtimeSession:
|
||||
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}
|
||||
'stale_flush': 0, 'read_blocks': 0, 'read_empty': 0,
|
||||
'flush': 0}
|
||||
self._spawn()
|
||||
|
||||
# ── process ─────────────────────────────────────────────────────────────
|
||||
@@ -264,26 +265,34 @@ class FxRealtimeSession:
|
||||
|
||||
def _flush_input_locked(self):
|
||||
"""Publish pending vào input ring. Ghi data trước, in_write += n sau →
|
||||
bridge đọc in_write 1 lần → thấy nguyên batch (take=4)."""
|
||||
bridge đọc in_write 1 lần → thấy nguyên batch (take=4). V24 NO-DROP:
|
||||
ring đầy → GIỮ pending (pump flush_input_stale 2ms retry) — không drop
|
||||
→ hết content gap + hết race pointer (drop cũ cập in_read vượt in_write
|
||||
→ avail > in_slots → ghi đè slot bridge đang đọc → torn frame). Cap
|
||||
2048 block (~11s) chống tích lũy vô hạn khi client dồn liên tục."""
|
||||
if not self._in_pending:
|
||||
return
|
||||
h = self.h
|
||||
if len(self._in_pending) > 2048:
|
||||
drop = len(self._in_pending) - 2048
|
||||
del self._in_pending[:drop] # drop block CŨ NHẤT (front)
|
||||
self.stats['drop_blocks'] += drop
|
||||
frames = self._in_pending
|
||||
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)
|
||||
# ring đầy → giữ nguyên pending; pump retry khi bridge drain
|
||||
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
|
||||
publish = frames[:n]
|
||||
if n < len(frames):
|
||||
del frames[:n] # publish n block CŨ NHẤT, giữ phần thừa làm pending
|
||||
self._in_pending_t0 = time.time() if frames else None
|
||||
else:
|
||||
self._in_pending = []
|
||||
self._in_pending_t0 = None
|
||||
for i in range(n):
|
||||
L, R = frames[i + drop]
|
||||
L, R = publish[i]
|
||||
slot = (h.in_write + i) & (h.in_slots - 1)
|
||||
bl = IN_L_OFF // 4 + slot * self.block_size
|
||||
br = IN_R_OFF // 4 + slot * self.block_size
|
||||
@@ -299,6 +308,17 @@ class FxRealtimeSession:
|
||||
self.stats['stale_flush'] += 1
|
||||
self._flush_input_locked()
|
||||
|
||||
def flush_all(self):
|
||||
"""V24: reset toàn bộ pipeline khi client resume sau stall — xóa
|
||||
input/output ring + pending → bridge xử lí audio MỚI, không backlog cũ
|
||||
(lag cộng dồn ~1s sau stall). Gọi khi nhận {cmd:'flush'} qua WS."""
|
||||
with self._lock:
|
||||
self._in_pending = []
|
||||
self._in_pending_t0 = None
|
||||
self.h.in_read = self.h.in_write
|
||||
self.h.out_read = self.h.out_write
|
||||
self.stats['flush'] += 1
|
||||
|
||||
def read_output(self):
|
||||
"""Drain output ring → list bytes (mỗi block 2048 bytes interleaved)."""
|
||||
out = []
|
||||
|
||||
Reference in New Issue
Block a user