Skip to main content

Silence Detection

Silence removal tells speech apart from pauses with voice activity detection rather than a volume threshold, so clicks and room noise do not count as speech. The model is Silero VAD (MIT, 1.3 MB, embedded in vv-vad) run by tract, which is pure Rust: there is no native ML runtime to ship.

silence-detection.png

vv-vad

  • The model is frozen to 16 kHz by scripts/freeze_silero_vad.py, because tract does not support the original's If branches on the sample rate.
  • Vad::push takes mono 16 kHz samples in pieces of any size and returns one speech probability per 512 samples (32 ms). Each chunk is preceded by a 64-sample context from the previous one, and the recurrent state is carried across chunks.
  • The optimized plan is built once per process (OnceLock).

From probabilities to cuts (vv-session silence.rs)

StageRule
media_speechDecodes one stream downmixed to mono at 16 kHz, feeding the VAD and an RMS level per 32 ms window. Reports progress and can be cancelled.
Speech runsStart at the threshold (default 0.5) and end 0.15 below it (hysteresis). Edges more than 12 dB below the run's median level are trimmed. Runs shorter than 0.1 s are dropped (a cough, a knock).
clip_rangesMedia seconds to timeline frames, using clip speed, rounded inwards so a frame partly in speech is kept.
timeline_cutsA pause counts only where every analysed clip is silent, even across a cut between clips. Pauses shorter than the minimum silence (default 0.3 s) are kept. Head and tail (default 0.1 s each) are kept next to speech only.

Applying

The dialog (vv-app/src/silence_dialog.rs) runs the analysis on a thread and caches it per (content hash, stream), so changing the parameters re-plans the cuts instantly. Apply removes the ranges with vv_core::edit::delete_ranges in ripple mode, as a single undo step. MCP agents use the same delete through the delete_ranges tool, with levels from get_audio_levels (analysis.rs).