fix(fx-rt): het crackle master FX - limiter attack smooth + crossfade khi bat/tat chain

- NativeInstrumentEngine: kAttack 0.9->0.1 (het zipper khi sum nhieu track)
- app.jsx: crossfade masterFxDryGain/masterFxGain thay hard-rewire graph luc
  activation/deactivation master chain FX; worklet bao 'started' -> ramp dung
  luc; PDC tre sau khi dry silent (tranh gap 80ms)
- sf-fx-realtime.js: fade-in 512 mau luc FILL xong / resume sau underrun
- index.html + addModule: cache-buster v=202608241200
- rebuild app.precompiled.js, copy bundle vao dist/ + src-tauri/resources/
- keo dai fix cac round truoc: FILL 16, FXRT_BURST_MS, REBUFFER, pacing,
  VST GUI click/focus, solo crackle
- them harness + plans/patch scripts
This commit is contained in:
2026-08-24 07:46:09 +07:00
parent 373a96cf7b
commit 10997a65b9
21 changed files with 1181 additions and 177 deletions
+24
View File
@@ -120,6 +120,7 @@ class FxRealtimeSession:
self._in_pending_t0 = None
self.proc = None
self._job_path = ""
self._chain_seq = 0
self._started_at = time.time()
# Chain hợp nhất (spec PLAN_DAW_A.md Phase 1): giữ đúng thứ tự slot.
self.chain = list(self.fx_chain or [])
@@ -163,6 +164,7 @@ class FxRealtimeSession:
job = {
"sample_rate": self.sample_rate,
"block_size": self.block_size,
"seq": self._chain_seq,
"fx_chain": self._chain_json_for_bridge(),
}
fd, self._job_path = tempfile.mkstemp(suffix=".json", prefix="fxrt_")
@@ -177,6 +179,28 @@ class FxRealtimeSession:
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
def set_chain(self, fx_chain):
"""Cập nhật chain trên session ĐANG CHẠY (không restart session):
ghi lại job file seq++ — bridge poll seq mỗi 500ms → setChain async
swap (worker load chain mới, audio giữ chain cũ tới khi swap) → load/
đổi VST FX + đổi preset GUI không ngắt âm/crackle."""
self.fx_chain = list(fx_chain or [])
self.chain = self.fx_chain
self._chain_seq += 1
if not self._job_path or not os.path.exists(self._job_path):
return False
try:
with open(self._job_path, "w", encoding="utf-8") as f:
json.dump({
"sample_rate": self.sample_rate,
"block_size": self.block_size,
"seq": self._chain_seq,
"fx_chain": self._chain_json_for_bridge(),
}, f)
except Exception:
return False
return True
def wait_ready(self, timeout=30.0):
deadline = time.time() + timeout
while time.time() < deadline: