- 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.
- LOAD job posts WM_CLOSE to window created on main thread (DestroyWindow from worker crashed 0xc000041d, muting all tracks)
- handleOpenGui + attach jobs skip when Vst3Instrument::hasAttachedView() (state_ && guiAttached_)
- attach failure posts WM_CLOSE; post_close_gui erases entry only when it points at the same hwnd
- cap openVstGuiRetry at 5 to stop frontend retry spam
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.
- 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
- preloadTrackInstruments: also load VST3 tracks into bridge (no GUI) on project open
- onStatusChange(connected): reload all VST tracks after bridge restart/down recovery
- GUI button (track strip + piano roll): ensure-load then open — no white window when bridge restarted and instrument gone
- __bridgeLoadedChannels map: skip reload if already loaded (avoids plugin state reset); reset on bridge down
- piano roll: wrap Synth+GUI buttons in shrink-0 row so GUI stays on same line; compact track-strip GUI button
- 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
Two guards: schedulePianoRollMidi skips native SF item scheduling while bridge is active; scheduleMidiNoteDispatch lets notes pass to the MIDI router when a piano-roll sub-tab is playing. Verified on Windows: SF2 peak 0.026, VST3 ACDD native GUI + peak 0.074 via Play button. Docs: TEST_NOTES.md, TASKS.md.
- bridgeAudioNode: ScriptProcessor 4096 drained only 1 of 16 blocks -> ~94% silence; now drains all queued blocks per callback, RING_DEPTH 8->24
- app.jsx: openNativeGUI had NO caller; wire after VST3 loadInstrument OK in track Synth dropdown + Plugin Manager Load Bridge
- main.cpp: OPEN_GUI polls up to 10s for LOAD completion (worker thread) before attach - fixes race 'no instrument loaded'
- index.html: bump bridgeAudioNode/app.precompiled cache versions
- render_engine: soundfont track không có plugin_id giờ vẫn dùng instrument sf2 thay vì rơi về wave synth
- fontawesome: sửa đường dẫn webfonts (bỏ ../) hết 404 icon
- media explorer: drag MIDI library items vào timeline tạo được midi item (sinh 6 MIDI loop mẫu + resolve drop file)
- app.jsx: 3 path gate window.SonicSF cho phép route playback qua SonicCarlaMidi
- Thêm ensureMasteringRouting() đồng bộ graph mastering + routing SF + route gains NGAY trước khi preview (keybed onMouseDown, playDrawPreview, playMidiPreview, mở piano roll tab) — hết phải bật/tắt nút power mới có tác dụng
- applyMasteringSettings sig thêm masterConnected/isBypassed — toggle power luôn re-apply tham số module
- Nut Synth TCP goi openInstrumentSelector (modal 'Select Instrument')
nhung modal chi co category SoundFont + GM - khong co muc VST (muc VST
chi ton tai o dropdown MixerStrip). Them 'VST Instruments' vao sidebar
trai modal: list instrumentSelectorData.vst_instruments (listPlugins),
click -> setTrackInstrumentWithUndo(trackId, v.id, v.name) + dong modal.
- _scan_vst_in_dirs + /scan: phat hien ca FOLDER ten X.vst3 (Windows VST3
= folder chua X.vst3.dll) - truoc day chi scan file.
- Ghi chu: pedalboard (engine VST) la headless - KHONG mo duoc GUI goc cua
plugin; danh sach + chon la co che chon nhac cu kha thi.
Verify engine frozen: /plugins/available tra ['PadMachine','UltraSynth']
(ca file va folder .vst3); /scan dong bo; test_plugin_api 3 passed.
Trieu chung tu user: Task Manager day daw_engine process, port 8000->8005
leo thang, engine.log co engine moi moi ~2.5-3.3s trong nhieu phut.
Goc re: app/core/vst_engine.py module-level:
HAS_PEDALBOARD = check_pedalboard_safe()
-> subprocess.run([sys.executable, '-c', 'import pedalboard'], timeout=2.0)
Khi frozen, sys.executable = daw_engine.exe. PyInstaller bootloader bo qua
'-c' va chay script nhung (desktop_engine.py = CA ENGINE) -> moi lan check
thu vien lai khoi dong mot engine moi (timeout 2s moi check, block startup
~4s) -> engine con import app.main -> lai chay check -> spawn tiep
=> de quy vo han, hang chuc engine, may nghen, load 6-7.5s.
Fix:
- vst_engine: check_pedalboard_safe/check_pyfluidsynth_safe dung
importlib.util.find_spec (micro-giay, khong spawn, hoat dong frozen).
- desktop_engine.main(): guard phong thu - neu bi goi voi argv[1]=='-c'
(dang bi dung nhu python interpreter) thi thoat ngay khong chay server.
Verify tren Linux (engine frozen):
- STARTUP: 6-7.5s -> 1.24-1.33s (3 lan lien tiep)
- sau 15s: dung 1 engine, 0 process '-c import' (truoc: hang chuc)
- 86 tests pass; test suite 34s -> 14s (may het bi spam).
Spawn.log cho thay: resource_dir()/daw_engine/daw_engine.exe exists=false
tren ca ban cai lan target/release - engine KHONG duoc bundle dung cho.
Goc re (doc source tauri-utils/src/resources.rs + tauri-cli/src/interface/rust.rs):
- bundle.resources dang ARRAY ['resources/daw_engine']: ResourcePaths::new
(Slice) -> target = resource_relpath(path) GIU TIEN TO 'resources/'
-> engine nam o /resources/daw_engine/...
- lib.rs cu tim o /daw_engine/... -> exists=false.
Fix:
- tauri.conf.json: resources dang MAP {'resources/daw_engine': 'daw_engine/'}
-> resources_map -> Walk mode, dest.join(strip_prefix) -> giu nguyen cay
_internal, dich chuan /daw_engine/ (xac nhan tauri-cli
BundleResources::Map -> settings.resources_map, dong 1467-1469).
- lib.rs: do them 3 vi tri fallback (legacy resources/ prefix, portable
exe_dir, dev exe_dir/../resources) + log diagnostic day du vao spawn.log
(liet ke noi dung resource_dir/exe_dir khi khong tim thay).
- build_windows.ps1: them huong dan xac minh spawn.log sau khi cai dat.