FIX: sửa lỗi không load soundfont trên window

This commit is contained in:
2026-08-10 09:29:05 +07:00
parent 250b94cd88
commit 85ac80bb4f
4 changed files with 91 additions and 10 deletions
+42
View File
@@ -174,9 +174,51 @@ async def list_plugins(current_user: dict = Depends(get_current_user)):
for v in extra:
by_id.setdefault(v["id"], v)
avail["vst_instruments"] = list(by_id.values())
# Tương tự cho SOUNDFONT: gộp .sf2/.sf3 từ plugin_dirs user — nếu không,
# nút Synth không liệt kê instrument của soundfont trong thư mục user thêm.
sf_by_id = {s["id"]: s for s in avail["soundfonts"]}
for s in _scan_soundfonts_in_dirs(d["plugin_dirs"]):
sf_by_id.setdefault(s["id"], s)
avail["soundfonts"] = list(sf_by_id.values())
return avail
def _scan_soundfonts_in_dirs(dirs: list) -> list:
"""Walk các thư mục (plugin_dirs user) → danh sách soundfont .sf2/.sf3
(id = tên file; name từ .meta nếu có — cùng format PluginManager)."""
found = {}
for d in dirs:
if not d or not os.path.isdir(d):
continue
for root, dirs2, files in os.walk(d):
for f in files:
low = f.lower()
if not (low.endswith(".sf2") or low.endswith(".sf3")):
continue
base_id = os.path.splitext(f)[0]
if base_id in found:
continue
meta = {}
mp = os.path.join(root, os.path.splitext(f)[0] + ".meta")
if os.path.isfile(mp):
try:
with open(mp, "r", encoding="utf-8") as mf:
meta = json.load(mf)
except Exception:
meta = {}
name = meta.get("original_name", f) if meta else f
found[base_id] = {
"id": base_id,
"name": name,
"file": f,
"display": os.path.splitext(name)[0][:40],
"source": "user",
}
# Không walk sâu thêm (soundfont thường đặt ngay trong thư mục khai báo)
dirs2[:] = []
return list(found.values())
def _scan_vst_in_dirs(dirs: list) -> list:
"""Walk các thư mục (plugin_dirs user) → danh sách VST giống /scan:
file .vst3/.dll/.so + FOLDER tên X.vst3 (Windows VST3 = folder chứa
+34 -4
View File
@@ -5337,11 +5337,16 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
};
const confirmPluginDir = () => {
const p = pmPicker && pmPicker.path;
if (p && !pmDirs.includes(p)) setPmDirs(prev => [...prev, p]);
if (p && !pmDirs.includes(p)) {
setPmDirs(prev => [...prev, p]);
// T ĐNG lưu + scan nút Synth có ngay danh sách instrument
clearTimeout(window.__pmAutoScanTimer);
window.__pmAutoScanTimer = setTimeout(() => { saveAndScanDirs(); }, 600);
}
setPmPicker(null);
};
const pickPluginFolder = async () => {
const addDir = (p) => { if (p && !pmDirs.includes(p)) setPmDirs(prev => [...prev, p]); };
const addDir = (p) => { if (p && !pmDirs.includes(p)) { setPmDirs(prev => [...prev, p]); clearTimeout(window.__pmAutoScanTimer); window.__pmAutoScanTimer = setTimeout(() => { saveAndScanDirs(); }, 600); } };
try {
// 1) NATIVE dialog qua engine (Tauri bridge IFileDialog/Explorer;
// fallback PowerShell/zenity/osascript) UI chy localhost:8000 nên
@@ -5372,6 +5377,9 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
};
const removePluginDir = (dir) => {
setPmDirs(prev => prev.filter(d => d !== dir));
// T đng lưu + scan li sau khi xóa thư mc
clearTimeout(window.__pmAutoScanTimer);
window.__pmAutoScanTimer = setTimeout(() => { saveAndScanDirs(); }, 600);
};
// Expand 1 soundfont đc danh sách instrument (bank/program/name) bên trong
// qua API soundfont-instruments/{id} hin th nút "Chèn vào Synth".
@@ -5610,9 +5618,16 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
React.createElement('div', { className: 'text-[10px] text-zinc-500' }, v.type || 'VST3')
)
),
React.createElement('div', { className: 'flex items-center gap-2' },
(window.SonicRuntime && window.SonicRuntime.capabilities && window.SonicRuntime.capabilities.features && window.SonicRuntime.capabilities.features.carla_local) && React.createElement('button', {
onClick: (e) => { e.stopPropagation(); window.SonicAPI.openInCarla(v.id).then(function (r) { if (r && r.success) showToast('Đã mở Carla với ' + (v.name || v.id), 'success'); }).catch(function (err) { showToast('Lỗi mở Carla: ' + (err.message || err), 'error'); }); },
className: 'text-[10px] bg-teal-800 hover:bg-teal-700 text-white px-2 py-1 rounded transition shrink-0',
title: 'Mở trong Carla (native GUI)'
}, '🎛 Carla'),
React.createElement('span', { className: 'text-[10px] bg-violet-950/40 text-violet-400 px-2 py-0.5 rounded-full border border-violet-800/30' }, v.type || 'VST3')
)
)
)
) :
(localData.soundfonts?.length === 0 ?
React.createElement('div', { className: 'flex items-center justify-center h-32 text-zinc-500 text-xs' }, 'No SoundFonts found. Upload one below.') :
@@ -5760,7 +5775,12 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
React.createElement('div', { key: 'sv_' + i, className: 'flex items-center gap-2 text-[11px] text-zinc-300' },
React.createElement('span', { className: 'w-16 shrink-0 text-zinc-500 font-mono text-[9px] truncate' }, v.type || 'VST'),
React.createElement('span', { className: 'truncate' }, v.name),
React.createElement('span', { className: 'text-[9px] text-zinc-600 font-mono truncate ml-auto' }, v.dir)
React.createElement('span', { className: 'text-[9px] text-zinc-600 font-mono truncate ml-auto' }, v.dir),
(window.SonicRuntime && window.SonicRuntime.capabilities && window.SonicRuntime.capabilities.features && window.SonicRuntime.capabilities.features.carla_local) && React.createElement('button', {
onClick: () => { window.SonicAPI.openInCarla(null, v.path).then(function (r) { if (r && r.success) showToast('Đã mở Carla với ' + v.name, 'success'); }).catch(function (err) { showToast('Lỗi mở Carla: ' + (err.message || err), 'error'); }); },
className: 'text-[9px] bg-teal-800 hover:bg-teal-700 text-white px-1.5 py-0.5 rounded transition shrink-0',
title: 'Mở trong Carla (native GUI)'
}, '🎛')
)
),
React.createElement('div', { className: 'text-[10px] font-bold text-amber-300 uppercase flex items-center gap-1 mt-2' },
@@ -30051,7 +30071,17 @@ STRICT CONSTRAINTS:
(instrumentSelectorData?.vst_instruments?.length > 0) && /*#__PURE__*/React.createElement("div", { className: "mt-3 text-[10px] text-zinc-500 uppercase font-bold px-1 flex items-center gap-1" }, /*#__PURE__*/React.createElement("i", { "data-lucide": "cpu", className: "w-3 h-3 text-violet-400" }), "VST Instruments"),
(instrumentSelectorData?.vst_instruments || []).map(v => /*#__PURE__*/React.createElement("button", {
key: 'vst_modal_' + (v.id || v.name),
onClick: () => { setTrackInstrumentWithUndo(instrumentSelectorTrackId, v.id, v.name || v.id); closeInstrumentSelector(); },
onClick: () => {
setTrackInstrumentWithUndo(instrumentSelectorTrackId, v.id, v.name || v.id);
closeInstrumentSelector();
// T ĐNG m Carla vi VSTi va chn (desktop + Carla local)
// Carla load sn plugin + keyboard o đ preview realtime.
if (window.SonicRuntime && window.SonicRuntime.capabilities && window.SonicRuntime.capabilities.features && window.SonicRuntime.capabilities.features.carla_local) {
window.SonicAPI.openInCarla(v.id).then(function (r) {
if (r && r.success) showToast('Đã mở Carla với ' + (v.name || v.id) + ' — chọn preset, bấm keyboard để preview', 'success');
}).catch(function (err) { showToast('Lỗi mở Carla: ' + (err.message || err), 'error'); });
}
},
className: "w-full text-left px-3 py-2 text-sm rounded bg-zinc-800 hover:bg-violet-800 text-zinc-300 flex items-center justify-between gap-2"
}, /*#__PURE__*/React.createElement("span", { className: "truncate" }, v.name || v.id), /*#__PURE__*/React.createElement("span", { className: "text-[9px] text-cyan-400 shrink-0" }, v.type || "VST")))
),
File diff suppressed because one or more lines are too long
+5
View File
@@ -3041,3 +3041,8 @@
- **Tóm tắt thay đổi:** (1) `POST /api/v1/plugins/open-in-carla` viết lại: sinh file project `.carxs` (định dạng XML chính thức từ source Carla — `carla.exe [FILE]` nhận project file) chứa `<Plugin><Info><Type>VST3</Type><Binary>path</Binary>...` → Carla mở lên **plugin đã load sẵn** kèm on-screen MIDI keyboard (PixmapKeyboard) để preview realtime. VST3 (file `.vst3`/folder `X.vst3` Windows) auto-load; VST2 (`.dll`/`.so` ngoài `.vst3`) → Carla trống để Add Plugin thủ công (cần uniqueID). Project lưu `{STORAGE_DIR}/carla_projects/`, tự dọn sau 1 ngày. (2) Frontend: chọn VSTi trong dropdown Synth → **tự động** `openInCarla(v.id)` (chỉ khi desktop + carla_local) — nhấn nút Synth, thêm VSTi là Carla tự gọi và load luôn VSTi đó. (3) Plugin Manager section "Carla Bridge": thêm **ô nhập tay thư mục chứa carla.exe + nút Lưu** (bên cạnh "Định vị Carla..." dùng picker) — khai báo 1 lần là xong.
- **Các file ảnh hưởng:** `app/api/v1/plugins.py` (+ `_write_carla_project`), `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` (rebuild), `md/52_CARLA_BRIDGE.md`, `wiki.md`
- **Ghi chú/Test (nếu có):** `pytest`: 86 passed, 7 skipped. Test `_write_carla_project`: VST3 file → carxs hợp lệ (CARLA-PROJECT VERSION='2.5', Type VST3, Binary, Active Yes, ControlChannel 1); Windows VST3 folder → OK; VST2 .dll → '' (không sinh); XML escape tên đặc biệt (A&B <C>) parse OK. End-to-end endpoint với Carla giả: 200, project_file sinh + chứa Binary, cmd = [carla.exe, carxs]. Rebuild bundle BUILD OK (node qua PATH nvm v24).
### [2026-08-09] Task: Fix — Carla không được gọi + Soundfont không scan được trên Windows
- **Tóm tắt thay đổi:** (1) Bug backend: `GET /api/v1/plugins/available` chỉ gộp VST từ `plugin_dirs` user, KHÔNG gộp soundfont → nút Synth rỗng khi soundfont nằm trong thư mục user thêm. Fix: thêm `_scan_soundfonts_in_dirs()` (walk .sf2/.sf3 + meta) và merge vào `avail["soundfonts"]`. (2) Auto-launch Carla giờ phủ TẤT CẢ bề mặt click VSTi: modal "Select Instrument" (vst_instruments), dropdown track strip (đã có), Plugin Manager tab VST + kết quả Scan (thêm nút 🎛 mỗi dòng, scan dùng path trực tiếp). (3) Plugin Manager: Add Directory / remove dir → **tự động lưu + scan** (debounce 600ms) — không cần bấm Scan tay. (4) Sync file static mới (app.precompiled.js, api.js, runtime.js, app.jsx, index.html) vào `dist/daw_engine/_internal/app/` để bản packaged nhận fix frontend.
- **Các file ảnh hưởng:** `app/api/v1/plugins.py`, `app/static/js/app.jsx`, `app/static/js/app.precompiled.js` (rebuild), `dist/daw_engine/_internal/app/static/**`, `dist/daw_engine/_internal/app/templates/index.html`, `wiki.md`
- **Ghi chú/Test (nếu có):** Test /available với thư mục sf2 user → soundfont xuất hiện (total 2). pytest: 86 passed, 7 skipped. Rebuild bundle BUILD OK. ⚠️ Backend fix (plugins.py) cần **rebuild PyInstaller trên Windows** (dist backend nằm trong exe); frontend đã patch sẵn trong dist.