Files
SonicForgeStudio/big_tasks.md
T

87 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sonic Forge Studio — Big Tasks (ROADMAP G0–G4)
Trạng thái: 2026-08-15. Bug gốc: PLAY + mở GUI VST → câm, GUI không load.
User duyệt roadmap G0–G4 ("Hãy bắt đầu"). Đang ở G1.
## Ngữ cảnh kỹ thuật
- Logs: `C:/Users/locpham/AppData/Roaming/SonicForgeDAW/logs/` — `bridge.log` (stdout bridge), `spawn.log` (watchdog restart), `engine.log`.
- VST test: `C:\Program Files\Common Files\VST3\Nexus.vst3`.
- SHM: `SonicForge_DAW_IPC_<APP_PID>`; offsets: controlQueue @2836 (8×1040), controlQueueCount @11156, attach_in_flight @11160, writeIndex @4, size 11164. Control types: 2=LOAD, 3=TRANSPORT (0=STOP/1=PLAY), 4=OPEN_GUI.
- Branch: `standalone-shm-bridge`. App: sonicforge-daw, daw_engine, daw_vst_bridge (3 process).
- Probe: `native_bridge/build/Release/gui_probe.exe` — standalone VST3 GUI attach probe.
Variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker | ui_thread | two_workers_close.
- File CRLF toàn bộ native_bridge + CMakeLists.txt → patch multi-line qua python `io.open(newline='')`.
## G0 — Hoàn thành (đã commit, branch standalone-shm-bridge)
- G0.1 (1839480): defer OPEN_GUI khi PLAY.
- G0.2 (2f78618): openVstGuiRetry ≤2 lần + `__bridgePlaying` guard (app.precompiled.js).
- G0.3 (5313d85): watchdog giữ 15s khi attach-in-flight (SHM flag offset 11160) + RAII guard.
- G0.4 (eac7f2f): save/restore preset quanh reload — captureState/restoreState trong Vst3Instrument (getState/setState qua MemoryStream), gọi trong post_close_gui; INativeInstrument.h virtual no-op; CMakeLists link memorystream.cpp vào daw_vst_bridge + sdk_common. VERIFIED PASS (probe_g04_preset.py).
## G1 — 1 UiThread chung (ĐANG LÀM)
Mục tiêu: 1 thread chung host mọi VST3 instance thay per-channel workers → hết "2 thread 1 DLL" hang/crash (bridge_two HANG/EXIT3221225477 vs shared_worker OK).
### G1.1 — Probe variant ui_thread (alias shared_worker) [HOÀN THÀNH]
- [x] Patch gui_probe.cpp: usage string, branch `variant == "shared_worker" || variant == "ui_thread"`, isDeferred list.
- [x] Fix build: CMakeLists gui_probe target thêm `memorystream.cpp` + `sdk_common` (LNK2019).
- [x] Fix crash 0xC0000409: ProbeWorker thiếu destructor join → std::terminate. Thêm destructor + join + closeGUI trước teardown + gỡ IME. VERIFIED `shared_worker 15`: RESULT done=1 aRes=1 bRes=1, EXIT=0, 6.73s. Commit 3587a34.
### G1.2 — Chuyển tạo window + attach job từ ChannelWorker → UiThread mới (1 thread chung) [HOÀN THÀNH — 9e6e72a]
- [x] Thay per-channel workers (16) bằng 1 UiThread: COM STA + message pump + job queue.
- [x] Audio loop bỏ PeekMessageW.
- [x] Option B (đóng editor channel khác trước attach) chuyển PostMessage(WM_CLOSE)+5s → close_editor_now đồng bộ (1 thread: job close sau attach job cùng queue → không bao giờ chạy).
- [x] VERIFIED: build EXIT=0; live test PASS — LOAD 7, PLAY writeIndex advancing, defer OK, attach OK, Option B close đồng bộ OK, WM_CLOSE→reload OK, 0 crash/restart.
### G1.3 — Close/resize/IME sang UiThread + bỏ pump gate cũ [HOÀN THÀNH — b92c85e]
### G1.4 — Bỏ mute-when-editor-open [HOÀN THÀNH — 7629783]
- [x] channelQuiet chi con reloading (bo editorOpenFn check); xoa setEditorOpenPredicate/g_editorOpen/g_editorPathCount/clear_editor_open.
- [x] VERIFIED: build PASS; live test PASS — PLAY+editor open writeIndex advancing (het cam), LOAD-while-editor OK, close/reopen x4 OK, regression G1.2 PASS, 0 crash/restart.
### G1.5 — Regression driver stress [HOÀN THÀNH]
- [x] stress driver native_bridge/debug/test_g15_stress.py: 5 + 10 iterations LOAD/OPEN_GUI/PLAY/STOP/WM_CLOSE Nexus+EZkeys.
- [x] VERIFIED: PASS ca hai run — PLAY+editor open writeIndex advancing moi iter (delta ~290-330), WM_CLOSE state restored OK, bridge alive 30512, 0 EXCEPTION/FAILED/crash.
## G2 — Persistence restart (2-3 tuần)
Mục tiêu: restart không mất instrument — "stop/play nghe lại" ngay sau respawn.
- G2.1 save/restore đầy đủ: toàn bộ trạng thái instrument (path, preset, params, transport position) ra đĩa.
- G2.2 dump state trước kill: trước khi DAW/bridge bị kill, dump trạng thái hiện tại.
- G2.3 auto-reload sau respawn: sau khi bridge respawn, tự load lại từ dump.
- G2.4 bỏ !isPlayingRef chặn restore: gỡ guard chặn restore khi đang PLAY.
## G3 — Health / Watchdog thông minh (1-2 tuần)
Mục tiêu: Watchdog thông minh, cô lập lỗi.
- G3.1 heartbeat riêng: heartbeat bridge/engine tách khỏi logic audio.
- G3.2 phân loại stall: phân biệt hang/crash/stall để xử lý đúng mức.
- G3.3 SEH quanh process từng channel: 1 plugin crash → chỉ mute channel đó, không sập cả bridge.
## G4 — Sandbox plugin (tuỳ chọn, 2-3 tháng)
Mục tiêu: plugin process con — VST3 không có chuẩn OOP như AUv3 → wrapper tự làm.
Lưu ý: G1+G3 đã lo 90% kịch bản. Khuyến nghị làm sau.
## Kiến trúc hiện tại (main.cpp 1254 dòng, sau G1.4) — tham chiếu G1.2–G1.5
- 1 UiThread chung (class ChannelWorker, main.cpp ~236): COM STA + job queue + message pump (cap 128 msg/iteration). MỌI window (native VST + editor children) thuộc thread này; CBT hook IME per-thread + priority ABOVE_NORMAL. loadPlugin/createView/close_editor_now/LOAD jobs đều chạy ở đây — thread sống cả đời bridge, COM STA apartment không chết → Nexus attached() không treo. (G1.1/G1.2)
- Audio/main loop (main.cpp ~972-1235): drain control queue → pending-GUI sweep → MIDI snapshot → dispatch sample-accurate (A11: sort theo sampleOffset, renderSegment từng sub-block) → renderAll → bridgeWriteIndex++ → sync real-time. KHÔNG còn PeekMessageW (G1.3); try/catch net bọc cả loop (plugin exception không escape).
- Control (SHM): controlQueue @2836 (8×1040), count @11156, attachInFlight @11160, writeIndex @4. Types: 1=PANIC, 2=LOAD, 3=TRANSPORT (0=STOP flush notes + CC64, 1=PLAY, 2=SET_POSITION), 4=OPEN_GUI.
- OPEN_GUI (handleOpenGui): defer khi chưa load hoặc đang PLAY → g_pendingGui, sweep khi transportStopped && loaded (G0.1); attach job trên UiThread (g_loadMutex serialize createView); attach-in-flight báo watchdog qua SHM attachInFlight RAII (G0.3); Option B đóng editor channel khác ĐỒNG BỘ qua close_editor_now (G1.2 — bỏ PostMessage(WM_CLOSE)+5s timeout).
- LOAD (assign trên UiThread): setReloading(ch + mọi same-path channel) TRƯỚC khi vào DLL; hide + detach editor cũ (closeGUI + destroy_window_on_owner); restore unmute sau job (g_closeInFlight + g_attachPaths ngăn unmute nhầm).
- Guards còn lại: g_loadMutex (serialize mọi plugin-DLL entry: createInstance + createView), reloadingCh_[16] (engine — channelQuiet CHỈ mute khi reloading; G1.4 bỏ mute-when-editor-open), g_attachPaths + g_attachMutex (attach-in-flight registry), g_closeInFlight[16], g_guiMutex + g_guiWindows + g_hwndToCh (window registry: giữ sống + reuse + WM_DESTROY cleanup), g_pendingGui + g_pendingGuiMutex, g_cbtHook (strip IMC trên JUCE windows — chống IME recursion 0xC00000FD), destroy_window_on_owner (destroy editor child trên UiThread, không cross-thread — hết 0xc000041d).
- VstWindowProc: WM_CLOSE → post_close_gui + hide (KHÔNG DefWindowProc destroy khi view attached — tránh cross-thread destroy); WM_DESTROY → registry cleanup + post_close_gui; WM_SIZE → MoveWindow child + post_resize_view; WM_ERASEBKGND/WM_PAINT no-op (chống trắng loá).
- G0.4: save/restore preset quanh close/reload — captureState/restoreState qua MemoryStream (post_close_gui/LOAD); bridge + gui_probe link memorystream.cpp.
- Watchdog thread riêng (main.cpp ~600): parent chết → TerminateProcess ngay + check birth-time chống PID reuse (V8 bug 2).
## Learnings
- read_file lỗi với main.cpp/lib.rs/gui_probe.cpp → dùng `sed -n` qua exec_command.
- MemoryStream::write cần 3 args (buffer, size, int32* numBytesWritten); seek(0, kIBSeekSet, nullptr). memorystream.cpp không thuộc target SDK → add source trực tiếp vào mỗi target dùng Vst3Instrument.cpp (bridge ĐÃ thêm, gui_probe ĐÃ thêm).
- tasklist CSV parse: dùng `subprocess.run(['tasklist'])` full output + `line.split()[1]`.
- Probe wait_log: log cùng job xuất hiện ~đồng thời → dùng chung `since` window, không reset.
- Probe PASS có thể dương tính giả → luôn kiểm tra bridge.log/spawn.log thực.
- Deploy: taskkill 3 process → chờ 2s → copy exe → start → chờ 5s → tasklist lấy PID mới → update APP_PID trong probe.
- `sleep` không tồn tại trong sh → `python -c "import time; time.sleep(N)"`.
- `timeout` Windows khác GNU → dùng python subprocess wrapper.
- CRLF: `s.count(old)` với `\n` thật trong heredoc `<<'EOF'` KHÔNG khớp CRLF; dùng script .py file thay vì `python -c` chuỗi dài (escaping lỗi).
- 0xC0000409 trong ucrtbase = fail-fast/terminate (không phải C++ exception thường bắt được bằng catch(...) nếu là SEH/fail-fast).