refactor(bridge): split fx bridge, stabilize VST3 host chain, drop old install engines
- remove diagnostic canaries from audio hot path (crackle fix) and stuck C5 note raw-MIDI test (Vst2Instrument::init noteOff) - split native bridge into daw_vst_bridge (VSTi) + fx_vst_bridge (VST FX) - VST3: processData prepare before setActive, zero input buses, accept both .vstpreset and bridge-state blob in RenderJob presets - native_render/vst_engine: bridge-exe discovery + render_fx_chain + /fx-gui - delete old install/ engine binaries (install via sidecar+resources now) - add BUILD_LAYOUT.md, build_selfcheck, tools/ diagnostics
This commit is contained in:
@@ -701,12 +701,12 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
(exit 0). Không spawn 2 lần cho cùng plugin đang mở (tránh trùng GUI).
|
||||
"""
|
||||
enforce_password_changed(current_user)
|
||||
from app.core.native_render import find_bridge_exe
|
||||
exe = find_bridge_exe()
|
||||
from app.core.native_render import find_fx_bridge_exe
|
||||
exe = find_fx_bridge_exe()
|
||||
if not exe:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Không tìm thấy bridge native (daw_vst_bridge.exe). "
|
||||
detail="Không tìm thấy bridge native (fx_vst_bridge.exe). "
|
||||
"Hãy chạy bản desktop đã build đầy đủ.",
|
||||
)
|
||||
plugin_path = (req.path or "").strip()
|
||||
|
||||
+27
-13
@@ -19,33 +19,47 @@ from fastapi import HTTPException
|
||||
from app.config import settings
|
||||
|
||||
BRIDGE_EXE = "daw_vst_bridge.exe" if os.name == "nt" else "daw_vst_bridge"
|
||||
FX_BRIDGE_EXE = "fx_vst_bridge.exe" if os.name == "nt" else "fx_vst_bridge"
|
||||
|
||||
|
||||
def find_bridge_exe() -> str:
|
||||
"""Đường dẫn bridge exe: env SF_BRIDGE_PATH → bundle (frozen) → install/ (dev)."""
|
||||
env = os.getenv("SF_BRIDGE_PATH", "").strip()
|
||||
def find_bridge_exe(exe_name=BRIDGE_EXE, env_key="SF_BRIDGE_PATH") -> str:
|
||||
"""Đường dẫn bridge exe: env <env_key> → bundle (frozen) → src-tauri/binaries → install/ (dev)."""
|
||||
env = os.getenv(env_key, "").strip()
|
||||
if env and os.path.isfile(env):
|
||||
return env
|
||||
# Tauri externalBin renames sidecars → <name>-x86_64-pc-windows-msvc.exe.
|
||||
sidecar = exe_name[:-4] + "-x86_64-pc-windows-msvc.exe" if exe_name.lower().endswith(".exe") else exe_name
|
||||
cands = []
|
||||
if getattr(sys, "frozen", False):
|
||||
root = getattr(sys, "_MEIPASS", "")
|
||||
if root:
|
||||
cands += [os.path.join(root, "binaries", BRIDGE_EXE),
|
||||
os.path.join(root, BRIDGE_EXE)]
|
||||
cands += [os.path.join(root, "binaries", exe_name),
|
||||
os.path.join(root, "binaries", sidecar),
|
||||
os.path.join(root, exe_name),
|
||||
os.path.join(root, sidecar)]
|
||||
# PyInstaller onedir: daw_engine.exe nam <app_root>/daw_engine/, bridge
|
||||
# sidecar nam canh app root (khong trong _MEIPASS) - tim ca exe_dir.
|
||||
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
|
||||
cands += [os.path.join(exe_dir, "binaries", BRIDGE_EXE),
|
||||
os.path.join(exe_dir, BRIDGE_EXE),
|
||||
os.path.join(os.path.dirname(exe_dir), "binaries", BRIDGE_EXE),
|
||||
os.path.join(os.path.dirname(exe_dir), BRIDGE_EXE)]
|
||||
cands.append(os.path.join(settings.BASE_DIR, "install", BRIDGE_EXE))
|
||||
cands += [os.path.join(exe_dir, "binaries", exe_name),
|
||||
os.path.join(exe_dir, "binaries", sidecar),
|
||||
os.path.join(exe_dir, exe_name),
|
||||
os.path.join(exe_dir, sidecar),
|
||||
os.path.join(os.path.dirname(exe_dir), "binaries", exe_name),
|
||||
os.path.join(os.path.dirname(exe_dir), "binaries", sidecar),
|
||||
os.path.join(os.path.dirname(exe_dir), exe_name),
|
||||
os.path.join(os.path.dirname(exe_dir), sidecar)]
|
||||
# Canonical build output (build_native_bridge.ps1) + legacy install/ (dev).
|
||||
cands.append(os.path.join(settings.BASE_DIR, "src-tauri", "binaries", sidecar))
|
||||
cands.append(os.path.join(settings.BASE_DIR, "install", exe_name))
|
||||
for p in cands:
|
||||
if p and os.path.isfile(p):
|
||||
return p
|
||||
return ""
|
||||
|
||||
|
||||
def find_fx_bridge_exe() -> str:
|
||||
"""Đường dẫn FX bridge (fx_vst_bridge.exe): SF_FX_BRIDGE_PATH → bundle → install/."""
|
||||
return find_bridge_exe(FX_BRIDGE_EXE, "SF_FX_BRIDGE_PATH")
|
||||
def normalize_audio_peak(audio, target_db=0.0):
|
||||
"""Scale audio (numpy array, peak) về target_db nếu vượt; giữ nguyên nếu dưới.
|
||||
|
||||
@@ -209,18 +223,18 @@ def render_fx_chain(input_wav, fx_chain, sample_rate, out_path=None) -> str:
|
||||
out_path; lỗi → HTTPException (501 thiếu bridge, 404 plugin lạ, 500 render).
|
||||
"""
|
||||
from app.core.vst_engine import resolve_plugin_path
|
||||
exe = find_bridge_exe()
|
||||
exe = find_fx_bridge_exe()
|
||||
if not exe:
|
||||
raise HTTPException(
|
||||
status_code=501,
|
||||
detail="native_bridge không khả dụng — đặt SF_BRIDGE_PATH hoặc cài daw_vst_bridge",
|
||||
detail="native_bridge không khả dụng — đặt SF_FX_BRIDGE_PATH hoặc cài fx_vst_bridge",
|
||||
)
|
||||
slots = []
|
||||
for fx in fx_chain or []:
|
||||
typ = fx.get("type")
|
||||
if typ == "vst3":
|
||||
p = (fx.get("path") or "").strip()
|
||||
resolved = p if os.path.isfile(p) else resolve_plugin_path(p, ".vst3")
|
||||
resolved = p if os.path.exists(p) else resolve_plugin_path(p, ".vst3")
|
||||
if not resolved:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
|
||||
@@ -481,8 +481,8 @@ class PluginManager:
|
||||
"""
|
||||
if not refresh and self._fx_scan_cache is not None:
|
||||
return self._fx_scan_cache
|
||||
from app.core.native_render import find_bridge_exe
|
||||
exe = find_bridge_exe()
|
||||
from app.core.native_render import find_fx_bridge_exe
|
||||
exe = find_fx_bridge_exe()
|
||||
results = []
|
||||
if exe:
|
||||
dirs = [d for d in [self.vst_dir] + self.extra_vst_dirs
|
||||
@@ -629,7 +629,8 @@ def resolve_plugin_path(name_or_path: str, ext: str = "") -> str:
|
||||
dirs = list(_load_user_plugin_dirs()) or []
|
||||
except Exception:
|
||||
pass
|
||||
dirs += [settings.VST_DIR, settings.SOUNDFONT_DIR]
|
||||
from app.config import settings as _st
|
||||
dirs += [_st.VST_DIR, _st.SOUNDFONT_DIR]
|
||||
for base_dir in dirs:
|
||||
if not os.path.isdir(base_dir):
|
||||
continue
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user