1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! 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 (native `ort`, 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`](VadEvent::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()`](VoiceActivityDetector::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`.
pub use ;
pub use ;
use async_trait;
use AudioFormat;
use MaybeSendSync;
/// A speech-state transition, emitted by a [`VoiceActivityDetector`].
/// The stage-facing voice-activity capability: audio in, speech edges out.
///
/// Segmenter-class engines (sherpa's VAD, platform VADs) implement this
/// directly; raw per-window scorers are lifted into it by [`Debounced`]. It is
/// the trait [`VadStage`] drives.
///
/// Like every other pipecrab interface it takes `&self`, so an in-flight call
/// can be dropped on a barge-in interrupt without tearing state. `?Send` on
/// `wasm32` matches pipecrab's single-threaded execution model, so one
/// implementation runs unchanged on a current-thread executor and in the
/// browser, where `Send` bounds cannot be satisfied.
/// The raw-model tier: per-window speech probability.
///
/// This is what a bare silero build implements — the web onnxruntime-web build
/// and any future raw model. A scorer becomes a [`VoiceActivityDetector`]
/// through [`Debounced`], which owns the windowing, threshold, and hangover.
///
/// `?Send` on `wasm32` matches pipecrab's single-threaded execution model, as on
/// [`VoiceActivityDetector`].
/// Why a [`VoiceActivityDetector::process`] or [`SpeechScorer::score`] call
/// failed.
///
/// Mirrors the message-plus-kind shape of the pipeline's other error types
/// (e.g. `pipecrab-stt`'s `SttError`) so the conversion at a stage boundary is
/// direct.