4.3 KiB
4.3 KiB
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_BLOCKtrongFxRealtimeIPC.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/masterFxGainrampesetTargetAtTime(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:
POST /fx-realtime/start→session_id+ws_url,block_size=256.- WS binary 2048 B/bản tin, text JSON 3 lệnh (
set_param,latency,flush) + 2 phản hồi (latency,flush_done). - Thay hop Python bằng Rust:
worklet → Tauri IPC/Rust → SHM, giữ nguyên SHM layout vớifx_vst_bridge.