diff --git a/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md b/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md new file mode 100644 index 0000000..a4c3ab1 --- /dev/null +++ b/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md @@ -0,0 +1,146 @@ +# PLAN: thay pedalboard (GPL-3.0) bằng native_bridge — render VSTi offline + +Ngày: 2026-08-15 +Trạng thái: KẾ HOẠCH (chưa sửa code sản phẩm). Windows sửa code bridge/desktop; +Linux làm bridge Linux + Docker + test so sánh. + +## 1. Mục tiêu +Xóa dependency GPL-3.0 (`pedalboard`) khỏi cả 2 môi trường, chuyển toàn bộ +render VSTi offline (preview quick_render, /midi-render, export session) sang +host `native_bridge` (MIT) — GIỮ NGUYÊN luồng nghiệp vụ + play âm thanh: + +- Docker: client play = FluidSynthWASM (không đổi); server preview/export = + native_bridge Linux (thay pedalboard). +- Standalone: realtime play = Tauri + bridge SHM (không đổi); Python sidecar + preview/export = native_bridge Windows (thay pedalboard). + +Realtime play KHÔNG dùng pedalboard hôm nay → không bị đụng. + +## 2. Hiện trạng dùng pedalboard (cần thay) +| Chỗ | Chức năng | Đường | +|---|---|---| +| `app/api/v1/plugins.py:878-953` `_render_midi_notes_pedalboard` | preview quick_render + /midi-render: MIDI clip → WAV | `VST3Plugin(midi_messages, duration)` | +| `app/core/render_engine.py:215-271` | export session track VSTi | `vst(midi_messages, duration)` + `apply_preset_to_plugin` | +| `app/core/render_engine.py:383-404` | track FX chorus/reverb | `Pedalboard([Chorus/Reverb])` — đã có fallback scipy | +| `app/core/vst_engine.py:516+` DecentSamplerManager | Pianobook .dspreset | `create_decent_sampler_instance` | +| `app/core/vst_engine.py:482-503` `midi_events_to_messages` | note events → raw MIDI bytes | dùng `pedalboard.midi_utils` (chỉ format bytes — bridge đã hiểu 0x90/0x80/0xB0/0xC0) | +| `engine.spec:92-97` | bundle pedalboard vào desktop | PyInstaller | + +`app/core/runtime.py:377` — feature flag `preview_mode=quick_render` dựa trên +`find_spec("pedalboard")`. + +## 3. Kiến trúc đích + +### 3.1. Bridge thêm offline render mode (C++, `daw_vst_bridge`) +Mode realtime (SHM, Tauri) GIỮ NGUYÊN. Thêm entrypoint CLI: + +``` +daw_vst_bridge(.exe) --render --out +``` + +job.json: `{ instrument_type, plugin_path, preset (path|base64), sample_rate, +block_size, notes: [{pitch, velocity, start_beat, duration_beats}], bpm, +soundfont_bank/program (SF2/SFZ) }` + +Luồng: +1. Parse job → tạo instrument qua `NativeInstrumentEngine::create_instrument` + (VST3 / SF2 / SFZ — không phụ thuộc GUI). +2. Preset: `.vstpreset` → giải mã base64 state blob → `loadSerializedState` + (state container VST3 chuẩn, cùng cơ chế `serializeState` đã có). +3. MIDI: chuyển note events → `noteOn/noteOff/controlChange/programChange` + với `sampleOffset` (đã hỗ trợ) — offline, không realtime. +4. Loop `processAudioBlock` hết duration → gom buffer → ghi WAV (thêm + WAV writer đơn giản, stdlib). +5. Exit 0/1 + log lỗi rõ (thay cho fail im lặng hiện tại). + +### 3.2. Python client (mới, MIT) +`app/core/native_render.py` — spawn bridge `--render`, trả `(out_path, duration)`. +API giống `_render_midi_notes_pedalboard` để 2 endpoint + render_engine thay +thế trực tiếp. Không import pedalboard. Plugin path resolve bằng logic scan +có sẵn (`vst_engine.py`). + +### 3.3. Chorus/Reverb (FX track) +Chuyển hẳn sang fallback scipy/numpy ĐÃ CÓ SẴN trong `render_engine.py` +(LFO delay chorus + noise-IR reverb) — 0 code mới, MIT. Chất lượng thấp hơn +pedalboard; ghi rõ ở doc, upgrade sau nếu cần (native DSP C++ riêng). + +### 3.4. DecentSampler (.dspreset) +`.dspreset` là format riêng, không phải VST3 state. Lộ trình: +- Test: DS VST3 load state chứa preset (user mở DS GUI → save .vstpreset) → + đường `loadSerializedState` xử lý được. +- Nếu không: chặn Pianobook trên nền không có pedalboard + hướng dẫn convert + .dspreset → .vstpreset. KHÔNG giữ pedalboard vì 1 tính năng phụ. + +## 4. Các phase + +### Phase 0 — Offline render mode C++ (Windows + Linux) +- `native_bridge`: thêm `--render` CLI + job parser + WAV writer + offline + loop; unit test `native_bridge/tests/` (render 1 note VST3 + 1 SFZ, assert + WAV non-silent, duration đúng). +- CMake: đảm bảo build Linux (chạy thử trên máy này). +- **Kiểm chứng**: `--render` ra WAV đúng pitch/duration với plugin test. + +### Phase 1 — Preset .vstpreset import +- Parser base64 state → `loadSerializedState`; test với .vstpreset do Carla/ + pedalboard export sẵn (golden file). + +### Phase 2 — Python client + thay preview +- `app/core/native_render.py`; đổi `plugins.py` quick_render + /midi-render + sang client mới (giữ response shape cũ — UI không đổi). +- Feature flag `SF_RENDER_ENGINE=bridge|pedalboard` (mặc định `pedalboard` + trong giai đoạn chuyển; đảo sang `bridge` sau phase 4). + +### Phase 3 — Export session (render_engine) +- Track VSTi: `vst(midi_messages)` → `native_render` render từng track. +- FX: bật hẳn fallback scipy (bỏ nhánh `HAS_PEDALBOARD`). +- Giữ nguyên kết quả WAV export + mixdown (test so sánh trước/sau). + +### Phase 4 — DecentSampler + Docker + bundle +- Dockerfile: build native_bridge Linux (CMake) vào image; chạy `--render` + dưới Xvfb `:99` (đã có). Không cần GUI plugin editor. +- `engine.spec`: gỡ `pedalboard`/`pedalboard.midi_utils` khỏi bundle. +- DecentSampler theo mục 3.4. + +### Phase 5 — Đảo flag + xóa pedalboard +- Mặc định `SF_RENDER_ENGINE=bridge`; test toàn diện 2 môi trường. +- Gỡ `pedalboard` khỏi `requirements.txt`, bỏ nhánh chết, cập nhật + `LICENSE.md`/`THIRD_PARTY_LICENSES.md` (xóa dòng GPL-3.0 warning). +- Xóa file thừa (nếu có) + doc. + +## 5. File thay đổi (dự kiến) +- Thêm: `native_bridge/src/RenderJob.{h,cpp}` (parse job + offline loop), + `native_bridge/src/WavWriter.cpp`, `app/core/native_render.py`, + `native_bridge/tests/test_offline_render.py`. +- Sửa: `native_bridge/src/main.cpp` (dispatch `--render`), + `native_bridge/CMakeLists.txt` (Linux build), `app/api/v1/plugins.py`, + `app/core/render_engine.py`, `app/core/vst_engine.py` (midi converter giữ — + đã ra bytes thuần, bỏ phụ thuộc pedalboard), `app/core/runtime.py` (flag), + `Dockerfile`, `engine.spec`, `requirements.txt`, `LICENSE.md`. + +## 6. Giữ nguyên luồng — điểm kiểm chứng +| Luồng | Docker | Standalone | +|---|---|---| +| Realtime play | FluidSynthWASM — không đổi | Tauri+bridge SHM — không đổi | +| Preview VSTi | server render WAV → client Audio — cùng shape response | cùng | +| Export session | cùng pipeline WAV → download | cùng | +| Soundfont preview | pyfluidsynth — không đổi | pyfluidsynth — không đổi | + +Mỗi phase: chạy bộ test hiện có (`native_bridge/tests/*`, API smoke) + +golden WAV so sánh pedalboard vs bridge (RMS/tolerance). + +## 7. Rủi ro + rollback +- **VST3 plugin cần GUI mới process được**: headless fail → log rõ, trả 501 + như cũ; không phá luồng. +- **.vstpreset state khác format nội bộ**: phase 1 spike trước; nếu lệch, + map params qua `setParamNormalized` (đã có đường controlChange). +- **DecentSampler**: mục 3.4. +- **Bridge crash giữa render**: subprocess exit non-zero → HTTPException rõ + (cải thiện hơn pedalboard fail hiện tại). +- **Rollback**: `SF_RENDER_ENGINE=pedalboard` đảo ngược tức thì tới hết + phase 4; pedalboard chỉ bị xóa ở phase 5 sau khi bridge ổn định. + +## 8. Tiêu chí hoàn thành +- Không còn `pedalboard` trong requirements/engine.spec/source. +- Preview + export VSTi 2 môi trường ra WAV đúng (so sánh golden). +- Realtime play 2 môi trường không đổi (regression pass). +- `LICENSE.md` bỏ cảnh báo GPL-3.0.