feat: open native VST FX GUI from mastering panel + FX rack (bridge --open-fx-gui)
This commit is contained in:
@@ -499,6 +499,12 @@ class OpenInCarlaRequest(BaseModel):
|
||||
plugin_path: Optional[str] = None
|
||||
|
||||
|
||||
class FxGuiRequest(BaseModel):
|
||||
"""Mở native GUI của VST FX qua bridge (Windows desktop)."""
|
||||
path: str
|
||||
name: Optional[str] = None
|
||||
|
||||
|
||||
class PreviewRequest(BaseModel):
|
||||
"""Quick-render preview: pedalboard render clip ngắn bằng ĐÚNG plugin +
|
||||
preset (cùng code path với export) → trả wav để browser phát.
|
||||
@@ -677,6 +683,66 @@ async def open_in_carla(req: OpenInCarlaRequest, current_user: dict = Depends(ge
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Không mở được Carla: {e}")
|
||||
|
||||
@router.post("/fx-gui")
|
||||
async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_current_user)):
|
||||
"""Mở native GUI của VST FX (mastering chain / FX rack) qua bridge.
|
||||
|
||||
Bridge có mode `--open-fx-gui <job.json>` (Windows): spawn tiến trình riêng
|
||||
giữ vòng lặp message pump, tạo window 800x600 + attach IPlugView của plugin.
|
||||
Đây là GUI tách rời — user chỉnh thông số, đóng window là bridge thoát
|
||||
(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()
|
||||
if not exe:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Không tìm thấy bridge native (daw_vst_bridge.exe). "
|
||||
"Hãy chạy bản desktop đã build đầy đủ.",
|
||||
)
|
||||
plugin_path = (req.path or "").strip()
|
||||
if not plugin_path:
|
||||
raise HTTPException(status_code=400, detail="Thiếu đường dẫn VST plugin.")
|
||||
# Tránh mở 2 GUI cùng plugin: nếu tiến trình bridge fx-gui cho plugin này
|
||||
# đang chạy → trả started=False (frontend có thể focus window cũ).
|
||||
for proc in _FX_GUI_PROCESSES:
|
||||
if proc is not None and proc.poll() is None and getattr(proc, "_sf_plugin_path", None) == plugin_path:
|
||||
return {"success": True, "started": False, "already_running": True, "cmd": []}
|
||||
job = {"path": plugin_path, "name": req.name or os.path.basename(plugin_path)}
|
||||
fd, job_path = tempfile.mkstemp(prefix="sf_fx_gui_", suffix=".json", dir=tempfile.gettempdir())
|
||||
try:
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
json.dump(job, f)
|
||||
except Exception:
|
||||
os.close(fd)
|
||||
raise HTTPException(status_code=500, detail="Không ghi được job file cho bridge.")
|
||||
cmd = [exe, "--open-fx-gui", job_path]
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, close_fds=os.name != "nt")
|
||||
proc._sf_plugin_path = plugin_path
|
||||
_register_fx_gui_process(proc)
|
||||
return {"success": True, "started": True, "already_running": False, "cmd": cmd}
|
||||
except Exception as e:
|
||||
try:
|
||||
os.remove(job_path)
|
||||
except Exception:
|
||||
pass
|
||||
raise HTTPException(status_code=500, detail=f"Không mở được GUI của VST: {e}")
|
||||
|
||||
|
||||
_FX_GUI_PROCESSES = [] # list[subprocess.Popen] — các bridge fx-gui do app spawn
|
||||
|
||||
def _register_fx_gui_process(proc):
|
||||
global _FX_GUI_PROCESSES
|
||||
_prune_fx_gui_processes()
|
||||
_FX_GUI_PROCESSES.append(proc)
|
||||
|
||||
def _prune_fx_gui_processes():
|
||||
global _FX_GUI_PROCESSES
|
||||
_FX_GUI_PROCESSES = [p for p in _FX_GUI_PROCESSES if p is not None and p.poll() is None]
|
||||
|
||||
|
||||
|
||||
# ── Carla bridge lifecycle ─────────────────────────────────────────────────
|
||||
# App spawn Carla (open_in_carla) và PHẢI có khả năng dừng nó khi track chuyển
|
||||
|
||||
Reference in New Issue
Block a user