cascadevad-onnx 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cascadevad_onnx-0.1.0/CHANGELOG.md +17 -0
- cascadevad_onnx-0.1.0/LICENSE +17 -0
- cascadevad_onnx-0.1.0/MANIFEST.in +5 -0
- cascadevad_onnx-0.1.0/NOTICE +14 -0
- cascadevad_onnx-0.1.0/PKG-INFO +135 -0
- cascadevad_onnx-0.1.0/README.md +81 -0
- cascadevad_onnx-0.1.0/cascadevad/__init__.py +36 -0
- cascadevad_onnx-0.1.0/cascadevad/api.py +109 -0
- cascadevad_onnx-0.1.0/cascadevad/audio.py +107 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/__init__.py +8 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/fsmn.py +157 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/marblenet.py +113 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/pyannote.py +122 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/speechbrain.py +105 -0
- cascadevad_onnx-0.1.0/cascadevad/backends/ten.py +126 -0
- cascadevad_onnx-0.1.0/cascadevad/base.py +186 -0
- cascadevad_onnx-0.1.0/cascadevad/cascade.py +379 -0
- cascadevad_onnx-0.1.0/cascadevad/cli.py +80 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/__init__.py +10 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/common.py +155 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/fsmn.py +80 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/marblenet.py +85 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/pyannote.py +54 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/silero.py +56 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/speechbrain.py +94 -0
- cascadevad_onnx-0.1.0/cascadevad/convert/ten.py +72 -0
- cascadevad_onnx-0.1.0/cascadevad/data/fsmn_cmvn.npz +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/marblenet_mel_fb.npy +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/marblenet_window.npy +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/sb_vad_mel_fb.npy +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/sb_vad_window.npy +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/silero_vad.onnx +0 -0
- cascadevad_onnx-0.1.0/cascadevad/data/ten_vad_coeff.npz +0 -0
- cascadevad_onnx-0.1.0/cascadevad/features.py +126 -0
- cascadevad_onnx-0.1.0/cascadevad/onnx_backend.py +136 -0
- cascadevad_onnx-0.1.0/cascadevad/registry.py +287 -0
- cascadevad_onnx-0.1.0/cascadevad/resolver.py +98 -0
- cascadevad_onnx-0.1.0/cascadevad/segment.py +129 -0
- cascadevad_onnx-0.1.0/cascadevad/signature.py +104 -0
- cascadevad_onnx-0.1.0/cascadevad/version.py +12 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/PKG-INFO +135 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/SOURCES.txt +62 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/dependency_links.txt +1 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/entry_points.txt +2 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/requires.txt +36 -0
- cascadevad_onnx-0.1.0/cascadevad_onnx.egg-info/top_level.txt +1 -0
- cascadevad_onnx-0.1.0/pyproject.toml +64 -0
- cascadevad_onnx-0.1.0/setup.cfg +4 -0
- cascadevad_onnx-0.1.0/setup.py +6 -0
- cascadevad_onnx-0.1.0/test/resources/README.md +13 -0
- cascadevad_onnx-0.1.0/test/resources/speech.wav +0 -0
- cascadevad_onnx-0.1.0/test/test_api.py +102 -0
- cascadevad_onnx-0.1.0/test/test_audio.py +54 -0
- cascadevad_onnx-0.1.0/test/test_backends.py +60 -0
- cascadevad_onnx-0.1.0/test/test_cascade.py +155 -0
- cascadevad_onnx-0.1.0/test/test_cli.py +35 -0
- cascadevad_onnx-0.1.0/test/test_custom_onnx.py +54 -0
- cascadevad_onnx-0.1.0/test/test_download.py +40 -0
- cascadevad_onnx-0.1.0/test/test_features.py +35 -0
- cascadevad_onnx-0.1.0/test/test_internals.py +76 -0
- cascadevad_onnx-0.1.0/test/test_registry.py +78 -0
- cascadevad_onnx-0.1.0/test/test_resolver.py +50 -0
- cascadevad_onnx-0.1.0/test/test_segment.py +68 -0
- cascadevad_onnx-0.1.0/test/test_signature.py +47 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
Initial release of **cascadevad**, a derivative work of
|
|
6
|
+
[vadonnx](https://github.com/TigreGotico/vadonnx) (© TigreGótico Lda, Apache-2.0).
|
|
7
|
+
|
|
8
|
+
- Renamed the package to `cascadevad`.
|
|
9
|
+
- Added the cost-adaptive **confidence-gated cascade** (`cascadevad.cascade`): a cheap gate
|
|
10
|
+
VAD runs on every frame and a strong expert VAD is invoked only where it would change the
|
|
11
|
+
decision, with pluggable routing, an admission-control SLA, calibration, and a fused
|
|
12
|
+
cost-adaptive ensemble.
|
|
13
|
+
- Re-hosted the ONNX model mirror at
|
|
14
|
+
[`aryashah00/cascadevad-models`](https://huggingface.co/aryashah00/cascadevad-models)
|
|
15
|
+
(one subfolder per model).
|
|
16
|
+
- Unified multi-backend VAD serving over ONNX Runtime (Silero, MarbleNet, pyannote, FSMN,
|
|
17
|
+
SpeechBrain, TEN) inherited from the upstream project.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 TigreGóticoLda
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
cascadevad
|
|
2
|
+
Copyright 2026 aistra
|
|
3
|
+
|
|
4
|
+
This product is a derivative work of "vadonnx"
|
|
5
|
+
Copyright 2026 TigreGótico Lda (https://github.com/TigreGotico/vadonnx),
|
|
6
|
+
licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
7
|
+
|
|
8
|
+
Modifications by aistra (Arya Shah <arya.shah@aistra.com>): renamed the package to
|
|
9
|
+
cascadevad, added the cost-adaptive confidence-gated cascade (cascadevad/cascade.py),
|
|
10
|
+
and re-hosted the ONNX model mirror at https://huggingface.co/aryashah00/cascadevad-models.
|
|
11
|
+
|
|
12
|
+
Bundled and downloaded VAD model weights retain their respective upstream licenses
|
|
13
|
+
(Silero, NVIDIA NeMo Frame-VAD/MarbleNet, pyannote segmentation-3.0, FunASR FSMN,
|
|
14
|
+
SpeechBrain, TEN). See docs/licensing.md for per-model license terms.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cascadevad-onnx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Load arbitrary Voice Activity Detection (VAD) models behind a unified ONNX API
|
|
5
|
+
Author-email: Arya Shah <arya.shah@aistra.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/arya-aistra/cascadevad
|
|
8
|
+
Project-URL: Repository, https://github.com/arya-aistra/cascadevad
|
|
9
|
+
Project-URL: Models, https://huggingface.co/aryashah00/cascadevad-models
|
|
10
|
+
Keywords: vad,voice activity detection,onnx,speech,silero,audio
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: NOTICE
|
|
25
|
+
Requires-Dist: numpy
|
|
26
|
+
Requires-Dist: onnxruntime>=1.17
|
|
27
|
+
Requires-Dist: huggingface_hub
|
|
28
|
+
Provides-Extra: fsmn
|
|
29
|
+
Requires-Dist: kaldi-native-fbank; extra == "fsmn"
|
|
30
|
+
Provides-Extra: mic
|
|
31
|
+
Requires-Dist: sounddevice; extra == "mic"
|
|
32
|
+
Provides-Extra: resample
|
|
33
|
+
Requires-Dist: scipy; extra == "resample"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: kaldi-native-fbank; extra == "all"
|
|
36
|
+
Requires-Dist: sounddevice; extra == "all"
|
|
37
|
+
Requires-Dist: scipy; extra == "all"
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest; extra == "test"
|
|
40
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
41
|
+
Provides-Extra: convert
|
|
42
|
+
Requires-Dist: onnx; extra == "convert"
|
|
43
|
+
Requires-Dist: huggingface_hub; extra == "convert"
|
|
44
|
+
Requires-Dist: requests; extra == "convert"
|
|
45
|
+
Provides-Extra: convert-marblenet
|
|
46
|
+
Requires-Dist: cascadevad-onnx[convert]; extra == "convert-marblenet"
|
|
47
|
+
Requires-Dist: nemo_toolkit[asr]; extra == "convert-marblenet"
|
|
48
|
+
Requires-Dist: torch; extra == "convert-marblenet"
|
|
49
|
+
Provides-Extra: convert-speechbrain
|
|
50
|
+
Requires-Dist: cascadevad-onnx[convert]; extra == "convert-speechbrain"
|
|
51
|
+
Requires-Dist: speechbrain; extra == "convert-speechbrain"
|
|
52
|
+
Requires-Dist: torch; extra == "convert-speechbrain"
|
|
53
|
+
Dynamic: license-file
|
|
54
|
+
|
|
55
|
+
# cascadevad
|
|
56
|
+
|
|
57
|
+
Load arbitrary **Voice Activity Detection** models behind a single, unified API — every
|
|
58
|
+
model runs through [ONNX Runtime](https://onnxruntime.ai/). One streaming/batch
|
|
59
|
+
interface, one audio-format story, pluggable models.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from cascadevad import load_vad
|
|
63
|
+
|
|
64
|
+
vad = load_vad("silero") # bundled, works fully offline
|
|
65
|
+
prob = vad.process_chunk(pcm_bytes) # streaming → float in [0, 1]
|
|
66
|
+
segments = vad.get_speech_segments(audio, sample_rate=16000)
|
|
67
|
+
# -> [SpeechSegment(start=0.32, end=2.27), SpeechSegment(start=3.27, end=4.45), ...]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Why
|
|
71
|
+
|
|
72
|
+
Every VAD ships its own loader, audio format, feature pipeline and state handling.
|
|
73
|
+
`cascadevad` hides that behind one `VADModel` interface: feed it audio (raw `int16`
|
|
74
|
+
bytes, numpy arrays, any sample rate) and get back per-frame speech probabilities or
|
|
75
|
+
ready-made speech segments. Models are described *declaratively* by an
|
|
76
|
+
[`IOSignature`](docs/custom_models.md), so a single generic engine drives most of them
|
|
77
|
+
and you can point the same API at any custom `.onnx` file.
|
|
78
|
+
|
|
79
|
+
- **Lightweight runtime** — only `numpy`, `onnxruntime`, `huggingface_hub`.
|
|
80
|
+
- **Offline by default** — a small Silero model is bundled in the wheel.
|
|
81
|
+
- **Streaming and batch** — `process_chunk()` for live audio, `get_speech_segments()` /
|
|
82
|
+
`probabilities()` for whole buffers.
|
|
83
|
+
- **Bring your own model** — load any ONNX VAD by path/URL with a signature.
|
|
84
|
+
- **Extensible** — third parties register backends/models via entry points.
|
|
85
|
+
|
|
86
|
+
## Install
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv pip install cascadevad-onnx # runtime (numpy + onnxruntime + huggingface_hub)
|
|
90
|
+
uv pip install "cascadevad-onnx[mic]" # + microphone examples
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Models
|
|
94
|
+
|
|
95
|
+
| name | rate | parity vs upstream | notes |
|
|
96
|
+
|------|------|--------------------|-------|
|
|
97
|
+
| `silero` / `silero-8k` / `silero-op15` | 16k / 8k / 16k | MAE 0 | bundled default, raw PCM |
|
|
98
|
+
| `marblenet` / `marblenet-int8` | 16k | MAE 4e-4 | NVIDIA NeMo Frame-VAD, multilingual ([license](docs/licensing.md)) |
|
|
99
|
+
| `pyannote` / `pyannote-int8` | 16k | MAE 0 | pyannote segmentation-3.0, windowed |
|
|
100
|
+
| `fsmn` / `fsmn-quant` | 16k | tracks upstream | FunASR FSMN-VAD; needs `cascadevad-onnx[fsmn]` |
|
|
101
|
+
| `speechbrain` | 16k | MAE 0 | SpeechBrain CRDNN, LibriParty-trained |
|
|
102
|
+
| `ten` | 16k | — | feature extractor provided by TEN's native library |
|
|
103
|
+
|
|
104
|
+
See [docs/backends.md](docs/backends.md) for per-model detail and the
|
|
105
|
+
[benchmark](benchmark/results/REPORT.md) for measured comparisons across datasets,
|
|
106
|
+
including WebRTC and energy baselines.
|
|
107
|
+
|
|
108
|
+
Models other than the bundled Silero are downloaded on first use from the
|
|
109
|
+
[`aryashah00/cascadevad-models`](https://huggingface.co/aryashah00/cascadevad-models) HuggingFace
|
|
110
|
+
repo and cached under `$XDG_DATA_HOME/cascadevad`. See [docs/backends.md](docs/backends.md) for
|
|
111
|
+
per-model detail and parity notes.
|
|
112
|
+
|
|
113
|
+
## CLI
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
cascadevad list # list available models
|
|
117
|
+
cascadevad probe silero # print a model's ONNX input/output signature
|
|
118
|
+
cascadevad segment speech.wav # print detected speech segments of a WAV
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Documentation
|
|
122
|
+
|
|
123
|
+
- [Quickstart](docs/quickstart.md)
|
|
124
|
+
- [Streaming](docs/streaming.md)
|
|
125
|
+
- [Custom models & `IOSignature`](docs/custom_models.md)
|
|
126
|
+
- [Backends & parity notes](docs/backends.md)
|
|
127
|
+
- [Plugins](docs/plugins.md)
|
|
128
|
+
- [Model conversion](docs/conversion.md)
|
|
129
|
+
- [Licensing](docs/licensing.md)
|
|
130
|
+
- [API reference](docs/api.md)
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
Apache-2.0. Bundled/downloaded model weights retain their upstream licenses — see
|
|
135
|
+
[docs/licensing.md](docs/licensing.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# cascadevad
|
|
2
|
+
|
|
3
|
+
Load arbitrary **Voice Activity Detection** models behind a single, unified API — every
|
|
4
|
+
model runs through [ONNX Runtime](https://onnxruntime.ai/). One streaming/batch
|
|
5
|
+
interface, one audio-format story, pluggable models.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from cascadevad import load_vad
|
|
9
|
+
|
|
10
|
+
vad = load_vad("silero") # bundled, works fully offline
|
|
11
|
+
prob = vad.process_chunk(pcm_bytes) # streaming → float in [0, 1]
|
|
12
|
+
segments = vad.get_speech_segments(audio, sample_rate=16000)
|
|
13
|
+
# -> [SpeechSegment(start=0.32, end=2.27), SpeechSegment(start=3.27, end=4.45), ...]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
Every VAD ships its own loader, audio format, feature pipeline and state handling.
|
|
19
|
+
`cascadevad` hides that behind one `VADModel` interface: feed it audio (raw `int16`
|
|
20
|
+
bytes, numpy arrays, any sample rate) and get back per-frame speech probabilities or
|
|
21
|
+
ready-made speech segments. Models are described *declaratively* by an
|
|
22
|
+
[`IOSignature`](docs/custom_models.md), so a single generic engine drives most of them
|
|
23
|
+
and you can point the same API at any custom `.onnx` file.
|
|
24
|
+
|
|
25
|
+
- **Lightweight runtime** — only `numpy`, `onnxruntime`, `huggingface_hub`.
|
|
26
|
+
- **Offline by default** — a small Silero model is bundled in the wheel.
|
|
27
|
+
- **Streaming and batch** — `process_chunk()` for live audio, `get_speech_segments()` /
|
|
28
|
+
`probabilities()` for whole buffers.
|
|
29
|
+
- **Bring your own model** — load any ONNX VAD by path/URL with a signature.
|
|
30
|
+
- **Extensible** — third parties register backends/models via entry points.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv pip install cascadevad-onnx # runtime (numpy + onnxruntime + huggingface_hub)
|
|
36
|
+
uv pip install "cascadevad-onnx[mic]" # + microphone examples
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Models
|
|
40
|
+
|
|
41
|
+
| name | rate | parity vs upstream | notes |
|
|
42
|
+
|------|------|--------------------|-------|
|
|
43
|
+
| `silero` / `silero-8k` / `silero-op15` | 16k / 8k / 16k | MAE 0 | bundled default, raw PCM |
|
|
44
|
+
| `marblenet` / `marblenet-int8` | 16k | MAE 4e-4 | NVIDIA NeMo Frame-VAD, multilingual ([license](docs/licensing.md)) |
|
|
45
|
+
| `pyannote` / `pyannote-int8` | 16k | MAE 0 | pyannote segmentation-3.0, windowed |
|
|
46
|
+
| `fsmn` / `fsmn-quant` | 16k | tracks upstream | FunASR FSMN-VAD; needs `cascadevad-onnx[fsmn]` |
|
|
47
|
+
| `speechbrain` | 16k | MAE 0 | SpeechBrain CRDNN, LibriParty-trained |
|
|
48
|
+
| `ten` | 16k | — | feature extractor provided by TEN's native library |
|
|
49
|
+
|
|
50
|
+
See [docs/backends.md](docs/backends.md) for per-model detail and the
|
|
51
|
+
[benchmark](benchmark/results/REPORT.md) for measured comparisons across datasets,
|
|
52
|
+
including WebRTC and energy baselines.
|
|
53
|
+
|
|
54
|
+
Models other than the bundled Silero are downloaded on first use from the
|
|
55
|
+
[`aryashah00/cascadevad-models`](https://huggingface.co/aryashah00/cascadevad-models) HuggingFace
|
|
56
|
+
repo and cached under `$XDG_DATA_HOME/cascadevad`. See [docs/backends.md](docs/backends.md) for
|
|
57
|
+
per-model detail and parity notes.
|
|
58
|
+
|
|
59
|
+
## CLI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cascadevad list # list available models
|
|
63
|
+
cascadevad probe silero # print a model's ONNX input/output signature
|
|
64
|
+
cascadevad segment speech.wav # print detected speech segments of a WAV
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Documentation
|
|
68
|
+
|
|
69
|
+
- [Quickstart](docs/quickstart.md)
|
|
70
|
+
- [Streaming](docs/streaming.md)
|
|
71
|
+
- [Custom models & `IOSignature`](docs/custom_models.md)
|
|
72
|
+
- [Backends & parity notes](docs/backends.md)
|
|
73
|
+
- [Plugins](docs/plugins.md)
|
|
74
|
+
- [Model conversion](docs/conversion.md)
|
|
75
|
+
- [Licensing](docs/licensing.md)
|
|
76
|
+
- [API reference](docs/api.md)
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
Apache-2.0. Bundled/downloaded model weights retain their upstream licenses — see
|
|
81
|
+
[docs/licensing.md](docs/licensing.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""cascadevad — load arbitrary Voice Activity Detection models behind a unified ONNX API.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
from cascadevad import load_vad
|
|
6
|
+
|
|
7
|
+
vad = load_vad("silero") # bundled, works offline
|
|
8
|
+
prob = vad.process_chunk(pcm_bytes) # streaming, float in [0, 1]
|
|
9
|
+
segments = vad.get_speech_segments(audio, sample_rate=16000)
|
|
10
|
+
"""
|
|
11
|
+
from .api import list_models, load_vad, register_model
|
|
12
|
+
from .base import VADModel
|
|
13
|
+
from .cascade import (
|
|
14
|
+
AdmissionController,
|
|
15
|
+
CascadeVAD,
|
|
16
|
+
UncertaintyBandRouter,
|
|
17
|
+
)
|
|
18
|
+
from .registry import ModelSpec
|
|
19
|
+
from .segment import SpeechSegment, probs_to_segments
|
|
20
|
+
from .signature import IOSignature
|
|
21
|
+
from .version import __version__
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"load_vad",
|
|
25
|
+
"list_models",
|
|
26
|
+
"register_model",
|
|
27
|
+
"VADModel",
|
|
28
|
+
"CascadeVAD",
|
|
29
|
+
"UncertaintyBandRouter",
|
|
30
|
+
"AdmissionController",
|
|
31
|
+
"IOSignature",
|
|
32
|
+
"ModelSpec",
|
|
33
|
+
"SpeechSegment",
|
|
34
|
+
"probs_to_segments",
|
|
35
|
+
"__version__",
|
|
36
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Public entry points: :func:`load_vad`, :func:`list_models`, :func:`register_model`."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from .base import VADModel
|
|
7
|
+
from .registry import (
|
|
8
|
+
ModelSpec,
|
|
9
|
+
get_backend,
|
|
10
|
+
get_spec,
|
|
11
|
+
list_models,
|
|
12
|
+
register_model,
|
|
13
|
+
)
|
|
14
|
+
from .resolver import (
|
|
15
|
+
download_url,
|
|
16
|
+
is_onnx_path,
|
|
17
|
+
is_url,
|
|
18
|
+
resolve_spec_file,
|
|
19
|
+
sidecar_signature,
|
|
20
|
+
)
|
|
21
|
+
from .signature import IOSignature, coerce_signature
|
|
22
|
+
|
|
23
|
+
__all__ = ["load_vad", "list_models", "register_model"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_vad(
|
|
27
|
+
name: str = "silero",
|
|
28
|
+
*,
|
|
29
|
+
signature: Union[IOSignature, dict, None] = None,
|
|
30
|
+
threshold: Optional[float] = None,
|
|
31
|
+
neg_threshold: Optional[float] = None,
|
|
32
|
+
providers: Optional[List[str]] = None,
|
|
33
|
+
intra_threads: int = 1,
|
|
34
|
+
inter_threads: int = 1,
|
|
35
|
+
revision: Optional[str] = None,
|
|
36
|
+
cache_dir: Optional[str] = None,
|
|
37
|
+
**backend_kwargs,
|
|
38
|
+
) -> VADModel:
|
|
39
|
+
"""Load a VAD model behind the unified :class:`~cascadevad.base.VADModel` API.
|
|
40
|
+
|
|
41
|
+
``name`` may be a built-in/registered/plugin model name (e.g. ``"silero"``,
|
|
42
|
+
``"ten"``, ``"fsmn"``, ``"marblenet"``), a local ``.onnx`` path, or an
|
|
43
|
+
``http(s)://`` URL. For an arbitrary custom ONNX file, pass ``signature`` (an
|
|
44
|
+
:class:`IOSignature` or dict) describing its IO; if omitted, a
|
|
45
|
+
``<model>.signature.json`` sidecar is used when present.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
name: model name, ``.onnx`` path, or URL.
|
|
49
|
+
signature: required for custom ONNX without a sidecar; ignored for known models
|
|
50
|
+
unless you want to override the registry signature.
|
|
51
|
+
threshold: activation threshold for speech. When omitted, each backend's own
|
|
52
|
+
default applies (0.5 for most models, 0.7 for ``speechbrain``).
|
|
53
|
+
neg_threshold: deactivation threshold (defaults to ``threshold - 0.15``).
|
|
54
|
+
providers: ONNX Runtime execution providers (default CPU).
|
|
55
|
+
intra_threads / inter_threads: ORT threading (default 1 each, low latency).
|
|
56
|
+
revision: pin a HuggingFace revision (overrides the registry default).
|
|
57
|
+
cache_dir: override the download cache directory.
|
|
58
|
+
**backend_kwargs: forwarded to the backend constructor.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
A ready-to-use :class:`~cascadevad.base.VADModel`.
|
|
62
|
+
"""
|
|
63
|
+
sig = coerce_signature(signature)
|
|
64
|
+
|
|
65
|
+
# common backend kwargs; pass threshold only when given so each backend keeps its
|
|
66
|
+
# own default otherwise.
|
|
67
|
+
common = dict(providers=providers, intra_threads=intra_threads,
|
|
68
|
+
inter_threads=inter_threads, neg_threshold=neg_threshold,
|
|
69
|
+
**backend_kwargs)
|
|
70
|
+
if threshold is not None:
|
|
71
|
+
common["threshold"] = threshold
|
|
72
|
+
|
|
73
|
+
# 1. explicit local .onnx file
|
|
74
|
+
if is_onnx_path(name) and not is_url(name):
|
|
75
|
+
import os
|
|
76
|
+
|
|
77
|
+
if not os.path.isfile(name):
|
|
78
|
+
raise FileNotFoundError(f"no such ONNX file: {name}")
|
|
79
|
+
sig = sig or sidecar_signature(name)
|
|
80
|
+
if sig is None:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"loading a raw .onnx ({name}) requires a `signature` "
|
|
83
|
+
"or a <model>.signature.json sidecar"
|
|
84
|
+
)
|
|
85
|
+
from .onnx_backend import OnnxVAD
|
|
86
|
+
|
|
87
|
+
return OnnxVAD(name, sig, **common)
|
|
88
|
+
|
|
89
|
+
# 2. URL
|
|
90
|
+
if is_url(name):
|
|
91
|
+
path = download_url(name, cache_dir)
|
|
92
|
+
sig = sig or sidecar_signature(path)
|
|
93
|
+
if sig is None:
|
|
94
|
+
raise ValueError("loading a URL model requires a `signature`")
|
|
95
|
+
from .onnx_backend import OnnxVAD
|
|
96
|
+
|
|
97
|
+
return OnnxVAD(path, sig, **common)
|
|
98
|
+
|
|
99
|
+
# 3. registry name
|
|
100
|
+
spec: Optional[ModelSpec] = get_spec(name)
|
|
101
|
+
if spec is None:
|
|
102
|
+
raise KeyError(
|
|
103
|
+
f"unknown model {name!r}; known: {list_models()} "
|
|
104
|
+
"(or pass a .onnx path / URL with a signature)"
|
|
105
|
+
)
|
|
106
|
+
path = resolve_spec_file(spec, revision, cache_dir)
|
|
107
|
+
sig = sig or spec.signature
|
|
108
|
+
backend_cls = get_backend(spec.backend)
|
|
109
|
+
return backend_cls(path, sig, **common)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Audio normalization, resampling and WAV reading helpers (numpy-only core).
|
|
2
|
+
|
|
3
|
+
Everything in :mod:`cascadevad` works internally with mono ``float32`` PCM in the range
|
|
4
|
+
``[-1, 1]``. These helpers convert the various shapes users pass in (raw ``int16``
|
|
5
|
+
bytes, numpy int/float arrays, stereo) into that canonical form and resample between
|
|
6
|
+
sample rates.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import wave
|
|
11
|
+
from typing import Tuple, Union
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
AudioLike = Union[bytes, bytearray, memoryview, np.ndarray]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_float32_mono(audio: AudioLike) -> np.ndarray:
|
|
19
|
+
"""Convert audio of various dtypes/containers to mono float32 in [-1, 1].
|
|
20
|
+
|
|
21
|
+
Accepts raw little-endian ``int16`` PCM bytes, or a numpy array of dtype
|
|
22
|
+
``int16`` / ``int32`` / ``float32`` / ``float64``. Multi-channel arrays
|
|
23
|
+
(shape ``(n, channels)``) are downmixed by averaging channels.
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(audio, (bytes, bytearray, memoryview)):
|
|
26
|
+
arr = np.frombuffer(bytes(audio), dtype="<i2").astype(np.float32) / 32768.0
|
|
27
|
+
return arr
|
|
28
|
+
|
|
29
|
+
if not isinstance(audio, np.ndarray):
|
|
30
|
+
raise TypeError(
|
|
31
|
+
f"audio must be bytes or np.ndarray, got {type(audio).__name__}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
arr = audio
|
|
35
|
+
# downmix to mono
|
|
36
|
+
if arr.ndim == 2:
|
|
37
|
+
arr = arr.mean(axis=1)
|
|
38
|
+
elif arr.ndim > 2:
|
|
39
|
+
raise ValueError(f"audio array must be 1-D or 2-D, got {arr.ndim}-D")
|
|
40
|
+
|
|
41
|
+
if arr.dtype == np.float32:
|
|
42
|
+
out = arr
|
|
43
|
+
elif arr.dtype == np.float64:
|
|
44
|
+
out = arr.astype(np.float32)
|
|
45
|
+
elif arr.dtype == np.int16:
|
|
46
|
+
out = arr.astype(np.float32) / 32768.0
|
|
47
|
+
elif arr.dtype == np.int32:
|
|
48
|
+
out = arr.astype(np.float32) / 2147483648.0
|
|
49
|
+
elif arr.dtype == np.uint8: # unsigned 8-bit PCM, midpoint 128
|
|
50
|
+
out = (arr.astype(np.float32) - 128.0) / 128.0
|
|
51
|
+
else:
|
|
52
|
+
raise TypeError(f"unsupported audio dtype: {arr.dtype}")
|
|
53
|
+
|
|
54
|
+
return np.ascontiguousarray(out, dtype=np.float32)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def resample(x: np.ndarray, src_sr: int, dst_sr: int) -> np.ndarray:
|
|
58
|
+
"""Resample mono float32 audio from ``src_sr`` to ``dst_sr``.
|
|
59
|
+
|
|
60
|
+
Uses ``scipy.signal.resample_poly`` when SciPy is installed (the ``[resample]``
|
|
61
|
+
extra), otherwise falls back to linear interpolation via :func:`numpy.interp`,
|
|
62
|
+
which is adequate for voice activity detection framing.
|
|
63
|
+
"""
|
|
64
|
+
if src_sr == dst_sr or x.size == 0:
|
|
65
|
+
return x.astype(np.float32, copy=False)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
from scipy.signal import resample_poly # type: ignore
|
|
69
|
+
|
|
70
|
+
from math import gcd
|
|
71
|
+
|
|
72
|
+
g = gcd(int(src_sr), int(dst_sr))
|
|
73
|
+
up, down = int(dst_sr) // g, int(src_sr) // g
|
|
74
|
+
return resample_poly(x, up, down).astype(np.float32)
|
|
75
|
+
except Exception:
|
|
76
|
+
# linear interpolation fallback (no extra deps)
|
|
77
|
+
n_out = int(round(x.shape[0] * dst_sr / src_sr))
|
|
78
|
+
if n_out <= 0:
|
|
79
|
+
return np.zeros(0, dtype=np.float32)
|
|
80
|
+
src_idx = np.linspace(0.0, x.shape[0] - 1, num=n_out, dtype=np.float64)
|
|
81
|
+
return np.interp(src_idx, np.arange(x.shape[0]), x).astype(np.float32)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def read_wav(path: str) -> Tuple[np.ndarray, int]:
|
|
85
|
+
"""Read a PCM WAV file into mono float32 audio. Returns ``(audio, sample_rate)``.
|
|
86
|
+
|
|
87
|
+
Uses the standard-library :mod:`wave` module so no third-party audio dependency
|
|
88
|
+
is required for examples and tests.
|
|
89
|
+
"""
|
|
90
|
+
with wave.open(path, "rb") as wf:
|
|
91
|
+
sr = wf.getframerate()
|
|
92
|
+
n_channels = wf.getnchannels()
|
|
93
|
+
sampwidth = wf.getsampwidth()
|
|
94
|
+
raw = wf.readframes(wf.getnframes())
|
|
95
|
+
|
|
96
|
+
if sampwidth == 2:
|
|
97
|
+
arr = np.frombuffer(raw, dtype="<i2").astype(np.float32) / 32768.0
|
|
98
|
+
elif sampwidth == 4:
|
|
99
|
+
arr = np.frombuffer(raw, dtype="<i4").astype(np.float32) / 2147483648.0
|
|
100
|
+
elif sampwidth == 1:
|
|
101
|
+
arr = (np.frombuffer(raw, dtype=np.uint8).astype(np.float32) - 128.0) / 128.0
|
|
102
|
+
else:
|
|
103
|
+
raise ValueError(f"unsupported WAV sample width: {sampwidth} bytes")
|
|
104
|
+
|
|
105
|
+
if n_channels > 1:
|
|
106
|
+
arr = arr.reshape(-1, n_channels).mean(axis=1)
|
|
107
|
+
return np.ascontiguousarray(arr, dtype=np.float32), sr
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Optional backend subclasses for models that need glue beyond a plain signature."""
|
|
2
|
+
from .fsmn import FsmnVAD
|
|
3
|
+
from .marblenet import MarbleVAD
|
|
4
|
+
from .pyannote import PyannoteVAD
|
|
5
|
+
from .speechbrain import SbVAD
|
|
6
|
+
from .ten import TenVAD
|
|
7
|
+
|
|
8
|
+
__all__ = ["TenVAD", "FsmnVAD", "MarbleVAD", "SbVAD", "PyannoteVAD"]
|