Files
SonicForgeStudio/DESIGN_DOCKER_VSTI_PLAY.md
T

133 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Thiết kế kiến trúc: Docker play âm VSTi ở client (giữ nguyên luồng cũ)
Ngày: 2026-08-15
Trạng thái: THIẾT KẾ (chưa sửa code sản phẩm). Linux làm thiết kế + spike khả
thi; code theo thoả thuận triển khai.
## 1. Mục tiêu
Docker (server headless + browser UI) có thể play âm VSTi ở client,
DỰA TRÊN kiến trúc cũ của docker: client tự phát âm (FluidSynthWASM / SonicSF),
không streaming realtime từ server. VSTi không chạy được trong browser →
chuyển thành asset SoundFont (SF2/SF3) render sẵn từ VSTi trên server, client
dùng nguyên luồng cũ.
## 2. Kiến trúc docker hiện tại (giữ nguyên)
- Server: FastAPI (`app/main.py`) + Celery worker; mount `/opt/daw_engine/vst3`,
`/opt/daw_engine/soundfonts`, `/opt/daw_engine/samples/pianobook`.
- `runtime.detect()`: docker → `environment=docker`; client dùng
FluidSynthWASM (`window.SonicSF`) cho mọi track soundfont.
- Play track: `scheduleMidiNoteDispatch` → (bridge không active) →
`window.SonicSF.playNote(...)` — phát SF2 đã load vào WASM.
- Preview VSTi hiện có: `preview_mode=quick_render` nếu `pedalboard` có —
server render clip ngắn ra WAV → client play `Audio`. KHÔNG phải realtime,
KHÔNG phải theo timeline.
- SoundFontConverter: SF2→SF3 (ogg) / SF3→SF2. LƯU Ý: client WASM KHÔNG decode
ogg → client cần SF2; SF3 chỉ dùng server-side (fluidsynth native).
## 3. Vấn đề
Track gán VSTi trong docker: `isVstTrackEngine=true`, `routeCarla=false`
(không có Carla), `nativeSf=false` → rơi vào `SonicSF.playNote` với preset
soundfont không tồn tại → câm (hoặc âm mặc định sai).
## 4. Thiết kế đề xuất: VSTi → SF2/SF3 asset → client luồng cũ
### Nguyên lý
Biến 1 nhạc cụ VSTi (VST3 + preset) thành 1 bộ samples render sẵn (per-note,
velocity layer), đóng gói SF2 (client) + SF3 (server native), đăng ký như
soundfont bình thường → client chọn instrument như soundfont → mọi luồng play
cũ (scheduleMidiNoteDispatch → SonicSF.playNote) hoạt động không đổi.
### Pipeline (server, mới)
1. **Registry instrument** (storage/instruments.json): `{instrument_id, vst_path,
preset_id|preset_bytes(.vstpreset), bank, program, render_params}`.
Preset nguồn: upload .vstpreset (VST3 state — pedalboard đọc được) hoặc
dùng state mặc định của plugin. (.dspreset DecentSampler: spike thất bại —
xem mục 6.)
2. **Note renderer** (Celery task, chạy 1 lần / cache):
- Với mỗi note 0..127 (hoặc dải cấu hình) × velocity layer (1-3):
`VST3Plugin.process(midi, duration, sr)` → PCM mono/stereo.
- Dải nốt: mặc định render root note mỗi 3 bán âm (41 sample/nhạc cụ), các
nốt còn lại pitch-shift bằng FluidSynth (SF2 root + correction) — giảm
dung lượng 4x; chất lượng chấp nhận được cho preview.
- Velocity layers: 1 (ff) hoặc 2 (mf+ff) theo cấu hình.
- Duration: 2-4s có fade; loop: nếu plugin phát sustain được (note on dài),
lấy loop region từ phần sustain ổn định; nếu không → one-shot + release.
- Percussion (bank 128): map nốt → chính nó, không pitch-shift.
3. **SF2 builder** (component MỚI phải viết): WAV samples → SF2 binary
(RIFF/sfbk: sdta+smpl, pdta: phdr/pbag/pgen/inst/ibag/igen/shdr).
Sau đó tái dùng `SoundFontConverter.convert_sf2_to_sf3` để có SF3 cho
server-side (tùy chọn).
4. **Lưu + đăng ký**: `storage/soundfonts/vsti_<id>.sf2` (+ .sf3); thêm vào
scan soundfont để API `listPlugins`/`listSoundfontInstruments` trả về như
soundfont thường (thêm cờ `origin: "vsti"`).
5. **Client** (đổi TỐI THIỂU): instrument picker hiển thị nhạc cụ VSTi như
soundfont; chọn → track `synth_engine = {type:"soundfont", soundfont_id,
bank, program}` → MỌI luồng play cũ chạy nguyên vẹn (SonicSF.playNote).
### Cache & vòng đời
- Key: `sha256(vst_path + preset_bytes + render_params)` → build 1 lần, tái
dùng. API `POST /api/v1/instruments/{id}/build` + `GET .../status` (queued/
building/ready/failed).
- Xoá instrument → xoá asset kèm.
### API mới (server)
- `GET /api/v1/instruments` — danh sách (kèm trạng thái build).
- `POST /api/v1/instruments` — tạo (vst_path, preset upload, params).
- `POST /api/v1/instruments/{id}/build`, `GET /api/v1/instruments/{id}/status`.
- `DELETE /api/v1/instruments/{id}`.
- Không đổi API phát lại hiện tại.
## 5. Ước lượng & giới hạn
- 41 sample × 1 velocity × 3s × 44.1k stereo PCM ≈ 43 MB → ogg (SF3) ≈ 8-12 MB;
client SF2 giữ PCM (WASM không decode ogg) ≈ 40 MB — lớn. Giảm: mono (~20MB),
giảm dải nốt (mỗi 4-6 bán âm), velocity 1, duration 2s. Chấp nhận tradeoff
preview vs dung lượng; tham số hoá trong render_params.
- Build tốn CPU (41 note × 2-4s render, 1 nhạc cụ ~2-5 phút trên 1 core) →
chạy Celery worker, cache vĩnh viễn.
- Chất lượng: pitch-shift bằng FluidSynth không bằng plugin thật; âm VSTi
phức tạp (modulation, chorus) mất chi tiết khi đóng gói sample. Đây là giới
hạn của hướng "giữ luồng cũ client-side".
## 6. Kết quả spike (đã chạy trong container sonicforgestudio-web-1)
- `pedalboard 0.9.19` có sẵn ✓; `DISPLAY=:99` ✓.
- `VST3Plugin.load` OK: DecentSampler.vst3; instrument API
`plugin.process(midi, duration, sr)` chạy (trả shape 2×88200) ✓.
- SCAN FAIL: Vital.vst3, Surge_XT.vst3 — "unsupported plugin format or scan
failure" (JUCE không scan được trong container; nghi thiếu thư viện của
plugin bundle — cần khảo sát tiếp: ldd .so bên trong .vst3, thử cài libs).
- PRESET FAIL: mọi .dspreset pianobook load thất bại trong container:
- "failed to load data from preset file" (1 preset/process)
- "database is locked" (nhiều preset/process — DecentSampler dùng SQLite nội
bộ; có thể cần HOME writable + 1 instance/process + chdir preset dir —
chdir đã thử).
- App có `DecentSamplerManager.create_decent_sampler_instance` (chdir preset
dir) — chưa verify được trên Linux container; có thể chỉ chạy trên Windows.
- Chưa test được .vstpreset (VST3 state) vì không có VST scan OK để áp.
### Việc cần làm trước khi chốt code
1. Điều tra scan fail Vital/Surge: `ldd` .so trong bundle, thử cài
libvulkan/libgl extra; tìm 1 VST3 đơn giản scan được (ví dụ plugin test
hoặc VST3 SDK example) để có plugin "chuẩn" cho pipeline.
2. Verify .vstpreset flow: tạo preset thật từ Windows (Carla) → upload →
pedalboard container render thử.
3. Điều tra DecentSampler preset: chạy thử với HOME=writable, 1 instance,
chdir; nếu vẫn fail → loại .dspreset khỏi phạm vi v1, dùng .vstpreset.
4. Viết SF2 builder + unit test đọc lại bằng pyfluidsynth (pattern
`_sf3_plays_audio`).
## 7. Kế hoạch triển khai (sau khi spike ổn)
1. Spike mở rộng (mục 6) — chọn plugin/preset khả thi.
2. Server: SF2 builder + note renderer + registry + API + Celery task.
3. Server: đăng ký asset vào scan soundfont.
4. Client: instrument picker hiển thị nhạc cụ VSTi (origin=vsti); chọn →
synth_engine soundfont. KHÔNG đổi luồng play.
5. Test: chọn nhạc cụ VSTi → play timeline → nghe đúng âm; xác nhận luồng cũ
(WASM) không đổi cho soundfont thường.
## 8. Ranh giới
- Docker/standalone tách riêng: pipeline server chỉ bật khi
`runtime.environment=docker` (hoặc mọi server headless); standalone Windows
giữ Carla/bridge như cũ.
- Thiết kế này không sửa code sản phẩm; commit code theo thoả thuận sau khi
spike + chốt phương án.