Fix AI proxy origin in Tauri, SF2 program_select, VSTi GUI reopen + sample rate bridge

- aiGateway.js: use window.API_BASE_URL (engine port) instead of window.location.origin (tauri://localhost asset protocol returned index.html -> JSON parse error); check content-type before parsing response
- NativeInstrumentEngine.cpp: fluid_synth_program_select for SF2 instrument + program change
- Vst3Instrument.cpp: activate all audio/event buses, channel=0 forced, GUI resize/reopen, kEvent using fix
- main.cpp: VST GUI reopen via WM_DESTROY, OleInitialize, steady_clock loop
- lib.rs: BridgeSampleRate state, restart_bridge_with_sample_rate command, borrow fix
- app.jsx/nativeBridgeService.js: isMidiTrack widening, bridge masterbus connect, held MIDI notes, sample-rate restart, openNativeGUI channel
This commit is contained in:
2026-08-13 11:26:39 +07:00
parent 49549aba2e
commit d168328004
8 changed files with 218 additions and 59 deletions
+34 -5
View File
@@ -1416,6 +1416,20 @@ function getAudioContext() {
window.SonicMidiRouter.setBridgeConnected(connected);
console.log('[Bridge] bootstrap connected=', connected);
if (connected) {
try {
console.log('[Bridge] Requesting restart to match sampleRate:', audioCtx.sampleRate);
await window.__TAURI__.core.invoke('restart_bridge_with_sample_rate', { sampleRate: audioCtx.sampleRate });
for (var retry = 0; retry < 10; retry++) {
await new Promise(r => setTimeout(r, 100));
var st2 = await window.NativeBridgeService.queryStatus();
if (st2 && st2.connected) {
console.log('[Bridge] Reconnected at sampleRate:', audioCtx.sampleRate);
break;
}
}
} catch (err) {
console.warn('[Bridge] restart error:', err);
}
window.BridgeAudioNode.init(audioCtx);
window.NativeBridgeService.onAudio(function (l, r) { window.BridgeAudioNode.onAudio(l, r); });
if (window.AudioRoutingEngine) window.AudioRoutingEngine.connect(window.BridgeAudioNode, null, null);
@@ -15378,7 +15392,7 @@ const App = () => {
// 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); } catch (e) {}
try { await window.NativeBridgeService.openNativeGUI(instrumentId, bch); } catch (e) {}
}
}
})();
@@ -15647,6 +15661,12 @@ const App = () => {
var bTracks = activeTracksRef.current || [];
bTracks.forEach(function (bt) {
if (!bt.isArmed) return;
if (window.triggerMidiVuActivity) {
window.triggerMidiVuActivity(bt.id, scaledVel);
}
try {
heldMidiNotesRef.current[bt.id] = (heldMidiNotesRef.current[bt.id] || 0) + 1;
} catch (err) {}
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_ON', channel: bt.id, pitch: pitch, velocity: scaledVel / 127, percussion: !!(bt.synth_engine && bt.synth_engine.soundfont_bank === 128) }); } catch (e) {}
});
} else if (window.SonicSF) {
@@ -15722,6 +15742,14 @@ const App = () => {
// D1: bridge note-off qua router (channel trùng NOTE_ON track.id).
var bStopTracks = activeTracksRef.current || [];
bStopTracks.forEach(function (bst) {
try {
if (heldMidiNotesRef.current[bst.id] !== undefined) {
heldMidiNotesRef.current[bst.id] = Math.max(0, heldMidiNotesRef.current[bst.id] - 1);
if (heldMidiNotesRef.current[bst.id] === 0) {
delete heldMidiNotesRef.current[bst.id];
}
}
} catch (err) {}
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_OFF', channel: bst.id, pitch: pitch, velocity: 0, percussion: !!(bst.synth_engine && bst.synth_engine.soundfont_bank === 128) }); } catch (e) {}
});
} else if (window.SonicSF && window.SonicSF.stopNote) {
@@ -21216,7 +21244,8 @@ const App = () => {
// NEVER affects the soundfont instrument, and the bypass never touches
// audio clips/sections.
let sfEntry = null, sfOut = null, sfPan = null, sfAnalyser = null, sfRouteGain = null, sfDryGain = null, sfMods = [];
if ((track.midiItems && track.midiItems.length > 0)) {
const isMidiTrack = (track.midiItems && track.midiItems.length > 0) || track.type === 'MIDI' || !!track.instrumentId;
if (isMidiTrack) {
sfEntry = context.createGain();
sfOut = context.createGain();
sfPan = context.createStereoPanner();
@@ -21401,7 +21430,7 @@ const App = () => {
}
}
}
const midiAudible = list.filter(t => (t.midiItems && t.midiItems.length > 0) && computeTrackAudibleGain(list, t) > 0);
const midiAudible = list.filter(t => ((t.midiItems && t.midiItems.length > 0) || t.isArmed || t.type === 'MIDI' || !!t.instrumentId) && computeTrackAudibleGain(list, t) > 0);
console.log('[SFRoute] activeTab=' + _activeTabId, 'midiAudible=' + midiAudible.map(t => t.id + '(midi' + (t.midiItems || []).length + ')').join(',') || 'none');
if (midiAudible.length === 1) {
const t = midiAudible[0];
@@ -21454,8 +21483,8 @@ const App = () => {
}
// D5: bridge active + không track nào audible ngt khi graph (v
// masterBus qua updateSfRouting ln sau khi có track).
if (window.AudioRoutingEngine && window.SonicMidiRouter && window.SonicMidiRouter.isBridgeActive()) {
window.AudioRoutingEngine.disconnect();
if (window.AudioRoutingEngine && window.SonicMidiRouter && window.SonicMidiRouter.isBridgeActive() && window.BridgeAudioNode) {
window.AudioRoutingEngine.connect(window.BridgeAudioNode, null, null);
} else if (window.SonicSF && window.SonicSF.setOutputDestination) {
window.SonicSF.setOutputDestination(null);
}