Files
SonicForgeStudio/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md
admin dc331f39d8 phase5: mặc định RENDER_ENGINE=bridge, gỡ pedalboard khỏi requirements/source/LICENSE
- config.py: SF_RENDER_ENGINE default 'bridge' (pedalboard GPL-3.0 đã gỡ)
- requirements.txt: bỏ pedalboard==0.9.19
- vst_engine.py: bỏ HAS_PEDALBOARD/check_pedalboard_safe/load_vst/import pedalboard;
  midi_events_to_messages giữ làm utility thuần (không gate pedalboard)
- render_engine.py: bỏ nhánh elif vst and HAS_PEDALBOARD + import thừa
- plugins.py: preview/midi-render chỉ qua native_bridge; guard SF_RENDER_ENGINE=bridge
- runtime.py: _vst_render_available() check bridge exe (SF_BRIDGE_PATH/install/) thay find_spec('pedalboard')
- LICENSE.md/THIRD_PARTY_LICENSES.md: bỏ cảnh báo GPL-3.0 pedalboard, đánh số lại
- tests: bỏ test golden so pedalboard + test requires_pedalboard; 114 passed 0 skip
2026-08-17 12:58:51 +07:00

7.9 KiB

PLAN: thay pedalboard (GPL-3.0) bằng native_bridge — render VSTi offline

Ngày: 2026-08-15 Trạng thái: HOÀN THÀNH — Phase 0-4 xong; Phase 5 xong (RENDER_ENGINE mặc định bridge; gỡ pedalboard khỏi requirements.txt + source (vst_engine/render_engine/plugins/runtime); midi_events_to_messages giữ làm utility thuần; LICENSE.md + THIRD_PARTY_LICENSES.md bỏ cảnh báo GPL-3.0; tests 114 pass, 0 skip). Docker image build chưa test được (máy không có docker) — recipe verify qua WSL.

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 <job.json> --out <clip.wav>

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.