feat: VST FX rack per-track + masterbus render via native_bridge

This commit is contained in:
2026-08-17 21:31:57 +07:00
parent 8fbacd1d2b
commit b261eb0d7e
18 changed files with 1805 additions and 14 deletions
+66
View File
@@ -390,6 +390,39 @@ class PythonRenderEngine:
except Exception as e:
logger.warning("[RenderEngine] Fallback Reverb failed: %s", e)
# Track FX chain (VST3/builtin) — FX Rack per-track: render track
# buffer qua native_bridge --render-fx. Ưu tiên fx_chain; fx_type
# cũ (chorus/reverb scipy) giữ làm fallback khi không có fx_chain.
track_fx_chain = track.get("vst_fx_chain") or []
if track_fx_chain and not track.get("vst_fx_bypass"):
if settings.RENDER_ENGINE == "bridge":
try:
from app.core.native_render import render_fx_chain
tmp_in = os.path.join(
settings.PROCESSED_DIR,
f"trackfx_in_{os.getpid()}_{np.random.randint(100000)}.wav")
tmp_out = tmp_in.replace("trackfx_in_", "trackfx_out_")
try:
sf.write(tmp_in, track_buffer.T, self.sample_rate, subtype="FLOAT")
render_fx_chain(input_wav=tmp_in, fx_chain=track_fx_chain,
sample_rate=self.sample_rate, out_path=tmp_out)
data, _ = sf.read(tmp_out, dtype="float32", always_2d=True)
if data.shape[1] >= 2:
track_buffer = data.T[:2, :]
else:
track_buffer = np.repeat(data.T, 2, axis=0)
finally:
for f in (tmp_in, tmp_out):
if os.path.exists(f):
try:
os.remove(f)
except Exception:
pass
except Exception as e:
logger.warning("[RenderEngine] Track FX chain skipped: %s", e)
else:
logger.warning("[RenderEngine] Track FX chain cần RENDER_ENGINE=bridge — bỏ qua")
# Process track volume
gain_linear = 10 ** (vol_db / 20.0)
processed_track = track_buffer * gain_linear
@@ -427,6 +460,39 @@ class PythonRenderEngine:
_cache={},
)
# Masterbus FX chain (VST3 FX / builtin) — render qua native_bridge
# --render-fx, rồi volume_db master; backward compatible (không có
# master.fx_chain → giữ nguyên luồng cũ).
master = main_session.get("master") or {}
fx_chain = master.get("fx_chain") or []
if fx_chain and not master.get("bypass"):
if settings.RENDER_ENGINE == "bridge":
try:
from app.core.native_render import render_fx_chain
tmp_in = os.path.join(settings.PROCESSED_DIR,
f"master_in_{os.getpid()}_{np.random.randint(100000)}.wav")
tmp_out = tmp_in.replace("master_in_", "master_out_")
try:
sf.write(tmp_in, master_buffer.T, self.sample_rate, subtype="FLOAT")
render_fx_chain(input_wav=tmp_in, fx_chain=fx_chain,
sample_rate=self.sample_rate, out_path=tmp_out)
data, _ = sf.read(tmp_out, dtype="float32", always_2d=True)
if data.shape[1] >= 2:
master_buffer = data.T[:2, :]
else:
master_buffer = np.repeat(data.T, 2, axis=0)
finally:
for f in (tmp_in, tmp_out):
if os.path.exists(f):
try:
os.remove(f)
except Exception:
pass
except Exception as e:
logger.warning("[RenderEngine] Masterbus FX skipped: %s", e)
else:
logger.warning("[RenderEngine] Masterbus FX cần RENDER_ENGINE=bridge — bỏ qua")
# Normalization to prevent clipping
max_peak = np.max(np.abs(master_buffer))
if max_peak > 1.0: