pipecrab-vad: the voice-activity-detection interface, in two tiers.
Voice activity has two natural shapes, and this crate names both:
- [
VoiceActivityDetector] — the stage-facing capability: audio in, speech edges out ([VadEvent::SpeechStarted] / [VadEvent::SpeechStopped]). Segmenter-class engines that already speak in segments — sherpa's VAD, platform VADs — implement this directly. It is what [VadStage] drives. - [
SpeechScorer] — the raw-model tier: a per-window speech probability. This is what a bare silero build (nativeort, browser onnxruntime-web) exposes. A scorer becomes a detector through the [Debounced] adapter, which owns the windowing, threshold, and hangover.
Probabilities used to live on the one VAD trait; they moved down to
[SpeechScorer]. A segmenter never has a
probability to hand back, and anything downstream that wants confidence
(a turn manager, prosody) composes its own [SpeechScorer] rather than
leaning on the VAD to surface one.
The edge contract
Across the lifetime of a [VoiceActivityDetector], events alternate,
starting with SpeechStarted: started, stopped,
started, stopped, … This is a documented invariant that [VadStage] and
everything downstream of it trust.
[VadStage] turns those edges into a gate: it owns a pre-roll ring and
emits speech-only audio, bracketed by the edges. Note the contract this
inverts — downstream of the gate, SpeechStarted precedes an
utterance's audio (pre-roll included) and SpeechStopped follows its
last chunk. (The older lane-discipline design emitted the edge after the
chunk that triggered it; that wording is gone.) See [VadStage] for the full
gate algorithm.
No runtime format detection
Both traits take a bare &[f32], which carries no sample rate. Samples are
interpreted as input_format(); no
runtime detection is possible. The stage enforces the format fatally before
any audio reaches an engine, so an engine never sees nonconforming samples
through the pipeline — which is why [VadError] carries no format-mismatch
variant.
Platform-neutral and wasm32-checkable: the concrete engines live elsewhere,
each behind these traits, so the interface itself carries no backend
dependency and compiles for the host and wasm32-unknown-unknown.