# 20260815 — KẾ HOẠCH THỰC HIỆN G4 (SANDBOX PLUGIN / PROCESS CON) Nguồn: `big_tasks.md` — header **G4 — Sandbox plugin (3-5x G1+G2, 2-3 tháng)**. Trạng thái roadmap: G0 ✅, G1 ✅, G2 ✅, G3 ✅ (G3.1–G3.3). Branch: `standalone-shm-bridge`. User chủ động yêu cầu: làm dirty trước, sau đó làm luôn G4. --- ## 1. PHÂN TÍCH G4 ### 1.1 Mục tiêu VST3 chạy trong process con (`plugin_host.exe`) — crash plugin chỉ chết process con, bridge + GUI + các plugin khác sống. VST3 không có chuẩn out-of-process như AUv3 → wrapper tự làm. ### 1.2 Hiện trạng (đã verify trong code) | Thành phần | File | Ghi chú | |---|---|---| | VST3 host | `native_bridge/src/Vst3Instrument.cpp` + `include/Vst3Instrument.h` | pimpl (`Vst3HostState`), độc lập, reuse được trong process con: `loadPlugin(path, sr)` (tự activate + setupProcessing với maxBlockSize_), `openGUI(HWND)`, `processAudioBlock(L,R,n)` (zero output buffer trước process, copy ra ngoài), MIDI dispatch `noteOn/noteOff/controlChange/programChange/pitchBend`, `closeGUI()`, `reload()`. | | SHM layout | `include/SharedMemoryIPC.h` | `SharedAudioBufferIPC` 11168B: `bridgeWriteIndex`@4, `masterLeft`@8 (float[256]), `masterRight`@1032, `midiQueue[64]`@2064 (12B/evt), `midiQueueCount`@2832, `controlQueue[8]`@2836, `heartbeat`@11164 (G3.1). Helpers `shm_open/shm_create/shm_ptr/shm_close/shm_write_midi/shm_write_control` trong `SharedMemoryIPC.cpp` (trước G4.1 không có prototype trong header — đã bổ sung). | | CMake target pattern | `native_bridge/CMakeLists.txt` | `gui_probe`/`seh_channel_test`: `HAVE_VST3SDK=1` + `${VST3_SDK_TARGET} sdk_hosting sdk_common` + `module_win32.cpp` + `memorystream.cpp` + `${FLUIDSYNTH_LIBRARY} ${SFIZZ_LIBRARY} winmm`. | ### 1.3 Khoảng trống (gap) 1. VST3 chạy cùng process bridge → 1 plugin crash có thể hạ bridge (đã giảm thiểu phần nào bằng SEH G3.3 nhưng vẫn chung process). 2. Chưa có cơ chế spawn con + SHM con (MIDI in / audio out / heartbeat) + giám sát respawn. ### 1.4 Nguyên tắc thiết kế (đề xuất) - Mỗi plugin 1 process con `plugin_host.exe`, argv: `--shm --path [--sr] [--block] [--parent ]`. - Con tự tạo mapping SHM riêng (tái dùng layout `SharedAudioBufferIPC` — không phát sinh struct mới, bridge đọc được nguyên layout). - GUI attach trên main thread con (editorhost pattern: attach rồi pump); heartbeat ~10Hz thread riêng (pattern G3.1). - Con exit khi parent chết (kiểm tra `parent_alive` mỗi block) — chống orphan. - G4.2/G4.3: bridge mở mapping con thay vì con tự tạo; pool + watchdog respawn; ring buffer realtime. --- ## 2. PHÂN RÃ TASKS + ESTIMATE | ID | Task | File đích | Nội dung | Estimate | |---|---|---|---|---| | G4.1 | PoC plugin_host độc lập | `src/plugin_host_main.cpp`, `CMakeLists.txt`, `include/SharedMemoryIPC.h` | argv(shm,path,sr,block,parent) → COM STA → load VST3 → GUI attach → audio loop qua SHM (drain MIDI → dispatch → processAudioBlock → ghi masterLeft/Right → bridgeWriteIndex++) → heartbeat thread → exit khi parent chết. Done khi: Nexus chạy trong con, GUI + audio OK, kill con → chỉ con chết. | 1-2 ngày | | G4.2 | Bridge → client pool | `main.cpp` (bridge) | bridge spawn 1 con/plugin, forward MIDI, đọc audio mix, watchdog (crash → respawn, mute/restore như G3.3). | 2-3 tuần | | G4.3 | IPC audio realtime | SHM ring buffer | double-buffer SHM con, latency <20ms, 6 Nexus song song không xrun. | 2-3 tuần | ### Phụ thuộc `G4.2 → G4.1` (cần plugin_host chạy được độc lập) · `G4.3 → G4.2`. ### Thứ tự triển khai khuyến nghị G4.1 → G4.2 → G4.3. --- ## 3. KẾ HOẠCH THỰC HIỆN (G4.1 PoC — hoàn tất hôm nay) ### G4.1a — Đọc code, chốt API host - `Vst3Instrument`: `init(sr, block)` set `sampleRate_/maxBlockSize_` (gọi trước `loadPlugin` — loadPlugin dùng `maxBlockSize_` trong `setupProcessing`; default 256 = AUDIO_BLOCK_SIZE). `loadPlugin` tự activate + setProcessing + prepare. `processAudioBlock` zero output rồi process, copy stereo ra ngoài, clear eventList. - Drain MIDI theo convention bridge (main.cpp): snapshot `midiQueue[0..count-1]` (bounded 64), reset `midiQueueCount=0`, dispatch theo command 0x9/0x8/0xB/0xC/0xE. - Heartbeat: thread riêng `Sleep(100)` → `heartbeat++` (pattern G3.1). ### G4.1b — Viết `plugin_host_main.cpp` - Parse argv `--shm/--path/--sr/--block/--parent`. - `CoInitializeEx(STA)` trên main thread (GUI thuộc main thread con). - `shm_create(name)` — PoC: con sở hữu mapping (G4.2 đổi thành mở). - `init(sr,block)` → `loadPlugin(path,sr)` → tạo own top-level window → `openGUI(hwnd)` (editorhost pattern) → heartbeat thread → main loop: pump messages + drain MIDI + `processAudioBlock` + `bridgeWriteIndex++` + `parent_alive` check → `closeGUI()` + `CoUninitialize()` + `shm_close`. - ASCII-only, LF → normalize CRLF (repo convention). ### G4.1c — CMake target `plugin_host` + build - Copy pattern `gui_probe`: `src/plugin_host_main.cpp + src/SharedMemoryIPC.cpp + src/Vst3Instrument.cpp` + SDK hosting libs (không cần NativeInstrumentEngine.cpp). - Bổ sung prototype `shm_open/shm_create/shm_ptr/shm_close` vào `SharedMemoryIPC.h` (trước đây chỉ có trong .cpp, plugin_host cần khai báo). - Build: `cmake --build build --config Release --target plugin_host -- /m` PASS → `build/Release/plugin_host.exe`. ### G4.1d — Probe với Nexus (probe_g41.py) - Spawn con với `C:\Program Files\Common Files\VST3\Nexus.vst3`, SHM `SonicForge_PluginHost_Probe`. - Verify: heartbeat tăng (hb=1 trong ~1s); GUI window xuất hiện (4 hwnd thuộc pid con, gồm PluginHostClass + editor Nexus); ghi note-on C4 → audio peak 0.38274 (nonzero); terminate con → app 15928 + bridge 20996 vẫn sống (isolation). ### G4.1e — Commit + journal + push --- ## 4. RỦI RO | Rủi ro | Ảnh hưởng | Giảm thiểu | |---|---|---| | Con chết khi đang render → dữ liệu audio mất 1 block | Click/pop | G4.3 ring buffer + G4.2 mute/restore; PoC chấp nhận. | | Parent chết → con orphan | Process treo | `--parent` + check mỗi block (đã làm). | | Plugin GUI yêu cầu thread/COM riêng | Hang/blank view | Attach trên main thread con (editorhost pattern đã verify qua gui_probe). | | SHM con đổi layout sau này | Mất tương thích | Tái dùng `SharedAudioBufferIPC` — layout ổn định append-only. | --- ## 5. BÁO CÁO KẾT QUẢ (đã thực hiện) | ID | Cài đặt | Kiểm tra | Bugs bắt được | Fix | Trạng thái | |---|---|---|---|---|---| | G4.1a | Chốt API host từ Vst3Instrument + convention drain MIDI/heartbeat (main.cpp). | — | — | — | ✅ | | G4.1b | `plugin_host_main.cpp`: argv parse, COM STA, shm_create, loadPlugin, openGUI own window, audio loop + heartbeat + parent check. | Build PASS (sau fix prototype SHM). | `ShmHandle/shm_create/shm_ptr/shm_close` không có prototype trong header → C2065. | Thêm prototype vào `SharedMemoryIPC.h`. | ✅ | | G4.1c | CMake target `plugin_host` (pattern gui_probe, không engine). | Build PASS → `build/Release/plugin_host.exe`. | Patch block bị chèn vào giữa `if(WIN32)` của gui_probe + `endif()` thừa. | Script sửa vị trí + xóa endif. | ✅ | | G4.1d | Probe: spawn + Nexus + heartbeat + GUI + audio + isolation. | PASS: hb=1 (1s); 4 hwnd; peak 0.38274; kill con → app 15928 + bridge 20996 sống. | Probe python đọc SHM AV — `MapViewOfFile` restype mặc định c_int cắt con trỏ 64-bit. | Set `restype=c_void_p`. | ✅ | | G4.1e | Commit + journal + push. | — | — | — | ✅ | ### Nhật ký thực hiện ### G4.1 PoC — Hoàn tất (2026-08-15) - `native_bridge/src/plugin_host_main.cpp` (mới): process con sandbox — argv `--shm/--path/--sr/--block/--parent`; COM STA; `shm_create` (PoC con sở hữu mapping, G4.2 đổi thành `shm_open`); `init` → `loadPlugin` → own window + `openGUI` (editorhost pattern) → heartbeat thread `Sleep(100)` (G3.1 pattern) → main loop pump + drain MIDI (snapshot 64, reset count, dispatch 0x9/0x8/0xB/0xC/0xE) + `processAudioBlock(masterLeft, masterRight, block)` + `bridgeWriteIndex++` + `parent_alive` check mỗi block (chống orphan) → `closeGUI` + `CoUninitialize` + `shm_close`. - `include/SharedMemoryIPC.h`: bổ sung prototype helpers `shm_open/shm_create/shm_ptr/shm_close` (chỉ khai báo, không đổi layout — struct giữ nguyên 11168B). - `CMakeLists.txt`: target `plugin_host` (src/plugin_host_main.cpp + SharedMemoryIPC.cpp + Vst3Instrument.cpp + SDK hosting libs). Build Release PASS. - Verify (probe_g41.py): Nexus (`C:\Program Files\Common Files\VST3\Nexus.vst3`) load trong con OK; heartbeat tăng hb=1 sau ~1s; GUI window 4 hwnd thuộc pid con; note-on C4 (vel 100) → peak audio 0.38274 (nonzero); terminate con → `sonicforge-daw.exe` 15928 + `daw_vst_bridge.exe` 20996 vẫn sống — isolation đúng yêu cầu G4.1. - Lưu ý test-env: probe python phải set `OpenFileMappingA.restype = MapViewOfFile.restype = c_void_p` (Win64 con trỏ 64-bit, mặc định c_int cắt → AV khi đọc view). - G4.2 (bridge → pool) chưa bắt đầu — bước tiếp theo sau PoC PASS. ## G4.2 — Bridge → client process pool (sandbox per channel) ✅ - `include/SandboxVst3Host.h` + `src/SandboxVst3Host.cpp` (mới): `INativeInstrument` wrapper quanh plugin_host.exe. `loadPlugin(path, sr, channel)` → `shm_create("SonicForge_PluginHost__")` + `CreateProcessA` plugin_host.exe (cạnh daw_vst_bridge.exe, cùng install/) với `--open --shm ... --path ... --sr ... --block ... --parent `; chờ child heartbeat ≤60s. MIDI noteOn/noteOff/controlChange/programChange/pitchBend → `shm_write_midi` (velocity float→0..127). `processAudioBlock` copy snapshot masterLeft/Right (G4.3 thay bằng ring buffer). `watchdogLoop` (thread riêng): Sleep 500ms, child chết → `alive_=false` + log + respawn sau 1s; 3 fail liên tiếp → mute vĩnh viễn đến khi reload. Destructor: stop_ + join + TerminateProcess + shm_close. - `src/plugin_host_main.cpp`: thêm flag `--open` → `shm_open` thay vì `shm_create` (bridge tạo mapping, con mở). - `include/SharedMemoryIPC.h`: thêm prototype `shm_write_midi` (default args data2/data3 chỉ ở header); bỏ default args trùng trong `SharedMemoryIPC.cpp` (C2572). - `src/NativeInstrumentEngine.cpp`: `create_instrument` VST3 case — `SF_SANDBOX_VST3` env set → `SandboxVst3Host`, ngược lại `Vst3Instrument` (regression an toàn); `assign` dùng dynamic_cast: Vst3 → loadPlugin(path,sr)+setChannel, Sandbox → loadPlugin(path,sr,channel). assign() chạy trên uiWorker (ChannelWorker) — spawn blocking OK. - `CMakeLists.txt`: thêm `src/SandboxVst3Host.cpp` vào target `daw_vst_bridge`. - Build Release PASS (`daw_vst_bridge.exe` + `plugin_host.exe`). - Deploy: kill app+bridge → copy exe mới vào install/ (dùng shutil, `copy` shell fail im lặng — exe cũ 3900416B không có sandbox, phải verify chuỗi `SF_SANDBOX_VST3` trong binary) → start lại app. - Verify (probe_g42.py, bridge standalone env `SF_SANDBOX_VST3=1` + state file rỗng): LOAD Nexus ch0 → plugin_host pid spawn; child heartbeat OK; GUI 4 hwnd thuộc pid con; note-on C4 → peak 0.38276; **kill child** → bridge sống, heartbeat tiếp tục, respawn pid mới sau ~1.5s, GUI mới 4 hwnd, audio lại 0.38275 — PASS. - Lưu ý probe: bridge chỉ OPEN mapping `SonicForge_DAW_IPC` (Rust shell tạo) → probe phải `CreateFileMappingA` trước; state file cũ (7 instruments) làm guard G2.4 skip LOAD → dùng `SF_STATE_FILE` trỏ file rỗng. - Regression: G1.5 stress 3 iter PASS (bridge alive, writeIndex delta 286); G2.5 stress 2 iter PASS (kill bridge → watchdog respawn → restore preset exact + LOAD-skip OK) — không env SF_SANDBOX_VST3 → in-process như cũ, không đụng. - Commit: `G4.2: bridge-side plugin process pool — SandboxVst3Host spawns/respawns plugin_host.exe per channel, MIDI forward + audio snapshot + watchdog mute/respawn, crash isolation verified`. - Commit: `G4.1: sandbox plugin host (plugin_host.exe) PoC — Nexus runs in child process, GUI + audio via SHM, crash isolation verified`. ## G4.3 - Double-buffer sandbox SHM, latency <20ms, multi-instance no-xrun - `include/SharedMemoryIPC.h`: struct `SandboxHostIPC` = base `SharedAudioBufferIPC` (11168B giu nguyen) + `writeSlot` @11168 + `audioLeft[2][256]` @11172 + `audioRight[2][256]` @13220; `shm_open/shm_create` nhan size param (mac dinh `sizeof(SharedAudioBufferIPC)`), them `shm_sandbox_ptr`. Header rewrite toan bo CRLF (patch anchor sai vi tri lan dau lam hong header - da fix bang rewrite_g43_header.py, build PASS sau do). - `src/plugin_host_main.cpp`: `shm_sandbox_ptr`; render vao slot `1 - (writeSlot&1)` roi publish `writeSlot = slot` + `bridgeWriteIndex++` (double buffer: child cham khong tear, bridge doc block cu nhat da publish). `timeBeginPeriod(1)` de `Sleep(1)` that su ~1ms (khong phai 15.6ms tick mac dinh). - `include/SandboxVst3Host.h` + `src/SandboxVst3Host.cpp`: `ipc_` kieu `SandboxHostIPC*`; `shm_create` size `sizeof(SandboxHostIPC)`; `processAudioBlock` doc `audioLeft[writeSlot&1]`. Watchdog log kem exit code (GetExitCodeProcess) de debug. - Fix quan trong: plugin_host phai `shm_open(shmName, sizeof(SandboxHostIPC))` - mac dinh 11168B chi map duoc base, doc audioLeft vuot view -> AV crash sau LOAD (children chet lien tuc). Bug thu 2: probe `kids = [x for x in pl if x not in kids]` thay the kids bang phan tu moi - mat phan tu cu, bao FAIL "expected 2 children" du ca 2 con song. Fix union `dict.fromkeys(kids+pl)`. - Build Release PASS (daw_vst_bridge.exe + plugin_host.exe). Deploy shutil.copy2 (kill proc giu file truoc). - Verify (probe_g43.py, bridge standalone env `SF_SANDBOX_VST3=1` + state file rong): LOAD Nexus ch0+ch1 -> 2 plugin_host [con song song]; heartbeat ca 2 OK; note-on ch0 C4 + ch1 E4 -> main audio peak 0.71835 (tong 2 channel); writeSlot flip 64 blocks/s = latency 15.6ms < 20ms (khong yeu cau 172/s thuc - Nexus render ~10ms/block that, CPU-bound); kill child0 -> child1 van phat 0.36972, bridge song + heartbeat + child0 respawn - PASS. - Regression: G1.5 stress 3 iter PASS (writeIndex delta 286); G2.5 stress 2 iter PASS (kill bridge -> respawn -> preset exact + LOAD-skip OK). App deploy (install/sonicforge-daw.exe) dang chay, bridge pid moi. - FIX idle-skip (G4.3.1): plugin_host_main.cpp them `activeNotes` dem (note-on ++, note-off --, clamp >= 0) + `idle` flag. Khi khong con note dang giu va peak block < 0.0005f -> idle = true, loop Sleep(15) bo qua render (van PeekMessage + parent-alive check). Bo `Sleep(1)` hot loop -> `Sleep(0)` yield. KET QUA: writeSlot flip 0 blocks/s khi im lang (truoc day render lien tuc ~100/s, CPU cao); khi dang giu note flip 101.6 blocks/s = latency 9.8ms (truoc 64/s = 15.6ms) - ca 2 loi G4.3 xac nhan da het. - Verify lai: probe_g43.py PASS (2 children song song, peak 0.67273, kill child0 -> child1 0.36972, isolation OK); dbg_rate.py idle 0 flips/3s; G1.5 3 iter PASS (delta 287); G2.5 2 iter PASS. Deploy install/plugin_host.exe moi. App dang chay pid 21396.