fix(v8): 5 bugs — orphan bridge watchdog+state overwrite, stuck-note stop race, white VST GUI retry, VSTi auto-load via saved plugin_path, track-header button overflow

- native bridge: dedicated watchdog thread (parent death / PID-reuse via GetProcessTimes) -> TerminateProcess self; transportStopped flag drops note-on after STOP + CC64 sustain release + allNotesOff on stop
- tauri: set_bridge_child overwrites managed BridgeProcess state (app.manage is no-op when state exists) so Destroyed handler kills restarted bridge
- ui: stopAllPlayback syncs isPlayingRef/subTabsRef + second transport stop at 150ms; onStatusChange resets __bridgeLoadedChannels at callback start (auto-restart never emits bridge-down); openVstGuiRetry waits for async C++ load before OPEN_GUI; loadVstToBridge uses synth_engine.plugin_path / listPlugins fallback and persists plugin_path on load; shrink track-header Synth/GUI buttons
This commit is contained in:
2026-08-13 21:14:31 +07:00
parent e3f5dad2ac
commit 96d31ee685
5 changed files with 134 additions and 22 deletions
+52 -9
View File
@@ -6175,8 +6175,25 @@ const loadVstToBridge = async (track) => {
const bch = window.SonicMidiRouter.allocateChannel(track.id, false);
if (window.__bridgeLoadedChannels[bch]) return true;
try {
const r = await window.SonicAPI.bridgeLoad({ name: instrumentId, path: null, instrumentType: 'VST3', channel: bch });
const p = r && r.path ? r.path : null;
// V8 bug 5: engine _resolve_bridge_asset chi match FILE theo filename
// KHONG walk folder VST3 ("X.vst3/X.vst3.dll") 404. Dung path da luu
// (synth_engine.plugin_path) hoac cache tu listPlugins.
let knownPath = track.synth_engine && track.synth_engine.plugin_path;
if (!knownPath && window.__bridgePluginPaths) knownPath = window.__bridgePluginPaths[instrumentId];
const r = await window.SonicAPI.bridgeLoad({ name: instrumentId, path: knownPath || null, instrumentType: 'VST3', channel: bch });
let p = r && r.path ? r.path : null;
if (!p && knownPath) p = knownPath;
if (!p && window.SonicAPI.listPlugins) {
try {
const pl = await window.SonicAPI.listPlugins();
const v = ((pl && pl.vst_instruments) || []).find(x => x.id === instrumentId || x.name === instrumentId);
if (v && v.path) {
p = v.path;
if (!window.__bridgePluginPaths) window.__bridgePluginPaths = {};
window.__bridgePluginPaths[instrumentId] = v.path;
}
} catch (e2) { console.warn('[Bridge] listPlugins fallback fail:', e2); }
}
if (!p) return false;
const ok = await window.NativeBridgeService.loadInstrument(p, 'VST3', bch);
if (ok) window.__bridgeLoadedChannels[bch] = true;
@@ -6193,7 +6210,17 @@ const ensureAndOpenVstGui = async (trackId, instrumentId) => {
const ok = await loadVstToBridge({ id: trackId, instrumentId });
if (!ok) return;
}
try { await window.NativeBridgeService.openNativeGUI(instrumentId, bch); } catch (e) { console.warn('[Bridge] openNativeGUI fail:', e); }
try { await openVstGuiRetry(instrumentId, bch, 5); } catch (e) { console.warn('[Bridge] openNativeGUI fail:', e); }
};
// V8 bug 4: C++ load instrument ASYNC (worker thread) OPEN_GUI gui toi truoc
// khi assign() xong instruments.get(guiCh) null "GUI attach FAILED"
// nen trang. Retry mo GUI nhieu lan, cach 400ms cho load xong.
const openVstGuiRetry = async (instrumentId, bch, attempts) => {
for (let i = 0; i < (attempts || 5); i++) {
try { await window.NativeBridgeService.openNativeGUI(instrumentId, bch); return true; } catch (e) { console.warn('[Bridge] openNativeGUI retry', i + 1, e); }
await new Promise(r => setTimeout(r, 400));
}
return false;
};
// Module-level so both ProfileModal (open project) and App (auto-restore) can
// warm the FluidSynth font cache for each track's instrument. This only loads
@@ -15473,10 +15500,17 @@ const App = () => {
Object.keys(window.__bridgeLastProgram).forEach(k => { if (k.indexOf(_pfx) === 0) delete window.__bridgeLastProgram[k]; });
}
if (ok) { if (!window.__bridgeLoadedChannels) window.__bridgeLoadedChannels = {}; window.__bridgeLoadedChannels[bch] = true; }
if (ok && btype === 'VST3' && p) {
try {
if (!window.__bridgePluginPaths) window.__bridgePluginPaths = {};
window.__bridgePluginPaths[instrumentId] = p;
updateActiveTracks(prev => prev.map(t => t.id === trackId ? { ...t, synth_engine: { ...(t.synth_engine || {}), plugin_path: p } } : t));
} catch (e) {}
}
// Requirement 2: chn VST3 qua nút Synth load xong m native GUI
// (C++ attach editor vào ca s bridge t to control type=4, hwnd=0).
if (ok && btype === 'VST3' && window.NativeBridgeService.openNativeGUI) {
try { await window.NativeBridgeService.openNativeGUI(instrumentId, bch); } catch (e) {}
try { await openVstGuiRetry(instrumentId, bch, 5); } catch (e) {}
}
}
})();
@@ -17059,6 +17093,10 @@ const App = () => {
if (!window.NativeBridgeService) return;
setBridgeUi(s => ({ ...s, connected: !!window.NativeBridgeService.isBridgeConnected }));
window.NativeBridgeService.onStatusChange(function (st) {
// V8 bug 4: reset map NGAY DAU callback (ca connected lan !connected)
// Rust auto-restart KHONG emit bridge-down khi thanh cong map cu true
// GUI click skip load attach null nen trang.
window.__bridgeLoadedChannels = {};
setBridgeUi(s => ({ ...s, connected: !!st.connected }));
if (!st.connected) {
// Bridge down/restart C++ mt hết instrument reset map đ ln ti
@@ -22461,6 +22499,8 @@ const App = () => {
};
const stopAllPlayback = () => {
try {
isPlayingRef.current = false;
try { if (subTabsRef.current) subTabsRef.current = subTabsRef.current.map(s => ({ ...s, isPlaying: false })); } catch (e) {}
activeSourcesRef.current.forEach(src => {
try {
src.stop();
@@ -22484,6 +22524,9 @@ const App = () => {
// ngân tc thì; A13 C++ flush note-off).
if (window.NativeBridgeService && window.NativeBridgeService.isBridgeConnected) {
try { window.NativeBridgeService.transport('stop'); } catch (e) {}
// V8 bug 3: transport stop thu 2 sau 150ms note-on timer guard sync qua
// React effect (cham) van bay toi bridge sau STOP retrigger am treo.
setTimeout(() => { try { window.NativeBridgeService.transport('stop'); } catch (e) {} }, 150);
}
if (window.SonicSF) {
try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); }
@@ -29797,20 +29840,20 @@ STRICT CONSTRAINTS:
className: "text-zinc-500 font-normal"
}, track.fxType || "None")), /*#__PURE__*/React.createElement("button", {
onClick: (e) => { e.stopPropagation(); openInstrumentSelector(track.id); },
className: "px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-amber-400 border border-amber-800 rounded text-xs font-bold flex items-center gap-1 max-w-[120px]"
className: "px-1.5 py-0.5 bg-zinc-800 hover:bg-zinc-700 text-amber-400 border border-amber-800 rounded text-[10px] font-bold flex items-center gap-1 max-w-[70px] shrink-0"
}, /*#__PURE__*/React.createElement("span", {
className: "inline-flex items-center shrink-0"
}, /*#__PURE__*/React.createElement("i", {
"data-lucide": "music",
className: "w-3 h-3"
})), /*#__PURE__*/React.createElement("span", { className: "truncate text-[10px]" }, track.instrumentName || track.instrumentId || "Synth"), /*#__PURE__*/React.createElement("i", { "data-lucide": "chevron-down", className: "w-3 h-3 shrink-0" }))), (track.instrumentId && typeof track.instrumentId === 'string' && !track.instrumentId.startsWith('sf_') ? /*#__PURE__*/React.createElement("button", {
className: "w-2.5 h-2.5 shrink-0"
})), /*#__PURE__*/React.createElement("span", { className: "truncate text-[10px] min-w-0" }, track.instrumentName || track.instrumentId || "Synth"), /*#__PURE__*/React.createElement("i", { "data-lucide": "chevron-down", className: "w-3 h-3 shrink-0" }))), (track.instrumentId && typeof track.instrumentId === 'string' && !track.instrumentId.startsWith('sf_') ? /*#__PURE__*/React.createElement("button", {
onClick: (e) => {
e.stopPropagation();
ensureAndOpenVstGui(track.id, track.instrumentId);
},
title: "Mở lại GUI VSTi",
className: "px-1.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-violet-300 border border-violet-800 rounded text-[10px] font-bold flex items-center gap-1 shrink-0"
}, /*#__PURE__*/React.createElement("i", { "data-lucide": "sliders-horizontal", className: "w-3 h-3 shrink-0" }), /*#__PURE__*/React.createElement("span", { className: "truncate text-[9px]" }, "GUI")) : null), /*#__PURE__*/React.createElement("div", {
className: "px-1 py-0.5 bg-zinc-800 hover:bg-zinc-700 text-violet-300 border border-violet-800 rounded text-[9px] font-bold flex items-center gap-0.5 shrink-0"
}, /*#__PURE__*/React.createElement("i", { "data-lucide": "sliders-horizontal", className: "w-2.5 h-2.5 shrink-0" }), /*#__PURE__*/React.createElement("span", { className: "truncate text-[8px] min-w-0" }, "GUI")) : null), /*#__PURE__*/React.createElement("div", {
onMouseDown: e => handleTrackResizeMouseDown(e, track.id),
className: "absolute bottom-0 left-0 right-0 h-1 cursor-ns-resize z-30 hover:bg-cyan-500/50 transition-colors",
onClick: e => e.stopPropagation()
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -50,7 +50,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608132011" defer></script>
<script src="/static/js/app.precompiled.js?v=202608132130" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {