- 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
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:
- Parse job → tạo instrument qua
NativeInstrumentEngine::create_instrument(VST3 / SF2 / SFZ — không phụ thuộc GUI). - Preset:
.vstpreset→ giải mã base64 state blob →loadSerializedState(state container VST3 chuẩn, cùng cơ chếserializeStateđã có). - MIDI: chuyển note events →
noteOn/noteOff/controlChange/programChangevớisampleOffset(đã hỗ trợ) — offline, không realtime. - Loop
processAudioBlockhết duration → gom buffer → ghi WAV (thêm WAV writer đơn giản, stdlib). - 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
loadSerializedStatexử 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--renderCLI + job parser + WAV writer + offline loop; unit testnative_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:
--renderra 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; đổiplugins.pyquick_render + /midi-render sang client mới (giữ response shape cũ — UI không đổi).- Feature flag
SF_RENDER_ENGINE=bridge|pedalboard(mặc địnhpedalboardtrong giai đoạn chuyển; đảo sangbridgesau phase 4).
Phase 3 — Export session (render_engine)
- Track VSTi:
vst(midi_messages)→native_renderrender 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
--renderdưới Xvfb:99(đã có). Không cần GUI plugin editor. engine.spec: gỡpedalboard/pedalboard.midi_utilskhỏ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ỡ
pedalboardkhỏirequirements.txt, bỏ nhánh chết, cập nhậtLICENSE.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
pedalboardtrong 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.mdbỏ cảnh báo GPL-3.0.