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:
+52
-9
@@ -6175,8 +6175,25 @@ const loadVstToBridge = async (track) => {
|
|||||||
const bch = window.SonicMidiRouter.allocateChannel(track.id, false);
|
const bch = window.SonicMidiRouter.allocateChannel(track.id, false);
|
||||||
if (window.__bridgeLoadedChannels[bch]) return true;
|
if (window.__bridgeLoadedChannels[bch]) return true;
|
||||||
try {
|
try {
|
||||||
const r = await window.SonicAPI.bridgeLoad({ name: instrumentId, path: null, instrumentType: 'VST3', channel: bch });
|
// V8 bug 5: engine _resolve_bridge_asset chi match FILE theo filename —
|
||||||
const p = r && r.path ? r.path : null;
|
// 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;
|
if (!p) return false;
|
||||||
const ok = await window.NativeBridgeService.loadInstrument(p, 'VST3', bch);
|
const ok = await window.NativeBridgeService.loadInstrument(p, 'VST3', bch);
|
||||||
if (ok) window.__bridgeLoadedChannels[bch] = true;
|
if (ok) window.__bridgeLoadedChannels[bch] = true;
|
||||||
@@ -6193,7 +6210,17 @@ const ensureAndOpenVstGui = async (trackId, instrumentId) => {
|
|||||||
const ok = await loadVstToBridge({ id: trackId, instrumentId });
|
const ok = await loadVstToBridge({ id: trackId, instrumentId });
|
||||||
if (!ok) return;
|
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
|
// 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
|
// 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]; });
|
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) { 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: chọn VST3 qua nút Synth → load xong mở native GUI
|
// Requirement 2: chọn VST3 qua nút Synth → load xong mở native GUI
|
||||||
// (C++ attach editor vào cửa sổ bridge tự tạo — control type=4, hwnd=0).
|
// (C++ attach editor vào cửa sổ bridge tự tạo — control type=4, hwnd=0).
|
||||||
if (ok && btype === 'VST3' && window.NativeBridgeService.openNativeGUI) {
|
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;
|
if (!window.NativeBridgeService) return;
|
||||||
setBridgeUi(s => ({ ...s, connected: !!window.NativeBridgeService.isBridgeConnected }));
|
setBridgeUi(s => ({ ...s, connected: !!window.NativeBridgeService.isBridgeConnected }));
|
||||||
window.NativeBridgeService.onStatusChange(function (st) {
|
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 }));
|
setBridgeUi(s => ({ ...s, connected: !!st.connected }));
|
||||||
if (!st.connected) {
|
if (!st.connected) {
|
||||||
// Bridge down/restart → C++ mất hết instrument → reset map để lần tới
|
// Bridge down/restart → C++ mất hết instrument → reset map để lần tới
|
||||||
@@ -22461,6 +22499,8 @@ const App = () => {
|
|||||||
};
|
};
|
||||||
const stopAllPlayback = () => {
|
const stopAllPlayback = () => {
|
||||||
try {
|
try {
|
||||||
|
isPlayingRef.current = false;
|
||||||
|
try { if (subTabsRef.current) subTabsRef.current = subTabsRef.current.map(s => ({ ...s, isPlaying: false })); } catch (e) {}
|
||||||
activeSourcesRef.current.forEach(src => {
|
activeSourcesRef.current.forEach(src => {
|
||||||
try {
|
try {
|
||||||
src.stop();
|
src.stop();
|
||||||
@@ -22484,6 +22524,9 @@ const App = () => {
|
|||||||
// ngân tức thì; A13 C++ flush note-off).
|
// ngân tức thì; A13 C++ flush note-off).
|
||||||
if (window.NativeBridgeService && window.NativeBridgeService.isBridgeConnected) {
|
if (window.NativeBridgeService && window.NativeBridgeService.isBridgeConnected) {
|
||||||
try { window.NativeBridgeService.transport('stop'); } catch (e) {}
|
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) {
|
if (window.SonicSF) {
|
||||||
try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); }
|
try { window.SonicSF.stopAll(); } catch (e) { console.warn('[Stop] stopAll error:', e); }
|
||||||
@@ -29797,20 +29840,20 @@ STRICT CONSTRAINTS:
|
|||||||
className: "text-zinc-500 font-normal"
|
className: "text-zinc-500 font-normal"
|
||||||
}, track.fxType || "None")), /*#__PURE__*/React.createElement("button", {
|
}, track.fxType || "None")), /*#__PURE__*/React.createElement("button", {
|
||||||
onClick: (e) => { e.stopPropagation(); openInstrumentSelector(track.id); },
|
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", {
|
}, /*#__PURE__*/React.createElement("span", {
|
||||||
className: "inline-flex items-center shrink-0"
|
className: "inline-flex items-center shrink-0"
|
||||||
}, /*#__PURE__*/React.createElement("i", {
|
}, /*#__PURE__*/React.createElement("i", {
|
||||||
"data-lucide": "music",
|
"data-lucide": "music",
|
||||||
className: "w-3 h-3"
|
className: "w-2.5 h-2.5 shrink-0"
|
||||||
})), /*#__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", {
|
})), /*#__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) => {
|
onClick: (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
ensureAndOpenVstGui(track.id, track.instrumentId);
|
ensureAndOpenVstGui(track.id, track.instrumentId);
|
||||||
},
|
},
|
||||||
title: "Mở lại GUI VSTi",
|
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"
|
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-3 h-3 shrink-0" }), /*#__PURE__*/React.createElement("span", { className: "truncate text-[9px]" }, "GUI")) : null), /*#__PURE__*/React.createElement("div", {
|
}, /*#__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),
|
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",
|
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()
|
onClick: e => e.stopPropagation()
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -50,7 +50,7 @@
|
|||||||
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
|
<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/promptTemplateManager.js?v=202607281039"></script>
|
||||||
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></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">
|
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -209,6 +209,37 @@ int main(int argc, char* argv[]) {
|
|||||||
uint32_t parentPid = 0;
|
uint32_t parentPid = 0;
|
||||||
if (const char* e = std::getenv("SF_PARENT_PID")) parentPid = (uint32_t)std::atoi(e);
|
if (const char* e = std::getenv("SF_PARENT_PID")) parentPid = (uint32_t)std::atoi(e);
|
||||||
|
|
||||||
|
// V8 bug 2: watchdog THREAD rieng - main loop chi check parent_alive giua
|
||||||
|
// cac block; neu VST process() chan loop thi khong bao gio thoat -> orphan.
|
||||||
|
// Thread nay chay doc lap, parent chet -> TerminateProcess ngay. Kem
|
||||||
|
// start-time check chong PID reuse (OpenProcess tra handle cua process
|
||||||
|
// khac chiem lai PID -> tuong parent con song mai).
|
||||||
|
if (parentPid != 0) {
|
||||||
|
std::thread([parentPid]() {
|
||||||
|
auto proc_birth = [](HANDLE h) -> uint64_t {
|
||||||
|
FILETIME c, e, k, u;
|
||||||
|
if (GetProcessTimes(h, &c, &e, &k, &u))
|
||||||
|
return (uint64_t(c.dwHighDateTime) << 32) | c.dwLowDateTime;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
uint64_t birth = 0;
|
||||||
|
{
|
||||||
|
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, parentPid);
|
||||||
|
if (h) { birth = proc_birth(h); CloseHandle(h); }
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
Sleep(2000);
|
||||||
|
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, parentPid);
|
||||||
|
if (!h) { TerminateProcess(GetCurrentProcess(), 0); return; }
|
||||||
|
uint64_t nowBirth = proc_birth(h);
|
||||||
|
CloseHandle(h);
|
||||||
|
if (birth != 0 && nowBirth != 0 && nowBirth != birth) {
|
||||||
|
TerminateProcess(GetCurrentProcess(), 0); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Real-time-ish timing: 1ms scheduler resolution
|
// Real-time-ish timing: 1ms scheduler resolution
|
||||||
timeBeginPeriod(1);
|
timeBeginPeriod(1);
|
||||||
@@ -252,6 +283,10 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
const uint32_t block = AUDIO_BLOCK_SIZE;
|
const uint32_t block = AUDIO_BLOCK_SIZE;
|
||||||
uint64_t playheadSamples = 0;
|
uint64_t playheadSamples = 0;
|
||||||
|
// V8 bug 3: khi STOP da xu ly, bo qua NOTE_ON (velocity>0) den sau - JS
|
||||||
|
// note-on timer co the bay toi sau STOP (guardPlay tre do React re-render)
|
||||||
|
// -> retrigger note -> VST loop am. Chi PLAY moi nhan note-on lai.
|
||||||
|
bool transportStopped = false;
|
||||||
|
|
||||||
auto dispatch = [&](const SharedAudioBufferIPC::MidiEventIPC& evt) {
|
auto dispatch = [&](const SharedAudioBufferIPC::MidiEventIPC& evt) {
|
||||||
auto* inst = instruments.get(evt.channel);
|
auto* inst = instruments.get(evt.channel);
|
||||||
@@ -261,9 +296,10 @@ int main(int argc, char* argv[]) {
|
|||||||
// sampleOffset LUON LUON = 0 khi den day: events duoc dispatch ngay
|
// sampleOffset LUON LUON = 0 khi den day: events duoc dispatch ngay
|
||||||
// truoc segment chua no (A11 splitting), nen offset tuong doi la 0.
|
// truoc segment chua no (A11 splitting), nen offset tuong doi la 0.
|
||||||
// Truyen offset tuyet doi truoc day lam sfizz/VST3 trigger tre.
|
// Truyen offset tuyet doi truoc day lam sfizz/VST3 trigger tre.
|
||||||
if (evt.velocity > 0)
|
if (evt.velocity > 0) {
|
||||||
|
if (transportStopped) return; // V8 bug 3: drop note-on sau STOP
|
||||||
inst->noteOn(evt.channel, evt.pitch, evt.velocity / 127.0f, 0);
|
inst->noteOn(evt.channel, evt.pitch, evt.velocity / 127.0f, 0);
|
||||||
else
|
} else
|
||||||
inst->noteOff(evt.channel, evt.pitch, 0);
|
inst->noteOff(evt.channel, evt.pitch, 0);
|
||||||
break;
|
break;
|
||||||
case 0x8:
|
case 0x8:
|
||||||
@@ -363,9 +399,17 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cout << "[NativeBridge] PANIC — all notes off" << std::endl;
|
std::cout << "[NativeBridge] PANIC — all notes off" << std::endl;
|
||||||
} else if (c.type == 3) { // TRANSPORT (A13)
|
} else if (c.type == 3) { // TRANSPORT (A13)
|
||||||
if (c.arg0 == 0) { // STOP → flush every note immediately
|
if (c.arg0 == 0) { // STOP → flush every note immediately
|
||||||
|
transportStopped = true;
|
||||||
|
// Release sustain pedal TRUOC (CC64=0) — nhieu VSTi giu note
|
||||||
|
// khi pedal con down -> note-off cua allNotesOff bi bo qua
|
||||||
|
// -> am treo loop (V8 bug 3).
|
||||||
|
for (uint32_t ch = 0; ch < 16; ++ch) {
|
||||||
|
if (auto* inst = instruments.get(ch)) inst->controlChange(ch, 64, 0);
|
||||||
|
}
|
||||||
instruments.allNotesOff();
|
instruments.allNotesOff();
|
||||||
std::cout << "[NativeBridge] transport STOP — all notes off" << std::endl;
|
std::cout << "[NativeBridge] transport STOP — all notes off" << std::endl;
|
||||||
} else if (c.arg0 == 1) { // PLAY
|
} else if (c.arg0 == 1) { // PLAY
|
||||||
|
transportStopped = false;
|
||||||
playheadSamples = c.arg1;
|
playheadSamples = c.arg1;
|
||||||
std::cout << "[NativeBridge] transport PLAY playhead=" << playheadSamples << std::endl;
|
std::cout << "[NativeBridge] transport PLAY playhead=" << playheadSamples << std::endl;
|
||||||
} else if (c.arg0 == 2) { // SET_POSITION (seek while stopped)
|
} else if (c.arg0 == 2) { // SET_POSITION (seek while stopped)
|
||||||
|
|||||||
+17
-3
@@ -24,6 +24,20 @@ struct EngineProcess(Mutex<Option<CommandChild>>);
|
|||||||
struct BridgeProcess(Mutex<Option<CommandChild>>);
|
struct BridgeProcess(Mutex<Option<CommandChild>>);
|
||||||
struct BridgeSampleRate(Mutex<u32>);
|
struct BridgeSampleRate(Mutex<u32>);
|
||||||
|
|
||||||
|
/// Overwrite the managed bridge child. `app.manage` is a NO-OP once the state
|
||||||
|
/// exists (StateManager::set keeps the first value), so restarts would leave
|
||||||
|
/// the state at None and the Destroyed handler would never kill the new
|
||||||
|
/// bridge — orphan daw_vst_bridge.exe. First call still uses manage().
|
||||||
|
fn set_bridge_child(app: &AppHandle, child: Option<CommandChild>) {
|
||||||
|
if let Some(state) = app.try_state::<BridgeProcess>() {
|
||||||
|
if let Ok(mut g) = state.0.lock() {
|
||||||
|
*g = child;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app.manage(BridgeProcess(Mutex::new(child)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Kill the currently managed bridge child (if any) — a restart MUST never
|
/// Kill the currently managed bridge child (if any) — a restart MUST never
|
||||||
/// leave the old bridge running, else two daw_vst_bridge.exe race on the same
|
/// leave the old bridge running, else two daw_vst_bridge.exe race on the same
|
||||||
/// SHM (double instrument load, double OPEN_GUI, garbled control queue).
|
/// SHM (double instrument load, double OPEN_GUI, garbled control queue).
|
||||||
@@ -145,7 +159,7 @@ fn spawn_bridge(
|
|||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
Ok((mut rx, child)) => {
|
Ok((mut rx, child)) => {
|
||||||
app.manage(BridgeProcess(Mutex::new(Some(child))));
|
set_bridge_child(app, Some(child));
|
||||||
// B3: redirect bridge stdout/stderr → %APPDATA%/SonicForgeDAW/logs/bridge.log
|
// B3: redirect bridge stdout/stderr → %APPDATA%/SonicForgeDAW/logs/bridge.log
|
||||||
// (tauri-shell pipe rồi vứt rx; đọc event ghi file — E5 UI tail được).
|
// (tauri-shell pipe rồi vứt rx; đọc event ghi file — E5 UI tail được).
|
||||||
let log_dir = std::env::var("APPDATA").unwrap_or_else(|_| ".".into());
|
let log_dir = std::env::var("APPDATA").unwrap_or_else(|_| ".".into());
|
||||||
@@ -175,14 +189,14 @@ fn spawn_bridge(
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to spawn daw_vst_bridge {bridge_exe:?}: {e}");
|
println!("Failed to spawn daw_vst_bridge {bridge_exe:?}: {e}");
|
||||||
app.manage(BridgeProcess(Mutex::new(None)));
|
set_bridge_child(app, None);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("daw_vst_bridge binary not found — app will use WASM fallback");
|
println!("daw_vst_bridge binary not found — app will use WASM fallback");
|
||||||
app.manage(BridgeProcess(Mutex::new(None)));
|
set_bridge_child(app, None);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user