# DESKTOP INSTALL PLAN — SonicForge Studio (bản cài trực tiếp trên OS)

Mô hình: **server chạy nền trên máy người dùng (localhost), client mở bằng browser**.
Không cần Electron — tận dụng stack hiện có (FastAPI + static JS precompiled).
Soundfonts do **người dùng tự tải vào thư mục riêng** và **khai báo** cho ứng dụng.

---

## 1. KIẾN TRÚC

```
┌─────────────────────────── NGƯỜI DÙNG (1 máy) ───────────────────────────┐
│  Browser ──http://127.0.0.1:8000──▶ FastAPI (uvicorn, chạy NỀN)          │
│                                        │                                 │
│  ~/SonicForgeStudio/                    ├─ data/    (DB, uploads, cache) │
│   ├─ data/sonicforge.db                 ├─ soundfonts/ (user tự bỏ .sf2) │
│   ├─ soundfonts/*.sf2/.sf3              └─ quét nền 30s (scanner có sẵn) │
│   └─ config.json  ← khai báo folder soundfont                            │
└──────────────────────────────────────────────────────────────────────────┘
```

## 2. ĐÓNG GÓI CÀI ĐẶT (per-OS) — server nền + browser client

### Thành phần đóng gói
| Thành phần | Vai trò |
|---|---|
| Python backend (`app/`, `requirements.txt`) | Server — **PyInstaller `--onedir`** → binary (không cần Python trên máy user; bảo vệ source tốt hơn so với chạy .py) |
| `app/static/` (precompiled.js + css + vendor) | Client — đóng vào package, server phục vụ |
| VST3 plugins (`vst_plugins/`) | Copy theo platform (win .dll / mac .vst3 / linux .so) |
| Service wrapper | Tự khởi động server nền + mở browser khi login |

### Cài đặt + service nền theo OS
| OS | Installer | Service nền | Auto-open browser |
|---|---|---|---|
| **Windows** | Inno Setup (`.exe`) | Windows Service qua `NSSM`/`WinSW` hoặc Task Scheduler (logon) | `start http://127.0.0.1:8000` |
| **macOS** | `.dmg` + `.app` (PyInstaller) | `launchd` LaunchAgent (`~/Library/LaunchAgents`) | `open http://127.0.0.1:8000` |
| **Linux** | `.deb` / `.AppImage` | systemd **user** service (`~/.config/systemd/user/`) | `xdg-open http://127.0.0.1:8000` |

- Server bind **`127.0.0.1`** (không lộ mạng), port mặc định 8000 (config được).
- App gồm 2 tiến trình nhỏ: `web` (uvicorn) + `worker` (celery) — hoặc gộp worker vào web ở chế độ desktop (đơn giản: `--pool=solo`, chạy celery trong tiến trình riêng nếu cần render nặng).

## 3. THƯ MỤC DỮ LIỆU NGƯỜI DÙNG
```
~/SonicForgeStudio/
├── data/            # DB, uploads, processed (thay app/storage khi chạy desktop)
├── soundfonts/      # USER tự tải .sf2/.sf3 vào đây (mặc định được quét)
└── config.json      # cấu hình: soundfont_dirs, port, autostart...
```
- `config.py`: thêm `DATA_DIR` (env `SFDATA_DIR`, mặc định `~/SonicForgeStudio/data`), `SOUNDFONT_DIRS`.

## 4. SOUNDFONT DO NGƯỜI DÙNG QUẢN LÝ (tải + khai báo)
**Cơ chế hiện có (tận dụng):** `app/core/soundfont_scanner.py` — `SoundFontAutoScanner` quét nền 30s
`/opt/daw_engine/soundfonts` + `storage/soundfonts` → catalog → API list qua `app/api/v1/plugins.py`.

**Việc cần làm (mở rộng):**
1. **Scanner nhận folder người dùng:** constructor nhận thêm `user_dirs` (từ `SOUNDFONT_DIRS` env + `config.json`) — merge vào catalog (ưu tiên: user > system).
2. **Khai báo folder — 2 cách:**
   - **UI** (chính): Settings → "Soundfonts" → nút **Add folder…** (chọn thư mục chứa .sf2) → lưu vào `config.json` → gọi scanner rescan.
   - **config.json** (thủ công): `{ "soundfont_dirs": ["D:/SF", "/Users/me/sf"] }`.
3. **API:** thêm endpoint `POST /api/v1/plugins/soundfonts/dirs` (đăng ký folder) + `GET .../dirs` (liệt kê) — scanner reload.
4. **UI danh sách:** hiển thị catalog (tên SF, kích thước, folder nguồn) — chọn = load vào FluidSynth (luồng có sẵn qua `SonicSF.selectInstrument`).
5. **Số hóa:** không upload file — server đọc trực tiếp từ đường dẫn user khai báo (không nhân đôi dữ liệu).

## 5. BUILD + PHÁT HÀNH
```
code → build.mjs (precompiled + ?v=) → PyInstaller (server binary) → đóng installer theo OS
→ ký số (tùy chọn) → phát hành (GitHub Releases / trang riêng)
```
- **GitHub Actions matrix** (windows-latest / macos-latest / ubuntu-latest): test → build → installer artifact.
- Installer gồm: binary server, static/, VST plugins nền tảng, script tạo service + mở browser, mặc định tạo `~/SonicForgeStudio/` lần chạy đầu.

### 5.1 Tối ưu bundle daw_engine (bản 1.1 — 409MB → ~120-150MB)

Nguyên nhân nặng cũ: `librosa` kéo theo `numba`+`llvmlite` (~171MB) + `scikit-learn`
(~17MB), spec quét toàn bộ `scipy` (~78MB), bundle cả `celery`/`redis` (~40MB).

Đã xử lý:
- **`app/core/audio_features.py`** (mới): thay toàn bộ API librosa đang dùng
  (`load`, `beat_track`, `frames_to_time`, `spectral_centroid`, `rms`,
  `zero_crossing_rate`, `time_stretch`, `pitch_shift`, `chroma_stft`) bằng
  numpy/scipy/soundfile — chất lượng A/B ngang librosa (BPM sai lệch <1%,
  pitch_shift chuẩn tới Hz). Các module `analyzer.py`, `dsp_utils.py`,
  `sub_tab_dsp.py`, `ai_dsp_engine.py` đã chuyển sang shim.
- **`app/tasks/worker.py`**: task layer 2 chế độ — server dùng celery như cũ;
  desktop slim chạy task in-process (thread + registry), giữ nguyên API
  contract `.delay()` / `/tasks/{id}` nên frontend KHÔNG phải đổi.
- **`engine.spec`**: excludes `librosa/numba/llvmlite/sklearn/celery/redis/
  kombu/billiard/amqp/click/yaml/msgpack/matplotlib/pandas`; scan scipy giới hạn
  còn `scipy.signal` (goi duy nhất app còn dùng).
- **`src-tauri/tauri.conf.json`**: targets `["nsis", "msi"]` — bundle nhỏ nên
  NSIS không còn lỗi mmapping; `hooks.nsh` cài VC++ Redistributable (MSI không
  chạy hooks → máy thiếu VC++ → daw_engine.exe không chạy — đây là nguyên nhân
  "build xong không chạy daw_engine" trên Windows).
- **Fix layout resources (bản 1.1.1 — `exists=false` trong spawn.log)**:
  `bundle.resources` dạng ARRAY `["resources/daw_engine"]` copy engine tới
  `$RESOURCE_DIR/resources/daw_engine/...` (giữ tiền tố `resources/` — đọc
  source `tauri-utils/src/resources.rs`) trong khi lib.rs tìm ở
  `$RESOURCE_DIR/daw_engine/...` → `exists=false`. Đổi sang dạng MAP
  `{"resources/daw_engine": "daw_engine/"}` (Walk mode, giữ nguyên cây
  `_internal`, đích chuẩn `daw_engine/`). `src-tauri/src/lib.rs` đồng thời dò
  thêm 3 vị trí fallback (legacy/portable/dev) + ghi diagnostic đầy đủ vào
  `%APPDATA%/SonicForgeDAW/logs/spawn.log` (liệt kê nội dung resource_dir khi
  không tìm thấy).

Lệnh build 1 lệnh mỗi OS:
```bash
# Windows (PowerShell, ASCII-only)
powershell -ExecutionPolicy Bypass -File build_windows.ps1
# Linux (cần binutils: sudo apt-get install -y binutils)
bash build_linux.sh
# macOS (cần codesign/notarize khi phát hành)
bash build_macos.sh
```

## 5.2 KIẾN TRÚC 2 SIDECAR (Native Host Bridge — bản 1.2)

Từ bản 1.2, bản desktop chạy **Tauri v2** shell + **2 sidecar** (không còn browser):

```
┌───────────────────────── SonicForgeDAW (Tauri v2) ─────────────────────────┐
│  WebView2 (UI HTML5/JS — app.precompiled.js)                               │
│    ▲  invoke  ▲ bridge-audio (event)                                       │
│  Rust shell (src-tauri)                                                    │
│    ├── spawn+watchdog ──► daw_engine.exe      (FastAPI sidecar, port 8000) │
│    └── spawn+health ────► daw_vst_bridge.exe  (C++ Native Host Bridge)     │
│                             │  SharedMemory "SonicForge_DAW_IPC"           │
│                             │  (MidiEventIPC / ControlEventIPC / audio)    │
│                             └─ FluidSynth / sfizz / VST3 host              │
└────────────────────────────────────────────────────────────────────────────┘
```

- **MIDI → âm**: Web MIDI/timeline → `push_midi_event` (Rust ghi SHM) →
  bridge render block 256 (FluidSynth/sfizz/VST3) → Rust audio pump emit
  `bridge-audio` → WebView2 ScriptProcessor → track FX → master → loa.
- **Fallback**: bridge chết → `SonicSF` (FluidSynth WASM) tự bật, app không crash.
- **YC runtime**: cả `daw_engine.exe` lẫn `daw_vst_bridge.exe` cần VC++ 2015-2022
  Redistributable — `hooks.nsh` (NSIS) cài `vc_redist.x64.exe`; MSI không chạy
  hooks → máy sạch nên cài NSIS hoặc tự cài redist.
- **Binaries**: engine = `src-tauri/resources/daw_engine/` (bundle.resources),
  bridge = `src-tauri/binaries/daw_vst_bridge-x86_64-pc-windows-msvc.exe`
  (`bundle.externalBin`).
- **Build bridge**: `build/scripts/build_native_bridge.ps1` (vcpkg fluidsynth/
  sfizz + submodule vst3sdk + CMake MSVC) → copy exe vào `src-tauri/binaries/`.
- **API mới**: `GET /api/v1/bridge/status`, `POST /api/v1/bridge/load`,
  `GET /api/v1/bridge/log` (engine giữ trung gian IPC file
  `%APPDATA%/SonicForgeDAW/ipc/`).
- **Logs**: `%APPDATA%/SonicForgeDAW/logs/spawn.log` (engine+bridge), `bridge.log`.

## 6. CẬP NHẬT
- **Version check**: khi mở app, gọi endpoint version (file `version.json` đóng kèm + so sánh remote) → thông báo bản mới + link tải installer.
- Cập nhật = chạy installer mới (ghi đè, GIỮ NGUYÊN `~/SonicForgeStudio/` — data + soundfonts không đụng).
- `?v=` cache-bust JS mỗi bản (cơ chế đã có) — browser không dính cache cũ.

## 7. CHECKLIST CODE CẦN LÀM
- [ ] `config.py`: `DATA_DIR`, `SOUNDFONT_DIRS` (env + config.json)
- [ ] `soundfont_scanner.py`: nhận `user_dirs`, merge catalog, ưu tiên user
- [ ] API: `POST/GET .../soundfonts/dirs` (đăng ký/liệt kê folder) + rescan
- [ ] UI Settings → Soundfonts (Add folder, Browse, list, load)
- [ ] `soundfontStorage.js`: chuyển hướng sang catalog folder-scan (giữ upload path cho dự án cũ)
- [ ] PyInstaller spec: bundle static/ + vendor (libfluidsynth wasm, vst), bind 127.0.0.1
- [ ] Service wrappers: Windows (NSSM/WinSW), macOS (launchd), Linux (systemd user) + auto-open browser
- [ ] Installer scripts: Inno Setup (.exe), dmg (macOS), deb/AppImage (Linux)
- [ ] CI matrix build 3 OS + release artifacts
- [ ] `version.json` + in-app update check
