fix: SF2 bank select, midi multi-key cut, VSTi reopen hang, multi-track stuck
- NativeInstrumentEngine: track GM bank per channel (CC0/CC32), use bank in programChange - app.jsx: send CC0/CC32+PROGRAM before notes via __ensureBridgeProgram, dedupe, clear dedupe after async LOAD - audioRoutingEngine/bridgeAudioNode: idempotent connect (no disconnect-flush on re-connect), fixes note cut & multi-track stuck - main.cpp: remove 10s poll in OPEN_GUI control job (blocked realtime loop, watchdog race), VstWindowProc stores channel not inst pointer, cleanup gui maps on WM_DESTROY
This commit is contained in:
+37
-9
@@ -68,6 +68,30 @@ const resolveTrackInstrumentCtx = (track, tracks) => {
|
||||
return { ch, program: undefined, synthEngine: undefined, sfId: undefined, bank: 0, prog: 0 };
|
||||
};
|
||||
|
||||
// ── Bridge program-change helper (D8/A12): gửi CC0/CC32 (bank THẬT) + PROGRAM
|
||||
// qua bridge trước note-on — dedupe theo track+bank+program (không spam mỗi
|
||||
// note; reset khi load instrument mới). Dùng chung mọi đường: timeline,
|
||||
// preview, keybed. Trước đây bank hardcode 0 + preview/keybed KHÔNG gửi
|
||||
// program change → SF2 preset ở bank≠0 ra piano sai.
|
||||
window.__ensureBridgeProgram = (trkId, program, synthEngine) => {
|
||||
try {
|
||||
if (!window.SonicMidiRouter || !window.SonicMidiRouter.isBridgeActive()) return;
|
||||
const _bank = synthEngine && synthEngine.soundfont_bank !== undefined ? synthEngine.soundfont_bank : 0;
|
||||
const _prog = (program !== undefined && program !== null)
|
||||
? program
|
||||
: (synthEngine && synthEngine.soundfont_program !== undefined ? synthEngine.soundfont_program : undefined);
|
||||
if (_prog === undefined || _prog === null) return;
|
||||
const _key = trkId + ':' + _bank + ':' + _prog;
|
||||
const _lp = window.__bridgeLastProgram || (window.__bridgeLastProgram = {});
|
||||
if (_lp[_key]) return;
|
||||
_lp[_key] = true;
|
||||
const _isPerc = _bank === 128;
|
||||
window.SonicMidiRouter.pushEvent({ cmd: 'CC', channel: trkId, pitch: 0, velocity: 0, data2: (_bank >> 7) & 0x7F, percussion: _isPerc });
|
||||
window.SonicMidiRouter.pushEvent({ cmd: 'CC', channel: trkId, pitch: 32, velocity: 0, data2: _bank & 0x7F, percussion: _isPerc });
|
||||
window.SonicMidiRouter.pushEvent({ cmd: 'PROGRAM', channel: trkId, pitch: 0, velocity: 0, data2: _prog, percussion: _isPerc });
|
||||
} catch (e) { console.warn('[Bridge] ensureBridgeProgram error:', e); }
|
||||
};
|
||||
|
||||
// Đảm bảo FluidSynth channel của track đã select ĐÚNG instrument trước khi
|
||||
// notes bắn. Fire-and-forget: playNote tự load + retry nếu SF chưa load xong
|
||||
// (dedup sẵn trong loadSoundFont) — không chặn, không gây stall khi mở tab.
|
||||
@@ -9327,6 +9351,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
||||
playNativeSfNote(kbTrk, pitch, 100 / 127, 5000, undefined, 'kb_' + st.trackId + '_' + pitch);
|
||||
}
|
||||
if (window.SonicMidiRouter && window.SonicMidiRouter.isBridgeActive()) {
|
||||
try { if (window.__ensureBridgeProgram) window.__ensureBridgeProgram(kbTrk ? kbTrk.id : 0, kbCtx.program, kbCtx.synthEngine); } catch (er) {}
|
||||
window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_ON', channel: kbTrk ? kbTrk.id : 0, pitch: pitch, velocity: 100 / 127, percussion: !!(kbCtx && kbCtx.synthEngine && kbCtx.synthEngine.soundfont_bank === 128) });
|
||||
} else if (window.SonicSF && !kbNative) {
|
||||
// ⚠️ FIX: giữ note theo thời gian bấm phím — durationMs lớn (5s)
|
||||
@@ -9354,6 +9379,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
||||
playNativeSfNote(kbTrk, pitch, 100 / 127, 5000, undefined, 'kb_' + st.trackId + '_' + pitch);
|
||||
}
|
||||
if (window.SonicMidiRouter && window.SonicMidiRouter.isBridgeActive()) {
|
||||
try { if (window.__ensureBridgeProgram) window.__ensureBridgeProgram(kbTrk ? kbTrk.id : 0, kbCtx.program, kbCtx.synthEngine); } catch (er) {}
|
||||
window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_ON', channel: kbTrk ? kbTrk.id : 0, pitch: pitch, velocity: 100 / 127, percussion: !!(kbCtx && kbCtx.synthEngine && kbCtx.synthEngine.soundfont_bank === 128) });
|
||||
} else if (window.SonicSF && !(isStandaloneSf() && isSfTrackEngine(kbCtx.synthEngine) && !shouldRouteCarla(kbCtx.synthEngine))) {
|
||||
// giữ note khi kéo qua phím (mouse enter) — dừng bằng mouseup/leave
|
||||
@@ -15389,6 +15415,13 @@ const App = () => {
|
||||
if (p) {
|
||||
const ok = await window.NativeBridgeService.loadInstrument(p, btype, bch);
|
||||
console.log('[Bridge] track', trackId, '-> bridge', btype, 'ch', bch, ok ? 'OK' : 'FAIL', p);
|
||||
// Reset program-change dedupe của track — LOAD async (C++ worker): CC/
|
||||
// PROGRAM gửi trước khi load xong bị drop (inst chưa có) → note kế tiếp
|
||||
// phải gửi lại program change để ra ĐÚNG instrument.
|
||||
if (ok && window.__bridgeLastProgram) {
|
||||
const _pfx = trackId + ':';
|
||||
Object.keys(window.__bridgeLastProgram).forEach(k => { if (k.indexOf(_pfx) === 0) delete window.__bridgeLastProgram[k]; });
|
||||
}
|
||||
// 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).
|
||||
if (ok && btype === 'VST3' && window.NativeBridgeService.openNativeGUI) {
|
||||
@@ -21690,6 +21723,7 @@ const App = () => {
|
||||
if (window.SonicMidiRouter && window.SonicMidiRouter.isBridgeActive()) {
|
||||
const isPerc = !!(track && track.synth_engine && track.synth_engine.soundfont_bank === 128);
|
||||
const tch = track ? track.id : 0;
|
||||
try { if (window.__ensureBridgeProgram) window.__ensureBridgeProgram(tch, program, track && track.synth_engine); } catch (e) {}
|
||||
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_ON', channel: tch, pitch: pitch || 60, velocity: velocity, percussion: isPerc }); } catch (e) {}
|
||||
setTimeout(function () {
|
||||
try { window.SonicMidiRouter.pushEvent({ cmd: 'NOTE_OFF', channel: tch, pitch: pitch || 60, velocity: 0, percussion: isPerc }); } catch (e) {}
|
||||
@@ -21726,16 +21760,10 @@ const App = () => {
|
||||
const trkId = track ? track.id : 0;
|
||||
// Percussion (bank 128) → router allocateChannel ch 9; melodic → round-robin.
|
||||
const isPerc = !!(synthEngine && synthEngine.soundfont_bank === 128);
|
||||
// D8: track đổi instrument → gửi CC0/CC32 (bank 0) + program change qua
|
||||
// bridge (A12) một lần mỗi program mới mỗi channel.
|
||||
// D8: track đổi instrument → gửi CC0/CC32 (bank THẬT từ synthEngine) +
|
||||
// program change qua bridge (A12) — helper dedupe theo track+bank+program.
|
||||
if (program !== undefined && program !== null) {
|
||||
const lastP = window.__bridgeLastProgram || (window.__bridgeLastProgram = {});
|
||||
if (lastP[trkId] !== program) {
|
||||
lastP[trkId] = program;
|
||||
try { window.SonicMidiRouter.pushEvent({ cmd: 'CC', channel: trkId, pitch: 0, velocity: 0, data2: 0, percussion: isPerc }); } catch (e) {}
|
||||
try { window.SonicMidiRouter.pushEvent({ cmd: 'CC', channel: trkId, pitch: 32, velocity: 0, data2: 0, percussion: isPerc }); } catch (e) {}
|
||||
try { window.SonicMidiRouter.pushEvent({ cmd: 'PROGRAM', channel: trkId, pitch: 0, velocity: 0, data2: program, percussion: isPerc }); } catch (e) {}
|
||||
}
|
||||
try { if (window.__ensureBridgeProgram) window.__ensureBridgeProgram(trkId, program, synthEngine); } catch (e) {}
|
||||
}
|
||||
setTimeout(function () {
|
||||
if (!guardPlay()) return;
|
||||
|
||||
Reference in New Issue
Block a user