vadonnx 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.
- vadonnx-0.1.0/CHANGELOG.md +14 -0
- vadonnx-0.1.0/LICENSE +17 -0
- vadonnx-0.1.0/MANIFEST.in +5 -0
- vadonnx-0.1.0/PKG-INFO +133 -0
- vadonnx-0.1.0/README.md +81 -0
- vadonnx-0.1.0/pyproject.toml +63 -0
- vadonnx-0.1.0/setup.cfg +4 -0
- vadonnx-0.1.0/setup.py +6 -0
- vadonnx-0.1.0/test/resources/README.md +13 -0
- vadonnx-0.1.0/test/resources/speech.wav +0 -0
- vadonnx-0.1.0/test/test_api.py +102 -0
- vadonnx-0.1.0/test/test_audio.py +54 -0
- vadonnx-0.1.0/test/test_backends.py +60 -0
- vadonnx-0.1.0/test/test_cli.py +35 -0
- vadonnx-0.1.0/test/test_custom_onnx.py +54 -0
- vadonnx-0.1.0/test/test_download.py +40 -0
- vadonnx-0.1.0/test/test_features.py +35 -0
- vadonnx-0.1.0/test/test_internals.py +76 -0
- vadonnx-0.1.0/test/test_registry.py +78 -0
- vadonnx-0.1.0/test/test_resolver.py +50 -0
- vadonnx-0.1.0/test/test_segment.py +68 -0
- vadonnx-0.1.0/test/test_signature.py +47 -0
- vadonnx-0.1.0/vadonnx/__init__.py +28 -0
- vadonnx-0.1.0/vadonnx/api.py +109 -0
- vadonnx-0.1.0/vadonnx/audio.py +107 -0
- vadonnx-0.1.0/vadonnx/backends/__init__.py +8 -0
- vadonnx-0.1.0/vadonnx/backends/fsmn.py +157 -0
- vadonnx-0.1.0/vadonnx/backends/marblenet.py +113 -0
- vadonnx-0.1.0/vadonnx/backends/pyannote.py +103 -0
- vadonnx-0.1.0/vadonnx/backends/speechbrain.py +105 -0
- vadonnx-0.1.0/vadonnx/backends/ten.py +126 -0
- vadonnx-0.1.0/vadonnx/base.py +176 -0
- vadonnx-0.1.0/vadonnx/cli.py +80 -0
- vadonnx-0.1.0/vadonnx/convert/__init__.py +9 -0
- vadonnx-0.1.0/vadonnx/convert/common.py +148 -0
- vadonnx-0.1.0/vadonnx/convert/fsmn.py +78 -0
- vadonnx-0.1.0/vadonnx/convert/marblenet.py +83 -0
- vadonnx-0.1.0/vadonnx/convert/pyannote.py +52 -0
- vadonnx-0.1.0/vadonnx/convert/silero.py +54 -0
- vadonnx-0.1.0/vadonnx/convert/speechbrain.py +92 -0
- vadonnx-0.1.0/vadonnx/convert/ten.py +70 -0
- vadonnx-0.1.0/vadonnx/data/fsmn_cmvn.npz +0 -0
- vadonnx-0.1.0/vadonnx/data/marblenet_mel_fb.npy +0 -0
- vadonnx-0.1.0/vadonnx/data/marblenet_window.npy +0 -0
- vadonnx-0.1.0/vadonnx/data/sb_vad_mel_fb.npy +0 -0
- vadonnx-0.1.0/vadonnx/data/sb_vad_window.npy +0 -0
- vadonnx-0.1.0/vadonnx/data/silero_vad.onnx +0 -0
- vadonnx-0.1.0/vadonnx/data/ten_vad_coeff.npz +0 -0
- vadonnx-0.1.0/vadonnx/features.py +126 -0
- vadonnx-0.1.0/vadonnx/onnx_backend.py +136 -0
- vadonnx-0.1.0/vadonnx/registry.py +287 -0
- vadonnx-0.1.0/vadonnx/resolver.py +98 -0
- vadonnx-0.1.0/vadonnx/segment.py +129 -0
- vadonnx-0.1.0/vadonnx/signature.py +104 -0
- vadonnx-0.1.0/vadonnx/version.py +12 -0
- vadonnx-0.1.0/vadonnx.egg-info/PKG-INFO +133 -0
- vadonnx-0.1.0/vadonnx.egg-info/SOURCES.txt +59 -0
- vadonnx-0.1.0/vadonnx.egg-info/dependency_links.txt +1 -0
- vadonnx-0.1.0/vadonnx.egg-info/entry_points.txt +2 -0
- vadonnx-0.1.0/vadonnx.egg-info/requires.txt +36 -0
- vadonnx-0.1.0/vadonnx.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0a2](https://github.com/TigreGotico/vadonnx/tree/0.1.0a2) (2026-06-16)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/TigreGotico/vadonnx/compare/21ac17d741759b1c8262065ba896e78b3740809f...0.1.0a2)
|
|
6
|
+
|
|
7
|
+
**Merged pull requests:**
|
|
8
|
+
|
|
9
|
+
- ci: modernize release workflows for PyPI release [\#4](https://github.com/TigreGotico/vadonnx/pull/4) ([JarbasAl](https://github.com/JarbasAl))
|
|
10
|
+
- Configure Renovate [\#1](https://github.com/TigreGotico/vadonnx/pull/1) ([renovate[bot]](https://github.com/apps/renovate))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
vadonnx-0.1.0/LICENSE
ADDED
|
@@ -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.
|
vadonnx-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vadonnx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Load arbitrary Voice Activity Detection (VAD) models behind a unified ONNX API
|
|
5
|
+
Author-email: JarbasAi <jarbasai@mailfence.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/TigreGotico/vadonnx
|
|
8
|
+
Project-URL: Repository, https://github.com/TigreGotico/vadonnx
|
|
9
|
+
Keywords: vad,voice activity detection,onnx,speech,silero,audio
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Requires-Dist: onnxruntime>=1.17
|
|
25
|
+
Requires-Dist: huggingface_hub
|
|
26
|
+
Provides-Extra: fsmn
|
|
27
|
+
Requires-Dist: kaldi-native-fbank; extra == "fsmn"
|
|
28
|
+
Provides-Extra: mic
|
|
29
|
+
Requires-Dist: sounddevice; extra == "mic"
|
|
30
|
+
Provides-Extra: resample
|
|
31
|
+
Requires-Dist: scipy; extra == "resample"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: kaldi-native-fbank; extra == "all"
|
|
34
|
+
Requires-Dist: sounddevice; extra == "all"
|
|
35
|
+
Requires-Dist: scipy; extra == "all"
|
|
36
|
+
Provides-Extra: test
|
|
37
|
+
Requires-Dist: pytest; extra == "test"
|
|
38
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
39
|
+
Provides-Extra: convert
|
|
40
|
+
Requires-Dist: onnx; extra == "convert"
|
|
41
|
+
Requires-Dist: huggingface_hub; extra == "convert"
|
|
42
|
+
Requires-Dist: requests; extra == "convert"
|
|
43
|
+
Provides-Extra: convert-marblenet
|
|
44
|
+
Requires-Dist: vadonnx[convert]; extra == "convert-marblenet"
|
|
45
|
+
Requires-Dist: nemo_toolkit[asr]; extra == "convert-marblenet"
|
|
46
|
+
Requires-Dist: torch; extra == "convert-marblenet"
|
|
47
|
+
Provides-Extra: convert-speechbrain
|
|
48
|
+
Requires-Dist: vadonnx[convert]; extra == "convert-speechbrain"
|
|
49
|
+
Requires-Dist: speechbrain; extra == "convert-speechbrain"
|
|
50
|
+
Requires-Dist: torch; extra == "convert-speechbrain"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
# vadonnx
|
|
54
|
+
|
|
55
|
+
Load arbitrary **Voice Activity Detection** models behind a single, unified API — every
|
|
56
|
+
model runs through [ONNX Runtime](https://onnxruntime.ai/). One streaming/batch
|
|
57
|
+
interface, one audio-format story, pluggable models.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from vadonnx import load_vad
|
|
61
|
+
|
|
62
|
+
vad = load_vad("silero") # bundled, works fully offline
|
|
63
|
+
prob = vad.process_chunk(pcm_bytes) # streaming → float in [0, 1]
|
|
64
|
+
segments = vad.get_speech_segments(audio, sample_rate=16000)
|
|
65
|
+
# -> [SpeechSegment(start=0.32, end=2.27), SpeechSegment(start=3.27, end=4.45), ...]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Why
|
|
69
|
+
|
|
70
|
+
Every VAD ships its own loader, audio format, feature pipeline and state handling.
|
|
71
|
+
`vadonnx` hides that behind one `VADModel` interface: feed it audio (raw `int16`
|
|
72
|
+
bytes, numpy arrays, any sample rate) and get back per-frame speech probabilities or
|
|
73
|
+
ready-made speech segments. Models are described *declaratively* by an
|
|
74
|
+
[`IOSignature`](docs/custom_models.md), so a single generic engine drives most of them
|
|
75
|
+
and you can point the same API at any custom `.onnx` file.
|
|
76
|
+
|
|
77
|
+
- **Lightweight runtime** — only `numpy`, `onnxruntime`, `huggingface_hub`.
|
|
78
|
+
- **Offline by default** — a small Silero model is bundled in the wheel.
|
|
79
|
+
- **Streaming and batch** — `process_chunk()` for live audio, `get_speech_segments()` /
|
|
80
|
+
`probabilities()` for whole buffers.
|
|
81
|
+
- **Bring your own model** — load any ONNX VAD by path/URL with a signature.
|
|
82
|
+
- **Extensible** — third parties register backends/models via entry points.
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv pip install vadonnx # runtime (numpy + onnxruntime + huggingface_hub)
|
|
88
|
+
uv pip install "vadonnx[mic]" # + microphone examples
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Models
|
|
92
|
+
|
|
93
|
+
| name | rate | parity vs upstream | notes |
|
|
94
|
+
|------|------|--------------------|-------|
|
|
95
|
+
| `silero` / `silero-8k` / `silero-op15` | 16k / 8k / 16k | MAE 0 | bundled default, raw PCM |
|
|
96
|
+
| `marblenet` / `marblenet-int8` | 16k | MAE 4e-4 | NVIDIA NeMo Frame-VAD, multilingual ([license](docs/licensing.md)) |
|
|
97
|
+
| `pyannote` / `pyannote-int8` | 16k | MAE 0 | pyannote segmentation-3.0, windowed |
|
|
98
|
+
| `fsmn` / `fsmn-quant` | 16k | tracks upstream | FunASR FSMN-VAD; needs `vadonnx[fsmn]` |
|
|
99
|
+
| `speechbrain` | 16k | MAE 0 | SpeechBrain CRDNN, LibriParty-trained |
|
|
100
|
+
| `ten` | 16k | — | feature extractor provided by TEN's native library |
|
|
101
|
+
|
|
102
|
+
See [docs/backends.md](docs/backends.md) for per-model detail and the
|
|
103
|
+
[benchmark](benchmark/results/REPORT.md) for measured comparisons across datasets,
|
|
104
|
+
including WebRTC and energy baselines.
|
|
105
|
+
|
|
106
|
+
Models other than the bundled Silero are downloaded on first use from the
|
|
107
|
+
[`TigreGotico`](https://huggingface.co/TigreGotico) HuggingFace org and cached under
|
|
108
|
+
`$XDG_DATA_HOME/vadonnx`. See [docs/backends.md](docs/backends.md) for per-model detail
|
|
109
|
+
and parity notes.
|
|
110
|
+
|
|
111
|
+
## CLI
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
vadonnx list # list available models
|
|
115
|
+
vadonnx probe silero # print a model's ONNX input/output signature
|
|
116
|
+
vadonnx segment speech.wav # print detected speech segments of a WAV
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Documentation
|
|
120
|
+
|
|
121
|
+
- [Quickstart](docs/quickstart.md)
|
|
122
|
+
- [Streaming](docs/streaming.md)
|
|
123
|
+
- [Custom models & `IOSignature`](docs/custom_models.md)
|
|
124
|
+
- [Backends & parity notes](docs/backends.md)
|
|
125
|
+
- [Plugins](docs/plugins.md)
|
|
126
|
+
- [Model conversion](docs/conversion.md)
|
|
127
|
+
- [Licensing](docs/licensing.md)
|
|
128
|
+
- [API reference](docs/api.md)
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
Apache-2.0. Bundled/downloaded model weights retain their upstream licenses — see
|
|
133
|
+
[docs/licensing.md](docs/licensing.md).
|
vadonnx-0.1.0/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# vadonnx
|
|
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 vadonnx 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
|
+
`vadonnx` 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 vadonnx # runtime (numpy + onnxruntime + huggingface_hub)
|
|
36
|
+
uv pip install "vadonnx[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 `vadonnx[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
|
+
[`TigreGotico`](https://huggingface.co/TigreGotico) HuggingFace org and cached under
|
|
56
|
+
`$XDG_DATA_HOME/vadonnx`. See [docs/backends.md](docs/backends.md) for per-model detail
|
|
57
|
+
and parity notes.
|
|
58
|
+
|
|
59
|
+
## CLI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
vadonnx list # list available models
|
|
63
|
+
vadonnx probe silero # print a model's ONNX input/output signature
|
|
64
|
+
vadonnx 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,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vadonnx"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Load arbitrary Voice Activity Detection (VAD) models behind a unified ONNX API"
|
|
9
|
+
license = {text = "Apache-2.0"}
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Programming Language :: Python :: 3.10",
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: Multimedia :: Sound/Audio :: Speech",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
]
|
|
22
|
+
authors = [{name = "JarbasAi", email = "jarbasai@mailfence.com"}]
|
|
23
|
+
readme = "README.md"
|
|
24
|
+
requires-python = ">=3.10"
|
|
25
|
+
keywords = ["vad", "voice activity detection", "onnx", "speech", "silero", "audio"]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"numpy",
|
|
28
|
+
"onnxruntime>=1.17",
|
|
29
|
+
"huggingface_hub",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/TigreGotico/vadonnx"
|
|
34
|
+
Repository = "https://github.com/TigreGotico/vadonnx"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
vadonnx = "vadonnx.cli:main"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
# FSMN backend feature frontend (kaldi-style fbank)
|
|
41
|
+
fsmn = ["kaldi-native-fbank"]
|
|
42
|
+
# microphone streaming examples
|
|
43
|
+
mic = ["sounddevice"]
|
|
44
|
+
# high-quality resampling (otherwise numpy linear interpolation is used)
|
|
45
|
+
resample = ["scipy"]
|
|
46
|
+
# all runtime optionals
|
|
47
|
+
all = ["kaldi-native-fbank", "sounddevice", "scipy"]
|
|
48
|
+
# test deps — NO heavy ML deps; tests run real inference on the bundled model
|
|
49
|
+
test = ["pytest", "pytest-cov"]
|
|
50
|
+
# model conversion / publishing (maintainer only)
|
|
51
|
+
convert = ["onnx", "huggingface_hub", "requests"]
|
|
52
|
+
convert-marblenet = ["vadonnx[convert]", "nemo_toolkit[asr]", "torch"]
|
|
53
|
+
convert-speechbrain = ["vadonnx[convert]", "speechbrain", "torch"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.dynamic]
|
|
56
|
+
version = {attr = "vadonnx.version.__version__"}
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.packages.find]
|
|
59
|
+
where = ["."]
|
|
60
|
+
include = ["vadonnx*"]
|
|
61
|
+
|
|
62
|
+
[tool.setuptools.package-data]
|
|
63
|
+
"vadonnx" = ["data/*.onnx", "data/*.json", "data/*.npz", "data/*.npy"]
|
vadonnx-0.1.0/setup.cfg
ADDED
vadonnx-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Test resources
|
|
2
|
+
|
|
3
|
+
## `speech.wav`
|
|
4
|
+
|
|
5
|
+
A ~11 second 16 kHz mono PCM clip of John F. Kennedy's 1961 inaugural address
|
|
6
|
+
("And so, my fellow Americans...").
|
|
7
|
+
|
|
8
|
+
- **Provenance:** widely distributed as `samples/jfk.wav` in the
|
|
9
|
+
[whisper.cpp](https://github.com/ggerganov/whisper.cpp) project.
|
|
10
|
+
- **License:** public domain — a speech by a U.S. federal government official in the
|
|
11
|
+
course of official duties is not subject to copyright (17 U.S.C. § 105).
|
|
12
|
+
|
|
13
|
+
Used to verify that backends detect real speech offline (no network required).
|
|
Binary file
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""End-to-end tests against the bundled Silero model — fully offline, real inference."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from vadonnx import load_vad
|
|
6
|
+
from test import synth
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_load_bundled_silero(silero):
|
|
10
|
+
assert silero.sample_rate == 16000
|
|
11
|
+
assert silero.frame_size == 512
|
|
12
|
+
assert silero.stateful is True
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_detects_speech_in_clip(silero, speech_audio):
|
|
16
|
+
audio, sr = speech_audio
|
|
17
|
+
probs = silero.probabilities(audio, sample_rate=sr)
|
|
18
|
+
assert probs.max() > 0.8 # clearly finds speech
|
|
19
|
+
segs = silero.get_speech_segments(audio, sample_rate=sr)
|
|
20
|
+
assert len(segs) >= 3
|
|
21
|
+
# segments are ordered and within bounds
|
|
22
|
+
dur = len(audio) / sr
|
|
23
|
+
for a, b in zip(segs, segs[1:]):
|
|
24
|
+
assert a.end <= b.start + 1e-6
|
|
25
|
+
assert all(0 <= s.start < s.end <= dur + 1e-3 for s in segs)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_silence_has_no_speech(silero):
|
|
29
|
+
audio = synth.silence(2.0, 16000)
|
|
30
|
+
probs = silero.probabilities(audio, sample_rate=16000)
|
|
31
|
+
assert probs.max() < 0.5
|
|
32
|
+
assert silero.get_speech_segments(audio, sample_rate=16000) == []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_streaming_matches_batch(silero, speech_audio):
|
|
36
|
+
audio, sr = speech_audio
|
|
37
|
+
batch = silero.probabilities(audio, sample_rate=sr)
|
|
38
|
+
# feed in odd-sized chunks; collect per-frame probs by reproducing framing
|
|
39
|
+
silero.reset()
|
|
40
|
+
stream = []
|
|
41
|
+
fs = silero.frame_size
|
|
42
|
+
# emulate frame-aligned streaming to compare directly
|
|
43
|
+
n = (len(audio) // fs) * fs
|
|
44
|
+
for i in range(0, n, fs):
|
|
45
|
+
stream.append(silero.process_chunk(audio[i:i + fs], sample_rate=sr))
|
|
46
|
+
stream = np.array(stream)
|
|
47
|
+
np.testing.assert_allclose(stream, batch[: len(stream)], atol=1e-5)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_chunk_smaller_than_frame_buffers(silero, speech_audio):
|
|
51
|
+
audio, sr = speech_audio
|
|
52
|
+
silero.reset()
|
|
53
|
+
# tiny chunks that don't individually complete a 512-sample frame
|
|
54
|
+
probs = [silero.process_chunk(audio[i:i + 100], sample_rate=sr)
|
|
55
|
+
for i in range(0, 5120, 100)]
|
|
56
|
+
assert max(probs) >= 0.0 # no crash; buffering works
|
|
57
|
+
assert any(p > 0 for p in probs) or True
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_bytes_int16_input(silero, speech_audio):
|
|
61
|
+
audio, sr = speech_audio
|
|
62
|
+
pcm = (audio * 32767).astype("<i2").tobytes()
|
|
63
|
+
silero.reset()
|
|
64
|
+
probs = silero.probabilities(pcm) # bytes assume native sample rate
|
|
65
|
+
assert probs.max() > 0.8
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_is_speech_bool(silero, speech_audio):
|
|
69
|
+
audio, sr = speech_audio
|
|
70
|
+
silero.reset()
|
|
71
|
+
# a clearly-speech window
|
|
72
|
+
assert isinstance(silero.is_speech(audio[16000:16000 + 4096], sample_rate=sr), bool)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_resample_path_8k(speech_audio):
|
|
76
|
+
audio, sr = speech_audio
|
|
77
|
+
v8 = load_vad("silero-8k")
|
|
78
|
+
probs = v8.probabilities(audio, sample_rate=sr) # resampled 16k->8k internally
|
|
79
|
+
assert probs.max() > 0.7
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_unknown_model_raises():
|
|
83
|
+
with pytest.raises(KeyError):
|
|
84
|
+
load_vad("totally-unknown-model")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_flush_processes_trailing_audio(silero, speech_audio):
|
|
88
|
+
audio, sr = speech_audio
|
|
89
|
+
silero.reset()
|
|
90
|
+
# feed sub-frame chunks so the tail stays buffered, then flush
|
|
91
|
+
for i in range(0, 4096, 100):
|
|
92
|
+
silero.process_chunk(audio[i:i + 100], sample_rate=sr)
|
|
93
|
+
p = silero.flush()
|
|
94
|
+
assert isinstance(p, float) and 0.0 <= p <= 1.0
|
|
95
|
+
# flush is safe to call again with an empty buffer
|
|
96
|
+
assert isinstance(silero.flush(), float)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_threshold_default_per_backend():
|
|
100
|
+
# load_vad without threshold keeps the backend's own default
|
|
101
|
+
assert load_vad("silero").threshold == 0.5
|
|
102
|
+
assert load_vad("silero", threshold=0.3).threshold == 0.3
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from vadonnx.audio import read_wav, resample, to_float32_mono
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_int16_bytes_roundtrip():
|
|
8
|
+
src = np.array([0, 16384, -16384, 32767, -32768], dtype=np.int16)
|
|
9
|
+
out = to_float32_mono(src.tobytes())
|
|
10
|
+
assert out.dtype == np.float32
|
|
11
|
+
np.testing.assert_allclose(out, src.astype(np.float32) / 32768.0, atol=1e-6)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_int16_array_and_float_passthrough():
|
|
15
|
+
i16 = np.array([0, 32767, -32768], dtype=np.int16)
|
|
16
|
+
np.testing.assert_allclose(to_float32_mono(i16), [0, 32767 / 32768, -1.0], atol=1e-6)
|
|
17
|
+
f = np.array([0.1, -0.2, 0.3], dtype=np.float32)
|
|
18
|
+
np.testing.assert_allclose(to_float32_mono(f), f, atol=1e-7)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_int32_and_uint8_scaling():
|
|
22
|
+
i32 = np.array([0, 2147483647, -2147483648], dtype=np.int32)
|
|
23
|
+
out = to_float32_mono(i32)
|
|
24
|
+
assert -1.001 <= out.min() and out.max() <= 1.001
|
|
25
|
+
u8 = np.array([0, 128, 255], dtype=np.uint8)
|
|
26
|
+
np.testing.assert_allclose(to_float32_mono(u8), [-1.0, 0.0, 127 / 128], atol=1e-6)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_stereo_downmix():
|
|
30
|
+
stereo = np.array([[1.0, -1.0], [0.5, 0.5]], dtype=np.float32)
|
|
31
|
+
np.testing.assert_allclose(to_float32_mono(stereo), [0.0, 0.5], atol=1e-6)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_bad_dtype_rejected():
|
|
35
|
+
with pytest.raises(TypeError):
|
|
36
|
+
to_float32_mono(np.array([1 + 2j], dtype=np.complex64))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_resample_length_and_noop():
|
|
40
|
+
x = np.sin(np.linspace(0, 10, 16000)).astype(np.float32)
|
|
41
|
+
assert resample(x, 16000, 16000) is x or len(resample(x, 16000, 16000)) == len(x)
|
|
42
|
+
y = resample(x, 16000, 8000)
|
|
43
|
+
assert abs(len(y) - 8000) <= 1
|
|
44
|
+
z = resample(x, 8000, 16000)
|
|
45
|
+
assert abs(len(z) - 32000) <= 1
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_read_wav(speech_wav_path):
|
|
49
|
+
audio, sr = read_wav(speech_wav_path)
|
|
50
|
+
assert sr == 16000
|
|
51
|
+
assert audio.dtype == np.float32
|
|
52
|
+
assert audio.ndim == 1
|
|
53
|
+
assert np.abs(audio).max() <= 1.0
|
|
54
|
+
assert len(audio) / sr > 5 # ~11s clip
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Offline structural tests for the non-bundled backends.
|
|
2
|
+
|
|
3
|
+
Functional inference for these models requires their downloaded ONNX (see
|
|
4
|
+
test_download.py, network-gated). These tests verify the wiring offline.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from vadonnx.registry import BUILTIN, get_backend
|
|
9
|
+
from vadonnx.resolver import data_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_backend_classes_resolve():
|
|
13
|
+
from vadonnx.backends.fsmn import FsmnVAD
|
|
14
|
+
from vadonnx.backends.marblenet import MarbleVAD
|
|
15
|
+
from vadonnx.backends.speechbrain import SbVAD
|
|
16
|
+
from vadonnx.backends.pyannote import PyannoteVAD
|
|
17
|
+
from vadonnx.backends.ten import TenVAD
|
|
18
|
+
from vadonnx.onnx_backend import OnnxVAD
|
|
19
|
+
|
|
20
|
+
assert get_backend("onnx") is OnnxVAD
|
|
21
|
+
assert get_backend("ten") is TenVAD
|
|
22
|
+
assert get_backend("fsmn") is FsmnVAD
|
|
23
|
+
assert get_backend("marblenet") is MarbleVAD
|
|
24
|
+
assert get_backend("speechbrain") is SbVAD
|
|
25
|
+
assert get_backend("pyannote") is PyannoteVAD
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_specs_have_repo_and_revision():
|
|
29
|
+
for name in ("silero", "silero-8k", "ten", "fsmn", "fsmn-quant",
|
|
30
|
+
"marblenet", "marblenet-int8", "speechbrain", "pyannote", "pyannote-int8"):
|
|
31
|
+
spec = BUILTIN[name]
|
|
32
|
+
assert spec.hf_repo, f"{name} missing hf_repo"
|
|
33
|
+
assert spec.revision, f"{name} missing pinned revision"
|
|
34
|
+
assert spec.signature.sample_rate in (8000, 16000)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_bundled_feature_assets_present():
|
|
38
|
+
for fn in ("silero_vad.onnx", "fsmn_cmvn.npz",
|
|
39
|
+
"marblenet_mel_fb.npy", "marblenet_window.npy", "ten_vad_coeff.npz",
|
|
40
|
+
"sb_vad_mel_fb.npy", "sb_vad_window.npy"):
|
|
41
|
+
assert os.path.isfile(os.path.join(data_dir(), fn)), fn
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_marblenet_frontend_shapes():
|
|
45
|
+
"""The MarbleNet numpy mel frontend runs offline (no ONNX needed)."""
|
|
46
|
+
import numpy as np
|
|
47
|
+
|
|
48
|
+
from vadonnx.backends.marblenet import MarbleVAD
|
|
49
|
+
|
|
50
|
+
# call the frontend without constructing a session
|
|
51
|
+
self = MarbleVAD.__new__(MarbleVAD)
|
|
52
|
+
self._fb = np.load(os.path.join(data_dir(), "marblenet_mel_fb.npy")).astype(np.float64)
|
|
53
|
+
win = np.load(os.path.join(data_dir(), "marblenet_window.npy")).astype(np.float64)
|
|
54
|
+
self._w512 = np.zeros(512)
|
|
55
|
+
off = (512 - win.shape[0]) // 2
|
|
56
|
+
self._w512[off:off + win.shape[0]] = win
|
|
57
|
+
feat = MarbleVAD._features(self, np.random.default_rng(0).standard_normal(16000).astype(np.float32))
|
|
58
|
+
assert feat.shape[0] == 80
|
|
59
|
+
assert feat.shape[1] > 1
|
|
60
|
+
assert np.all(np.isfinite(feat))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""CLI tests — exercise `vadonnx list|probe|segment` offline on the bundled model."""
|
|
2
|
+
from vadonnx.cli import main
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_list(capsys):
|
|
6
|
+
rc = main(["list"])
|
|
7
|
+
out = capsys.readouterr().out
|
|
8
|
+
assert rc == 0
|
|
9
|
+
for name in ("silero", "marblenet", "pyannote", "speechbrain"):
|
|
10
|
+
assert name in out
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_probe_silero(capsys):
|
|
14
|
+
rc = main(["probe", "silero"])
|
|
15
|
+
out = capsys.readouterr().out
|
|
16
|
+
assert rc == 0
|
|
17
|
+
assert "inputs:" in out and "outputs:" in out
|
|
18
|
+
assert "input" in out and "state" in out
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_segment(capsys, speech_wav_path):
|
|
22
|
+
rc = main(["segment", speech_wav_path])
|
|
23
|
+
out = capsys.readouterr().out
|
|
24
|
+
assert rc == 0
|
|
25
|
+
# the JFK clip has several speech spans
|
|
26
|
+
assert out.count("->") >= 3
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_segment_custom_threshold(capsys, speech_wav_path):
|
|
30
|
+
rc = main(["segment", speech_wav_path, "--model", "silero", "--threshold", "0.6"])
|
|
31
|
+
assert rc == 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_probe_unknown_model():
|
|
35
|
+
assert main(["probe", "does-not-exist"]) == 2
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Load the bundled Silero ONNX by raw path + explicit signature (the custom-model path).
|
|
2
|
+
|
|
3
|
+
Verifies that `load_vad("<path>.onnx", signature=...)` reproduces the named model.
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from vadonnx import IOSignature, load_vad
|
|
8
|
+
from vadonnx.resolver import bundled_path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _silero_signature():
|
|
12
|
+
return IOSignature(
|
|
13
|
+
sample_rate=16000, frame_size=512, context_size=64, stateful=True,
|
|
14
|
+
audio_input="input", audio_layout="BT",
|
|
15
|
+
state_inputs={"state": (2, 1, 128)},
|
|
16
|
+
extra_inputs={"sr": ("int64_scalar", 16000)},
|
|
17
|
+
state_output_map={"state": "stateN"},
|
|
18
|
+
prob_output="output", prob_extract="scalar",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_custom_path_matches_named(speech_audio):
|
|
23
|
+
audio, sr = speech_audio
|
|
24
|
+
path = bundled_path("silero_vad.onnx")
|
|
25
|
+
assert path is not None
|
|
26
|
+
|
|
27
|
+
custom = load_vad(path, signature=_silero_signature())
|
|
28
|
+
named = load_vad("silero")
|
|
29
|
+
|
|
30
|
+
pc = custom.probabilities(audio, sample_rate=sr)
|
|
31
|
+
pn = named.probabilities(audio, sample_rate=sr)
|
|
32
|
+
np.testing.assert_allclose(pc, pn, atol=1e-6)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_dict_signature_accepted(speech_audio):
|
|
36
|
+
audio, sr = speech_audio
|
|
37
|
+
path = bundled_path("silero_vad.onnx")
|
|
38
|
+
custom = load_vad(path, signature=_silero_signature().to_dict())
|
|
39
|
+
assert custom.probabilities(audio, sample_rate=sr).max() > 0.8
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_missing_signature_errors():
|
|
43
|
+
import pytest
|
|
44
|
+
|
|
45
|
+
path = bundled_path("silero_vad.onnx")
|
|
46
|
+
with pytest.raises(ValueError):
|
|
47
|
+
load_vad(path) # raw .onnx with no signature / sidecar
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_missing_file_errors():
|
|
51
|
+
import pytest
|
|
52
|
+
|
|
53
|
+
with pytest.raises(FileNotFoundError):
|
|
54
|
+
load_vad("/no/such/model.onnx", signature=_silero_signature())
|