docs: thiết kế kiến trúc docker play VSTi ở client — giữ luồng cũ (VSTi→SF2/SF3 asset)
This commit is contained in:
@@ -0,0 +1,132 @@
|
|||||||
|
# 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.
|
||||||
Reference in New Issue
Block a user