visqol-python 3.6.0__tar.gz → 3.8.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.
- {visqol_python-3.6.0 → visqol_python-3.8.0}/CHANGELOG.md +81 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/CONTRIBUTING.md +23 -0
- visqol_python-3.8.0/MANIFEST.in +18 -0
- {visqol_python-3.6.0/visqol_python.egg-info → visqol_python-3.8.0}/PKG-INFO +70 -51
- {visqol_python-3.6.0 → visqol_python-3.8.0}/README.md +65 -45
- visqol_python-3.8.0/docs/benchmarks/2026-09-16-linux-x86_64.json +45 -0
- visqol_python-3.8.0/docs/benchmarks/2026-09-16-macos-arm64.json +45 -0
- visqol_python-3.8.0/docs/benchmarks/2026-09-20-windows-v3.8.0-workloads.json +201 -0
- visqol_python-3.8.0/docs/benchmarks/2026-09-20-windows-validation.json +61 -0
- visqol_python-3.8.0/docs/benchmarks/2026-09-20-windows-x86_64.json +45 -0
- visqol_python-3.8.0/docs/benchmarks/windows-2026-09-20.json +192 -0
- visqol_python-3.8.0/docs/portable-cpu-optimization.md +204 -0
- visqol_python-3.8.0/docs/portable-cpu-windows-validation.md +121 -0
- visqol_python-3.8.0/docs/releasing.md +102 -0
- visqol_python-3.8.0/docs/v3.8.0-validation.md +78 -0
- visqol_python-3.8.0/docs/windows-validation.md +164 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/pyproject.toml +14 -8
- visqol_python-3.8.0/tests/_fft_worker.py +9 -0
- visqol_python-3.8.0/tests/_legacy_kernels.py +188 -0
- visqol_python-3.8.0/tests/_result_comparison.py +23 -0
- visqol_python-3.8.0/tests/bench_platform.py +230 -0
- visqol_python-3.8.0/tests/bench_portable.py +111 -0
- visqol_python-3.8.0/tests/bench_release.py +239 -0
- visqol_python-3.8.0/tests/check_validation.py +43 -0
- visqol_python-3.8.0/tests/conformance_files.json +111 -0
- visqol_python-3.8.0/tests/conftest.py +49 -0
- visqol_python-3.8.0/tests/fetch_conformance_data.py +57 -0
- visqol_python-3.8.0/tests/requirements-min-accel.txt +8 -0
- visqol_python-3.8.0/tests/requirements-min-base.txt +5 -0
- visqol_python-3.8.0/tests/test_acceleration.py +114 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/tests/test_conformance.py +38 -9
- visqol_python-3.8.0/tests/test_parallel_correctness.py +68 -0
- visqol_python-3.8.0/tests/test_platform.py +220 -0
- visqol_python-3.8.0/tests/test_process_pool.py +39 -0
- visqol_python-3.8.0/tests/verify_distribution.py +180 -0
- visqol_python-3.8.0/tests/verify_release.py +62 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/__init__.py +1 -1
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/api.py +11 -4
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/numba_accel.py +167 -95
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/patch_selector.py +16 -15
- visqol_python-3.8.0/visqol/py.typed +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/quality_mapper.py +83 -7
- {visqol_python-3.6.0 → visqol_python-3.8.0/visqol_python.egg-info}/PKG-INFO +70 -51
- visqol_python-3.8.0/visqol_python.egg-info/SOURCES.txt +62 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol_python.egg-info/requires.txt +2 -8
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol_python.egg-info/top_level.txt +0 -1
- visqol_python-3.6.0/MANIFEST.in +0 -5
- visqol_python-3.6.0/tests/test_parallel_correctness.py +0 -96
- visqol_python-3.6.0/visqol_python.egg-info/SOURCES.txt +0 -33
- {visqol_python-3.6.0 → visqol_python-3.8.0}/LICENSE +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/setup.cfg +0 -0
- /visqol_python-3.6.0/visqol/py.typed → /visqol_python-3.8.0/tests/__init__.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/tests/test_quick.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/__main__.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/alignment.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/analysis_window.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/audio_utils.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/gammatone.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/model/lattice_tcditugenmeetpackhref_ls2_nl60_lr12_bs2048_learn.005_ep2400_train1_7_raw.tflite +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/model/libsvm_nu_svr_model.txt +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/nsim.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/patch_creator.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/signal_utils.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/visqol_core.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol/visqol_manager.py +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol_python.egg-info/dependency_links.txt +0 -0
- {visqol_python-3.6.0 → visqol_python-3.8.0}/visqol_python.egg-info/entry_points.txt +0 -0
|
@@ -4,6 +4,84 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [3.8.0] - 2026-09-20
|
|
10
|
+
|
|
11
|
+
### Performance
|
|
12
|
+
- Cache reference-patch statistics, replace DP predecessor scans with prefix
|
|
13
|
+
maxima, and evaluate independent candidates in parallel. Preserve the original
|
|
14
|
+
tie rule and reduction order.
|
|
15
|
+
- Fuse four Gammatone IIR stages with RMS accumulation, removing the large
|
|
16
|
+
intermediate buffer while preserving FP64 arithmetic and filter resets.
|
|
17
|
+
- Keep scoring models, search windows, alignment stages and public API unchanged.
|
|
18
|
+
Recorded warmed comparisons against accelerated v3.7.0 are 2.57× on Apple M4
|
|
19
|
+
Pro, 2.80× on AMD EPYC 9K84 and 2.29× on Windows Core Ultra 9 285H; see the
|
|
20
|
+
[protocol and raw measurements](docs/portable-cpu-windows-validation.md).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Support Windows Unicode SVR model paths using the native filename encoding,
|
|
24
|
+
short filenames and temporary copies. Try the system temp directory if the
|
|
25
|
+
user's temp path is also unusable. Invalid models fail during initialization.
|
|
26
|
+
- Read lattice model bytes through Python so Unicode installation paths work.
|
|
27
|
+
- Cap automatic Windows process counts at 61 and document the spawn entry-point
|
|
28
|
+
guard and the cost of starting pools for small batches.
|
|
29
|
+
- Keep benchmark scripts out of pytest collection and reject invalid explicit
|
|
30
|
+
conformance-data paths. Resolve NumPy/SciPy together and exclude unsupported
|
|
31
|
+
NumPy 1.x/Python 3.13 combinations.
|
|
32
|
+
|
|
33
|
+
### Tests and release validation
|
|
34
|
+
- Retain the frozen v3.7.0 kernel oracles, exact output checks, backend numerical
|
|
35
|
+
comparisons, real process-pool tests and Windows filename/CLI regressions.
|
|
36
|
+
- Tighten C++ MOS tolerances to 0.0002 for audio and 0.003 for speech, separately
|
|
37
|
+
from the exact old/new checks. Verify the official audio data by Git blob SHA.
|
|
38
|
+
- Validate both FFT backends on Linux, macOS and Windows; expand Windows to
|
|
39
|
+
Python 3.10–3.13 and exercise older compatible dependency combinations.
|
|
40
|
+
- Add long-audio, speech, batch and fresh-cache comparisons. Include the test
|
|
41
|
+
helpers, benchmarks and validation documents in sdists; keep wheels package-only.
|
|
42
|
+
- Gate publication on tag/version/changelog checks, full CI of the tagged commit
|
|
43
|
+
and installation of the built wheel in Unicode environments on three platforms.
|
|
44
|
+
Publish the tested artifact without rebuilding it. See [releasing](docs/releasing.md).
|
|
45
|
+
|
|
46
|
+
## [3.7.0] - 2026-05-30
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- **Audio-mode C++ parity in fine realignment** (`finely_align_and_recreate_patches`):
|
|
50
|
+
the previous "B2 optimisation" skipped the per-patch spectrogram rebuild
|
|
51
|
+
whenever the fine-alignment lag was zero, on the assumption that the rebuilt
|
|
52
|
+
spectrogram would be identical to the original patch. That assumption is
|
|
53
|
+
wrong. The original patch is sliced out of the **full-signal** Gammatone
|
|
54
|
+
spectrogram (warm IIR filter state, carrying history), whereas the rebuild
|
|
55
|
+
runs Gammatone over the **sliced** audio from a cold filter state — so the
|
|
56
|
+
leading frames differ even when no shift is applied. C++ ViSQOL always
|
|
57
|
+
rebuilds and keeps whichever scores higher, so skipping it silently dropped
|
|
58
|
+
that improvement. The skip is removed; every matched patch is now rebuilt and
|
|
59
|
+
compared, exactly as C++ does.
|
|
60
|
+
- **Impact:** audio MOS-LQO now matches the C++ binary to ULP. Max diff over
|
|
61
|
+
the 10 conformance cases dropped from **2.41 × 10⁻² → 1.17 × 10⁻⁴**; 9 of 10
|
|
62
|
+
cases are bit-exact (~10⁻¹⁴). The lone residual (`contrabassoon_24aac`,
|
|
63
|
+
1.17 × 10⁻⁴) is an unrelated sub-sample boundary-patch alignment rounding,
|
|
64
|
+
now smaller than the speech-mode parity gap.
|
|
65
|
+
- Existing audio scores shift by up to ~0.024 MOS where they were previously
|
|
66
|
+
too low; this is a move **toward** the C++ ground truth, not away from it.
|
|
67
|
+
- Speech polynomial and lattice scores are unaffected (bit-identical before
|
|
68
|
+
and after).
|
|
69
|
+
|
|
70
|
+
### Performance
|
|
71
|
+
- Fine realignment now rebuilds the spectrogram for every patch (including
|
|
72
|
+
`lag == 0`), so it no longer benefits from the zero-lag skip. Net effect is
|
|
73
|
+
roughly **1.4–1.5× slower realignment** (e.g. `guitar48_stereo` 0.44 s → 0.65 s
|
|
74
|
+
end-to-end with Numba + pyFFTW). This is the cost of exact C++ parity and is
|
|
75
|
+
consistent with the project's precision-first policy. Audio-mode RTF rises
|
|
76
|
+
from ~0.036 (3.6.0) to ~0.054 — still ~1.7× faster than the C++ binary
|
|
77
|
+
(~0.093) and faster than 3.5.0 (~0.067).
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
- **Dropped Python 3.9 support** (`requires-python = ">=3.10"`). Downstream
|
|
81
|
+
projects still on 3.9 should pin `visqol-python<3.7`.
|
|
82
|
+
- CI: bumped GitHub Actions to Node-24-compatible major versions
|
|
83
|
+
(checkout v5, setup-python v6, upload/download-artifact v5).
|
|
84
|
+
|
|
7
85
|
## [3.6.0] - 2026-05-27
|
|
8
86
|
|
|
9
87
|
### Added
|
|
@@ -218,6 +296,9 @@ All v3.5.0 conformance baselines preserved within ULP precision:
|
|
|
218
296
|
- Bundled SVR model (`libsvm_nu_svr_model.txt`)
|
|
219
297
|
- GitHub Actions workflow for auto-publish to PyPI via Trusted Publisher
|
|
220
298
|
|
|
299
|
+
[Unreleased]: https://github.com/talker93/visqol-python/compare/v3.7.0...HEAD
|
|
300
|
+
[3.8.0]: https://github.com/talker93/visqol-python/compare/v3.7.0...v3.8.0
|
|
301
|
+
[3.7.0]: https://github.com/talker93/visqol-python/compare/v3.6.0...v3.7.0
|
|
221
302
|
[3.6.0]: https://github.com/talker93/visqol-python/compare/v3.5.0...v3.6.0
|
|
222
303
|
[3.5.0]: https://github.com/talker93/visqol-python/compare/v3.4.0...v3.5.0
|
|
223
304
|
[3.4.0]: https://github.com/talker93/visqol-python/compare/v3.3.6...v3.4.0
|
|
@@ -37,6 +37,29 @@ pytest tests/test_quick.py -v
|
|
|
37
37
|
pytest tests/test_conformance.py -v --testdata /path/to/visqol/testdata
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
On Windows PowerShell, an isolated setup with all optional backends is:
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
python -m venv .venv
|
|
44
|
+
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"
|
|
45
|
+
.\.venv\Scripts\python.exe -m pytest tests -v
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The normal test suite includes real `spawn` multiprocessing, Unicode file
|
|
49
|
+
paths, and numerical comparisons of Numba/FFTW against the fallback paths.
|
|
50
|
+
Optional backend tests skip when their dependencies are absent; conformance
|
|
51
|
+
tests need the official testdata. Experimental `bench_*.py` scripts are not
|
|
52
|
+
collected by pytest.
|
|
53
|
+
|
|
54
|
+
For official testdata setup, repeatable performance measurements and the
|
|
55
|
+
Windows compatibility results, see [Windows validation](docs/windows-validation.md).
|
|
56
|
+
|
|
57
|
+
For accelerated changes, fetch verified official data with
|
|
58
|
+
`python tests/fetch_conformance_data.py` and run both FFT backends as described
|
|
59
|
+
in [release validation](docs/releasing.md). Preserve the frozen v3.7.0 kernels;
|
|
60
|
+
compare every public result field against them. The reusable CI workflow also
|
|
61
|
+
checks installed wheels and older compatible dependency combinations.
|
|
62
|
+
|
|
40
63
|
## Code Style
|
|
41
64
|
|
|
42
65
|
- **Type hints**: All public functions and methods must include type annotations.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include CHANGELOG.md
|
|
4
|
+
include CONTRIBUTING.md
|
|
5
|
+
recursive-include visqol/model *.txt
|
|
6
|
+
recursive-include docs *.md *.json
|
|
7
|
+
include tests/_*.py
|
|
8
|
+
include tests/conftest.py
|
|
9
|
+
include tests/test_*.py
|
|
10
|
+
include tests/bench_portable.py
|
|
11
|
+
include tests/bench_platform.py
|
|
12
|
+
include tests/bench_release.py
|
|
13
|
+
include tests/check_validation.py
|
|
14
|
+
include tests/verify_distribution.py
|
|
15
|
+
include tests/verify_release.py
|
|
16
|
+
include tests/requirements-min-*.txt
|
|
17
|
+
include tests/fetch_conformance_data.py
|
|
18
|
+
include tests/conformance_files.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visqol-python
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.0
|
|
4
4
|
Summary: ViSQOL - Virtual Speech Quality Objective Listener (Pure Python)
|
|
5
5
|
Author: Shan Jiang
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -14,7 +14,6 @@ Classifier: Development Status :: 4 - Beta
|
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Intended Audience :: Science/Research
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
22
21
|
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
23
22
|
Classifier: Topic :: Scientific/Engineering
|
|
24
23
|
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python: >=3.
|
|
24
|
+
Requires-Python: >=3.10
|
|
26
25
|
Description-Content-Type: text/markdown
|
|
27
26
|
License-File: LICENSE
|
|
28
27
|
Requires-Dist: numpy>=1.20
|
|
@@ -32,12 +31,12 @@ Requires-Dist: libsvm-official>=3.25
|
|
|
32
31
|
Provides-Extra: accel
|
|
33
32
|
Requires-Dist: numba>=0.57; extra == "accel"
|
|
34
33
|
Provides-Extra: lattice
|
|
35
|
-
Requires-Dist: ai-edge-litert>=2.1.5;
|
|
34
|
+
Requires-Dist: ai-edge-litert>=2.1.5; extra == "lattice"
|
|
36
35
|
Provides-Extra: fftw
|
|
37
36
|
Requires-Dist: pyfftw>=0.13; extra == "fftw"
|
|
38
37
|
Provides-Extra: all
|
|
39
38
|
Requires-Dist: numba>=0.57; extra == "all"
|
|
40
|
-
Requires-Dist: ai-edge-litert>=2.1.5;
|
|
39
|
+
Requires-Dist: ai-edge-litert>=2.1.5; extra == "all"
|
|
41
40
|
Requires-Dist: pyfftw>=0.13; extra == "all"
|
|
42
41
|
Provides-Extra: test
|
|
43
42
|
Requires-Dist: pytest>=7.0; extra == "test"
|
|
@@ -46,7 +45,7 @@ Requires-Dist: pytest>=7.0; extra == "dev"
|
|
|
46
45
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
47
46
|
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
48
47
|
Requires-Dist: numba>=0.57; extra == "dev"
|
|
49
|
-
Requires-Dist: ai-edge-litert>=2.1.5;
|
|
48
|
+
Requires-Dist: ai-edge-litert>=2.1.5; extra == "dev"
|
|
50
49
|
Requires-Dist: pyfftw>=0.13; extra == "dev"
|
|
51
50
|
Dynamic: license-file
|
|
52
51
|
|
|
@@ -71,10 +70,10 @@ ViSQOL compares a reference audio signal with a degraded version and outputs a *
|
|
|
71
70
|
- **Two speech quality mappers** matching C++ ViSQOL:
|
|
72
71
|
- **Lattice (default)** — deep-lattice TFLite network (`--use_lattice_model=true` in C++); requires the optional `[lattice]` extra
|
|
73
72
|
- **Polynomial (fallback)** — legacy exponential fit (`--use_lattice_model=false` in C++)
|
|
74
|
-
- **
|
|
73
|
+
- **Python scoring implementation**: installable with pip; some native dependencies need a compiler when a wheel is unavailable (see [platform setup](docs/portable-cpu-optimization.md))
|
|
75
74
|
- **Minimal dependencies**: 4 core pip packages (`numpy`, `scipy`, `soundfile`, `libsvm-official`)
|
|
76
|
-
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for
|
|
77
|
-
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment
|
|
75
|
+
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for parallel Gammatone filtering and DP patch matching, preserving strict FP64 arithmetic
|
|
76
|
+
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment and cross-correlation FFTs through FFTW3; see [Performance](#performance) for version comparisons
|
|
78
77
|
- **Batch & parallel evaluation**: `measure_batch(parallel=True)` for multi-process execution across CPU cores
|
|
79
78
|
- **Fully typed**: PEP 561 `py.typed`, strict mypy, ruff-enforced code style
|
|
80
79
|
|
|
@@ -159,31 +158,43 @@ print(f"MOS-LQO: {result.moslqo:.4f}")
|
|
|
159
158
|
```python
|
|
160
159
|
from visqol import VisqolApi
|
|
161
160
|
|
|
162
|
-
api = VisqolApi()
|
|
163
|
-
api.create(mode="audio")
|
|
164
161
|
|
|
165
|
-
|
|
166
|
-
(
|
|
167
|
-
("
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
def main():
|
|
163
|
+
api = VisqolApi()
|
|
164
|
+
api.create(mode="audio")
|
|
165
|
+
|
|
166
|
+
file_pairs = [
|
|
167
|
+
("ref1.wav", "deg1.wav"),
|
|
168
|
+
("ref2.wav", "deg2.wav"),
|
|
169
|
+
("ref3.wav", "deg3.wav"),
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# Sequential with progress callback
|
|
173
|
+
results = api.measure_batch(
|
|
174
|
+
file_pairs,
|
|
175
|
+
parallel=False,
|
|
176
|
+
progress_callback=lambda done, total: print(f"{done}/{total}"),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Multi-process parallel
|
|
180
|
+
results = api.measure_batch(file_pairs, parallel=True, max_workers=4)
|
|
181
|
+
|
|
182
|
+
for pair, result in zip(file_pairs, results):
|
|
183
|
+
if isinstance(result, Exception):
|
|
184
|
+
print(f"{pair}: FAILED — {result}")
|
|
185
|
+
else:
|
|
186
|
+
print(f"{pair}: MOS-LQO = {result.moslqo:.4f}")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if __name__ == "__main__":
|
|
190
|
+
main()
|
|
185
191
|
```
|
|
186
192
|
|
|
193
|
+
Save parallel batch code in a Python script and keep the `__main__` guard:
|
|
194
|
+
Windows starts fresh Python processes that import the script. For notebooks
|
|
195
|
+
or interactive sessions, use `parallel=False`. Process startup can make small
|
|
196
|
+
batches slower than sequential evaluation.
|
|
197
|
+
|
|
187
198
|
### Command Line
|
|
188
199
|
|
|
189
200
|
```bash
|
|
@@ -245,26 +256,34 @@ The `measure()` method returns a `SimilarityResult` object with:
|
|
|
245
256
|
|
|
246
257
|
## Performance
|
|
247
258
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
259
|
+
The v3.8.0 CPU kernels cache reference statistics, parallelize independent
|
|
260
|
+
patch candidates and fuse Gammatone filtering with energy accumulation.
|
|
261
|
+
They preserve scoring models, search ranges, both alignment stages and FP64
|
|
262
|
+
arithmetic. Install `visqol-python[accel,fftw]` to use the measured path.
|
|
263
|
+
|
|
264
|
+
Same-machine comparisons against **accelerated v3.7.0**, recorded during
|
|
265
|
+
kernel validation:
|
|
266
|
+
|
|
267
|
+
| Platform / CPU | v3.7.0 median | Optimized median | Speedup |
|
|
268
|
+
|---|---:|---:|---:|
|
|
269
|
+
| macOS arm64 / Apple M4 Pro | 1.080 s | 0.421 s | 2.57× |
|
|
270
|
+
| Linux x86-64 / AMD EPYC 9K84 | 1.510 s | 0.540 s | 2.80× |
|
|
271
|
+
| Windows x86-64 / Intel Core Ultra 9 285H | 1.074 s | 0.469 s | 2.29× |
|
|
272
|
+
|
|
273
|
+
Protocol: the same 12.454-second official guitar case, four Numba threads,
|
|
274
|
+
Numba 0.65.1, pyFFTW 0.15.0, five-run medians after warm-up, excluding WAV I/O
|
|
275
|
+
and first-use compilation. All tested old/new output fields were exactly equal
|
|
276
|
+
within each platform/backend. These ratios describe that workload; cold starts,
|
|
277
|
+
longer audio and batch process startup are measured separately by
|
|
278
|
+
`tests/bench_release.py`. Set `NUMBA_NUM_THREADS` before importing the package,
|
|
279
|
+
and budget workers × threads when using process pools.
|
|
280
|
+
|
|
281
|
+
See the [measurement records](docs/portable-cpu-windows-validation.md),
|
|
282
|
+
[integrated validation and workload results](docs/v3.8.0-validation.md), and
|
|
283
|
+
[release validation procedure](docs/releasing.md). The earlier Windows
|
|
284
|
+
[approximately 10× result](docs/windows-validation.md) compared accelerator
|
|
285
|
+
configurations within v3.7.0. Historical v3.6.0 measurements remain in the
|
|
286
|
+
[CHANGELOG](CHANGELOG.md); they predate the v3.7.0 alignment correction.
|
|
268
287
|
|
|
269
288
|
## Project Structure
|
|
270
289
|
|
|
@@ -19,10 +19,10 @@ ViSQOL compares a reference audio signal with a degraded version and outputs a *
|
|
|
19
19
|
- **Two speech quality mappers** matching C++ ViSQOL:
|
|
20
20
|
- **Lattice (default)** — deep-lattice TFLite network (`--use_lattice_model=true` in C++); requires the optional `[lattice]` extra
|
|
21
21
|
- **Polynomial (fallback)** — legacy exponential fit (`--use_lattice_model=false` in C++)
|
|
22
|
-
- **
|
|
22
|
+
- **Python scoring implementation**: installable with pip; some native dependencies need a compiler when a wheel is unavailable (see [platform setup](docs/portable-cpu-optimization.md))
|
|
23
23
|
- **Minimal dependencies**: 4 core pip packages (`numpy`, `scipy`, `soundfile`, `libsvm-official`)
|
|
24
|
-
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for
|
|
25
|
-
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment
|
|
24
|
+
- **Optional Numba acceleration**: `pip install visqol-python[accel]` for parallel Gammatone filtering and DP patch matching, preserving strict FP64 arithmetic
|
|
25
|
+
- **Optional pyFFTW backend**: `pip install visqol-python[fftw]` routes alignment and cross-correlation FFTs through FFTW3; see [Performance](#performance) for version comparisons
|
|
26
26
|
- **Batch & parallel evaluation**: `measure_batch(parallel=True)` for multi-process execution across CPU cores
|
|
27
27
|
- **Fully typed**: PEP 561 `py.typed`, strict mypy, ruff-enforced code style
|
|
28
28
|
|
|
@@ -107,31 +107,43 @@ print(f"MOS-LQO: {result.moslqo:.4f}")
|
|
|
107
107
|
```python
|
|
108
108
|
from visqol import VisqolApi
|
|
109
109
|
|
|
110
|
-
api = VisqolApi()
|
|
111
|
-
api.create(mode="audio")
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
(
|
|
115
|
-
("
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
111
|
+
def main():
|
|
112
|
+
api = VisqolApi()
|
|
113
|
+
api.create(mode="audio")
|
|
114
|
+
|
|
115
|
+
file_pairs = [
|
|
116
|
+
("ref1.wav", "deg1.wav"),
|
|
117
|
+
("ref2.wav", "deg2.wav"),
|
|
118
|
+
("ref3.wav", "deg3.wav"),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Sequential with progress callback
|
|
122
|
+
results = api.measure_batch(
|
|
123
|
+
file_pairs,
|
|
124
|
+
parallel=False,
|
|
125
|
+
progress_callback=lambda done, total: print(f"{done}/{total}"),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Multi-process parallel
|
|
129
|
+
results = api.measure_batch(file_pairs, parallel=True, max_workers=4)
|
|
130
|
+
|
|
131
|
+
for pair, result in zip(file_pairs, results):
|
|
132
|
+
if isinstance(result, Exception):
|
|
133
|
+
print(f"{pair}: FAILED — {result}")
|
|
134
|
+
else:
|
|
135
|
+
print(f"{pair}: MOS-LQO = {result.moslqo:.4f}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
main()
|
|
133
140
|
```
|
|
134
141
|
|
|
142
|
+
Save parallel batch code in a Python script and keep the `__main__` guard:
|
|
143
|
+
Windows starts fresh Python processes that import the script. For notebooks
|
|
144
|
+
or interactive sessions, use `parallel=False`. Process startup can make small
|
|
145
|
+
batches slower than sequential evaluation.
|
|
146
|
+
|
|
135
147
|
### Command Line
|
|
136
148
|
|
|
137
149
|
```bash
|
|
@@ -193,26 +205,34 @@ The `measure()` method returns a `SimilarityResult` object with:
|
|
|
193
205
|
|
|
194
206
|
## Performance
|
|
195
207
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
208
|
+
The v3.8.0 CPU kernels cache reference statistics, parallelize independent
|
|
209
|
+
patch candidates and fuse Gammatone filtering with energy accumulation.
|
|
210
|
+
They preserve scoring models, search ranges, both alignment stages and FP64
|
|
211
|
+
arithmetic. Install `visqol-python[accel,fftw]` to use the measured path.
|
|
212
|
+
|
|
213
|
+
Same-machine comparisons against **accelerated v3.7.0**, recorded during
|
|
214
|
+
kernel validation:
|
|
215
|
+
|
|
216
|
+
| Platform / CPU | v3.7.0 median | Optimized median | Speedup |
|
|
217
|
+
|---|---:|---:|---:|
|
|
218
|
+
| macOS arm64 / Apple M4 Pro | 1.080 s | 0.421 s | 2.57× |
|
|
219
|
+
| Linux x86-64 / AMD EPYC 9K84 | 1.510 s | 0.540 s | 2.80× |
|
|
220
|
+
| Windows x86-64 / Intel Core Ultra 9 285H | 1.074 s | 0.469 s | 2.29× |
|
|
221
|
+
|
|
222
|
+
Protocol: the same 12.454-second official guitar case, four Numba threads,
|
|
223
|
+
Numba 0.65.1, pyFFTW 0.15.0, five-run medians after warm-up, excluding WAV I/O
|
|
224
|
+
and first-use compilation. All tested old/new output fields were exactly equal
|
|
225
|
+
within each platform/backend. These ratios describe that workload; cold starts,
|
|
226
|
+
longer audio and batch process startup are measured separately by
|
|
227
|
+
`tests/bench_release.py`. Set `NUMBA_NUM_THREADS` before importing the package,
|
|
228
|
+
and budget workers × threads when using process pools.
|
|
229
|
+
|
|
230
|
+
See the [measurement records](docs/portable-cpu-windows-validation.md),
|
|
231
|
+
[integrated validation and workload results](docs/v3.8.0-validation.md), and
|
|
232
|
+
[release validation procedure](docs/releasing.md). The earlier Windows
|
|
233
|
+
[approximately 10× result](docs/windows-validation.md) compared accelerator
|
|
234
|
+
configurations within v3.7.0. Historical v3.6.0 measurements remain in the
|
|
235
|
+
[CHANGELOG](CHANGELOG.md); they predate the v3.7.0 alignment correction.
|
|
216
236
|
|
|
217
237
|
## Project Structure
|
|
218
238
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"baseline_commit": "1c3953ec4b6ed2e7fa2a2ce56eab3bad648d98d4",
|
|
3
|
+
"kernel_sha256": "dc39818c9518f8686b0cfd5711739ad28d14ac6de3921675767b91e3f5433071",
|
|
4
|
+
"platform": "Linux-5.4.241-1-tlinux4-0017.14-x86_64-with-glibc2.35",
|
|
5
|
+
"architecture": "x86_64",
|
|
6
|
+
"python": "3.10.20",
|
|
7
|
+
"versions": {
|
|
8
|
+
"numpy": "2.2.6",
|
|
9
|
+
"scipy": "1.15.3",
|
|
10
|
+
"numba": "0.65.1",
|
|
11
|
+
"llvmlite": "0.47.0",
|
|
12
|
+
"pyfftw": "0.15.0",
|
|
13
|
+
"ai-edge-litert": "2.2.0"
|
|
14
|
+
},
|
|
15
|
+
"threads": 4,
|
|
16
|
+
"threading_layer": "workqueue",
|
|
17
|
+
"fft_backend": "fftw",
|
|
18
|
+
"repeats": 5,
|
|
19
|
+
"case": "official guitar48_stereo_64kbps_aac",
|
|
20
|
+
"duration_sec": 12.453833333333334,
|
|
21
|
+
"protocol": "audio, FP64, search_window=60, both alignments enabled; warmed; excludes audio I/O",
|
|
22
|
+
"seconds": {
|
|
23
|
+
"optimized": [
|
|
24
|
+
0.5394072169438004,
|
|
25
|
+
0.5373025466687977,
|
|
26
|
+
0.5457052960991859,
|
|
27
|
+
0.5398548180237412,
|
|
28
|
+
0.5398879866115749
|
|
29
|
+
],
|
|
30
|
+
"legacy_3_7_0": [
|
|
31
|
+
1.4971719882450998,
|
|
32
|
+
1.5210356987081468,
|
|
33
|
+
1.5065979771316051,
|
|
34
|
+
1.5096658081747591,
|
|
35
|
+
1.5108016063459218
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"median_sec": {
|
|
39
|
+
"optimized": 0.5398548180237412,
|
|
40
|
+
"legacy_3_7_0": 1.5096658081747591
|
|
41
|
+
},
|
|
42
|
+
"speedup": 2.7964292579646264,
|
|
43
|
+
"moslqo": 4.349722308064309,
|
|
44
|
+
"all_outputs_exact": true
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"baseline_commit": "1c3953ec4b6ed2e7fa2a2ce56eab3bad648d98d4",
|
|
3
|
+
"kernel_sha256": "dc39818c9518f8686b0cfd5711739ad28d14ac6de3921675767b91e3f5433071",
|
|
4
|
+
"platform": "macOS-15.7.3-arm64-arm-64bit-Mach-O",
|
|
5
|
+
"architecture": "arm64",
|
|
6
|
+
"python": "3.13.2",
|
|
7
|
+
"versions": {
|
|
8
|
+
"numpy": "2.4.3",
|
|
9
|
+
"scipy": "1.17.1",
|
|
10
|
+
"numba": "0.65.1",
|
|
11
|
+
"llvmlite": "0.47.0",
|
|
12
|
+
"pyfftw": "0.15.0",
|
|
13
|
+
"ai-edge-litert": "2.2.0"
|
|
14
|
+
},
|
|
15
|
+
"threads": 4,
|
|
16
|
+
"threading_layer": "workqueue",
|
|
17
|
+
"fft_backend": "fftw",
|
|
18
|
+
"repeats": 5,
|
|
19
|
+
"case": "official guitar48_stereo_64kbps_aac",
|
|
20
|
+
"duration_sec": 12.453833333333334,
|
|
21
|
+
"protocol": "audio, FP64, search_window=60, both alignments enabled; warmed; excludes audio I/O",
|
|
22
|
+
"seconds": {
|
|
23
|
+
"optimized": [
|
|
24
|
+
0.40294154200091725,
|
|
25
|
+
0.4215942089977034,
|
|
26
|
+
0.40989537499990547,
|
|
27
|
+
0.4250234579994867,
|
|
28
|
+
0.42086304199983715
|
|
29
|
+
],
|
|
30
|
+
"legacy_3_7_0": [
|
|
31
|
+
1.0646232920007606,
|
|
32
|
+
1.0958797089988366,
|
|
33
|
+
1.092644124997605,
|
|
34
|
+
1.0716607499998645,
|
|
35
|
+
1.0800574999993842
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"median_sec": {
|
|
39
|
+
"optimized": 0.42086304199983715,
|
|
40
|
+
"legacy_3_7_0": 1.0800574999993842
|
|
41
|
+
},
|
|
42
|
+
"speedup": 2.566292100316573,
|
|
43
|
+
"moslqo": 4.349722308064313,
|
|
44
|
+
"all_outputs_exact": true
|
|
45
|
+
}
|