P0: cat torch/torchvision khoi engine bundle (1842MB -> 147MB) + hop dong API + golden conformance + baseline
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# Baseline P0 - SonicForgeStudio (Python engine)
|
||||
|
||||
Sinh boi `tools/p0_baseline.py` ngay 2026-10-12 21:00. Dung lam moc so sanh khi port Rust.
|
||||
|
||||
```json
|
||||
{
|
||||
"date": "2026-10-12 21:00",
|
||||
"bundle": {
|
||||
"dist/daw_engine": {
|
||||
"mb": 146.6,
|
||||
"files": 403
|
||||
},
|
||||
"src-tauri/resources/daw_engine": {
|
||||
"mb": 1841.8,
|
||||
"files": 4187
|
||||
},
|
||||
"has:torch": false,
|
||||
"has:torchvision": false,
|
||||
"has:sympy": false,
|
||||
"has:networkx": false,
|
||||
"has:tensorflow": false
|
||||
},
|
||||
"cold_start": {
|
||||
"startup_ms": 5700.4,
|
||||
"port": 8000,
|
||||
"rss_mb": 68.3
|
||||
},
|
||||
"import": {
|
||||
"import_app_main_ms": 730.9,
|
||||
"subprocess_wall_ms": 822.0
|
||||
},
|
||||
"render_mixdown": {
|
||||
"tracks": 4,
|
||||
"seconds_each": 30.0,
|
||||
"sr": 48000,
|
||||
"runs_ms": [
|
||||
1808.2,
|
||||
515.9,
|
||||
529.0
|
||||
],
|
||||
"min_ms": 515.9,
|
||||
"median_ms": 529.0,
|
||||
"realtime_factor": 0.0172
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -128,7 +128,7 @@ Chia hệ thống thành 5 tầng, mỗi tầng có "hệ số port" khác nhau:
|
||||
|
||||
### 4.1 Việc tối ưu "miễn phí", làm được NGAY (không cần port)
|
||||
|
||||
1. **Xoá `torch` + `torchvision` khỏi bundle** (không được import ở đâu trong source) → −1,5 GB, giảm thời gian build/install ngay lập tức. Chỉ cần loại khỏi môi trường build / `engine.spec` và kiểm tra lại bằng test suite.
|
||||
1. ✅ **Đã làm (P0)**: xoá `torch` + `torchvision` khỏi bundle → **1.842 MB → 146,6 MB**, không cần port. Chỉ cần thêm `excludes` trong `engine.spec`; đã kiểm chứng app không import torch (`tools/p0_baseline.py`, `md/BASELINE_P0.md`).
|
||||
2. Vá `app/static/js/app.jsx` 37k dòng bằng bundler thật (esbuild/vite) thay vì Babel 1-file → `app.precompiled.js` nhỏ hơn, build nhanh hơn, có source map.
|
||||
3. Tách `app.jsx` thành module (đang là 1 component khổng lồ) → giảm rủi ro regression, tiền đề bắt buộc nếu sau này port UI.
|
||||
4. Thay relay `WebView → WS → Python → SHM → C++` bằng **Tauri IPC trực tiếp tới Rust → SHM** cho phần control: bỏ 1 hop mạng loopback, bỏ khả năng treo do uvicorn, giảm jitter điều khiển. Đây là bước rẻ nhất trong hướng T4.
|
||||
@@ -170,11 +170,13 @@ Chia hệ thống thành 5 tầng, mỗi tầng có "hệ số port" khác nhau:
|
||||
|
||||
## 5. Kế hoạch triển khai theo giai đoạn
|
||||
|
||||
### P0 — Dọn dẹp trước khi port (1-3 ngày, không rủi ro)
|
||||
- [ ] Xác nhận `torch`/`torchvision` không dùng → loại khỏi build, đo lại bundle (mục tiêu <400 MB ngay).
|
||||
- [ ] Chốt "hợp đồng API": export OpenAPI JSON + danh sách message WS (`set_chain`, `set_param`, `flush`, `started`, `stats`…) làm đặc tả cho bản Rust.
|
||||
- [ ] Dựng **golden test**: bộ WAV/JSON mẫu cho từng endpoint DSP; chạy trên Python hiện tại, lưu hash → đây là tiêu chuẩn đúng cho Rust.
|
||||
- [ ] Đo baseline: thời gian start, RAM, thời gian scan plugin, thời gian render (ghi vào `md/`).
|
||||
### P0 — Dọn dẹp trước khi port — ✅ ĐÃ XONG (2026-10-12)
|
||||
- [x] Xác nhận `torch`/`torchvision` không dùng (block import torch → `scipy.signal` + mọi `app.core.*` import OK) → thêm exclude vào `engine.spec` (torch/torchvision/torchaudio/triton/sympy/networkx/tqdm/einops/optree/tensorflow/keras/jax).
|
||||
**Kết quả: bundle engine 1.841,8 MB / 4.187 file → 146,6 MB / 403 file (−92%)**, đã thay cả bản ship trong `src-tauri/resources/daw_engine`.
|
||||
- [x] Chốt "hợp đồng API": `md/api_contract/openapi.json` (86 path / 95 operation / 53 schema) + `md/api_contract/ws_protocol.md` (WS realtime + worklet).
|
||||
- [x] Golden test: `tools/golden_conformance.py` → `md/api_contract/golden/manifest.json`, 10 case DSP (zero-crossing, micro-crossfade, peak/RMS waveform, cut/loop, multitrack mixdown, analyze, LUFS/true-peak/mono-side). Tái lập ổn định (`--check` = 0 mismatch giữa các tiến trình); mixdown hash theo mẫu audio vì header WAV không ổn định.
|
||||
- [x] Đo baseline: `tools/p0_baseline.py` → `md/BASELINE_P0.md` (cold start **5.700 ms**, RSS **68 MB**, import `app.main` 731 ms, mixdown 4 track × 30 s: min **516 ms** = 0,017× realtime).
|
||||
|
||||
|
||||
### P1 — Rust service khung, chạy song song (2-4 tuần)
|
||||
- [ ] Crate `engine/` (axum): router, auth, health, `/api/v1/system`, `/tasks`.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"cases": {
|
||||
"analyzer.analyze_audio": "20fb044c076bc180810a92edb0cf247f62b2dea5e16f769208b634deeb6f36ef",
|
||||
"audio_editor.cut_and_loop_segment": "16848af659a11994904d4d08d3b91ba58224ce0f057cc950b645c0c3a8417226",
|
||||
"audio_editor.mix_multitrack_session": "46dcd4da24a0765cc78707eb4079fc289cc1e23bc741ecd12309683c59ce15bc",
|
||||
"dsp_utils.apply_micro_crossfade": "c43756937a0e5a2e285c9af1d8d848041033fbb3396057d8ca3bfc513bdca758",
|
||||
"dsp_utils.find_zero_crossing": "8ee6913ca5066eb6b4949040e6cb0f3d47589802de67b886d905b2ea79049dba",
|
||||
"dsp_utils.generate_peak_waveform": "1a629e10dd4207bb8faafee747eac4955b05d3e624236e0a13bb51bd0f2a5f82",
|
||||
"dsp_utils.generate_rms_waveform": "6ecf9662cf77a6a3e8b193dfef9b32d32bdff14c355a8915cd8f81ee44b80f5b",
|
||||
"loudness.integrated_lufs": "0914a33306e2832bba22f7bfed82a8c30c785752b3a55dab3bb5151354709972",
|
||||
"loudness.mono_side_ratio": "6ab9135f88cfd6092ccd2c3f0eb3285debd7227f9675b7e8a6cfe86b473b3c0c",
|
||||
"loudness.true_peak_db": "4726ed91ff71f4c2fd9f66bef790916cb7574bd715598af0ff146fa582520e56"
|
||||
},
|
||||
"env": {
|
||||
"numpy": "1.26.4",
|
||||
"python": "3.13.7",
|
||||
"sample_rate": 48000,
|
||||
"scipy": "1.17.1"
|
||||
},
|
||||
"generated_by": "tools/golden_conformance.py",
|
||||
"schema": 1
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
# Hợp đồng realtime FX (WS + worklet) — SonicForgeStudio
|
||||
|
||||
Trích từ `app/api/v1/plugins.py`, `app/core/fx_realtime.py`,
|
||||
`app/static/js/sf-fx-realtime.js`, `app/static/js/fx-rt-worker.js`, `app/static/js/app.jsx`
|
||||
tại HEAD `7c236d4`. Đây là đặc tả để port sang Rust (P1/P4).
|
||||
|
||||
## 1. HTTP (REST)
|
||||
|
||||
| Method | Path | Body | Trả về |
|
||||
|---|---|---|---|
|
||||
| POST | `/api/v1/plugins/fx-realtime/start` | `{fx_chain: [], sample_rate: 44100}` | `{success, session_id, ws_url, block_size, sample_rate}` |
|
||||
| POST | `/api/v1/plugins/fx-realtime/stop` | `{session_id}` | `{success, stopped}` |
|
||||
| POST | `/api/v1/plugins/fx-realtime/set-chain` | `{session_id, fx_chain}` | `{success, updated}` |
|
||||
| GET | `/api/v1/plugins/bridge/status` | — | trạng thái bridge/SHM |
|
||||
| POST | `/api/v1/plugins/render` | job render offline (VST) | file kết quả |
|
||||
| POST | `/api/v1/plugins/soundfont-render` | job render SF2 | WAV |
|
||||
|
||||
- `block_size` = **256 mẫu** (`FXRT_BLOCK` trong `FxRealtimeIPC.h`).
|
||||
- Auth: Bearer token hoặc `?token=`; desktop loopback không token → auto admin.
|
||||
|
||||
## 2. WebSocket audio
|
||||
|
||||
`GET /api/v1/plugins/ws/fx-realtime/{session_id}?token=...`
|
||||
|
||||
| Hướng | Kiểu frame | Nội dung |
|
||||
|---|---|---|
|
||||
| client → server | **binary** | 2048 B = 256 frame × 2 kênh `float32` interleaved |
|
||||
| server → client | **binary** | như trên, đã qua chain FX |
|
||||
| client → server | text JSON | `{"cmd":"set_param","slot":n,"key":"...","value":f}` · `{"cmd":"latency","slot":n,"samples":n}` · `{"cmd":"flush"}` |
|
||||
| server → client | text JSON | `{"cmd":"latency","slot":n,"samples":n}` · `{"cmd":"flush_done"}` |
|
||||
|
||||
Server: `ws_fx_realtime()` (plugins.py ~1344) — vòng lặp receive block → SHM ring →
|
||||
`fx_vst_bridge --realtime-fx` (hoặc `--juce-fx`) → SHM → send.
|
||||
|
||||
## 3. Worklet ↔ main thread (MessagePort)
|
||||
|
||||
`sf-fx-realtime.js` (AudioWorkletProcessor `sf-fx-realtime`).
|
||||
|
||||
Hằng số: `BLOCK=256`, `BURST=4` (gom 4 block = 1024 mẫu/frame), `FILL=40` (≈213 ms @48k),
|
||||
`RESUME_FILL=24`, `CAP=64` (≈341 ms), `FADE=512`, `HOLD_FADE=4`, `WRAP_FADE=256`.
|
||||
|
||||
| type | Hướng | Payload |
|
||||
|---|---|---|
|
||||
| `__direct` | main → worklet | chuyển `MessagePort` (worker↔worklet trực tiếp, bỏ main thread) |
|
||||
| `in` | worklet → main/worker | `{data: ArrayBuffer}` 1024 mẫu stereo interleaved |
|
||||
| `out` | worker → worklet | `{l, r: Float32Array}` block đã xử lý |
|
||||
| `started` | worklet → main | phát block đầu tiên (đã đủ FILL) |
|
||||
| `clear_queue` | main → worklet | `{mode: 'play'\|'seek'\|'loop'}` — wipe queue, `hasStarted=false` (mode `play`) |
|
||||
| `flush` | worklet → main | yêu cầu engine xả ring stale |
|
||||
| `flush_done` | worker → worklet | engine đã flush |
|
||||
| `flush_for_stop` | main → worklet | dừng phát (giữ queue) |
|
||||
| `set_param` | worklet → main | forward nguyên văn tới WS |
|
||||
| `latency` | main → worklet | `{samples}` — latency chain thật |
|
||||
| `stats` | worklet → main | `{underruns, holds, fades, silences, capDrops, fadeIns, queueLen}` (≈2 s/lần) |
|
||||
| `dbg` | worklet → main | `{tag, t, ...}`: `fx_in_first`, `fx_out_first`, `fx_play_first`, `fx_seek_first`, `fx_loop_clear`, `fx_seek_clear`, `fx_wrap_xfade` |
|
||||
| `dbg_state` | main → worklet | yêu cầu snapshot trạng thái nội bộ |
|
||||
| `dbg_state_r` | worklet → main | `{k, t, q, playing, hasStarted, silenced, flushPending, stopped, wrapMode, wrapFadeBlocks, latencySamples, resumeFill, inCount, outPos, fadeLeft, holdFadeLeft}` |
|
||||
|
||||
## 4. Đồng bộ playhead / PDC (phía UI, app.jsx)
|
||||
|
||||
- `FX_RT_WET_LAG_SAMPLES = 10240` (= FILL 40 × 256) — độ trễ nội dung khi vào FX path.
|
||||
- `pdcDelay.delayTime` = 10240/sr khi FX active, 0 khi dry (`fxRtApplyPdc`).
|
||||
- Crossfade dry↔wet: `masterFxDryGain` / `masterFxGain` rampe `setTargetAtTime(tc=0.025)`.
|
||||
- `fxRt.crossfade(token)` bất biến: `dryGain + fxGain = 1`.
|
||||
- Warm bypass: giữ session/node/ws khi PWR OFF giữa lúc play (`fxRtWarmPlayHold`).
|
||||
|
||||
## 5. Ghi chú cho bản Rust (P4)
|
||||
|
||||
Hợp đồng tối thiểu cần giữ để frontend không phải sửa:
|
||||
1. `POST /fx-realtime/start` → `session_id` + `ws_url`, `block_size=256`.
|
||||
2. WS binary 2048 B/bản tin, text JSON 3 lệnh (`set_param`, `latency`, `flush`) + 2 phản hồi (`latency`, `flush_done`).
|
||||
3. Thay hop Python bằng Rust: `worklet → Tauri IPC/Rust → SHM`, giữ nguyên SHM layout với `fx_vst_bridge`.
|
||||
Reference in New Issue
Block a user