9.8 KiB
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)
- 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.
- Track gain/pan sau track chain, trước khi sum vào master bus.
- Master insert chain serial đúng thứ tự UI, chạy trên mix tổng.
- Master fader là gain cuối cùng trước metering/hardware out (sau master chain).
- Live = Export: cùng 1 engine, cùng 1 chain descriptor, cùng 1 code DSP.
- PDC (Plugin Delay Compensation): plugin khai latency → track được align
(đã có sườn
_track_latency_samplesrender_engine.py:286; live chưa có). - Gain staging rõ: track input → chain → gain/pan → sum → master chain → fader.
- 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ùngBuiltinFxChain+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-fxslots chỉ nhậnvst3+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_chaincũ). - Thêm protocol:
SET_PARAM {slot, key, value}(realtime builtin automation),REPORT_LATENCY {slot, samples}→ engine track align (PDC). - Update
_chain_json_for_bridgefx_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+RenderFxJobslots: 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_chainsame 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_chainPython 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_samplestừ 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áchvstFxChain/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:
rebuildTrackFxGraphbỏ 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;chainKeyso toàn chain (không chỉ VST) → restart session khi đổi thứ tự/param builtin.- Worklet: thêm
SET_PARAMforward + queue drop → adaptive (ponytail), giữ cap.
Phase 5 — Verify (chuẩn DAW checklist)
- Build frontend
node build.mjsOK. - pytest tests/ (render_engine golden so bridge; plugin_api; fx_realtime).
- Golden: export WAV offline vs live capture (record masterBus) — diff < ngưỡng (live=export, chuẩn #5). Có test g2_bridge_smoke mẫu.
- Order test: chain [eq, vst3-delay, limiter] — verify delay chỉ áp sau EQ (impulse response), không gom cuối.
- Fader test: master chain active, fader -6dB → đỉnh giảm đúng, chain input không đổi (chuẩn #4).
- PDC: plugin latency 1024 samples → track bù, xuyên pha biến mất.
- Smoke: server + /capabilities + 1 render thật (master chain imager+maximizer
- vst3) — trước đây offline thiếu → giờ có.
- 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.