- calibrate: probe sandbox (jbridge) theo real-time pacing — child render qua SHM
consumer-sync, probe µs-speed chỉ đo được silence/attack → makeup=1.0 mọi channel.
Thêm virtual probeNeedsRealtime() (SandboxVst2Host → true), calibrate() Sleep
~1 block/5.8ms để child kịp nhận MIDI + render steady-state → makeup đo peak thật
(3 channel Qin ≈ 2.6, trước đây = 1.0 → âm lượng đồng đều giữa các track/VSTi).
- OPEN_GUI: SetWindowPos(HWND_TOPMOST) + SetForegroundWindow + BringWindowToTop
→ GUI VSTi hiển thị ở top window, tương tác ngay.
- /api/v1/plugins/available: filter vst_instruments + extra theo is_fx của
fx_vst_bridge --scan (trước merge entry) — modal Select Instrument chỉ còn
soundfont + VSTi, không hiện VST FX (90 → 33).
1) wavesynth tự phát khi mở app: probe calibrate() render vào live mix
(renderAll process cùng instance) -> thêm calibratingCh_[16], renderAll
bỏ qua channel đang calibrate; plugin_host panic (sustain off + all
notes off + 128 noteOff) ngay sau preset apply để startup im lặng.
2) bấm GUI mở 2 cửa sổ Qin: sandbox VST2 child tự sở hữu top-level window
-> ownsGuiWindow() + main.cpp forward OPEN_GUI control, không tạo native
parent / không registry double-open.
3) play phát nhạc cụ sai: restore clobber — seed preset đè patch thật trong
state file (ch3 63ba3667 -> 7f07473c) do saveStateJob chạy lúc child còn
seed. Fix: queuePreset(waitApply=true) chờ child apply xong (presetOut
memcmp), consume-before-apply (zero presetInLen trước khi apply), deferred
double-load re-check trong main.cpp skip assign trùng path, watchdog
respawn re-queue lastPreset_.
- Fix A (USER32 crash): WM_CLOSE no longer destroys the native VST
window from the main thread (plugin editor children live on the
channel worker thread). WM_CLOSE -> hide + closeGUI on worker;
WM_DESTROY erases registry. LOAD_INSTRUMENT close uses closeGUI +
SW_HIDE, waits on hasAttachedView() instead of registry erase.
- Fix B: per-process SHM name SonicForge_DAW_IPC_{pid} so stale
bridges from old builds can never attach to a new app's ring buffer.
- Fix C (stall): opening editor for a plugin whose other channel still
has a live instance of the same DLL (Nexus) deadlocked the audio
loop (reload+createView on worker while audio thread processed the
other instance). Silence same-path channels via setReloading during
reload/attach, restore afterwards; audio resumes.
- Rebuild bridge + update install/ portable package.
Root cause: Steinberg EventList defaults to maxSize=50 events. The STOP
all-notes-off sweep queues 128 note-offs per channel (16ch x 128 pitch),
so addEvent beyond 50 fails silently and the plugin never receives the
remaining note-offs — voices keep sustaining and the transport loops
forever until the bridge process is killed.
Fix: raise eventList.setMaxSize(4096) in loadPlugin. Also assign unique
noteIds per overlapping note-on (stack per pitch) so note-off releases
every voice at that pitch, and log addEvent result + channel in debug
output for verification.
- Close other channels' editor windows before attaching a new one (Option B)
- needsReload()/setReloading() guard: skip processAudioBlock during
terminate+reload teardown to avoid UAF/hang when reopening GUI
- Wait for editor registry cleanup (5s timeout) before creating window
- InstrumentEngineManager::assign: destroy replaced instrument OUTSIDE mu_
(VST3 terminate/removed can block -> audio loop stall -> bridge Not
Responding, transport stop hangs, notes never off)
- main.cpp LOAD job: DestroyWindow any open editor window of the channel
before assign (replace VSTi -> SF2 with editor alive hung in removed())
- Vst3Instrument: reload() fresh plugin instance on second openGUI after
close (Nexus etc. hang at view->attached() twice on same component)
- 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