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

10 KiB

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.