# TASKS: Nhúng VST GUI — tách theo máy (Linux / Windows) Nguồn: `PLAN_MASTERBUS_VST_GUI_EMBED.md` (Phase 0-4). Mỗi task ghi rõ máy thực hiện, file đụng, lệnh, deliverable, test, phụ thuộc. Nguyên tắc tách: - **Linux** (máy dev hiện tại): viết mọi source code (C++ bridge, Python API, frontend JSX), build frontend, chạy unit test (mock bridge), commit. - **Windows** (máy chạy VST thật): build bridge C++ (MSVC/vcpkg), mọi E2E thật với plugin (Ozone...) — BitBlt/PrintWindow/SendMessage chỉ chạy Windows. - Linux KHÔNG build được bridge (CMake bỏ VST3/GUI khi không WIN32; `main_linux.cpp` chỉ render offline). Linux KHÔNG có VST GUI để test E2E thật. Ký hiệu: `[L]` = làm trên Linux · `[W]` = làm trên Windows. --- ## PHASE 0 — Bridge `--fx-gui` (HTTP server + capture + input relay) ### Task 0L1 — Viết `FxGuiServer.cpp` (hoặc mở rộng RenderFxJob.cpp nếu <300 dòng) - Máy: `[L]` · File: `native_bridge/src/FxGuiServer.cpp` (mới) + `native_bridge/src/RenderFxJob.cpp` (hook mode `--fx-gui`) - Nội dung (giữ nguyên phần attach IPlugView có sẵn `fxGuiAttachSafe`): - Mode `--fx-gui `: sau khi attach view, khởi động HTTP server mini port ngẫu nhiên → in `SF_FXGUI_PORT=` ra stdout (Python đọc). - `GET /frame` → chụp HWND plugin bằng `BitBlt` (fallback `PrintWindow` `PW_RENDERFULLCONTENT`) → trả `image/jpeg` quality ~70. - `POST /input` `{type, x, y, button, deltaY, keyCode}` → `PostMessage`/`SendMessage` theo tọa độ scale client rect (gửi `WM_MOUSEMOVE` trước click). - `POST /close` → detach view, thoát exit 0. `GET /ping` → alive. - Toàn bộ Win32 API → bọc `#ifdef _WIN32` hoặc chỉ build khi WIN32 (xem Task 0L2). - Lệnh kiểm tra: không build được trên Linux — chỉ kiểm tra bằng mắt/syntax (`g++ -fsyntax-only` không đủ do thiếu windows.h → bỏ qua, sang Task 0W1). - Deliverable: source bridge có mode `--fx-gui`. - Phụ thuộc: không. ### Task 0L2 — Cập nhật `CMakeLists.txt` cho source mới - Máy: `[L]` · File: `native_bridge/CMakeLists.txt` - Thêm source file mới vào target `daw_vst_bridge` CHỈ khi `WIN32` (giữ Linux build không vỡ — Linux hiện build `main_linux.cpp` render-only). - Lệnh kiểm tra: `cmake -S native_bridge -B /tmp/nb-linux-check && cmake --build /tmp/nb-linux-check --target daw_vst_bridge` → PASS trên Linux. - Deliverable: Linux build vẫn xanh, Windows build có file mới. - Phụ thuộc: 0L1. ### Task 0W1 — Build bridge trên Windows - Máy: `[W]` · Lệnh: `powershell -ExecutionPolicy Bypass -File build/scripts/build_native_bridge.ps1` (bootstrap vcpkg, fluidsynth, sfizz 1.2.3, vst3sdk submodule, CMake VS2019 x64, copy `daw_vst_bridge.exe` + `libfluidsynth-3.dll` → `src-tauri\binaries\`). - Điều kiện máy: VS Build Tools 2019+, CMake, git, đã pull submodule (`git submodule update --init --recursive -- native_bridge/vst3sdk`). - Deliverable: `native_bridge/build/Release/daw_vst_bridge.exe` + sidecar trong `src-tauri\binaries\`. - Test: exe tồn tại, chạy `--help` không crash. - Phụ thuộc: 0L1, 0L2 (pull code mới về Windows trước). ### Task 0W2 — Self-check bridge `--fx-gui` thủ công - Máy: `[W]` - Lệnh: 1. Tạo `job.json`: `{"path": "C:\\VST\\Ozone11.vst3", "name": "Ozone 11"}`. 2. `native_bridge\build\Release\daw_vst_bridge.exe --fx-gui job.json` → đọc port từ stdout. 3. `curl http://127.0.0.1:/ping` → alive. 4. `curl -o frame.jpg http://127.0.0.1:/frame` → JPEG, kích thước = kích thước GUI plugin. 5. `curl -X POST http://127.0.0.1:/input -H "Content-Type: application/json" -d '{"type":"mousemove","x":100,"y":100,"button":0}'` rồi `{"type":"mousedown","x":100,"y":100,"button":0}` + `mouseup` → click trúng nút trong GUI (quan sát đổi trạng thái). 6. `curl -X POST http://127.0.0.1:/close` → process thoát exit 0. - Deliverable: /frame trả ảnh đúng, /input điều khiển được, /close sạch. - Phụ thuộc: 0W1. --- ## PHASE 1 — Python API (embed_url + registry + close + CORS) ### Task 1L1 — Mở rộng `open_fx_gui` + thêm `fx-gui/close` - Máy: `[L]` · File: `app/api/v1/plugins.py` - Nội dung: - Spawn bridge với `--fx-gui` (thay/giữ `--open-fx-gui`), đọc port từ stdout (`SF_FXGUI_PORT=`) với timeout → trả `{ success, embed_url: "http://127.0.0.1:", already_running }`. - Giữ registry `_FX_GUI_PROCESSES` + `_sf_plugin_path` chống mở trùng. - Thêm `POST /fx-gui/close` `{path}` → tìm process theo `_sf_plugin_path` → terminate. - CORS: đảm bảo `Access-Control-Allow-Origin` cho origin app (port động) — kiểm tra middleware hiện có trong `plugins.py`, thêm nếu thiếu. - Lệnh kiểm tra: `python3 -m pytest tests/test_plugin_api.py -q` → PASS (thêm unit test: mock `subprocess.Popen` → assert spawn args, embed_url parse, close kill, registry prune). - Deliverable: API mới + unit test. - Phụ thuộc: 0L1 (chỉ cần biết contract stdout port; có thể làm song song 0W). ### Task 1W1 — Test API thật trên Windows - Máy: `[W]` · Điều kiện: chạy app (backend FastAPI) + bridge đã build (0W1). - Lệnh: 1. `curl -X POST http://127.0.0.1:/api/v1/plugins/fx-gui/open -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"path":"C:\\VST\\Ozone11.vst3","name":"Ozone 11"}'` → `embed_url`. 2. `curl http:///frame` → 200 JPEG. 3. `curl -X POST .../fx-gui/close -d '{"path":"C:\\VST\\Ozone11.vst3"}'` → process chết (Task Manager / `Get-Process`). - Deliverable: API chạy thật, embed_url truy cập được, close sạch. - Phụ thuộc: 1L1, 0W1. --- ## PHASE 2 — MasteringModal: embed GUI trong panel ### Task 2L1 — Code embed trong `app/static/js/app.jsx` (MasteringModal + vstRow) - Máy: `[L]` · File: `app/static/js/app.jsx` - Nội dung (state ở component panel, truyền qua props như đã làm với `showCarla`): - State per-slot `guiOpen: { url } | null` (MasteringModal). - Nút GUI → toggle: mở → gọi API `fx-gui/open` → lưu `embed_url`; đang mở → đóng. - Vùng embed: `` refresh `setInterval` 100ms; overlay `onMouseDown/Move/Up` + `onWheel` → `fetch(url + '/input', POST)` tọa độ theo bounding rect; `onKeyDown/Up` trên container (chỉ khi panel focus). - Swap graph↔GUI: ẩn vùng canvas EQ/imager bằng `hidden` khi GUI mở, hiện khi đóng. - Click tên VST trong slot khi GUI đang đóng → mở lại embed (ping trước). - Nút X đóng GUI → `POST /close`, clearInterval, trả graph. Xóa slot khi GUI mở → cũng gọi `/close`. - `/ping` fail (bridge chết) → trả graph + toast. - Lệnh kiểm tra: - `node build.mjs` (hoặc `npm run build`) → BUILD OK (syntax JSX). - Chạy backend Linux + mở app → verify UI không vỡ (GUI nhúng không test được thật vì thiếu bridge — dùng mock nếu cần: server node trả /frame JPEG fake + /input log). - Deliverable: MasteringModal nhúng GUI + swap + đóng sạch. - Phụ thuộc: 1L1 (contract API). ### Task 2W1 — E2E thật MasteringModal - Máy: `[W]` · Điều kiện: 2L1 + 0W1 + 1W1 pass. - Test tay: mở app → Mastering Panel → thêm Ozone → GUI nhúng hiện TRONG panel (thay vùng graph) → click/scroll điều khiển plugin → đóng → graph trả về → click tên VST mở lại → Xóa slot khi GUI mở → GUI đóng + slot xóa, không crash. - Deliverable: UI mastering hoàn chỉnh. - Phụ thuộc: 2L1, 1W1. --- ## PHASE 3 — FXRackModal: embed GUI trong panel ### Task 3L1 — Code embed trong FXRackModal - Máy: `[L]` · File: `app/static/js/app.jsx` (FXRackModal — vùng scope/eq canvas) - Nội dung: tương tự 2L1 nhưng trong FXRackModal; lưu ý vùng thay thế là `scopeCanvasRef`/`eqCurveRef` (đã có fallback Carla ở đây — không đụng). - Lệnh kiểm tra: `node build.mjs` → BUILD OK; backend Linux + mở app → UI không vỡ. - Deliverable: FXRackModal nhúng GUI + swap + đóng sạch. - Phụ thuộc: 2L1 (tái dùng pattern), 1L1. ### Task 3W1 — E2E thật FXRackModal - Máy: `[W]` · Điều kiện: 3L1. - Test tay: FX Rack Panel → thêm VST FX → GUI nhúng trong panel → điều khiển → đóng → graph trả về. Track cũ có module Carla → vẫn hiện fallback "đã bị gỡ" + nút gỡ module (không vỡ). - Deliverable: UI fx rack hoàn chỉnh. - Phụ thuộc: 3L1. --- ## PHASE 4 — Dọn + commit + regression ### Task 4L1 — Dọn code, docs, commit - Máy: `[L]` - Nội dung: - Toast lỗi đầy đủ (mở GUI fail, bridge chết, close fail). - `clearInterval` khi unmount/close (tránh leak). - Cập nhật `PLAN_MASTERBUS_VST_GUI_EMBED.md` trạng thái HOÀN THÀNH + ghi chú giới hạn (FPS ~10-15, Windows-only). - `git add -A && git commit` (tin nhắn: `embed native VST GUI in mastering + fx rack`). - Lệnh kiểm tra: - `node build.mjs` → BUILD OK. - `python3 -m pytest tests/ -q` → toàn bộ test hiện có PASS (1 fail pre-existing `test_find_bridge_exe_dev` do thiếu binary Linux — ghi chú, không phải regression). - Deliverable: commit sạch, docs cập nhật. - Phụ thuộc: 2L1, 3L1 (code ổn). ### Task 4W1 — Regression full trên Windows - Máy: `[W]` · Điều kiện: 4L1 (pull code mới). - Test tay toàn bộ: Mastering + FX Rack GUI nhúng; render offline (`--render-fx`) không vỡ; Carla (MasteringModal) vẫn chạy; preset upload; scan plugin. - Deliverable: bản Windows sẵn sàng dùng. - Phụ thuộc: 4L1. --- ## Thứ tự thực hiện tối ưu ``` Linux: 0L1 → 0L2 ──→ 1L1 ──→ 2L1 ──→ 3L1 ──→ 4L1 (commit) Windows: 0W1 → 0W2 ──→ 1W1 ──→ 2W1 ──→ 3W1 ──→ 4W1 (pull code sau 0L2) (pull sau 2L1) ... ``` Song song được: 1L1 (contract stdout) với 0W1/0W2; 2L1/3L1 với 0W/1W. Chặn: mọi task Windows cần pull code từ Linux trước.