FIX: sửa lỗi không load instrument của soundfont trên window
This commit is contained in:
+82
-2
@@ -164,7 +164,7 @@ def get_scanner():
|
||||
@router.get("/available")
|
||||
async def list_plugins(current_user: dict = Depends(get_current_user)):
|
||||
d = _effective_dirs()
|
||||
pm = PluginManager(vst_dir=d["vst_dir"], sf_dir=d["soundfont_dir"], upload_sf_dir=UPLOAD_SF_DIR)
|
||||
pm = PluginManager(vst_dir=d["vst_dir"], sf_dir=d["soundfont_dir"], upload_sf_dir=UPLOAD_SF_DIR, extra_vst_dirs=d["plugin_dirs"])
|
||||
avail = pm.list_available()
|
||||
# Gộp VST từ plugin_dirs user đã scan — list_available() CHỈ quét vst_dir
|
||||
# env (mặc định /opt/daw_engine/vst3) → Synth dropdown không thấy VSTi mà
|
||||
@@ -369,7 +369,9 @@ async def soundfont_catalog(current_user: dict = Depends(get_current_user)):
|
||||
|
||||
@router.get("/soundfont-instruments/{sf_id}")
|
||||
async def list_soundfont_instruments(sf_id: str, current_user: dict = Depends(get_current_user)):
|
||||
pm = PluginManager(upload_sf_dir=UPLOAD_SF_DIR)
|
||||
d = _effective_dirs()
|
||||
# extra_vst_dirs = plugin_dirs user (chứa cả VST lẫn SoundFont) → tìm sf2 ở đó
|
||||
pm = PluginManager(upload_sf_dir=UPLOAD_SF_DIR, extra_vst_dirs=d["plugin_dirs"])
|
||||
presets = pm.list_soundfont_instruments(sf_id)
|
||||
return {"presets": presets, "count": len(presets)}
|
||||
|
||||
@@ -512,6 +514,84 @@ class PreviewRequest(BaseModel):
|
||||
preset_data: Optional[str] = None # base64 bytes .vstpreset (project nhúng)
|
||||
|
||||
|
||||
class CarlaMidiRequest(BaseModel):
|
||||
"""Gửi MIDI note từ track ARM → Carla (OSC /Carla/0/note_on|note_off).
|
||||
|
||||
Carla standalone bật OSC UDP mặc định cổng 22752 (source: CarlaEngineOsc,
|
||||
CarlaEngineData oscPortUDP=22752; override: env CARLA_OSC_UDP_PORT của
|
||||
Carla, hoặc SF_CARLA_OSC_PORT / osc_port trong carla_path.json của app).
|
||||
Plugin đầu tiên trong project .carxs do app sinh có pluginId = 0."""
|
||||
event: str # "note_on" | "note_off"
|
||||
note: int
|
||||
velocity: Optional[int] = 100
|
||||
channel: Optional[int] = 0
|
||||
|
||||
|
||||
@router.post("/carla-midi")
|
||||
async def carla_midi(req: CarlaMidiRequest):
|
||||
"""MIDI keyboard (piano roll / keybed) → Carla để preview VSTi realtime."""
|
||||
if req.event not in ("note_on", "note_off"):
|
||||
raise HTTPException(status_code=400, detail="event phải là note_on hoặc note_off")
|
||||
ok = _send_carla_osc(req.event, req.note, req.velocity if req.event == "note_on" else 0, req.channel)
|
||||
if not ok:
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail="Không gửi được OSC tới Carla — Carla đã mở chưa? (cổng OSC UDP mặc định 22752; "
|
||||
"nếu đổi cổng trong Carla, đặt SF_CARLA_OSC_PORT hoặc osc_port trong carla_path.json)",
|
||||
)
|
||||
return {"success": True, "event": req.event, "note": req.note, "channel": req.channel}
|
||||
|
||||
|
||||
def _carla_osc_port() -> int:
|
||||
"""Cổng OSC UDP của Carla: SF_CARLA_OSC_PORT env → osc_port trong
|
||||
storage/carla_path.json → mặc định 22752 (CarlaEngineData)."""
|
||||
try:
|
||||
p = os.environ.get("SF_CARLA_OSC_PORT")
|
||||
if p:
|
||||
return int(p)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from app.core.runtime import _carla_config_path
|
||||
with open(_carla_config_path(), "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
p = data.get("osc_port")
|
||||
if p:
|
||||
return int(p)
|
||||
except Exception:
|
||||
pass
|
||||
return 22752
|
||||
|
||||
|
||||
def _send_carla_osc(event: str, note: int, velocity: int, channel: int) -> bool:
|
||||
"""Gửi OSC UDP tới `/Carla/0/{event}` (plugin đầu tiên = plugin auto-load).
|
||||
|
||||
OSC message: path + typetag + int args, mỗi phần pad '\0' tới bội số 4.
|
||||
Đã xác minh từ source Carla: handleMsgNoteOn/NoteOff nhận `iii`/`ii` và
|
||||
tên client mặc định của app standalone là "Carla" (carla_host.py
|
||||
fClientName = CARLA_CLIENT_NAME or "Carla")."""
|
||||
try:
|
||||
import socket
|
||||
import struct
|
||||
port = _carla_osc_port()
|
||||
path = f"/Carla/0/{event}".encode("utf-8")
|
||||
typetag = b",iii" if event == "note_on" else b",ii"
|
||||
vals = [int(channel), int(note), int(velocity)] if event == "note_on" else [int(channel), int(note)]
|
||||
|
||||
def _pad(b: bytes) -> bytes:
|
||||
rem = len(b) % 4
|
||||
return b + b"\x00" * (4 - rem) if rem else b
|
||||
|
||||
msg = _pad(path) + _pad(typetag) + b"".join(struct.pack(">i", v) for v in vals)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.settimeout(0.5)
|
||||
s.sendto(msg, ("127.0.0.1", port))
|
||||
s.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@router.post("/open-in-carla")
|
||||
async def open_in_carla(req: OpenInCarlaRequest, current_user: dict = Depends(get_current_user)):
|
||||
"""Mở Carla với VSTi đã chọn — TỰ ĐỘNG load plugin (native GUI + keyboard).
|
||||
|
||||
Reference in New Issue
Block a user