Files
SonicForgeStudio/plans/PLAN_STANDALONE_WASM_PLAYBACK.md
T

4.1 KiB

PLAN — Standalone playback 100% client-side WASM (no Carla dependency)

Goal: fix "mastering fx chain không xử lí" + "vsti không qua main out" bằng cách chuyển live playback/preview của VSTi sang WASM (autosampled SF2 qua FluidSynth WASM → track sfEntry → FX chain → masterBus mastering → main out). Export WAV giữ nguyên backend Python (pedalboard VST3 + FluidSynth C++).

1. Server — autosample endpoint (VSTi → SF2)

  • tools/autosample_vsti.py: tách core thành autosample_sf2(...) callable (giữ main() argparse cho CLI).
  • app/api/v1/plugins.py: AutosampleRequest + POST /autosample — render preset qua từng note bằng pedalboard (đúng plugin+preset như export) → SF2 16-bit mono vào app/storage/soundfonts/{uuid}.sf2 + .meta → trả sf_id.
  • Chỉ SF2 (không SF3): client WASM load SF2 trực tiếp, endpoint download ưu tiên SF2 sẵn có. 30-60 note ≈ 10-20MB — chấp nhận, IndexedDB cache. ponytail: SF3 (OGG nhỏ hơn) khi cần size nhỏ — thêm --sf3 vào endpoint.

2. Server — mastering FX chain trong render_project

  • app/core/mastering_engine.py (mới): apply_mastering(buffer, settings, fs) tái hiện chain WebAudio client (initMasterBus + applyMasteringSettings):
    • eq (lowshelf 100 / peaking 822 Q0.7 / peaking 3200 Q1.2 / highshelf 10k)
    • imager (M/S width 4 band: 20-100 / 100-1k / 1k-6k / 6k-20k)
    • maximizer (boost → softclip atan → +upward comp → ceiling hard clip)
    • compressor (threshold/ratio/knee/makeup)
    • limiter (tanh soft clip)
    • exciter (hp 2k + saturator, dry/wet)
    • rebalance (M/S gains qua L/R crossfeed)
    • chain order từ settings['chain'], gate masterConnected && !isBypassed.
  • app/core/render_engine.py: render_project đọc project_json['mastering_settings'] → áp lên master_buffer trước sf.write; khi có mastering → clip [-1,1] (giống WAV encoder client), không mastering → normalize như cũ.

3. Client — VSTi live playback qua WASM autosample

  • app/static/js/services/api.js: thêm autosampleVsti(payload).
  • app/static/js/services/vstiAutosample.js (mới): SonicVstiAutosampleensure(synthEngine) POST autosample 1 lần, cache map plugin_id+preset → sf_id (localStorage), sfIdFor sync lookup.
  • app/static/js/services/soundfontPlayer.js: _playNoteFluid + applyAITrackInstrument fallback synthEngine.autosampled_sf_id khi thiếu soundfont_id; VSTi chưa sample → ensure rồi defer note (pendingNoteOns có sẵn).
  • app/static/js/services/runtime.js: SonicCarlaMidi.shouldRoute / shouldRoutePlayback trả false trừ khi window.__enableCarlaLivePlayback (mặc định off) — playback không phụ thuộc Carla nữa.
  • app/static/js/app.jsx:
    • shouldRouteCarla → false (VSTi đi qua SonicSF → masterBus → main out).
    • chọn instrument VSTi (applyTrackInstrument) → fire-and-forget SonicVstiAutosample.ensure(synth_engine).
    • timeline/local-loop/ghost/preview: VSTi đã chạy nhánh SonicSF.playNote sẵn (isSfTrackEngine false → else-if); autosampled_sf_id làm nó kêu đúng âm.
    • ensureMidiCapture đã capture node.sfEntry → VSTi vào midiCache → clientSideExport (offline mix qua track FX + mastering) hoạt động cho VSTi.
  • app/templates/index.html: script tag vstiAutosample.js.
  • Build: npm run build (app.jsx → app.precompiled.js).

4. Verify

  • pytest tests/ (kỳ vọng 108 pass / 1 skip / 1 env-specific fail như cũ).
  • Test mastering: render project JSON có mastering_settings (chain eq+imager+ maximizer) → assert output peak ≤ ceiling & khác bản không mastering.
  • Test autosample: gọi autosample_sf2 với VSTi (Nexus.vst3) → sf2utils parse sạch; endpoint return sf_id + file tồn tại.
  • Manual (browser): track VSTi → Preview/Play → âm qua masterBus (VU master nhảy), Export WAV server ra file có mastering.

5. Commit/push

  • Commit trên branch standalone. Push vẫn chờ user cấu hình git auth (credential.helper / token URL) — không retry khi chưa có.