Files
SonicForgeStudio/md/api_contract/ws_protocol.md
T

4.3 KiB
Raw Blame History

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.