master strip: L/R volume + LINK + MUTE that, bo soloBtn

This commit is contained in:
2026-08-22 21:41:54 +07:00
parent 4ae2f06c57
commit 2a5e2af04a
5 changed files with 290 additions and 74 deletions
+52
View File
@@ -303,6 +303,58 @@ def test_55_fader_after_master_chain(monkeypatch, tmp_path, fx_bridge):
assert 0.4 < p6 / p0 < 0.6, f"fader -6dB phải ~1/2 đỉnh: {p6:.4f}/{p0:.4f}"
assert 0.8 < p6 / base < 1.2, f"fader-6 phải ≈ base: {p6:.4f} vs {base:.4f}"
# ── 5.8 Master L/R volume + LINK + MUTE ─────────────────────────────────
def test_58_master_lr_link_mute(monkeypatch, tmp_path, fx_bridge):
"""Master strip L/R: volumeL_db/volumeR_db tách kênh, link_lr → R theo L,
muted → zero output. Fallback volume_db (project cũ) vẫn chạy."""
monkeypatch.setenv("SF_FX_BRIDGE_PATH", fx_bridge)
if not os.path.isfile(DAW_BRIDGE):
pytest.skip("daw_vst_bridge.exe chưa build")
monkeypatch.setenv("SF_BRIDGE_PATH", DAW_BRIDGE)
from app.api.v1 import plugins as pl
monkeypatch.setattr(pl.settings, "RENDER_ENGINE", "bridge")
from app.core.render_engine import PythonRenderEngine
engine = PythonRenderEngine()
def project(master):
track = _midi_track()
track["volume_db"] = -20.0 # tránh normalize (>1.0)
return {
"metadata": {"bpm": 120.0, "time_signature_numerator": 4},
"main_session": {
"length_bars": 1.0,
"tracks": [track],
"master": dict({"fx_chain": [], "bypass": False}, **master),
},
"section_store": {},
}
def render(name, master):
out = str(tmp_path / name)
engine.render_project(project(master), out)
data, _ = _read_wav(out)
return data
base = render("base.wav", {"volumeL_db": 0.0, "volumeR_db": 0.0, "link_lr": True})
baseL = float(np.max(np.abs(base[0])))
baseR = float(np.max(np.abs(base[1])))
assert baseL > 0
lr = render("lr.wav", {"volumeL_db": -6.0, "volumeR_db": 0.0, "link_lr": False})
assert 0.4 < float(np.max(np.abs(lr[0]))) / baseL < 0.6, f"L -6dB phải ~1/2 đỉnh: {float(np.max(np.abs(lr[0]))):.4f}/{baseL:.4f}"
assert abs(float(np.max(np.abs(lr[1]))) - baseR) / baseR < 0.15, "R phải giữ nguyên khi link_lr=false"
link = render("link.wav", {"volumeL_db": -6.0, "volumeR_db": -3.0, "link_lr": True})
ratio = float(np.max(np.abs(link[1]))) / float(np.max(np.abs(link[0])))
assert 0.9 < ratio < 1.1, f"link_lr: R phải theo L ({ratio:.3f})"
muted = render("muted.wav", {"volumeL_db": 0.0, "volumeR_db": 0.0,
"link_lr": True, "muted": True})
assert float(np.max(np.abs(muted))) < 1e-6, "MUTE master phải zero output"
legacy = render("legacy.wav", {"volume_db": -6.0})
assert 0.4 < float(np.max(np.abs(legacy[0]))) / baseL < 0.6 and 0.4 < float(np.max(np.abs(legacy[1]))) / baseR < 0.6, "fallback volume_db (project cũ) phải áp cả 2 kênh"
# ── 5.6 PDC ───────────────────────────────────────────────────────────────
def test_56_pdc_aligns_tracks(monkeypatch, tmp_path, fx_bridge):