# Kế hoạch: Port SonicForgeStudio / ScoreFXStudio sang Rust > Ngày: 2026-09-07 · Phạm vi: `C:/Users/locpham/SonicForgeStudio` (branch `main`, HEAD `19c1e03`) > Mọi số liệu dưới đây đo trực tiếp trên repo, không ước lượng từ cảm tính. --- ## 1. Hiện trạng (đo được) ### 1.1 Kiến trúc thật ``` ┌─────────────────────────────────────────────────────────────────┐ │ WebView2 (Chromium) — React 19 + Web Audio API │ │ app/static/js/app.jsx 37.376 dòng (1 file component) │ │ app/static/js/services/*.js 24 module (~4.000 dòng) │ │ sf-fx-realtime.js (AudioWorklet) / fx-rt-worker.js (WebWorker) │ └───────┬─────────────────────────────────────────────────────────┘ │ HTTP REST (92 endpoint) + WebSocket ──► audio realtime ┌───────▼─────────────────────────────────────────────────────────┐ │ Sidecar Python: daw_engine.exe (PyInstaller) │ │ FastAPI + uvicorn (port 8000-8010 tự chọn) │ │ 25.562 dòng .py (đã track) / 16 router, 177 pytest test │ │ numpy · scipy · soundfile · librosa · mido · pyfluidsynth │ │ celery/redis chỉ ở bản Docker, desktop chạy in-process │ └───────┬─────────────────────────────────────────────────────────┘ │ Shared Memory "SonicForge_DAW_IPC" (control ring + audio ring) ┌───────▼─────────────────────────────────────────────────────────┐ │ C++/JUCE native bridge (~18.500 dòng, git-tracked) │ │ dawn: daw_vst_bridge.exe · fx_vst_bridge.exe · plugin_host.exe │ │ JUCE 8.0.7 + VST3 SDK + FluidSynth + sfizz (submodule) │ └─────────────────────────────────────────────────────────────────┘ ▲ │ spawn/kill/watchdog, SHM, MIDI, cửa sổ VST GUI ┌───────┴─────────────────────────────────────────────────────────┐ │ Tauri v2 shell (Rust) — src-tauri/src/lib.rs 988 dòng + shm.rs │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.2 Số liệu kích thước / khối lượng | Hạng mục | Số đo | |---|---| | Bundle engine `src-tauri/resources/daw_engine` | **1.842 MB / 4.187 file** | | `dist/daw_engine` (PyInstaller build) | 1.818 MB / 3.118 file | | `native_bridge/` (gồm build artifacts + submodule) | 9.212 MB / 84.130 file | | `src-tauri/target/release` | 4.671 MB / 7.003 file | | `app/` (source + storage + static) | 444 MB / 312 file | | Frontend precompiled | `app.precompiled.js` 1,5 MB (Babel từ 1 file JSX) | | Python source (tracked) | 25.562 dòng | | C++/JUCE (tracked) | ~18.500 dòng | | Rust hiện có | ~1.200 dòng (chỉ shell/sidecar/SHM) | | REST endpoints | 92 (`plugins.py` chiếm 30) | | Test Python | 177 test (`pytest`) | | Test JS | 1 (`chordTheory.test.js`) | ### 1.3 Nợ kỹ thuật đã lộ ra qua debug M26 (bằng chứng cho phần tối ưu) - Đường audio realtime: `worklet → WS → Python → SHM → plugin → về`. Để bù độ trễ tích luỹ phải đệm `FILL = 40 block = 10.240 mẫu ≈ **213 ms @48k**` và bù bằng `pdcDelay` + đồng bộ playhead thủ công. - Lỗi còn tồn: PWR ON giữa lúc play → biên độ vượt mức rồi mới ổn định; phải thêm diag `m26d_` để đo envelope. - `masterFxGain`/`masterFxDryGain` crossfade + `warm bypass` + anchor/PDC = rất nhiều trạng thái ngầm, khó suy luận, dễ regression. - Nguyên nhân gốc không phải Python chậm (RTT engine ~10 ms) mà là **kiến trúc 3 tầng nối bằng WS + ring buffer**. - `torch` + `torchvision` có trong bundle nhưng **không hề được import** trong source app → ~1,5 GB rác. --- ## 2. Có port sang Rust được không? — **Được, nhưng không phải viết lại 1 lần** Chia hệ thống thành 5 tầng, mỗi tầng có "hệ số port" khác nhau: | Tầng | Nội dung | Rust hóa? | Ghi chú | |---|---|---|---| | T0 | Tauri shell, SHM, watchdog, spawn/kill | **Đã là Rust** | ~1.2k dòng, giữ | | T1 | C++/JUCE plugin host + VST3 + FluidSynth/sfizz | **Giữ nguyên**, gọi qua FFI | 18.5k dòng, viết lại = tự sát | | T2 | FastAPI server: REST 92 endpoint, auth, project/preset/plugin scan, task registry | **Port được, nên port** | Python thuần, chuyển axum + serde + sqlx dễ | | T3 | DSP offline: render/cắt/ghép/loop/normalize/analyze | **Port được, ưu tiên trung bình** | numpy/scipy/librosa → ndarray/rustfft/symphonia | | T4 | Realtime audio engine (hiện = Web Audio trong WebView) | **Port được — nhưng đây mới là phần đắt** | cpal/WASAPI + DSP graph; bỏ được WS + 213 ms FILL | | T5 | UI (app.jsx 37k dòng) | **Khuyến nghị KHÔNG port (P0-P5)** | Port sang egui/iced = 6-12 tháng, gần như không tăng giá trị | **Kết luận:** làm được ở mức T2+T3 (sidecar → Rust, giữ nguyên UI + C++ bridge) trong ~2-4 tháng/1-2 dev, lợi ích rõ ràng và rủi ro thấp. T4 là nơi "ăn tiền" về performance nhưng là dự án lớn (6-12 tháng). T5 chỉ nên động vào khi UI web đã chạm trần. --- ## 3. Ưu / khuyết điểm ### 3.1 Ưu điểm | # | Ưu điểm | Mức độ | Bằng chứng | |---|---|---|---| | 1 | **Giảm kích thước bundle cực mạnh**: 1,84 GB → ~40-80 MB | Rất cao | PyInstaller nuôi cả Python runtime + numpy/scipy/librosa (+torch rác) | | 2 | **Khởi động nhanh**: cold start sidecar Python 1-4 s → Rust <100 ms | Cao | Log `fx_loop` cho thấy chờ session/engine | | 3 | **RAM thấp hơn**: bỏ Python interpreter + numpy/scipy/librosa (~150-300 MB) | Cao | | | 4 | **Hết install/AV phiền**: PyInstaller bundle hay bị SmartScreen/AV chặn; binary Rust native sạch | Cao | | | 5 | **1 toolchain, 1 ngôn ngữ** cho shell + engine (Cargo), bỏ Python venv/PyInstaller/spec | Trung bình-cao | | | 6 | **An toàn + ổn định bộ nhớ**: bỏ lớp ctypes/subprocess/`os.kill` trên Windows (đã từng giết nhầm tiến trình cha) | Cao | comment `desktop_engine.py` mô tả bug watchdog | | 7 | **Đường realtime ngắn hơn** (nếu làm T4): bỏ WS relay → latency và jitter giảm, bỏ FILL 213 ms + PDC hack | Rất cao | bug M26 hiện tại | | 8 | **Đóng gói đa nền tảng** dễ hơn (musl/static, cross-compile) | Trung bình | | | 9 | **Hiệu năng DSP offline** ổn định, song song hoá bằng rayon/tokio, không bị GIL | Trung bình-cao | render batch hiện chạy threadpool/Celery | ### 3.2 Khuyết điểm / rủi ro | # | Khuyết điểm | Mức độ | Giảm thiểu | |---|---|---|---| | 1 | **Hệ sinh thái audio/AI kém hơn Python**: librosa→aubio/analyze mất một số thuật toán (beat track, chroma, HPSS) | Cao | Giữ 1 "Python oracle" off-line; port dần từng hàm kèm golden test | | 2 | **VST3 host trong Rust non hơn JUCE** (`carl` crate) | Cao | **Không port T1** — gọi C++ qua FFI (cc/cxx) | | 3 | **Chi phí viết lại**: 92 endpoint + 25.5k dòng Python + 37k dòng JSX | Rất cao | Strangler pattern theo phase, không big-bang | | 4 | **Mất Celery/Redis/scale-out** ở bản Docker (nếu vẫn cần) | Trung bình | Giữ nhánh Docker Python song song, hoặc tokio + queue | | 5 | **Windows audio (WASAPI/ASIO) nhiều bẫy driver**, khác mỗi máy | Cao | Làm T4 sau cùng, có fallback Web Audio | | 6 | **Song song 2 codebase** trong giai đoạn chuyển tiếp → chi phí đôi, dễ lệch behaviour | Cao | Cổng conformance test bắt buộc trước khi bỏ Python | | 7 | Một số thư viện Python khó thay: `pyfluidsynth`(→ FFI OK), `sf2utils`, `jsonschema` | Thấp-TB | FFI cho fluidsynth/sfizz vốn đã có sẵn trong native_bridge | | 8 | Tuyển/duy trì kỹ năng Rust + audio DSP khó hơn Python | Trung bình | | | 9 | Rủi ro "port xong vẫn còn bug cũ" nếu không sửa kiến trúc realtime | Trung bình | Bug M26 gốc là kiến trúc, không phải ngôn ngữ | --- ## 4. Performance & tối ưu — trước/sau dự kiến | Chỉ số | Hiện tại | Sau T2+T3 (Rust sidecar, UI web) | Sau T4 (Rust audio engine) | |---|---|---|---| | Cài đặt / bundle | 1.842 MB | ~60-120 MB | ~60-120 MB | | Cold start engine | 1-4 s | 50-150 ms | 30-80 ms | | RAM khi idle | ~350-600 MB (Chromium+Python+numpy) | ~250-400 MB | ~150-250 MB (nếu bỏ WebView) | | Endpoint scan VST | giây → phút (đọc filesystem trong Python) | nhanh hơn 5-20× (song song, không GIL) | như trái | | Offline render multitrack | threadpool/Celery | rayon, zero-copy | rayon | | Latency realtime FX | ~213 ms (FILL) + jitter WS | **không đổi** (nếu chưa làm T4) | **5-15 ms**, bỏ PDC/anchor | | Jitter/underrun | có (`u`,`h`,`f`,`cap` trong `fx_stats`) | như cũ | gần như hết | | Độ phức tạp trạng thái | cao (warm bypass, crossfade, anchor, PDC) | như cũ | thấp (1 engine, 1 đồng hồ) | ### 4.1 Việc tối ưu "miễn phí", làm được NGAY (không cần port) 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. ### 4.2 Tối ưu theo tính năng (sau port) | Tính năng hiện tại | Cách tối ưu bằng Rust | |---|---| | Quét plugin VST3/SF2/SFZ (`plugins.py`, 30 endpoint) | `jwalk`/`rayon` song song, cache index bằng `sled`/SQLite; bỏ quét lại | | Project/preset/user config | `serde` + `sqlx`/`rusqlite`, zero-copy JSON | | Offline render & mix (`render_engine.py`) | `ndarray` + `rustfft` + `rubato`, rayon theo track, block-based streaming thay vì load hết | | BPM/beat/analysis (`librosa`) | `aubio-rs`/`bliss-audio` hoặc tự viết onset/spectral (FFT đã có) — **kiểm chứng bằng golden WAV** | | Cắt/ghép/loop + zero-crossing (`ai_dsp_engine.py`) | vài chục dòng ndarray — nhanh hơn numpy ở đoạn nhỏ, không cấp phát | | Soundfont (FluidSynth/sfizz) | giữ FFI C++, chỉ thay lớp điều khiển sang Rust | | Realtime FX master/track | cpal/WASAPI + graph trong Rust, plugin qua FFI → bỏ worklet queue, FILL, PDC, anchor | | Auth/phiên | `argon2` + `jsonwebtoken` (tương đương bcrypt/PyJWT) | | AI proxy/preset | `reqwest` + `serde` — bỏ httpx | | Task queue (Celery) | `tokio` + channel/`apalis`; bản desktop vốn đã in-process | ### 4.3 Ánh xạ thư viện | Python | Rust | |---|---| | FastAPI + uvicorn | `axum` + `tokio` + `tower` | | pydantic | `serde` + `validator` | | numpy / scipy | `ndarray` / `rustfft`, `rubato`, `biquad` | | soundfile / pydub | `symphonia` (decode) + `hound` (WAV, BWF) | | librosa | `aubio-rs`, `bliss-audio`, `realfft` | | mido | `midly` (hoặc giữ mido nếu chưa cần) | | pyfluidsynth / sf2utils | FFI tới `libfluidsynth` (đã có DLL) / tự đọc SF2 chunk | | celery + redis | `tokio::task`, `apalis` (chỉ khi cần) | | httpx | `reqwest` | | jsonschema | `jsonschema` crate | | jinja2 (template) | `askama`/`minijinja` | | bcrypt/PyJWT | `argon2`, `jsonwebtoken` | | VST3 host | **`carl`** (tham khảo) hoặc **FFI JUCE hiện có** ← chọn cái sau | --- ## 5. Kế hoạch triển khai theo giai đoạn ### 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`. - [ ] Chọn cổng động giống `desktop_engine.py` (8000-8010), nhưng tích hợp luôn vào Tauri thay vì exe riêng. - [ ] `engine.spec` không còn cần thiết cho đường Rust; giữ sidecar Python làm "oracle" chạy cạnh (đổi port) cho các endpoint chưa port. - [ ] Test: `cargo test` + chạy lại bộ conformance P0 so với Python. ### P2 — Endpoint "dễ" (4-6 tuần) - [ ] `auth`, `user_config`, `presets`, `projects`, `files`, `media`, `system` → Rust (song song, có feature flag ở frontend). - [ ] Chuyển storage sang SQLite/`sled` (giữ khả năng đọc file cũ để migrate). - [ ] Frontend: `app/static/js/services/api.js` trỏ theo cờ; kiểm thử trên cả 2 engine. ### P3 — DSP offline (6-10 tuần) - [ ] Port `render_engine`, `dsp_utils`, `audio_editor`, `loudness`, `wav_bwf`, `sub_tab_dsp`, `ai_dsp_engine`. - [ ] `analyzer` + `audio_features` (librosa) — port có golden test, chấp nhận sai số <1e-4 hoặc thay thuật toán tương đương. - [ ] `soundfont_*` và `vst_engine` scan → Rust + FFI. ### P4 — Realtime control path bridge (3-5 tuần) ← tối ưu rẻ, hiệu quả cao - [ ] Thay WS `fx-realtime` + `plugins.py` relay bằng Tauri command → Rust → SHM trực tiếp. - [ ] Giữ nguyên worklet JS và C++ bridge; chỉ bỏ hop Python. - [ ] Đo lại: jitter điều khiển, độ ổn định PWR ON/OFF (tiêu chí: hết bug M26 không cần diag). ### P5 — (Tuỳ chọn) Rust audio engine thật (3-6 tháng) - [ ] `cpal` (WASAPI/ASIO) + DSP graph + plugin FFI; WebView chỉ còn UI. - [ ] Bỏ hoàn toàn worklet/FILL/PDC; latency mục tiêu ≤10 ms. - [ ] Chỉ bắt đầu khi P0-P4 đã ổn định và có người chịu trách nhiệm audio. ### P6 — (Tuỳ chọn) UI (6-12 tháng) — **chỉ khi cần** - [ ] Tách `app.jsx` thành module trước; đo lại. - [ ] Cân nhắc `egui`/`iced` hoặc giữ webview vĩnh viễn (khuyến nghị: giữ). ### Lộ trình rút gọn nếu chỉ có 1 dev / thời gian hạn chế **P0 → P1 → P2 → P4**. Bỏ P3 nếu Python DSP vẫn chạy tốt; bỏ P5/P6. Kết quả: bundle nhỏ, start nhanh, realtime sạch hơn, chi phí thấp nhất. --- ## 6. Tiêu chí Go / No-Go **Nên port nếu:** - Ưu tiên số 1 là kích thước cài đặt, tốc độ khởi động, ổn định phân phối (đang đúng với dự án này: bundle 1,84 GB). - Có ≥1 dev biết Rust + DSP và ≥3 tháng. - Có thể giữ C++ bridge và UI web (không big-bang). **Không nên port nếu:** - Cần thêm nhiều tính năng AI/audio phân tích mới (hệ sinh thái Python vượt trội). - Không có bộ test conformance (port sẽ tạo bug ngầm không phát hiện được). - Định viết lại tất cả cùng lúc, gồm cả UI và plugin host. --- ## 7. Tóm tắt một câu Port sang Rust **khả thi và đáng làm**, nhưng giá trị thật nằm ở **(a)** thay sidecar Python bằng Rust (giảm 1,84 GB → ~100 MB, start nhanh, bỏ PyInstaller/AV), **(b)** đưa control path realtime qua Tauri/SHM để bỏ hop WS và các hack FILL/PDC; phần UI 37k dòng JSX và plugin host C++/JUCE **nên giữ nguyên**; và việc nên làm ngay lập tức, không cần port, là **xoá `torch`/`torchvision` khỏi bundle**.