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:
@@ -0,0 +1,72 @@
|
||||
"""Phase 1 chain hợp nhất + control protocol (PLAN_DAW_A.md).
|
||||
|
||||
Unit test không spawn bridge: dùng object.__new__ để bỏ qua __init__/subprocess,
|
||||
chỉ test logic thuần của FxRealtimeSession.
|
||||
"""
|
||||
import threading
|
||||
|
||||
from app.core import fx_realtime
|
||||
|
||||
|
||||
def make_session(chain):
|
||||
s = object.__new__(fx_realtime.FxRealtimeSession)
|
||||
s.chain = list(chain)
|
||||
s._lock = threading.Lock()
|
||||
s.pending_params = {}
|
||||
s.latencies = {}
|
||||
return s
|
||||
|
||||
|
||||
CHAIN = [
|
||||
{"type": "vst3", "path": "C:\\Plugins\\Eq.vst3",
|
||||
"preset_b64": "AAA=", "bypass": False, "active": True},
|
||||
{"type": "eq", "params": {"g1": 0.5}, "bypass": False, "active": True},
|
||||
{"type": "compressor", "params": {"threshold": -12.0, "ratio": 4.0},
|
||||
"bypass": True, "active": True},
|
||||
{"type": "vst3", "path": "C:\\Plugins\\Disabled.vst3", "active": False},
|
||||
{"type": "limiter", "params": {}, "bypass": False, "active": True},
|
||||
]
|
||||
|
||||
|
||||
def test_chain_json_keeps_order_and_skips_inactive():
|
||||
"""_chain_json_for_bridge: giữ NGUYÊN thứ tự UI (vst3+builtin xen kẽ),
|
||||
loại slot active=false, giữ type/path/preset_b64/params/bypass."""
|
||||
s = make_session(CHAIN)
|
||||
out = s._chain_json_for_bridge()
|
||||
assert [o["type"] for o in out] == ["vst3", "eq", "compressor", "limiter"]
|
||||
assert out[0]["path"] == "C:\\Plugins\\Eq.vst3"
|
||||
assert out[0]["preset_b64"] == "AAA="
|
||||
assert out[1]["params"] == {"g1": 0.5}
|
||||
assert out[2]["bypass"] is True
|
||||
assert out[2]["params"] == {"threshold": -12.0, "ratio": 4.0}
|
||||
# params rỗng → không đưa key params (khớp job schema)
|
||||
assert out[3]["type"] == "limiter"
|
||||
assert "params" not in out[3]
|
||||
|
||||
|
||||
def test_chain_json_handles_malformed_slots():
|
||||
s = make_session([None, "x", {"active": False}, {}, {"type": "eq"}])
|
||||
out = s._chain_json_for_bridge()
|
||||
assert [o["type"] for o in out] == ["eq"]
|
||||
|
||||
|
||||
def test_set_param_report_latency_drain_roundtrip():
|
||||
"""Control protocol Phase 1: set_param/report_latency/drain hoạt động
|
||||
đúng (engine nhận từ WS text, giữ pending_params + latencies)."""
|
||||
s = make_session([])
|
||||
s.set_param(1, "g1", 0.75)
|
||||
s.set_param(1, "g2", -1.5)
|
||||
s.set_param(3, "threshold", -20.0)
|
||||
s.report_latency(1, 128)
|
||||
s.report_latency(3, 256)
|
||||
assert s.get_latencies() == {1: 128, 3: 256}
|
||||
assert s.drain_params() == {1: {"g1": 0.75, "g2": -1.5},
|
||||
3: {"threshold": -20.0}}
|
||||
assert s.drain_params() == {}
|
||||
|
||||
|
||||
def test_set_param_overwrites_same_key():
|
||||
s = make_session([])
|
||||
s.set_param(0, "wet", 0.5)
|
||||
s.set_param(0, "wet", 0.9)
|
||||
assert s.drain_params() == {0: {"wet": 0.9}}
|
||||
Reference in New Issue
Block a user