Files
SonicForgeStudio/TASKS_LINUX_WINDOWS.md
3dtours c382afe2a6 feat: embed native VST GUI in mastering + fx rack panels (bridge --fx-gui frame capture + input relay)
Linux parts (0L1-4L1 per TASKS_LINUX_WINDOWS.md):
- native_bridge: FxGuiServer.cpp HTTP server (Winsock) — /frame BitBlt +
  PrintWindow fallback -> JPEG (libjpeg-turbo q70), /input PostMessage relay
  (mouse/wheel/key, coords scaled), /close, /ping; RenderFxJob fxGuiSetup +
  run_fx_gui_server (offscreen); main.cpp --fx-gui dispatch; vcpkg +
  CMakeLists WIN32-only + JPEG.
- api: open_fx_gui embed flag spawn --fx-gui, parse SF_FXGUI_PORT -> embed_url,
  fx-gui/close endpoint; 4 unit tests.
- ui: VstGuiEmbed component (frame refresh 100ms, ping 2s, input relay,
  scale coords) shared by MasteringModal + FXRackModal; swap graph<->GUI,
  cleanup on unmount, dead-bridge toast fallback.
- docs: TASKS_LINUX_WINDOWS.md, WALKTHROUGH_WINDOWS.md, PLAN status.
Windows tasks 0W1-4W1 remain (build + E2E real VST).
2026-08-17 22:37:55 +07:00

184 lines
10 KiB
Markdown

# 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 <job.json>`: sau khi attach view, khởi động HTTP server mini
port ngẫu nhiên → in `SF_FXGUI_PORT=<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:<port>/ping` → alive.
4. `curl -o frame.jpg http://127.0.0.1:<port>/frame` → JPEG, kích thước = kích thước GUI plugin.
5. `curl -X POST http://127.0.0.1:<port>/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:<port>/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:<port>", 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:<app>/api/v1/plugins/fx-gui/open -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"path":"C:\\VST\\Ozone11.vst3","name":"Ozone 11"}'` → `embed_url`.
2. `curl http://<embed_url>/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: `<img src={url + '/frame'}>` 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.