Phase 3: engine render chain hop nhat — track/master doc mastering_settings.chain (imager/maximizer), PDC probe FX_LATENCIES, fix daw_vst_bridge link BuiltinFxChain, pytest 3.2/3.3
This commit is contained in:
@@ -82,12 +82,13 @@ def test_render_fx_chain_builtin_gain_e2e():
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
inp = os.path.join(td, "in.wav")
|
||||
sf.write(inp, data, sr, subtype="FLOAT")
|
||||
out = native_render.render_fx_chain(
|
||||
out, _lat = native_render.render_fx_chain(
|
||||
input_wav=inp,
|
||||
fx_chain=[{"type": "builtin", "id": "gain", "params": {"db": 6.0}}],
|
||||
sample_rate=sr,
|
||||
)
|
||||
assert os.path.isfile(out)
|
||||
assert _lat == [0] # builtin gain: latency 0
|
||||
d, _ = sf.read(out, dtype="float32", always_2d=True)
|
||||
rms = float(np.sqrt(np.mean(d ** 2)))
|
||||
assert rms > 0.09 # ~2x 0.05
|
||||
|
||||
@@ -152,4 +152,79 @@ def sf_read_peaks(path):
|
||||
import numpy as np
|
||||
data, _ = _sf.read(path, dtype="float32", always_2d=True)
|
||||
return data, float(np.max(np.abs(data)))
|
||||
_FX_BRIDGE_TEST_PATH = r"C:/Users/locpham/SonicForgeStudio/native_bridge/build/Release/fx_vst_bridge.exe"
|
||||
|
||||
|
||||
def _master_project(chain, pan=0.6, **flat):
|
||||
"""Project với mastering_settings.chain + flat mastering knobs (giống UI)."""
|
||||
track = _midi_track("__no_such_plugin__")
|
||||
track["volume_db"] = -20.0 # tránh normalize (>1.0) làm hỏng tỉ lệ
|
||||
track["pan"] = pan # constant-power pan → L≠R: master có side content
|
||||
proj = {
|
||||
"metadata": {"bpm": 120.0, "time_signature_numerator": 4},
|
||||
"main_session": {"length_bars": 1.0, "tracks": [track]},
|
||||
"section_store": {},
|
||||
}
|
||||
if chain is not None:
|
||||
ms = {"chain": chain}
|
||||
ms.update(flat)
|
||||
proj["mastering_settings"] = ms
|
||||
return proj
|
||||
|
||||
|
||||
def _side_rms(path):
|
||||
data, _ = _sf.read(path, dtype="float32", always_2d=True)
|
||||
side = data[:, 0] - data[:, 1]
|
||||
return float(np.sqrt(np.mean(side ** 2)))
|
||||
|
||||
|
||||
def _render_rms(path):
|
||||
data, _ = _sf.read(path, dtype="float32", always_2d=True)
|
||||
return float(np.sqrt(np.mean(data ** 2)))
|
||||
|
||||
|
||||
def test_render_master_chain_imager_maximizer_via_bridge(monkeypatch, tmp_path):
|
||||
"""Phase 3.2: master dùng mastering_settings.chain (hết bất đối xứng) —
|
||||
imager w=0 giết side, w=200 tăng side, maximizer boost RMS — qua bridge."""
|
||||
if not os.path.isfile(_FX_BRIDGE_TEST_PATH):
|
||||
pytest.skip("fx bridge chưa build")
|
||||
monkeypatch.setenv("SF_FX_BRIDGE_PATH", _FX_BRIDGE_TEST_PATH)
|
||||
monkeypatch.setenv("SF_BRIDGE_PATH", _BRIDGE_TEST_PATH)
|
||||
from app.api.v1 import plugins as pl
|
||||
monkeypatch.setattr(pl.settings, "RENDER_ENGINE", "bridge")
|
||||
engine = PythonRenderEngine()
|
||||
|
||||
def render(proj, name):
|
||||
out = str(tmp_path / name)
|
||||
engine.render_project(proj, out)
|
||||
return out
|
||||
|
||||
base = _side_rms(render(_master_project(None), "base.wav"))
|
||||
s0 = _side_rms(render(_master_project(
|
||||
[{"id": "imager", "type": "imager", "name": "Imager", "active": True}],
|
||||
w1=0, w2=0, w3=0, w4=0), "imager0.wav"))
|
||||
s2 = _side_rms(render(_master_project(
|
||||
[{"id": "imager", "type": "imager", "name": "Imager", "active": True}],
|
||||
w1=200, w2=200, w3=200, w4=200), "imager200.wav"))
|
||||
assert base > 0.001 # pan tạo side content
|
||||
assert s0 < 0.05 * base # w=0 → side≈0 (imager đã được áp qua bridge)
|
||||
assert s2 > 1.5 * base # w=200 → side tăng
|
||||
|
||||
rm0 = _render_rms(render(_master_project(
|
||||
[{"id": "maximizer", "type": "maximizer", "name": "Max", "active": True}],
|
||||
maxGain=0.0), "max0.wav"))
|
||||
rm6 = _render_rms(render(_master_project(
|
||||
[{"id": "maximizer", "type": "maximizer", "name": "Max", "active": True}],
|
||||
maxGain=6.0), "max6.wav"))
|
||||
assert rm6 > 1.3 * rm0 # boost 6dB → RMS tăng
|
||||
|
||||
|
||||
def test_probe_track_latency_no_vst3_returns_zero(monkeypatch):
|
||||
"""Phase 3.3: probe latency chỉ spawn bridge khi chain có vst3 —
|
||||
chain builtin-only → 0, không spawn exe."""
|
||||
from app.api.v1 import plugins as pl
|
||||
monkeypatch.setattr(pl.settings, "RENDER_ENGINE", "bridge")
|
||||
track = _midi_track("__no_such_plugin__")
|
||||
track["fx_chain"] = [{"type": "builtin", "id": "gain", "params": {"db": 6.0}}]
|
||||
from app.core.render_engine import _probe_track_latency
|
||||
assert _probe_track_latency(track, 44100) == 0
|
||||
|
||||
Reference in New Issue
Block a user