feat: native folder picker (Explorer) + Synth liet ke VSTi da scan + fix VU leak (ban 1.1.4)
1) Folder picker dung WINDOW EXPLORER (khong prompt nhap tay):
- src-tauri/src/lib.rs: IPC bridge — thread watcher ipc/pick_dir.request
-> run_on_main_thread -> dialog().file().blocking_pick_folder()
(tauri-plugin-dialog = IFileDialog/Explorer) -> ghi pick_dir.response;
ghi marker tauri_bridge_ready luc setup.
- app/api/v1/plugins.py: POST /pick-dir (def sync -> threadpool, khong
auth) — uu tien Tauri bridge, fallback PowerShell FolderBrowserDialog
(Win) / osascript (macOS) / zenity-kdialog (Linux).
- app.jsx pickPluginFolder: 1) pickPluginDir (native) -> 2) __TAURI__
invoke -> 3) in-app browser -> 4) prompt (cuoi cung).
2) Nut Synth liet ke VSTi da scan (truoc day rong):
- Root: list_available() chi quet settings.VST_DIR (mac dinh
/opt/daw_engine/vst3) trong khi Plugin Manager scan plugin_dirs user.
- plugins.py list_plugins: gop _scan_vst_in_dirs(plugin_dirs) (file
.vst3/.dll/.so + folder X.vst3 Windows).
- vst_engine.py: PluginManager.extra_vst_dirs + _scan_plugins quet them
(ca folder .vst3) + get_plugin_manager doc plugin_dirs.json -> load_vst
tim thay plugin user scan khi render.
3) VU meter leak: section-tab play -> sang MAIN SESSION -> track MAIN van
animate theo am section.
- Root: VU tick fallback _sub_ (sub-node section co analyser) chay cho
ca canvas MAIN; startSubTabPlayback ghi node o key track MAIN.
- Fix: fallback _sub_ chi ap dung cho canvas SECTION (isSessVu); 2 trigger
piano-roll them prefix _sess_ theo st.parent_tab_id.
Verify: 86 tests pass; engine frozen STARTUP 1.35s, 1 engine, 0 spawn '-c';
pick-dir IPC mock tra dung path; scan VST user dirs OK.
This commit is contained in:
+19
-14
@@ -5333,28 +5333,30 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
setPmPicker(null);
|
||||
};
|
||||
const pickPluginFolder = async () => {
|
||||
const addDir = (p) => { if (p && !pmDirs.includes(p)) setPmDirs(prev => [...prev, p]); };
|
||||
try {
|
||||
// 1) NATIVE dialog qua engine (Tauri bridge → IFileDialog/Explorer;
|
||||
// fallback PowerShell/zenity/osascript) — UI chạy localhost:8000 nên
|
||||
// __TAURI__ không có, window.prompt vô hiệu trong WebView2.
|
||||
try {
|
||||
const d = await window.SonicAPI.pickPluginDir();
|
||||
if (d && typeof d.path === 'string' && d.path) { addDir(d.path); return; }
|
||||
} catch (e) { /* fallthrough */ }
|
||||
// 2) Tauri dialog trực tiếp (chỉ khi page được Tauri serve)
|
||||
if (window.__TAURI__ && window.__TAURI__.core && window.__TAURI__.core.invoke) {
|
||||
const sel = await window.__TAURI__.core.invoke('plugin:dialog|open', {
|
||||
options: { directory: true, multiple: false }
|
||||
});
|
||||
if (typeof sel === 'string' && sel) {
|
||||
if (!pmDirs.includes(sel)) setPmDirs(prev => [...prev, sel]);
|
||||
}
|
||||
return;
|
||||
if (typeof sel === 'string' && sel) { addDir(sel); return; }
|
||||
}
|
||||
// Trinh duyet thu muc in-app (backend) — hoat dong khi UI chay tren
|
||||
// localhost:8000 (khong co __TAURI__, window.prompt vo hieu trong WebView2)
|
||||
// 3) Trình duyệt thư mục in-app (backend) — fallback mọi OS
|
||||
await openPluginPicker();
|
||||
return;
|
||||
} catch (e) {
|
||||
// Fallback cuoi: prompt nhap tay (browser thuan, khong phai WebView2)
|
||||
// 4) Cuối cùng: prompt nhập tay (browser thuần, không phải WebView2)
|
||||
try {
|
||||
const manual = window.prompt('Nhập đường dẫn thư mục plugin (VST / SoundFont):');
|
||||
if (manual && manual.trim()) {
|
||||
const p = manual.trim();
|
||||
if (!pmDirs.includes(p)) setPmDirs(prev => [...prev, p]);
|
||||
}
|
||||
if (manual && manual.trim()) addDir(manual.trim());
|
||||
} catch (e2) {
|
||||
showToast('Browse failed: ' + (e.message || e), 'error');
|
||||
}
|
||||
@@ -8902,7 +8904,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
||||
keybedMouseDownRef.current = true;
|
||||
try {
|
||||
if (window.triggerMidiVuActivity) {
|
||||
window.triggerMidiVuActivity(st.trackId, 100);
|
||||
window.triggerMidiVuActivity((st.parent_tab_id && st.parent_tab_id.startsWith('session_') ? '_sess_' : '') + st.trackId, 100);
|
||||
}
|
||||
if (window.SonicSF) {
|
||||
window.SonicSF.playNote(pitch, 100, 500, undefined, kbCtx.program, null, kbCtx.ch, kbCtx.synthEngine);
|
||||
@@ -8915,7 +8917,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
||||
if (keybedMouseDownRef.current) {
|
||||
try {
|
||||
if (window.triggerMidiVuActivity) {
|
||||
window.triggerMidiVuActivity(st.trackId, 100);
|
||||
window.triggerMidiVuActivity((st.parent_tab_id && st.parent_tab_id.startsWith('session_') ? '_sess_' : '') + st.trackId, 100);
|
||||
}
|
||||
if (window.SonicSF) {
|
||||
window.SonicSF.playNote(pitch, 100, 200, undefined, kbCtx.program, null, kbCtx.ch, kbCtx.synthEngine);
|
||||
@@ -26322,7 +26324,10 @@ STRICT CONSTRAINTS:
|
||||
// Section item trên MAIN track: VU theo sub-nodes của track này (key
|
||||
// <trackId>_sub_*) — âm section đi thẳng masterBus.input (không qua
|
||||
// node chính) → node chính không có tín hiệu → VU track đứng yên.
|
||||
if (audioPeak <= 0.001) {
|
||||
// ⚠️ CHỈ fallback cho canvas SECTION (_sess_) — KHÔNG cho canvas MAIN:
|
||||
// section-tab đang play → chuyển sang MAIN SESSION → VU track MAIN
|
||||
// không được animate theo âm section (user bug).
|
||||
if (audioPeak <= 0.001 && isSessVu) {
|
||||
for (var sk in trackNodes) {
|
||||
if (sk.indexOf(trackId + '_sub_') === 0) {
|
||||
const sn = trackNodes[sk];
|
||||
|
||||
Reference in New Issue
Block a user