Files
SonicForgeStudio/PLAN_MASTERBUS_FX_RACK_VST.md
T

9.3 KiB

PLAN: Masterbus + FX Rack hỗ trợ chèn VST FX (Ozone, Scaler) — xử lý âm thanh

Ngày: 2026-08-15 Trạng thái: ĐỀ XUẤT — chưa code. Kế thừa nền: PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md HOÀN THÀNH (Phase 0-5, commit dc331f3) — pedalboard GPL-3.0 đã gỡ, RENDER_ENGINE mặc định "bridge", native_bridge (MIT) render VSTi offline qua --render <job.json> --out <wav>; tests 114 pass / 0 skip.

1. Mục tiêu

Chèn VST FX thật (Ozone mastering chain, Scaler, …) vào 2 tầng xử lý âm thanh:

  • Masterbus: chain FX áp lên toàn bộ mix (mastering).
  • FX Rack per-track: chain FX áp lên từng track (insert).

Offline export chạy qua native_bridge (giữ nguyên luồng RENDER_ENGINE hiện tại). Realtime trong browser KHÔNG chạy được VST → giữ WebAudio builtin, VST FX dùng quick_render preview (âm thật = âm export, như cơ chế preset đang có).

2. Hiện trạng (cần mở rộng)

Tầng Hiện tại Hạn chế
Realtime browser (app/static/js/app.jsx) FX chain WebAudio per-track (chorus/reverb), mastering chain Ozone-style (route dry/mastering, createMasteringRoute) WebAudio node — KHÔNG chạy được binary .vst3; data model chỉ có fxChain + mastering toggle, chưa có slot plugin
Offline export (app/core/render_engine.py) Track FX: `fx_type=="chorus" "reverb" fallback scipy/numpy; master bus = sum track buffers + normalize peak (max_peak`)
Bridge (native_bridge/src/*) Chỉ instrument host: MIDI → audio (NativeInstrumentEngine, VST3/SF2/SFZ) Chưa có audio-effect processing path; RenderJob.cpp chỉ nhận notes, không nhận input WAV
Scan plugin (app/core/vst_engine.py) Quét .vst3/.so/.dll, list vst_instruments Không phân loại instrument vs effect

Tham chiếu doc cũ: md/45_MASTERING_MODULE.md (spec WebAudio Ozone) — giữ nguyên, chỉ bổ sung VST path. app.jsx tham chiếu mastering_expand.md + unified_fx_rack_panel.md — 2 file này không tồn tại → gỡ tham chiếu hoặc viết mới trong Phase 3.

3. Kiến trúc đích

3.1. Bridge: FX render mode (C++, MIT)

Thêm entrypoint CLI song song mode instrument:

daw_vst_bridge(.exe) --render-fx <job.json> --in <input.wav> --out <output.wav>

job.json (mở rộng format hiện có):

{
  "sample_rate": 44100,
  "block_size": 512,
  "fx_chain": [
    { "type": "vst3", "path": "C:/.../Ozone 11.vst3", "preset_b64": "...", "bypass": false },
    { "type": "builtin", "id": "normalize", "params": { "peak": 0.95 } }
  ]
}

Luồng:

  1. Đọc input WAV (16/24/32-bit, mono/stereo → convert stereo float) — thêm WAV reader (WAV writer đã có).
  2. Với mỗi slot trong fx_chain, process toàn bộ buffer qua NativeFxEngine::processAudio(audio_in, audio_out) — audio-input, KHÔNG MIDI. VST3 effect: load plugin_path, loadSerializedState từ preset_b64 (cùng cơ chế preset đã có), processBlock từng block; bypass=true → copy thẳng.
  3. builtin = fallback không cần VST (normalize, gain) — dùng chung cho Docker headless.
  4. Ghi WAV stereo 16-bit. Exit code như mode instrument (0 ok, 1 job error, 2 load fail, 3 crash — SEH guard như SandboxVst3Host).

Lưu ý kỹ thuật:

  • Phân biệt class category VST3: instrument (kInstrument) vs effect (kAudioEffect) — dùng getClassInfo/category string để host đúng interface (audio-in/audio-out thay vì MIDI-in).
  • VST2 FX (type:"vst2") thêm sau nếu cần; V1 chỉ VST3 + builtin.
  • Linux bridge chỉ SF2/SFZ (VST3 hosting Windows-only) → FX VST chỉ Windows/desktop; Docker fallback builtin. Capabilities API báo đúng (features.vst_fx_render).

3.2. Python client (app/core/native_render.py — mở rộng)

render_fx_chain(input_wav: str, fx_chain: list[dict], sample_rate: int) -> str

Spawn bridge --render-fx, trả path WAV. Chịu trách nhiệm: resolve plugin path, base64 encode preset file, timeout, rc → exception. Không import pedalboard.

3.3. Masterbus (offline)

Session thêm key master:

"master": { "fx_chain": [...], "volume_db": 0, "mute": false, "bypass": false }

render_project():

  1. master_buffer = render_session_container(...) (giữ nguyên).
  2. Nếu master.bypass false → master_buffer = render_fx_chain(wav, master.fx_chain).
  3. Áp volume_db; giữ normalize-peak làm builtin slot cuối (hoặc bỏ khi chain có master limiter — config theo chain, mặc định giữ).

Session cũ không có master → skip, không vỡ (backward compatible).

3.4. FX Rack per-track (offline)

Track thêm key fx_chain (mảng insert slots, thay thế fx_type string):

"fx_chain": [
  { "plugin_id": "ozone", "path": "...", "preset_b64": "...", "bypass": false }
]

Trong render_session_container, sau khi mix audio track (trước volume/pan): nếu fx_chain có VST slot → render track buffer qua bridge trước khi cộng vào session. fx_type legacy ("chorus"/"reverb") giữ nguyên fallback scipy — cùng lúc chỉ dùng một; ưu tiên fx_chain nếu có.

3.5. Scan + phân loại FX (app/core/vst_engine.py)

  • Thêm list_fx(): quét cùng thư mục, đọc category VST3 để tách effect khỏi instrument (instrument = class kInstrument; effect = kAudioEffect/kFx).
  • Không có SDK VST3 ở Python? → dùng chính bridge: thêm mode --scan <dir> (C++ dùng VST3 getClassInfo đã có) trả JSON [{path, is_fx, name}]. Bridge scan là nguồn chân lý, Python chỉ cache.

3.6. API + UI (V1 tối thiểu)

  • POST /api/v1/plugins/fx-render { input_wav_url, fx_chain } → WAV (dùng cho preview + export thử nghiệm; pattern giống /api/v1/plugins/midi-render).
  • GET /api/v1/plugins/fx → danh sách FX (từ scan bridge).
  • UI: FX Rack panel per-track + masterbus — danh sách slot, chọn plugin, bypass toggle, order (up/down), nút "Mở trong Carla / Chỉnh preset" → save .vstpreset (cơ chế CARLA_BRIDGE có sẵn, md/52_CARLA_BRIDGE.md).
  • Preset-based V1: state plugin = .vstpreset file, chưa có param automation (khớp giới hạn preset bridge hiện tại). Dry/wet: bỏ qua V1 (Ozone/Scaler xử lý trong chain), thêm sau nếu cần.

3.7. Realtime

  • Browser không chạy VST: giữ nguyên WebAudio builtin FX + mastering chain.
  • Track/masterbus có VST slot → khi play, nếu slot không bypass: dùng quick_render preview (đã có preview_mode: "quick_render" — render clip ngắn qua bridge → WAV → phát), ghi chú "preview = âm export". Nếu chỉ builtin → WebAudio trực tiếp.
  • Chưa realtime VST streaming (Tauri SHM) trong phạm vi plan này — ghi là future work.

3.8. Scaler — OPEN QUESTION (quyết định trong Phase 1)

Scaler 2 là MIDI/chord tool, không phải audio-effect processor:

  • Chạy như VSTi insert (đường VSTi hiện có): nhận MIDI → output audio cho chord — không cần FX path mới, dùng render_session_container instrument nhánh có sẵn.
  • Hoặc chỉnh chord offline rồi render note events — cần UI riêng (ngoài plan). → Đề xuất V1: Scaler = VSTi, không vào FX Rack; FX Rack/masterbus dành cho audio-effect VST3 (Ozone). Nếu user muốn Scaler trong FX Rack → đưa vào Phase 4 như audio-FX bọc ngoài (unlikely, cần confirm).

4. Phases

Phase Nội dung Deliverable
0 Bridge --render-fx: WAV reader, NativeFxEngine VST3 effect (audio-in/out), builtin normalize/gain, SEH guard CLI render FX demo: WAV in → WAV out, test với Ozone
1 Bridge --scan: phân loại instrument/effect; Python native_render.render_fx_chain + vst_engine.list_fx /api/v1/plugins/fx + /api/v1/plugins/fx-render
2 Masterbus: session key master, render_project chạy chain, backward compatible Export session có mastering VST
3 FX Rack per-track: track fx_chain, render_engine integrate; UI panel (slot/bypass/order/preset upload) Track VST FX + UI
4 Realtime preview quick_render cho slot VST; gỡ/làm rõ tham chiếu doc thiếu (mastering_expand.md) Preview thật; doc sạch

Mỗi phase kết thúc = commit + test pass. Phase 0-1 có thể merge (cùng chạm bridge).

5. Test

  • Bridge: self-check WAV in→out (identity khi bypass; gain builtin đúng dB); --scan trên folder có 1 instrument + 1 effect giả → đúng nhãn.
  • Python: unit test render_fx_chain với builtin chain (không cần VST) — input wav → output wav tồn tại, đúng sr; master key vắng → output không đổi so với trước (regression).
  • E2E (thủ công, máy có Ozone): export session có master Ozone chain → nghe khác rõ so với không chain, không clip.
  • Chạy full test suite hiện có — không phá 114 tests.

6. License

  • Ozone (iZotope/Native Instruments) + Scaler (PluginBoutique/Scale Software): plugin thương mại — KHÔNG nhúng, chỉ host bằng trình load của native_bridge (MIT). Không bundle binary plugin vào repo.
  • native_bridge giữ MIT; code mới (WAV reader, NativeFxEngine) cùng license.
  • Không quay lại pedalboard (GPL-3.0) dưới mọi hình thức.