feat(fx): Phase 1 DAW chain hop nhat — fx_realtime chain JSON giu thu tu, SET_PARAM/REPORT_LATENCY qua WS text, native_render chain builtin+vst3 xen ke, pytest
This commit is contained in:
+46
-8
@@ -86,21 +86,37 @@ class FxRealtimeSession:
|
||||
self.proc = None
|
||||
self._job_path = ""
|
||||
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 [])
|
||||
# Control protocol (SET_PARAM / REPORT_LATENCY) — Phase 1: giữ ở engine;
|
||||
# Phase 2.8: C++ consume qua SHM control ring.
|
||||
self.pending_params = {} # {slot_idx: {key: value}}
|
||||
self.latencies = {} # {slot_idx: samples}
|
||||
self._spawn()
|
||||
|
||||
# ── process ─────────────────────────────────────────────────────────────
|
||||
def _chain_json_for_bridge(self):
|
||||
"""JSON mảng {path,bypass,preset_b64} — RealtimeFxChain::buildChain đọc
|
||||
đúng các field này (type/name bỏ qua)."""
|
||||
"""JSON mảng slot theo spec Phase 1 — giữ NGUYÊN thứ tự + type/params;
|
||||
RealtimeFxChain::buildChain (C++) đọc {path,bypass,preset_b64} cho vst3
|
||||
và {type,params} cho builtin (Phase 2). Slot active=false bị loại trước
|
||||
khi gửi (UI tắt hẳn — không gửi engine)."""
|
||||
out = []
|
||||
for s in self.fx_chain:
|
||||
if not s or not s.get("path"):
|
||||
for s in self.chain:
|
||||
if not s or not isinstance(s, dict):
|
||||
continue
|
||||
out.append({
|
||||
"path": s["path"],
|
||||
if s.get("active") is False:
|
||||
continue
|
||||
slot = {
|
||||
"type": s.get("type", ""),
|
||||
"bypass": bool(s.get("bypass")),
|
||||
"preset_b64": s.get("preset_b64") or "",
|
||||
})
|
||||
}
|
||||
if s.get("path"):
|
||||
slot["path"] = s["path"]
|
||||
if s.get("preset_b64"):
|
||||
slot["preset_b64"] = s["preset_b64"]
|
||||
if isinstance(s.get("params"), dict) and s["params"]:
|
||||
slot["params"] = s["params"]
|
||||
out.append(slot)
|
||||
return out
|
||||
|
||||
def _spawn(self):
|
||||
@@ -203,6 +219,28 @@ class FxRealtimeSession:
|
||||
def heartbeat_age(self):
|
||||
return time.time() - self._started_at # placeholder: engine đọc h.heartbeat
|
||||
|
||||
# ── control protocol (SET_PARAM / REPORT_LATENCY) ────────────────────────
|
||||
# Phase 1: giữ ở engine, client gọi qua WS text frame. Phase 2.8: bridge
|
||||
# C++ đọc pending_params từ SHM control ring + ghi latency vào ring.
|
||||
def set_param(self, slot, key, value):
|
||||
with self._lock:
|
||||
self.pending_params.setdefault(slot, {})[str(key)] = value
|
||||
|
||||
def report_latency(self, slot, samples):
|
||||
with self._lock:
|
||||
self.latencies[int(slot)] = int(samples)
|
||||
|
||||
def drain_params(self):
|
||||
"""Trả + xóa toàn bộ pending param (bridge consume 1 lần)."""
|
||||
with self._lock:
|
||||
out = self.pending_params
|
||||
self.pending_params = {}
|
||||
return out
|
||||
|
||||
def get_latencies(self):
|
||||
with self._lock:
|
||||
return dict(self.latencies)
|
||||
|
||||
|
||||
# Registry toàn tiến trình (desktop app 1 user).
|
||||
_SESSIONS = {}
|
||||
|
||||
@@ -232,6 +232,8 @@ def render_fx_chain(input_wav, fx_chain, sample_rate, out_path=None) -> str:
|
||||
)
|
||||
slots = []
|
||||
for fx in fx_chain or []:
|
||||
if not isinstance(fx, dict) or fx.get("active") is False:
|
||||
continue
|
||||
typ = fx.get("type")
|
||||
if typ == "vst3":
|
||||
p = (fx.get("path") or "").strip()
|
||||
@@ -244,7 +246,15 @@ def render_fx_chain(input_wav, fx_chain, sample_rate, out_path=None) -> str:
|
||||
slots.append({"type": "vst3", "path": resolved,
|
||||
"preset_b64": fx.get("preset_b64", "") or "",
|
||||
"bypass": bool(fx.get("bypass"))})
|
||||
elif typ in ("eq", "eqpro", "imager", "maximizer", "compressor",
|
||||
"limiter", "exciter", "rebalance"):
|
||||
# Builtin DSP — chain hợp nhất (spec PLAN_DAW_A.md Phase 1). C++
|
||||
# RenderFxJob xử lý theo id/params (Phase 2); giữ NGUYÊN thứ tự.
|
||||
slots.append({"type": "builtin", "id": typ,
|
||||
"params": dict(fx.get("params") or {}),
|
||||
"bypass": bool(fx.get("bypass"))})
|
||||
elif typ == "builtin":
|
||||
# Legacy: {type: builtin, id: gain} — giữ cho project cũ.
|
||||
slots.append({"type": "builtin", "id": fx.get("id", "gain"),
|
||||
"params": dict(fx.get("params") or {}),
|
||||
"bypass": bool(fx.get("bypass"))})
|
||||
|
||||
Reference in New Issue
Block a user