Skip to main content

adk_audio/traits/
vad.rs

1//! Voice Activity Detection trait.
2//!
3//! # No backend ships with this crate
4//!
5//! `adk-audio` defines the [`VadProcessor`] boundary but implements it nowhere.
6//! Callers supply their own detector. The `vad` Cargo feature gates no code —
7//! it once pulled `webrtc-vad`, which this crate never imported.
8//!
9//! # `&self` is the constraint on any streaming backend
10//!
11//! [`VadProcessor::is_speech`] takes `&self`, and the trait is `Send + Sync`, so
12//! implementors are shared immutably (`Arc<dyn VadProcessor>` throughout
13//! [`crate::pipeline`] and the desktop turn detector). Every serious streaming
14//! VAD — Silero, TEN, Earshot — is *recurrent*: classifying a frame mutates
15//! per-stream state. Such a detector cannot implement this trait without
16//! interior mutability, and a `Mutex` in the per-frame path is exactly what a
17//! real-time audio loop must not have.
18//!
19//! So a stateful backend cannot simply implement `VadProcessor`. It needs one
20//! detector instance per stream, owned mutably by that stream, with an explicit
21//! `reset()` at session boundaries — and a documented compatibility wrapper for
22//! existing `Arc<dyn VadProcessor>` callers, not a silent lock.
23//!
24//! # This is a primitive, not a policy
25//!
26//! A `VadProcessor` reports whether audio contains speech. It carries no
27//! call-control authority on its own: endpointing, answering-machine detection,
28//! barge-in, and turn-taking are separate decisions layered above it. Provider
29//! server VAD remains the conversational turn-taking authority unless an
30//! application explicitly chooses otherwise.
31
32/// A detected speech segment within an audio frame.
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct SpeechSegment {
35    /// Start offset in milliseconds.
36    pub start_ms: u32,
37    /// End offset in milliseconds.
38    pub end_ms: u32,
39}
40
41use crate::frame::AudioFrame;
42
43/// Trait for Voice Activity Detection processors.
44///
45/// Used by the voice agent pipeline to gate STT inference
46/// to speech-only segments.
47pub trait VadProcessor: Send + Sync {
48    /// Returns `true` if the frame contains speech.
49    fn is_speech(&self, frame: &AudioFrame) -> bool;
50
51    /// Identify speech segments within the frame.
52    fn segment(&self, frame: &AudioFrame) -> Vec<SpeechSegment>;
53}