Files

9.8 KiB

PLAN — Phương án A: Engine xử lý toàn chain (chuẩn DAW)

Repo: C:/Users/locpham/SonicForgeStudio (branch standalone-shm-bridge, HEAD 5ad2c68)

0. Chuẩn DAW phải tuân thủ (KHÔNG bỏ qua)

  1. Track insert chain serial đúng thứ tự UI: builtin + VST3 xen kẽ đúng vị trí người dùng kéo; slot bypass chỉ bỏ qua slot đó, không đổi thứ tự phần còn lại.
  2. Track gain/pan sau track chain, trước khi sum vào master bus.
  3. Master insert chain serial đúng thứ tự UI, chạy trên mix tổng.
  4. Master fader là gain cuối cùng trước metering/hardware out (sau master chain).
  5. Live = Export: cùng 1 engine, cùng 1 chain descriptor, cùng 1 code DSP.
  6. PDC (Plugin Delay Compensation): plugin khai latency → track được align (đã có sườn _track_latency_samples render_engine.py:286; live chưa có).
  7. Gain staging rõ: track input → chain → gain/pan → sum → master chain → fader.
  8. Mono/Stereo: SF/mono track được pad thành stereo tại input engine; mọi xử lý stereo 2ch (bridge hiện đã stereo).

1. Kiến trúc đích

WebAudio chỉ còn I/O shim; mọi DSP (builtin + VST3) chạy trong fx_vst_bridge:

track instrument (SF/audio-clip) ─► [worklet track: capture raw]
                                      │ WS/SHM
                                      ▼
        fx_vst_bridge: TRACK CHAIN (builtin DSP C++ + VST3, đúng thứ tự) + PDC
                                      │ WS/SHM
                                      ▼
        WebAudio: track gain/pan ─► masterBus.input (sum)
                                      │ [worklet master: capture sum]
                                      │ WS/SHM
                                      ▼
        fx_vst_bridge: MASTER CHAIN (builtin DSP C++ + VST3, đúng thứ tự)
                                      │ WS/SHM
                                      ▼
        WebAudio: master fader ─► analyser ─► ctx.destination
  • Chain descriptor DUY NHẤT {type, path?, preset_b64?, params?, bypass} cho cả track lẫn master, live lẫn offline.
  • Engine dùng chung 1 code path: realtime loop (SHM) và --render-fx (WAV) cùng BuiltinFxChain + RealtimeFxChain.

2. Hiện trạng (đã xác minh)

  • Bridge realtime RealtimeFxChain (RenderFxJob.cpp:1051-1190) CHỈ VST3, in-place, SEH-guarded, worker thread swap chain. Không builtin DSP.
  • --render-fx slots chỉ nhận vst3 + builtin(gain/normalize) — native_render.py:219-234.
  • Frontend master: builtin chain chạy WebAudio (MASTER_MODULE_IO graph, app.jsx:1261-1310, rebuildMasteringGraph:1839); VST gom cuối qua fxRtStart:1682, worklet sau fader (output→worklet→analyser) — SAI: VST sau fader + order cố định.
  • Frontend track: builtin FX WebAudio (createFXModule:1450-1520, fxMods/sfMods app.jsx:23058-23104), VST splice SAU builtin (fxRtTrackSplice:23250-23270) — order cố định, bỏ qua vị trí UI.
  • Offline render_engine.py: track fx_chain(Python DSP eq/eqpro/comp/lim/exciter/ rebalance — KHÔNG có imager/maximizer) → vst_fx_chain(bridge); master chỉ master.fx_chain = vstFxChain từ app.jsx:11409 → builtin master chain imager/maximizer KHÔNG áp dụng offline → live ≠ export.
  • Worklet sf-fx-realtime.js: gom 128→256 block, WS round-trip, outQueue drop >32.
  • Python builtin DSP: _apply_builtin_fx_chain (render_engine.py:228-260) — 6 loại; JS WebAudio: 8 loại (MODULE_META app.jsx:13457-13468).
  • PDC: chỉ offline skeleton, plugin chưa báo latency (0).

3. Các bước

Phase 1 — Chain descriptor + protocol hợp nhất

  • Đặc tả JSON: slot = {type: vst3|eq|eqpro|imager|maximizer|compressor|limiter| exciter|rebalance, path?, preset_b64?, params?, bypass, active}. active:false = tắt hẳn (UI), bypass = bỏ qua slot giữ nguyên order.
  • job.json realtime + render-fx nhận chain hợp nhất (thay fx_chain cũ).
  • Thêm protocol: SET_PARAM {slot, key, value} (realtime builtin automation), REPORT_LATENCY {slot, samples} → engine track align (PDC).
  • Update _chain_json_for_bridge fx_realtime.py:33 + native_render.py:230 + job.

Chain slot JSON (đặc tả chính thức — chuẩn cho live + offline)

{
  "type": "vst3|eq|eqpro|imager|maximizer|compressor|limiter|exciter|rebalance",
  "path": "C:/.../plugin.vst3",          // bắt buộc khi type=vst3
  "preset_b64": "",                       // vst3: preset nội bộ (base64)
  "params": { },                          // builtin: tham số DSP (xem bảng dưới)
  "bypass": false,                        // bỏ qua slot khi process, GIỮ vị trí
  "active": true                          // false = slot bị tắt hẳn (UI xóa khỏi
                                          //   chain payload trước khi gửi engine)
}

Tham số builtin (khớp Python _apply_* + JS MASTER_MODULE_IO):

type params
eq g1..g4 dB (lowshelf100/peaking800/peaking3200/highshelf10000)
eqpro amount 0-200, bands[] {type,freq,q,gain,active} (RBJ)
imager w1..w4 width 4-band (JS gainLL/RL/LR/RR)
maximizer boost_db, ceiling_db, soft_clip bool
compressor threshold -60..0, ratio 1..20, makeup 0..12
limiter threshold -24..0, ceiling
exciter wet 0..100, hp_hz
rebalance g1..g4 dB 4-band (M/S)

Control protocol (qua WS text frame, cùng socket audio):

  • Client → engine: {"cmd":"set_param","slot":<idx>,"key":"threshold","value":-18.0}
  • Bridge → engine → client: {"cmd":"latency","slot":<idx>,"samples":1024}
  • Engine giữ pending_params[slot][key] + latencies[slot] per-session; C++ consume ở Phase 2.8 (SHM control ring).

Phase 2 — Bridge C++: BuiltinFxChain (8 DSP)

  • File mới native_bridge/src/BuiltinFxChain.cpp + header, port từ Python numpy (_apply_eq4/_apply_eqpro/_apply_compressor/_apply_limiter/_apply_exciter/ _apply_rebalance) VÀ JS WebAudio (imager/maximizer — MASTER_MODULE_IO graph app.jsx:1261-1310 + applyMasteringSettings:816-935) sang C++ stereo float. Khớp tham số: eq g1..g4 dB; eqpro bands(8, RBJ); compressor threshold/ratio/ makeup; limiter threshold/ceiling; exciter wet/hp; rebalance 4-band gains; imager 4-band width; maximizer boost/ceiling/soft-clip.
  • RealtimeFxChain::buildChain + RenderFxJob slots: nhận builtin → chạy BuiltinFxChain trước VST3 theo đúng thứ tự array.
  • SEH-guard chung; bypass giữ vị trí.
  • Test golden: native_bridge/tests/ — input WAV → C++ chain vs Python _apply_builtin_fx_chain same params → so SNR (mục tiêu > 60dB hoặc diff < 1e-4; biquad RMS khác nhau do floating — chấp nhận ngưỡng). Thêm case: chain xen kẽ builtin→vst3→builtin verify thứ tự (vst3 dummy delay).

Phase 3 — Engine Python: render + realtime dùng chain hợp nhất

  • render_engine.py: track/master đọc chain hợp nhất từ session payload; _apply_builtin_fx_chain Python chỉ giữ làm fallback khi RENDER_ENGINE≠bridge (ponytail: xóa hẳn khi bridge bắt buộc). Bỏ bất đối xứng imager/maximizer.
  • fx_realtime.py: session nhận chain hợp nhất, chuyển SET_PARAM/REPORT_LATENCY; master + track session chung protocol.
  • PDC offline: đọc latency_samples từ slot → align (render_engine.py:333 đã có pre-scan — nối với REPORT_LATENCY).

Phase 4 — Frontend: WebAudio chỉ I/O

  • Chain model: 1 mảng duy nhất chain (builtin + vst3 theo UI order). Bỏ tách vstFxChain/vstBypass/vst_fx_bypass (giữ backward: project cũ có vstFxChain → merge vào chain theo vị trí cũ: builtin → vst, đánh dấu migration).
  • Master: xóa MASTER_MODULE_IO graph + rebuildMasteringGraph node-web; thay 1 worklet master (masterBus.output → worklet → fader gain → analyser → dest). Fader (masterBus.output) chuyển SAU worklet — đúng chuẩn #4. applyMastering param thay bằng SET_PARAM push.
  • Track: rebuildTrackFxGraph bỏ fxMods/sfMods WebAudio builtin (giữ gain/panner/analyser); 1 worklet track ở chain input, engine trả chain output → legacy chorus/reverb (nếu còn) → gain/pan → masterBus.input.
  • fxRtChain()/fxRtTrackChain() đọc chain hợp nhất; chainKey so toàn chain (không chỉ VST) → restart session khi đổi thứ tự/param builtin.
  • Worklet: thêm SET_PARAM forward + queue drop → adaptive (ponytail), giữ cap.

Phase 5 — Verify (chuẩn DAW checklist)

  1. Build frontend node build.mjs OK.
  2. pytest tests/ (render_engine golden so bridge; plugin_api; fx_realtime).
  3. Golden: export WAV offline vs live capture (record masterBus) — diff < ngưỡng (live=export, chuẩn #5). Có test g2_bridge_smoke mẫu.
  4. Order test: chain [eq, vst3-delay, limiter] — verify delay chỉ áp sau EQ (impulse response), không gom cuối.
  5. Fader test: master chain active, fader -6dB → đỉnh giảm đúng, chain input không đổi (chuẩn #4).
  6. PDC: plugin latency 1024 samples → track bù, xuyên pha biến mất.
  7. Smoke: server + /capabilities + 1 render thật (master chain imager+maximizer
    • vst3) — trước đây offline thiếu → giờ có.
  8. Commit + push standalone-shm-bridge.

4. Rủi ro / quyết định

  • Latency live: +1 round-trip track + 1 master (~2-4 block mỗi hop). Chấp nhận (bản chất bridge); giữ queue cap, giảm blockMs.
  • Param automation realtime cho VST3 (preset thay đổi qua GUI): hiện chỉ preset_b64 lúc start — giữ nguyên phase này (ponytail: MIDI CC→param sau).
  • Migration project cũ: schema vẫn nhận vstFxChain (đọc cũ) nhưng ghi chain hợp nhất; không phá project cũ.
  • Không bỏ: bypass giữ order, PDC, fader cuối, live=export, gain staging.