# BÁO CÁO BUG: Mở VST GUI khi đang PLAY → dự án câm, GUI không load Ngày: 2026-08-15 · Repo: `C:/Users/locpham/SonicForgeStudio` · git status sạch ## 1. Tóm tắt Nguyên nhân gốc: **nhiều thread cùng vào 1 plugin DLL trong lúc `view->attached()`**. Khi đang PLAY, audio loop `process()` chạy trên các instance Nexus (ch0-4) cùng lúc worker thread của ch5 chạy `attachView()`. Nexus throw exception trong `createView/attached` → attach FAILED → frontend retry ~30 lần (0.3s/lần) → `bridgeWriteIndex` stall 3s → Tauri watchdog kill+respawn bridge → **mất hết instrument → câm toàn bộ**. GUI không bao giờ load được. Không phải "instrument track khác bị reset" (giả thuyết 1 sai ở bước này — reload reset state chỉ xảy ra khi GUI ĐÃ mở thành công rồi đóng rồi mở lại, không phải lần attach đầu). ## 2. Chuỗi bug đầy đủ (xác nhận live, bridge.log) ``` ... 6x Nexus loaded ch0-5 + EZkeys ch6 ... [NativeBridge] transport PLAY playhead=0 [NativeBridge] GUI attach EXCEPTION ch=5 — plugin threw (createView/attached) [NativeBridge] GUI attach FAILED hwnd=0000000002250F36 plugin=Nexus [tauri] bridge restarted (attempt 1) ← watchdog: writeIndex stall 3s [NativeBridge] Starting DAW Host Bridge Engine... ← restart, mất hết instrument ``` 1. `handleOpenGui` (main.cpp ~943): OPEN_GUI ch5 → silence mọi channel cùng plugin path → `g_loadMutex` → `try { reloadForGUI(); attachView(hwnd); } catch(...)`. 2. `attachView` (Vst3Instrument.cpp:550): `controller->createView(kEditor)` → `view->attached(hwnd, kPlatformTypeHWND)` → **Nexus throw** khi đang PLAY (audio loop process() cùng DLL đang chạy trên instance khác). 3. catch → `ok=false` → log EXCEPTION + FAILED → PostMessage WM_CLOSE (GUI không load). 4. Frontend `openVstGuiRetry` spam lại ~30 lần; mỗi lần chạy lại job (silence + mutex + reload/attach) → main loop bị nghẽn → `bridgeWriteIndex` stall 3s. 5. `lib.rs`: stall >3s → `kill_bridge` + `spawn_bridge` (restart_attempts==0) → mất toàn bộ instrument state → **CÂM**. ## 3. Bằng chứng thực nghiệm ### 3.1 Probe kiến trúc chính xác (bridge_two: 2 worker STA riêng + main pump) `gui_probe.exe bridge_two ` — kết quả 76 VST3 hệ thống (sweep): `native_bridge/debug/sweep_bridge_two_8s.json`, `probe_attach_detail.py` (bổ sung). ⚠️ **Hiệu chỉnh sweep cũ**: exit code 0xC0000409 của TẤT CẢ 76 plugin là **artifact của probe** — `ProbeWorker::th` không join → `~std::thread` trên joinable thread → `std::terminate` → fail-fast. RESULT line in ra nhưng không flush kịp. Không phải 76 plugin cùng crash teardown. Phân loại đúng dựa trên output per-attach: | Nhóm | Số plugin | Bằng chứng | Ý nghĩa | |---|---|---|---| | **HANG attach inst2** | 30 | `workerA openGUI inst1=1` rồi `workerB openGUI inst2` in ra, **không có kết quả** — Nexus 25s không về | 2 instance cùng DLL, 2 apartment → attach instance 2 HANG trong `view->attached()` | | **BOTH-OK** | 20 | `workerB openGUI inst2=1` — Kontakt, BlueARP, Ozone 11 (18) | Attach 2 instance OK; chỉ crash teardown artifact | | **HANG/crash attach inst1** | 18 | `workerA hwnd=` in ra, không in `openGUI inst1` — RX 11 (18) + ACTP | Chết ngay instance 1 | | **CRASH EARLY** | 8 | exit 1.2-4.6s, 0xC0000005 (AV): Melodyne, Orb Arpeggios/Melody, smartEQ3/live, smartreverb, DUNE3, ACDD | Crash thật trong attach | Không plugin nào HANG >18s (không WATCHDOG-KILL) — main pump cứu được các trường hợp cần pump; vấn đề là **conflict thread/apartment khi 2 instance cùng DLL**. ### 3.2 Nexus: hang khi 2 instance — OK khi 1 thread - `bridge_two` (2 worker STA, 2 instance Nexus): inst1 attach OK, **inst2 HANG 25s+** (xác nhận bằng run 25s). - `same_thread` (load + attach cùng 1 thread): **OK** (returned=1). - `main_own` (load+attach main thread): **OK** (RESULT done=1 attached_ok=1). - `two_workers_close` (đóng editor inst1 trước khi attach inst2, có closeGUI + attach qua temp thread): Nexus **HANG ngay attach inst1**; Kontakt chạy hết, OK cả 2. → 2 thread/apartment cùng 1 DLL = conflict thật (không riêng Nexus: 30 plugin hang inst2, 8 crash AV). Kontakt/Ozone/BlueARP chịu được. ### 3.3 Giả thuyết (a) reload mất state — XÁC NHẬN từ code `Vst3Instrument::reload()` (Vst3Instrument.cpp:615): `closeGUI()` + terminate + `delete state_` + `loadPlugin(path_)` **MỚI** → preset/state mất khi mở lại GUI (reopen). Kích hoạt bởi `reloadForGUI()` khi `hasAttachedOnce_ && !guiAttached_` (đã từng mở GUI rồi đóng). Không phải nguyên nhân của bug câm lần này (attach đầu chưa thành công nên chưa tới reload), nhưng là bug phụ: **mở GUI → đóng → mở lại = mất preset/state của track**. ## 4. Kết luận nguyên nhân 1. **Root cause**: attach GUI chạy worker thread trong khi audio loop `process()` đang chạy instance cùng DLL (2+ thread 1 DLL). Nexus throw → attach fail. Probe không audio: biểu hiện hang (inst2); app thật có audio: biểu hiện exception (ch5). Cùng gốc. 2. **Khuếch đại**: frontend retry spam 30× → stall 3s → watchdog tưởng bridge chết → restart → mất hết instrument → câm. Đây là lý do dự án chết hoàn toàn, không chỉ mất GUI. 3. `g_loadMutex` + silence hiện có chưa đủ: không đợi renderAll thoát hẳn instance cùng DLL trước khi `createView`, và Nexus vẫn throw ngay cả khi đã silence (silence chỉ ngăn process gọi tiếp, không chặn instance đang nằm trong process()). ## 5. Đề xuất fix (xếp theo ưu tiên) 1. **Chặn/defer OPEN_GUI khi transport PLAY** — đơn giản nhất, an toàn nhất: nếu đang PLAY, đợi transport STOP (hoặc tự tạm dừng) trước khi attach, resume sau. Loại bỏ hoàn toàn conflict audio-loop × attachView. (`ponytail:` người dùng muốn mở GUI khi đang chơi thì nâng cấp thành pause-resume tự động, hoặc đợi `setProcessing(false)` + đợi renderAll thoát.) 2. **Giới hạn retry**: bỏ `openVstGuiRetry` spam; thử tối đa 2-3 lần có backoff. 3. **Watchdog đừng giết khi đang attach GUI**: phân biệt stall do GUI job dài (có attach-in-flight flag) với stall do bridge chết thật; nâng ngưỡng stall khi có attach đang chạy. 4. **Fix state reset**: `reload()` nên save/restore state (`getState`/`setState`) thay vì load instance mới mất preset. (Bug phụ, độc lập.) 5. (Dài hạn) 1 apartment chung cho mọi instance cùng DLL (`shared_worker` pattern đã có trong probe) — loại conflict nhiều-thread-1-DLL triệt để. ## 6. Files liên quan - `native_bridge/src/main.cpp` — handleOpenGui (943), silence/close-other (1015-1090), attach job + catch (1132-1160) - `native_bridge/src/Vst3Instrument.cpp` — reload() (615), reloadForGUI() (528), attachView() (550), closeGUI() (640) - `native_bridge/src/gui_probe.cpp` — probe bridge_two (243), two_workers_close (318) - `native_bridge/debug/` — sweep_all_gui.py, sweep_bridge_two_8s.json, probe_attach_detail.py, probe_app4_live.py, probe_app4.py - Bridge log: `%AppData%/SonicForgeDAW/logs/bridge.log` Trạng thái live hiện tại: app câm (bridge 21932 sống, không instrument, transport STOP). Test thêm bằng `probe_app4_live.py` sẽ lại làm hư session — cần xác nhận trước khi chạy tiếp.