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.
Files changed (61) hide show
  1. vadonnx-0.1.0/CHANGELOG.md +14 -0
  2. vadonnx-0.1.0/LICENSE +17 -0
  3. vadonnx-0.1.0/MANIFEST.in +5 -0
  4. vadonnx-0.1.0/PKG-INFO +133 -0
  5. vadonnx-0.1.0/README.md +81 -0
  6. vadonnx-0.1.0/pyproject.toml +63 -0
  7. vadonnx-0.1.0/setup.cfg +4 -0
  8. vadonnx-0.1.0/setup.py +6 -0
  9. vadonnx-0.1.0/test/resources/README.md +13 -0
  10. vadonnx-0.1.0/test/resources/speech.wav +0 -0
  11. vadonnx-0.1.0/test/test_api.py +102 -0
  12. vadonnx-0.1.0/test/test_audio.py +54 -0
  13. vadonnx-0.1.0/test/test_backends.py +60 -0
  14. vadonnx-0.1.0/test/test_cli.py +35 -0
  15. vadonnx-0.1.0/test/test_custom_onnx.py +54 -0
  16. vadonnx-0.1.0/test/test_download.py +40 -0
  17. vadonnx-0.1.0/test/test_features.py +35 -0
  18. vadonnx-0.1.0/test/test_internals.py +76 -0
  19. vadonnx-0.1.0/test/test_registry.py +78 -0
  20. vadonnx-0.1.0/test/test_resolver.py +50 -0
  21. vadonnx-0.1.0/test/test_segment.py +68 -0
  22. vadonnx-0.1.0/test/test_signature.py +47 -0
  23. vadonnx-0.1.0/vadonnx/__init__.py +28 -0
  24. vadonnx-0.1.0/vadonnx/api.py +109 -0
  25. vadonnx-0.1.0/vadonnx/audio.py +107 -0
  26. vadonnx-0.1.0/vadonnx/backends/__init__.py +8 -0
  27. vadonnx-0.1.0/vadonnx/backends/fsmn.py +157 -0
  28. vadonnx-0.1.0/vadonnx/backends/marblenet.py +113 -0
  29. vadonnx-0.1.0/vadonnx/backends/pyannote.py +103 -0
  30. vadonnx-0.1.0/vadonnx/backends/speechbrain.py +105 -0
  31. vadonnx-0.1.0/vadonnx/backends/ten.py +126 -0
  32. vadonnx-0.1.0/vadonnx/base.py +176 -0
  33. vadonnx-0.1.0/vadonnx/cli.py +80 -0
  34. vadonnx-0.1.0/vadonnx/convert/__init__.py +9 -0
  35. vadonnx-0.1.0/vadonnx/convert/common.py +148 -0
  36. vadonnx-0.1.0/vadonnx/convert/fsmn.py +78 -0
  37. vadonnx-0.1.0/vadonnx/convert/marblenet.py +83 -0
  38. vadonnx-0.1.0/vadonnx/convert/pyannote.py +52 -0
  39. vadonnx-0.1.0/vadonnx/convert/silero.py +54 -0
  40. vadonnx-0.1.0/vadonnx/convert/speechbrain.py +92 -0
  41. vadonnx-0.1.0/vadonnx/convert/ten.py +70 -0
  42. vadonnx-0.1.0/vadonnx/data/fsmn_cmvn.npz +0 -0
  43. vadonnx-0.1.0/vadonnx/data/marblenet_mel_fb.npy +0 -0
  44. vadonnx-0.1.0/vadonnx/data/marblenet_window.npy +0 -0
  45. vadonnx-0.1.0/vadonnx/data/sb_vad_mel_fb.npy +0 -0
  46. vadonnx-0.1.0/vadonnx/data/sb_vad_window.npy +0 -0
  47. vadonnx-0.1.0/vadonnx/data/silero_vad.onnx +0 -0
  48. vadonnx-0.1.0/vadonnx/data/ten_vad_coeff.npz +0 -0
  49. vadonnx-0.1.0/vadonnx/features.py +126 -0
  50. vadonnx-0.1.0/vadonnx/onnx_backend.py +136 -0
  51. vadonnx-0.1.0/vadonnx/registry.py +287 -0
  52. vadonnx-0.1.0/vadonnx/resolver.py +98 -0
  53. vadonnx-0.1.0/vadonnx/segment.py +129 -0
  54. vadonnx-0.1.0/vadonnx/signature.py +104 -0
  55. vadonnx-0.1.0/vadonnx/version.py +12 -0
  56. vadonnx-0.1.0/vadonnx.egg-info/PKG-INFO +133 -0
  57. vadonnx-0.1.0/vadonnx.egg-info/SOURCES.txt +59 -0
  58. vadonnx-0.1.0/vadonnx.egg-info/dependency_links.txt +1 -0
  59. vadonnx-0.1.0/vadonnx.egg-info/entry_points.txt +2 -0
  60. vadonnx-0.1.0/vadonnx.egg-info/requires.txt +36 -0
  61. 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.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ recursive-include vadonnx/data *.onnx *.json *.npz *.npy
5
+ recursive-include test/resources *
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).
@@ -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"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
vadonnx-0.1.0/setup.py ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env python3
2
+ # Minimal shim so the shared `publish-alpha` automation can read the version
3
+ # via `python setup.py --version`. All real metadata lives in pyproject.toml.
4
+ from setuptools import setup
5
+
6
+ setup()
@@ -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())