Files
SonicForgeStudio/DESIGN_DOCKER_VSTI_PLAY.md

7.8 KiB
Raw Permalink Blame History

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.