Files
SonicForgeStudio/20260815-tasks-g4.md

15 KiB
Raw Permalink Blame History

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 <name> --path <vst3> [--sr] [--block] [--parent <pid>].
  • 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_<bridgePid>_<channel>") + CreateProcessA plugin_host.exe (cạnh daw_vst_bridge.exe, cùng install/) với --open --shm ... --path ... --sr ... --block ... --parent <bridgePid>; 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.