feat(fx): Phase 1 DAW chain hop nhat — fx_realtime chain JSON giu thu tu, SET_PARAM/REPORT_LATENCY qua WS text, native_render chain builtin+vst3 xen ke, pytest
This commit is contained in:
+164
@@ -0,0 +1,164 @@
|
||||
# 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)
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
Reference in New Issue
Block a user