fix(bridge): load dung VSTi theo track (V9) - bo early-return mui theo flag, load qua Tauri invoke khi co path, guard path sai (WaveObserver vs Nexus), chi mo GUI khi load OK; sync 6 ban JS + index.html ?v; kem cac fix bridge truoc (bugs 1-4, GUI restore)
This commit is contained in:
+87
-17
@@ -795,6 +795,21 @@ function makeDistortionCurve(k) {
|
||||
return curve;
|
||||
}
|
||||
|
||||
function makeSoftClipCurve(percent) {
|
||||
const n = 4096;
|
||||
const curve = new Float32Array(n);
|
||||
const p = Math.max(0, Math.min(100, percent));
|
||||
const t = 1 - (p / 100) * 0.8;
|
||||
const inv = 1 - t;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
const x = (i * 2) / (n - 1) - 1;
|
||||
const ax = Math.abs(x);
|
||||
if (ax < t) curve[i] = x;
|
||||
else curve[i] = Math.sign(x) * (t + inv * Math.tanh((ax - t) / inv));
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
function applyMasteringSettings(s) {
|
||||
console.log('[Mastering] applyMasteringSettings active=', !!(s && s.masterConnected), 'bypass=', !!(s && s.isBypassed), 'eqActive=', !!(s && s.eqActive), 'masterBus=', !!masterBus);
|
||||
if (!masterBus || !audioCtx || !s) return;
|
||||
@@ -858,10 +873,11 @@ function applyMasteringSettings(s) {
|
||||
const boostLinear = (s.maximizerActive) ? Math.pow(10, clamp(s.maxGain, -60, 30) / 20) : 1.0;
|
||||
masterBus.maximizerBoostGain.gain.setTargetAtTime(boostLinear, now, 0.01);
|
||||
|
||||
// Soft Clipper (identity passthrough when off — never null curve)
|
||||
// Soft Clipper (identity passthrough when off — never null curve).
|
||||
// Unity-gain curve: bo gain an cua atan (slope ~2.1x cu cong +6 dB len
|
||||
// maxGain -> tin hieu >~-12 dBFS bi hard-clip meo "be").
|
||||
if (s.maximizerActive && s.maxSoftClip > 0) {
|
||||
const k = 1 + (clamp(s.maxSoftClip, 0, 100) / 100) * 10;
|
||||
masterBus.maximizerSoftClipper.curve = makeDistortionCurve(k);
|
||||
masterBus.maximizerSoftClipper.curve = makeSoftClipCurve(s.maxSoftClip);
|
||||
} else {
|
||||
masterBus.maximizerSoftClipper.curve = new Float32Array([-1, 1]);
|
||||
}
|
||||
@@ -1717,6 +1733,7 @@ function getAudioContext() {
|
||||
await window.__TAURI__.core.invoke('restart_bridge_with_sample_rate', { sampleRate: audioCtx.sampleRate });
|
||||
// Bridge restart → mất hết instrument → reset map để reconnect reload.
|
||||
window.__bridgeLoadedChannels = {};
|
||||
window.__bridgeLoadedPaths = {};
|
||||
for (var retry = 0; retry < 10; retry++) {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
var st2 = await window.NativeBridgeService.queryStatus();
|
||||
@@ -6439,13 +6456,23 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData, onInsertInstrument }
|
||||
// reconnect + nút GUI (ensure-load rồi mới open — tránh nền trắng khi bridge
|
||||
// restart làm mất instrument). __bridgeLoadedChannels (ch→true) đánh dấu đã
|
||||
// load trong session; reset khi bridge down/restart → load lại đúng lúc.
|
||||
// V9: track.plugin_path/cache path có thể là plugin KHÁC instrumentId (VD
|
||||
// WaveObserver trong khi track là Nexus) → basename không chứa tên id → trả
|
||||
// null để engine resolve theo name / listPlugins — tránh LOAD nhầm VSTi.
|
||||
const _bridgeSafeVstPath = (path, instrumentId) => {
|
||||
if (!path || !instrumentId) return path;
|
||||
const base = String(path).split(/[\\/]/).pop().toLowerCase().replace(/\s+/g, '');
|
||||
const want = String(instrumentId).toLowerCase().replace(/\s+/g, '');
|
||||
return base.indexOf(want) !== -1 ? path : null;
|
||||
};
|
||||
const loadVstToBridge = async (track) => {
|
||||
if (!window.NativeBridgeService || !window.SonicMidiRouter || !window.SonicMidiRouter.isBridgeActive()) return false;
|
||||
const instrumentId = track.instrumentId || (track.synth_engine && track.synth_engine.plugin_id);
|
||||
if (!instrumentId || (typeof instrumentId === 'string' && instrumentId.startsWith('sf_'))) return false;
|
||||
if (!window.__bridgeLoadedChannels) window.__bridgeLoadedChannels = {};
|
||||
const bch = window.SonicMidiRouter.allocateChannel(track.id, false);
|
||||
if (window.__bridgeLoadedChannels[bch]) return true;
|
||||
if (!window.__bridgeLoadedPaths) window.__bridgeLoadedPaths = {};
|
||||
const tId = track.id || track.trackId;
|
||||
const bch = window.SonicMidiRouter.allocateChannel(tId, false);
|
||||
try {
|
||||
// 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
|
||||
@@ -6459,9 +6486,22 @@ const loadVstToBridge = async (track) => {
|
||||
if (v0 && v0.path) knownPath = v0.path;
|
||||
if (v0 && v0.type) btype = v0.type;
|
||||
}
|
||||
const r = await window.SonicAPI.bridgeLoad({ name: instrumentId, path: knownPath || null, instrumentType: btype, channel: bch });
|
||||
// V9: path sai (plugin_path cũ / cache của plugin khác) → bỏ qua, resolve theo tên.
|
||||
knownPath = _bridgeSafeVstPath(knownPath, instrumentId);
|
||||
// Đã load ĐÚNG path vào channel này rồi → skip (không LOAD trùng). Bỏ
|
||||
// early-return mù theo flag — flag sai khi C++ restore instrument khác.
|
||||
if (window.__bridgeLoadedPaths[bch] === knownPath) return true;
|
||||
// Có path đáng tin → load thẳng qua Tauri invoke (không cần POST/auth/CORS).
|
||||
if (knownPath) {
|
||||
const ok = await window.NativeBridgeService.loadInstrument(knownPath, btype, bch);
|
||||
if (ok) { window.__bridgeLoadedChannels[bch] = true; window.__bridgeLoadedPaths[bch] = knownPath; }
|
||||
console.log('[Bridge] auto-load track', track.id, '->', btype, 'ch', bch, ok ? 'OK' : 'FAIL', knownPath);
|
||||
return !!ok;
|
||||
}
|
||||
// Không có path → nhờ engine resolve theo name (POST). Fail (auth/CORS) →
|
||||
// fallback listPlugins bên dưới.
|
||||
const r = await window.SonicAPI.bridgeLoad({ name: instrumentId, path: null, instrumentType: btype, 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();
|
||||
@@ -6476,12 +6516,14 @@ const loadVstToBridge = async (track) => {
|
||||
}
|
||||
if (!p) return false;
|
||||
const ok = await window.NativeBridgeService.loadInstrument(p, btype, bch);
|
||||
if (ok) window.__bridgeLoadedChannels[bch] = true;
|
||||
if (ok) { window.__bridgeLoadedChannels[bch] = true; window.__bridgeLoadedPaths[bch] = p; }
|
||||
console.log('[Bridge] auto-load track', track.id, '->', btype, 'ch', bch, ok ? 'OK' : 'FAIL', p);
|
||||
return !!ok;
|
||||
} catch (e) { console.warn('[Bridge] loadVstToBridge fail:', track.id, e); return false; }
|
||||
};
|
||||
const ensureAndOpenVstGui = async (trackId, instrumentId) => {
|
||||
const ensureAndOpenVstGui = async (track) => {
|
||||
const trackId = track.id || track.trackId;
|
||||
const instrumentId = track.instrumentId || (track.synth_engine && track.synth_engine.plugin_id);
|
||||
if (!instrumentId || (typeof instrumentId === 'string' && instrumentId.startsWith('sf_'))) return;
|
||||
if (!window.__bridgeLoadedChannels) window.__bridgeLoadedChannels = {};
|
||||
const bch = (window.SonicMidiRouter && window.SonicMidiRouter.allocateChannel) ? window.SonicMidiRouter.allocateChannel(trackId, false) : 0;
|
||||
@@ -6497,10 +6539,14 @@ const ensureAndOpenVstGui = async (trackId, instrumentId) => {
|
||||
} catch (e) { console.warn('[Bridge] GUI via API fail:', trackId, e); }
|
||||
return;
|
||||
}
|
||||
if (!window.__bridgeLoadedChannels[bch]) {
|
||||
const ok = await loadVstToBridge({ id: trackId, instrumentId });
|
||||
if (!ok) return;
|
||||
}
|
||||
// V9: load (nếu chưa có ĐÚNG instrument) rồi CHỈ mở GUI khi load OK — không
|
||||
// mở mù khi fail (nếu load Nexus fail mà vẫn OPEN_GUI, C++ mở GUI của
|
||||
// instrument KHÁC đang restore trên channel — VD WaveObserver thay vì Nexus).
|
||||
// Bridge restore sẵn ĐÚNG instrument → LOAD trùng path → C++ double-load
|
||||
// guard skip → ok=true → GUI vẫn mở. Không có instrument → OPEN_GUI defer.
|
||||
let loadOk = false;
|
||||
try { loadOk = await loadVstToBridge(track); } catch (e) { console.warn('[Bridge] loadVstToBridge fail:', trackId, e); }
|
||||
if (!loadOk) return;
|
||||
try { await openVstGuiRetry(instrumentId, bch, 20); } catch (e) { console.warn('[Bridge] openNativeGUI fail:', e); }
|
||||
};
|
||||
// V8 bug 4 + V9 bug 3/6/7: C++ load instrument ASYNC (worker thread) — OPEN_GUI
|
||||
@@ -10236,7 +10282,7 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
|
||||
className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 max-w-[70px] ${st.instrumentName ? 'bg-violet-900 text-violet-300 border-violet-700' : 'bg-zinc-800 text-zinc-500 border-transparent hover:text-zinc-300'}`
|
||||
}, React.createElement("i", { "data-lucide": "music", className: "w-3 h-3 shrink-0" }), React.createElement("span", { className: "truncate text-[9px]" }, activeParentTrackName ? ('(' + activeParentTrackName + ') ' + (st.instrumentName || 'Synth')) : (st.instrumentName || 'Synth'))), (st.instrumentId && typeof st.instrumentId === 'string' && !st.instrumentId.startsWith('sf_') ? React.createElement("button", {
|
||||
onClick: () => {
|
||||
ensureAndOpenVstGui(st.trackId, st.instrumentId);
|
||||
ensureAndOpenVstGui(st);
|
||||
},
|
||||
title: "Mở lại GUI VSTi",
|
||||
className: "px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 shrink-0 bg-zinc-800 text-violet-300 border-violet-800 hover:bg-violet-900/40"
|
||||
@@ -11067,6 +11113,7 @@ const serializeProjectToSchema = (projectId, name, bpmVal, tracksList, subTabsLi
|
||||
duration: st.duration || 4,
|
||||
instrument_program: st.instrumentProgram,
|
||||
instrument_name: st.instrumentName,
|
||||
instrument_id: st.instrumentId || (st.synth_engine && st.synth_engine.plugin_id) || null,
|
||||
synth_engine: st.synth_engine || null,
|
||||
current_time: st.currentTime || 0,
|
||||
color: st.color || null
|
||||
@@ -11148,6 +11195,7 @@ const deserializeProjectFromSchema = (schemaObj) => {
|
||||
duration: st.duration || 4,
|
||||
instrumentProgram: st.instrument_program,
|
||||
instrumentName: st.instrument_name,
|
||||
instrumentId: st.instrument_id || (st.synth_engine && st.synth_engine.plugin_id) || null,
|
||||
synth_engine: st.synth_engine || null,
|
||||
currentTime: st.current_time || 0,
|
||||
color: st.color || null
|
||||
@@ -16097,7 +16145,10 @@ const App = () => {
|
||||
// channel, nếu không ensureAndOpenVstGui sẽ bỏ qua load khi chọn lại VST
|
||||
// (flag cũ còn true) → OPEN_GUI đến bridge khi channel chưa có instrument
|
||||
// → deferred mãi, nút GUI không mở gì.
|
||||
if (isSf && window.__bridgeLoadedChannels) delete window.__bridgeLoadedChannels[bch];
|
||||
if (isSf) {
|
||||
if (window.__bridgeLoadedChannels) delete window.__bridgeLoadedChannels[bch];
|
||||
if (window.__bridgeLoadedPaths) delete window.__bridgeLoadedPaths[bch];
|
||||
}
|
||||
// VST2 (.dll) scan type la 'VST2' - khong hardcode VST3 (se LOAD sai
|
||||
// loai -> create_instrument nullptr -> fail im lang).
|
||||
let btype = isSf ? 'SF2' : 'VST3';
|
||||
@@ -16113,6 +16164,14 @@ const App = () => {
|
||||
if (v0 && v0.type) btype = v0.type;
|
||||
}
|
||||
}
|
||||
// V9: path sai (plugin khác instrumentId) → bỏ qua, resolve theo name.
|
||||
if (!isSf) bpath = _bridgeSafeVstPath(bpath, instrumentId);
|
||||
// V9: VST→VST đổi instrument khác → path khác path đã load → xóa flag để
|
||||
// load mới (trước đây chỉ xóa khi isSf — flag cũ làm skip nhầm).
|
||||
if (window.__bridgeLoadedPaths && window.__bridgeLoadedPaths[bch] !== bpath) {
|
||||
delete window.__bridgeLoadedPaths[bch];
|
||||
if (window.__bridgeLoadedChannels) delete window.__bridgeLoadedChannels[bch];
|
||||
}
|
||||
(async () => {
|
||||
let p = bpath;
|
||||
try {
|
||||
@@ -16129,7 +16188,12 @@ const App = () => {
|
||||
const _pfx = trackId + ':';
|
||||
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 (!window.__bridgeLoadedPaths) window.__bridgeLoadedPaths = {};
|
||||
window.__bridgeLoadedPaths[bch] = p;
|
||||
}
|
||||
if (ok && btype === 'VST3' && p) {
|
||||
try {
|
||||
if (!window.__bridgePluginPaths) window.__bridgePluginPaths = {};
|
||||
@@ -17747,11 +17811,17 @@ const App = () => {
|
||||
// Rust auto-restart KHONG emit bridge-down khi thanh cong → map cu true →
|
||||
// GUI click skip load → attach null → nen trang.
|
||||
window.__bridgeLoadedChannels = {};
|
||||
window.__bridgeLoadedPaths = {};
|
||||
setBridgeUi(s => ({ ...s, connected: !!st.connected }));
|
||||
if (!st.connected) {
|
||||
// Bridge down/restart → C++ mất hết instrument → reset map để lần tới
|
||||
// load lại (nút GUI ensure-load trước khi open — tránh cửa sổ trắng).
|
||||
window.__bridgeLoadedChannels = {};
|
||||
window.__bridgeLoadedPaths = {};
|
||||
// FIX auto-play: bridge restart → page co the con isPlaying=true (stale)
|
||||
// → master-silence watchdog tu resume phat lai → am tu dong. Stop ngay
|
||||
// de page khong con "dang phat" — user muon nghe thi bam play.
|
||||
try { stopAllPlayback(); setIsPlaying(false); } catch (e) { console.warn('[Bridge] stop-on-down fail:', e); }
|
||||
return;
|
||||
}
|
||||
// Bridge (re)connect → load lại toàn bộ VST track (không mở GUI).
|
||||
@@ -30598,7 +30668,7 @@ STRICT CONSTRAINTS:
|
||||
})), /*#__PURE__*/React.createElement("span", { className: "truncate text-xs 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);
|
||||
ensureAndOpenVstGui(track);
|
||||
},
|
||||
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-xs font-bold flex items-center gap-1 shrink-0"
|
||||
|
||||
+2013
-1988
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user