audiosronnx 0.0.1__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.
- audiosronnx-0.0.1/LICENSE +17 -0
- audiosronnx-0.0.1/MANIFEST.in +3 -0
- audiosronnx-0.0.1/PKG-INFO +179 -0
- audiosronnx-0.0.1/README.md +142 -0
- audiosronnx-0.0.1/audiosronnx/__init__.py +39 -0
- audiosronnx-0.0.1/audiosronnx/api.py +45 -0
- audiosronnx-0.0.1/audiosronnx/audio.py +128 -0
- audiosronnx-0.0.1/audiosronnx/base.py +160 -0
- audiosronnx-0.0.1/audiosronnx/cli.py +103 -0
- audiosronnx-0.0.1/audiosronnx/engines/__init__.py +27 -0
- audiosronnx-0.0.1/audiosronnx/engines/lavasr.py +289 -0
- audiosronnx-0.0.1/audiosronnx/engines/novasr.py +99 -0
- audiosronnx-0.0.1/audiosronnx/resolver.py +92 -0
- audiosronnx-0.0.1/audiosronnx/version.py +13 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/PKG-INFO +179 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/SOURCES.txt +27 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/dependency_links.txt +1 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/entry_points.txt +2 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/requires.txt +16 -0
- audiosronnx-0.0.1/audiosronnx.egg-info/top_level.txt +1 -0
- audiosronnx-0.0.1/pyproject.toml +57 -0
- audiosronnx-0.0.1/setup.cfg +4 -0
- audiosronnx-0.0.1/test/test_audio.py +59 -0
- audiosronnx-0.0.1/test/test_base.py +77 -0
- audiosronnx-0.0.1/test/test_cli.py +28 -0
- audiosronnx-0.0.1/test/test_e2e_lavasr.py +29 -0
- audiosronnx-0.0.1/test/test_e2e_novasr.py +21 -0
- audiosronnx-0.0.1/test/test_registry.py +53 -0
- audiosronnx-0.0.1/test/test_resolver.py +48 -0
|
@@ -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,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audiosronnx
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Pure-ONNX multi-engine audio super-resolution / bandwidth extension (upscale speech to 48 kHz)
|
|
5
|
+
Author-email: JarbasAi <jarbasai@mailfence.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/TigreGotico/audiosronnx
|
|
8
|
+
Project-URL: Repository, https://github.com/TigreGotico/audiosronnx
|
|
9
|
+
Keywords: audio super resolution,bandwidth extension,speech enhancement,onnx,upsampling,lavasr,novasr,48khz
|
|
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: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: onnxruntime>=1.17
|
|
24
|
+
Requires-Dist: huggingface_hub
|
|
25
|
+
Requires-Dist: scipy
|
|
26
|
+
Requires-Dist: soundfile
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest; extra == "test"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
30
|
+
Provides-Extra: convert
|
|
31
|
+
Requires-Dist: torch; extra == "convert"
|
|
32
|
+
Requires-Dist: onnx; extra == "convert"
|
|
33
|
+
Requires-Dist: huggingface_hub; extra == "convert"
|
|
34
|
+
Requires-Dist: vocos; extra == "convert"
|
|
35
|
+
Requires-Dist: einops; extra == "convert"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# audiosronnx
|
|
39
|
+
|
|
40
|
+
Pure-ONNX, multi-engine **audio super-resolution / bandwidth extension**. Upscale
|
|
41
|
+
low-sample-rate or low-bandwidth speech — 8 kHz telephony, muffled recordings,
|
|
42
|
+
low-fidelity TTS — to a clean **48 kHz** signal. Inference runs entirely on
|
|
43
|
+
onnxruntime; there is **no torch at runtime**.
|
|
44
|
+
|
|
45
|
+
audiosronnx is the super-resolution sibling of the TigreGotico pure-ONNX speech
|
|
46
|
+
libraries ([`vadonnx`](https://github.com/TigreGotico/vadonnx),
|
|
47
|
+
[`voiceclonnx`](https://github.com/TigreGotico/voiceclonnx),
|
|
48
|
+
[`speakeronnx`](https://github.com/TigreGotico/speakeronnx)) and mirrors their
|
|
49
|
+
conventions: a single `load_sr(engine=...)` entry point, an engine registry, a
|
|
50
|
+
HuggingFace-backed model resolver with an XDG cache, and a small CLI.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install audiosronnx
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Runtime dependencies: `numpy`, `onnxruntime`, `scipy`, `soundfile`, `huggingface_hub`.
|
|
59
|
+
ONNX models are downloaded on first use from the Hugging Face Hub and cached under
|
|
60
|
+
`~/.local/share/audiosronnx`.
|
|
61
|
+
|
|
62
|
+
## Engines
|
|
63
|
+
|
|
64
|
+
| Engine | Input SR | Output SR | Size | CPU speed | License | Status |
|
|
65
|
+
|--------|----------|-----------|------|-----------|---------|--------|
|
|
66
|
+
| **lavasr** (default) | 8–48 kHz | 48 kHz | ~52 MB | ~50× realtime | Apache-2.0 | shipped |
|
|
67
|
+
| **novasr** | 16 kHz | 48 kHz | ~52 KB | ~1000× realtime | Apache-2.0 | shipped |
|
|
68
|
+
|
|
69
|
+
- **lavasr** — a Vocos-based bandwidth-extension model with a Linkwitz-Riley spectral
|
|
70
|
+
merge that preserves the original low band, plus an optional UL-UNAS denoiser. It
|
|
71
|
+
accepts any input rate from 8 to 48 kHz and is the best-quality engine. All spectral
|
|
72
|
+
DSP (STFT, ISTFT, mel filterbank, resampling, merge) runs in numpy/scipy, so no
|
|
73
|
+
`torch.stft` ever enters an ONNX graph.
|
|
74
|
+
- **novasr** — a tiny conv1d / BigVGAN-snake generator that upsamples 16 kHz to 48 kHz
|
|
75
|
+
in a single time-domain pass. Extremely fast and memory-light, at lower fidelity than
|
|
76
|
+
lavasr; useful for on-device enhancement and quick dataset restoration.
|
|
77
|
+
|
|
78
|
+
Both engines are derived from the LavaSR/NovaSR projects by Yatharth Sharma
|
|
79
|
+
([LavaSR](https://github.com/ysharma3501/LavaSR),
|
|
80
|
+
[NovaSR](https://github.com/ysharma3501/NovaSR)), Apache-2.0.
|
|
81
|
+
|
|
82
|
+
## Quickstart
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from audiosronnx import load_sr
|
|
86
|
+
|
|
87
|
+
sr = load_sr("lavasr") # default engine
|
|
88
|
+
|
|
89
|
+
# low-level primitive: numpy array or path in, (float32 mono, 48000) out
|
|
90
|
+
out, rate = sr.upscale("telephone_8k.wav") # -> (np.ndarray float32, 48000)
|
|
91
|
+
|
|
92
|
+
# array input (you supply the sample rate)
|
|
93
|
+
import soundfile as sf
|
|
94
|
+
audio, in_sr = sf.read("input.wav")
|
|
95
|
+
out, rate = sr.upscale(audio, in_sr)
|
|
96
|
+
|
|
97
|
+
# file / directory helpers
|
|
98
|
+
sr.upscale_file("in.wav", "out_48k.wav")
|
|
99
|
+
sr.upscale_dir("clips_in/", "clips_out_48k/")
|
|
100
|
+
|
|
101
|
+
# the fast tiny engine instead
|
|
102
|
+
fast = load_sr("novasr")
|
|
103
|
+
fast.upscale_file("in_16k.wav", "out_48k.wav")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
LavaSR options:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
sr = load_sr("lavasr", denoise=True, cutoff_hz=6000)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## CLI
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
audiosronnx list # list engines
|
|
116
|
+
audiosronnx probe input.wav # print format / duration
|
|
117
|
+
audiosronnx upscale in.wav out.wav # default engine (lavasr)
|
|
118
|
+
audiosronnx upscale in.wav out.wav --engine novasr
|
|
119
|
+
audiosronnx upscale in.wav out.wav --denoise # lavasr denoiser stage
|
|
120
|
+
audiosronnx upscale-dir clips_in/ clips_out/
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
- `load_sr(engine="lavasr", *, providers=None, cache_dir=None, revision=None, **kwargs) -> SRModel`
|
|
126
|
+
- `SRModel.upscale(audio, sample_rate=None) -> (np.ndarray float32 mono, 48000)` —
|
|
127
|
+
`audio` is a path, raw int16 PCM bytes, or a numpy array. The low-level primitive.
|
|
128
|
+
- `SRModel.upscale_file(in_path, out_path) -> str`
|
|
129
|
+
- `SRModel.upscale_dir(in_dir, out_dir) -> list[str]`
|
|
130
|
+
- `available_models()`, `get_engine(alias)`, `register_engine(entry)`, `ENGINE_REGISTRY`
|
|
131
|
+
|
|
132
|
+
### Custom / third-party engines
|
|
133
|
+
|
|
134
|
+
Subclass `SRModel`, implement `_upscale_array(audio, sample_rate) -> np.ndarray` (a
|
|
135
|
+
48 kHz mono float32 array), and register an `EngineEntry`:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from audiosronnx import SRModel, EngineEntry, register_engine
|
|
139
|
+
|
|
140
|
+
class MySR(SRModel):
|
|
141
|
+
output_sample_rate = 48000
|
|
142
|
+
def _upscale_array(self, audio, sample_rate):
|
|
143
|
+
... # run your ONNX session, return a 48 kHz float32 array
|
|
144
|
+
|
|
145
|
+
register_engine(EngineEntry(alias="mysr", adapter_class=MySR, license="MIT"))
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Model hosting
|
|
149
|
+
|
|
150
|
+
Exported ONNX weights are hosted on the Hugging Face Hub and pinned by revision:
|
|
151
|
+
|
|
152
|
+
- `TigreGotico/audiosronnx-lavasr` — `backbone.onnx`, `spec_head.onnx`, `denoiser_core.onnx`
|
|
153
|
+
- `TigreGotico/audiosronnx-novasr` — `novasr.onnx`
|
|
154
|
+
|
|
155
|
+
The export scripts under `conversion/` reproduce these from the upstream PyTorch
|
|
156
|
+
checkpoints and validate each ONNX graph against its PyTorch submodule (max absolute
|
|
157
|
+
error).
|
|
158
|
+
|
|
159
|
+
## Evaluated but not shipped
|
|
160
|
+
|
|
161
|
+
The audio super-resolution landscape was surveyed for other models to export. These
|
|
162
|
+
were evaluated and are **not** shipped, for the reasons given:
|
|
163
|
+
|
|
164
|
+
| Model | License | Reason not shipped |
|
|
165
|
+
|-------|---------|--------------------|
|
|
166
|
+
| **AP-BWE** | MIT | Permissive and ONNX-friendly (convolutional amplitude/phase predictor; STFT stays outside the graph). A strong candidate not yet packaged here. |
|
|
167
|
+
| **HiFi-GAN-BWE** | MIT | Permissive, time-domain, ships its own export script. A strong candidate not yet packaged here. |
|
|
168
|
+
| **FLowHigh** | MIT | Single-step flow matching, but depends on an external BigVGAN vocoder plus a mel/STFT front-end — a multi-component export rather than one clean graph. |
|
|
169
|
+
| **AudioSR** | MIT | ~6 GB latent-diffusion model (VAE + LDM + vocoder, iterative sampler, ~0.6× realtime on GPU). Not CPU-runnable at usable latency; impractical to export. |
|
|
170
|
+
| **NU-Wave2** | none | Diffusion (iterative sampler) and the repository ships no license file. |
|
|
171
|
+
| **mdctGAN** | unclear (NOASSERTION) | Unclear license, and its MDCT front-end relies on `torch.fft`, which exports to ONNX unreliably. |
|
|
172
|
+
| **VoiceFixer / NVSR** | MIT | Speech *restoration* rather than pure bandwidth extension; two-stage mel-predictor + neural vocoder, heavier and less focused than the shipped engines. |
|
|
173
|
+
|
|
174
|
+
An engine is only shipped when it exports cleanly to ONNX, runs on CPU via
|
|
175
|
+
onnxruntime, carries a permissive license, and produces verified 48 kHz output.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
Apache-2.0. The shipped model weights are Apache-2.0 (LavaSR, NovaSR).
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# audiosronnx
|
|
2
|
+
|
|
3
|
+
Pure-ONNX, multi-engine **audio super-resolution / bandwidth extension**. Upscale
|
|
4
|
+
low-sample-rate or low-bandwidth speech — 8 kHz telephony, muffled recordings,
|
|
5
|
+
low-fidelity TTS — to a clean **48 kHz** signal. Inference runs entirely on
|
|
6
|
+
onnxruntime; there is **no torch at runtime**.
|
|
7
|
+
|
|
8
|
+
audiosronnx is the super-resolution sibling of the TigreGotico pure-ONNX speech
|
|
9
|
+
libraries ([`vadonnx`](https://github.com/TigreGotico/vadonnx),
|
|
10
|
+
[`voiceclonnx`](https://github.com/TigreGotico/voiceclonnx),
|
|
11
|
+
[`speakeronnx`](https://github.com/TigreGotico/speakeronnx)) and mirrors their
|
|
12
|
+
conventions: a single `load_sr(engine=...)` entry point, an engine registry, a
|
|
13
|
+
HuggingFace-backed model resolver with an XDG cache, and a small CLI.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install audiosronnx
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Runtime dependencies: `numpy`, `onnxruntime`, `scipy`, `soundfile`, `huggingface_hub`.
|
|
22
|
+
ONNX models are downloaded on first use from the Hugging Face Hub and cached under
|
|
23
|
+
`~/.local/share/audiosronnx`.
|
|
24
|
+
|
|
25
|
+
## Engines
|
|
26
|
+
|
|
27
|
+
| Engine | Input SR | Output SR | Size | CPU speed | License | Status |
|
|
28
|
+
|--------|----------|-----------|------|-----------|---------|--------|
|
|
29
|
+
| **lavasr** (default) | 8–48 kHz | 48 kHz | ~52 MB | ~50× realtime | Apache-2.0 | shipped |
|
|
30
|
+
| **novasr** | 16 kHz | 48 kHz | ~52 KB | ~1000× realtime | Apache-2.0 | shipped |
|
|
31
|
+
|
|
32
|
+
- **lavasr** — a Vocos-based bandwidth-extension model with a Linkwitz-Riley spectral
|
|
33
|
+
merge that preserves the original low band, plus an optional UL-UNAS denoiser. It
|
|
34
|
+
accepts any input rate from 8 to 48 kHz and is the best-quality engine. All spectral
|
|
35
|
+
DSP (STFT, ISTFT, mel filterbank, resampling, merge) runs in numpy/scipy, so no
|
|
36
|
+
`torch.stft` ever enters an ONNX graph.
|
|
37
|
+
- **novasr** — a tiny conv1d / BigVGAN-snake generator that upsamples 16 kHz to 48 kHz
|
|
38
|
+
in a single time-domain pass. Extremely fast and memory-light, at lower fidelity than
|
|
39
|
+
lavasr; useful for on-device enhancement and quick dataset restoration.
|
|
40
|
+
|
|
41
|
+
Both engines are derived from the LavaSR/NovaSR projects by Yatharth Sharma
|
|
42
|
+
([LavaSR](https://github.com/ysharma3501/LavaSR),
|
|
43
|
+
[NovaSR](https://github.com/ysharma3501/NovaSR)), Apache-2.0.
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from audiosronnx import load_sr
|
|
49
|
+
|
|
50
|
+
sr = load_sr("lavasr") # default engine
|
|
51
|
+
|
|
52
|
+
# low-level primitive: numpy array or path in, (float32 mono, 48000) out
|
|
53
|
+
out, rate = sr.upscale("telephone_8k.wav") # -> (np.ndarray float32, 48000)
|
|
54
|
+
|
|
55
|
+
# array input (you supply the sample rate)
|
|
56
|
+
import soundfile as sf
|
|
57
|
+
audio, in_sr = sf.read("input.wav")
|
|
58
|
+
out, rate = sr.upscale(audio, in_sr)
|
|
59
|
+
|
|
60
|
+
# file / directory helpers
|
|
61
|
+
sr.upscale_file("in.wav", "out_48k.wav")
|
|
62
|
+
sr.upscale_dir("clips_in/", "clips_out_48k/")
|
|
63
|
+
|
|
64
|
+
# the fast tiny engine instead
|
|
65
|
+
fast = load_sr("novasr")
|
|
66
|
+
fast.upscale_file("in_16k.wav", "out_48k.wav")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
LavaSR options:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
sr = load_sr("lavasr", denoise=True, cutoff_hz=6000)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## CLI
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
audiosronnx list # list engines
|
|
79
|
+
audiosronnx probe input.wav # print format / duration
|
|
80
|
+
audiosronnx upscale in.wav out.wav # default engine (lavasr)
|
|
81
|
+
audiosronnx upscale in.wav out.wav --engine novasr
|
|
82
|
+
audiosronnx upscale in.wav out.wav --denoise # lavasr denoiser stage
|
|
83
|
+
audiosronnx upscale-dir clips_in/ clips_out/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API
|
|
87
|
+
|
|
88
|
+
- `load_sr(engine="lavasr", *, providers=None, cache_dir=None, revision=None, **kwargs) -> SRModel`
|
|
89
|
+
- `SRModel.upscale(audio, sample_rate=None) -> (np.ndarray float32 mono, 48000)` —
|
|
90
|
+
`audio` is a path, raw int16 PCM bytes, or a numpy array. The low-level primitive.
|
|
91
|
+
- `SRModel.upscale_file(in_path, out_path) -> str`
|
|
92
|
+
- `SRModel.upscale_dir(in_dir, out_dir) -> list[str]`
|
|
93
|
+
- `available_models()`, `get_engine(alias)`, `register_engine(entry)`, `ENGINE_REGISTRY`
|
|
94
|
+
|
|
95
|
+
### Custom / third-party engines
|
|
96
|
+
|
|
97
|
+
Subclass `SRModel`, implement `_upscale_array(audio, sample_rate) -> np.ndarray` (a
|
|
98
|
+
48 kHz mono float32 array), and register an `EngineEntry`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from audiosronnx import SRModel, EngineEntry, register_engine
|
|
102
|
+
|
|
103
|
+
class MySR(SRModel):
|
|
104
|
+
output_sample_rate = 48000
|
|
105
|
+
def _upscale_array(self, audio, sample_rate):
|
|
106
|
+
... # run your ONNX session, return a 48 kHz float32 array
|
|
107
|
+
|
|
108
|
+
register_engine(EngineEntry(alias="mysr", adapter_class=MySR, license="MIT"))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Model hosting
|
|
112
|
+
|
|
113
|
+
Exported ONNX weights are hosted on the Hugging Face Hub and pinned by revision:
|
|
114
|
+
|
|
115
|
+
- `TigreGotico/audiosronnx-lavasr` — `backbone.onnx`, `spec_head.onnx`, `denoiser_core.onnx`
|
|
116
|
+
- `TigreGotico/audiosronnx-novasr` — `novasr.onnx`
|
|
117
|
+
|
|
118
|
+
The export scripts under `conversion/` reproduce these from the upstream PyTorch
|
|
119
|
+
checkpoints and validate each ONNX graph against its PyTorch submodule (max absolute
|
|
120
|
+
error).
|
|
121
|
+
|
|
122
|
+
## Evaluated but not shipped
|
|
123
|
+
|
|
124
|
+
The audio super-resolution landscape was surveyed for other models to export. These
|
|
125
|
+
were evaluated and are **not** shipped, for the reasons given:
|
|
126
|
+
|
|
127
|
+
| Model | License | Reason not shipped |
|
|
128
|
+
|-------|---------|--------------------|
|
|
129
|
+
| **AP-BWE** | MIT | Permissive and ONNX-friendly (convolutional amplitude/phase predictor; STFT stays outside the graph). A strong candidate not yet packaged here. |
|
|
130
|
+
| **HiFi-GAN-BWE** | MIT | Permissive, time-domain, ships its own export script. A strong candidate not yet packaged here. |
|
|
131
|
+
| **FLowHigh** | MIT | Single-step flow matching, but depends on an external BigVGAN vocoder plus a mel/STFT front-end — a multi-component export rather than one clean graph. |
|
|
132
|
+
| **AudioSR** | MIT | ~6 GB latent-diffusion model (VAE + LDM + vocoder, iterative sampler, ~0.6× realtime on GPU). Not CPU-runnable at usable latency; impractical to export. |
|
|
133
|
+
| **NU-Wave2** | none | Diffusion (iterative sampler) and the repository ships no license file. |
|
|
134
|
+
| **mdctGAN** | unclear (NOASSERTION) | Unclear license, and its MDCT front-end relies on `torch.fft`, which exports to ONNX unreliably. |
|
|
135
|
+
| **VoiceFixer / NVSR** | MIT | Speech *restoration* rather than pure bandwidth extension; two-stage mel-predictor + neural vocoder, heavier and less focused than the shipped engines. |
|
|
136
|
+
|
|
137
|
+
An engine is only shipped when it exports cleanly to ONNX, runs on CPU via
|
|
138
|
+
onnxruntime, carries a permissive license, and produces verified 48 kHz output.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
Apache-2.0. The shipped model weights are Apache-2.0 (LavaSR, NovaSR).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""audiosronnx — pure-ONNX multi-engine audio super-resolution / bandwidth extension.
|
|
2
|
+
|
|
3
|
+
Upscale low-sample-rate or low-bandwidth speech (e.g. 8 kHz telephony) to a clean
|
|
4
|
+
48 kHz signal. Inference runs entirely on onnxruntime — no torch at runtime.
|
|
5
|
+
|
|
6
|
+
Quick start::
|
|
7
|
+
|
|
8
|
+
from audiosronnx import load_sr
|
|
9
|
+
|
|
10
|
+
sr = load_sr("lavasr") # default engine
|
|
11
|
+
out, rate = sr.upscale("telephone_8k.wav") # -> (float32 mono array, 48000)
|
|
12
|
+
sr.upscale_file("in.wav", "out_48k.wav")
|
|
13
|
+
|
|
14
|
+
Engines are downloaded on first use from Hugging Face Hub and cached under an XDG
|
|
15
|
+
data directory. ``load_sr("novasr")`` selects the tiny fast engine instead.
|
|
16
|
+
"""
|
|
17
|
+
from .api import load_sr
|
|
18
|
+
from .base import (
|
|
19
|
+
ENGINE_REGISTRY,
|
|
20
|
+
OUTPUT_SAMPLE_RATE,
|
|
21
|
+
EngineEntry,
|
|
22
|
+
SRModel,
|
|
23
|
+
available_models,
|
|
24
|
+
get_engine,
|
|
25
|
+
register_engine,
|
|
26
|
+
)
|
|
27
|
+
from .version import __version__
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"load_sr",
|
|
31
|
+
"available_models",
|
|
32
|
+
"SRModel",
|
|
33
|
+
"EngineEntry",
|
|
34
|
+
"ENGINE_REGISTRY",
|
|
35
|
+
"register_engine",
|
|
36
|
+
"get_engine",
|
|
37
|
+
"OUTPUT_SAMPLE_RATE",
|
|
38
|
+
"__version__",
|
|
39
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Public entry point: :func:`load_sr`."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from .base import SRModel, available_models, get_engine
|
|
7
|
+
|
|
8
|
+
# Importing the engines package registers every built-in adapter.
|
|
9
|
+
from . import engines # noqa: F401
|
|
10
|
+
|
|
11
|
+
__all__ = ["load_sr", "available_models"]
|
|
12
|
+
|
|
13
|
+
DEFAULT_ENGINE = "lavasr"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def load_sr(
|
|
17
|
+
engine: str = DEFAULT_ENGINE,
|
|
18
|
+
*,
|
|
19
|
+
providers: Optional[List[str]] = None,
|
|
20
|
+
cache_dir: Optional[str] = None,
|
|
21
|
+
revision: Optional[str] = None,
|
|
22
|
+
**engine_kwargs,
|
|
23
|
+
) -> SRModel:
|
|
24
|
+
"""Load an audio super-resolution engine behind the unified :class:`SRModel` API.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
engine: registry alias (``"lavasr"`` (default) or ``"novasr"``).
|
|
28
|
+
providers: onnxruntime execution providers (default CPU).
|
|
29
|
+
cache_dir: override the model download cache directory.
|
|
30
|
+
revision: pin a HuggingFace revision for the model weights.
|
|
31
|
+
**engine_kwargs: forwarded to the engine adapter (e.g. ``denoise=True``,
|
|
32
|
+
``cutoff_hz=6000`` for LavaSR).
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A ready-to-use :class:`~audiosronnx.base.SRModel`.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> from audiosronnx import load_sr
|
|
39
|
+
>>> sr = load_sr("lavasr")
|
|
40
|
+
>>> out, rate = sr.upscale("telephone_8k.wav") # -> (float32 array, 48000)
|
|
41
|
+
"""
|
|
42
|
+
entry = get_engine(engine)
|
|
43
|
+
return entry.adapter_class(
|
|
44
|
+
providers=providers, cache_dir=cache_dir, revision=revision, **engine_kwargs
|
|
45
|
+
)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Audio I/O and resampling helpers (numpy core, soundfile for file I/O).
|
|
2
|
+
|
|
3
|
+
Everything in :mod:`audiosronnx` works internally with mono ``float32`` PCM in the
|
|
4
|
+
range ``[-1, 1]``. These helpers convert the shapes users pass in (a WAV path or a
|
|
5
|
+
numpy array of various dtypes) into that canonical form, and resample between rates.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
import wave
|
|
11
|
+
from typing import Tuple, Union
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
AudioLike = Union[str, bytes, bytearray, memoryview, np.ndarray]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_float32_mono(audio: Union[bytes, bytearray, memoryview, np.ndarray]) -> 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`` / ``uint8`` / ``float32`` / ``float64``. Multi-channel
|
|
23
|
+
arrays (shape ``(n, channels)``) are downmixed by averaging channels.
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(audio, (bytes, bytearray, memoryview)):
|
|
26
|
+
return np.frombuffer(bytes(audio), dtype="<i2").astype(np.float32) / 32768.0
|
|
27
|
+
|
|
28
|
+
if not isinstance(audio, np.ndarray):
|
|
29
|
+
raise TypeError(f"audio must be bytes or np.ndarray, got {type(audio).__name__}")
|
|
30
|
+
|
|
31
|
+
arr = audio
|
|
32
|
+
if arr.ndim == 2:
|
|
33
|
+
arr = arr.mean(axis=1)
|
|
34
|
+
elif arr.ndim > 2:
|
|
35
|
+
raise ValueError(f"audio array must be 1-D or 2-D, got {arr.ndim}-D")
|
|
36
|
+
|
|
37
|
+
if arr.dtype == np.float32:
|
|
38
|
+
out = arr
|
|
39
|
+
elif arr.dtype == np.float64:
|
|
40
|
+
out = arr.astype(np.float32)
|
|
41
|
+
elif arr.dtype == np.int16:
|
|
42
|
+
out = arr.astype(np.float32) / 32768.0
|
|
43
|
+
elif arr.dtype == np.int32:
|
|
44
|
+
out = arr.astype(np.float32) / 2147483648.0
|
|
45
|
+
elif arr.dtype == np.uint8:
|
|
46
|
+
out = (arr.astype(np.float32) - 128.0) / 128.0
|
|
47
|
+
else:
|
|
48
|
+
raise TypeError(f"unsupported audio dtype: {arr.dtype}")
|
|
49
|
+
|
|
50
|
+
return np.ascontiguousarray(out, dtype=np.float32)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def resample(x: np.ndarray, src_sr: int, dst_sr: int) -> np.ndarray:
|
|
54
|
+
"""Resample mono float32 audio from ``src_sr`` to ``dst_sr``.
|
|
55
|
+
|
|
56
|
+
Uses ``scipy.signal.resample_poly`` when SciPy is installed (the default for a
|
|
57
|
+
full install), otherwise falls back to linear interpolation via
|
|
58
|
+
:func:`numpy.interp`.
|
|
59
|
+
"""
|
|
60
|
+
if src_sr == dst_sr or x.size == 0:
|
|
61
|
+
return x.astype(np.float32, copy=False)
|
|
62
|
+
try:
|
|
63
|
+
from scipy.signal import resample_poly # type: ignore
|
|
64
|
+
|
|
65
|
+
g = math.gcd(int(src_sr), int(dst_sr))
|
|
66
|
+
up, down = int(dst_sr) // g, int(src_sr) // g
|
|
67
|
+
return resample_poly(x, up, down).astype(np.float32)
|
|
68
|
+
except Exception:
|
|
69
|
+
n_out = int(round(x.shape[0] * dst_sr / src_sr))
|
|
70
|
+
if n_out <= 0:
|
|
71
|
+
return np.zeros(0, dtype=np.float32)
|
|
72
|
+
src_idx = np.linspace(0.0, x.shape[0] - 1, num=n_out, dtype=np.float64)
|
|
73
|
+
return np.interp(src_idx, np.arange(x.shape[0]), x).astype(np.float32)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def read_wav(path: str) -> Tuple[np.ndarray, int]:
|
|
77
|
+
"""Read audio into mono float32. Returns ``(audio, sample_rate)``.
|
|
78
|
+
|
|
79
|
+
Uses :mod:`soundfile` when available (handles WAV/FLAC/OGG and any subtype),
|
|
80
|
+
otherwise falls back to the standard-library :mod:`wave` module for PCM WAV.
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
import soundfile as sf # type: ignore
|
|
84
|
+
|
|
85
|
+
audio, sr = sf.read(str(path), dtype="float32", always_2d=False)
|
|
86
|
+
if audio.ndim > 1:
|
|
87
|
+
audio = audio.mean(axis=1)
|
|
88
|
+
return np.ascontiguousarray(audio, dtype=np.float32), int(sr)
|
|
89
|
+
except ImportError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
with wave.open(str(path), "rb") as wf:
|
|
93
|
+
sr = wf.getframerate()
|
|
94
|
+
n_channels = wf.getnchannels()
|
|
95
|
+
sampwidth = wf.getsampwidth()
|
|
96
|
+
raw = wf.readframes(wf.getnframes())
|
|
97
|
+
|
|
98
|
+
if sampwidth == 2:
|
|
99
|
+
arr = np.frombuffer(raw, dtype="<i2").astype(np.float32) / 32768.0
|
|
100
|
+
elif sampwidth == 4:
|
|
101
|
+
arr = np.frombuffer(raw, dtype="<i4").astype(np.float32) / 2147483648.0
|
|
102
|
+
elif sampwidth == 1:
|
|
103
|
+
arr = (np.frombuffer(raw, dtype=np.uint8).astype(np.float32) - 128.0) / 128.0
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError(f"unsupported WAV sample width: {sampwidth} bytes")
|
|
106
|
+
|
|
107
|
+
if n_channels > 1:
|
|
108
|
+
arr = arr.reshape(-1, n_channels).mean(axis=1)
|
|
109
|
+
return np.ascontiguousarray(arr, dtype=np.float32), sr
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def write_wav(path: str, audio: np.ndarray, sample_rate: int) -> None:
|
|
113
|
+
"""Write mono float32 ``audio`` as 16-bit PCM WAV to ``path``."""
|
|
114
|
+
audio = np.clip(np.asarray(audio, dtype=np.float32), -1.0, 1.0)
|
|
115
|
+
try:
|
|
116
|
+
import soundfile as sf # type: ignore
|
|
117
|
+
|
|
118
|
+
sf.write(str(path), audio, int(sample_rate), subtype="PCM_16")
|
|
119
|
+
return
|
|
120
|
+
except ImportError:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
pcm = (audio * 32767.0).astype("<i2")
|
|
124
|
+
with wave.open(str(path), "wb") as wf:
|
|
125
|
+
wf.setnchannels(1)
|
|
126
|
+
wf.setsampwidth(2)
|
|
127
|
+
wf.setframerate(int(sample_rate))
|
|
128
|
+
wf.writeframes(pcm.tobytes())
|