visqol-python 3.6.0__tar.gz → 3.7.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 (35) hide show
  1. {visqol_python-3.6.0 → visqol_python-3.7.0}/CHANGELOG.md +40 -0
  2. {visqol_python-3.6.0/visqol_python.egg-info → visqol_python-3.7.0}/PKG-INFO +5 -6
  3. {visqol_python-3.6.0 → visqol_python-3.7.0}/pyproject.toml +5 -6
  4. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/__init__.py +1 -1
  5. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/api.py +1 -2
  6. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/patch_selector.py +16 -15
  7. {visqol_python-3.6.0 → visqol_python-3.7.0/visqol_python.egg-info}/PKG-INFO +5 -6
  8. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol_python.egg-info/requires.txt +2 -8
  9. {visqol_python-3.6.0 → visqol_python-3.7.0}/CONTRIBUTING.md +0 -0
  10. {visqol_python-3.6.0 → visqol_python-3.7.0}/LICENSE +0 -0
  11. {visqol_python-3.6.0 → visqol_python-3.7.0}/MANIFEST.in +0 -0
  12. {visqol_python-3.6.0 → visqol_python-3.7.0}/README.md +0 -0
  13. {visqol_python-3.6.0 → visqol_python-3.7.0}/setup.cfg +0 -0
  14. {visqol_python-3.6.0 → visqol_python-3.7.0}/tests/test_conformance.py +0 -0
  15. {visqol_python-3.6.0 → visqol_python-3.7.0}/tests/test_parallel_correctness.py +0 -0
  16. {visqol_python-3.6.0 → visqol_python-3.7.0}/tests/test_quick.py +0 -0
  17. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/__main__.py +0 -0
  18. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/alignment.py +0 -0
  19. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/analysis_window.py +0 -0
  20. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/audio_utils.py +0 -0
  21. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/gammatone.py +0 -0
  22. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/model/lattice_tcditugenmeetpackhref_ls2_nl60_lr12_bs2048_learn.005_ep2400_train1_7_raw.tflite +0 -0
  23. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/model/libsvm_nu_svr_model.txt +0 -0
  24. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/nsim.py +0 -0
  25. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/numba_accel.py +0 -0
  26. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/patch_creator.py +0 -0
  27. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/py.typed +0 -0
  28. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/quality_mapper.py +0 -0
  29. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/signal_utils.py +0 -0
  30. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/visqol_core.py +0 -0
  31. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol/visqol_manager.py +0 -0
  32. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol_python.egg-info/SOURCES.txt +0 -0
  33. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol_python.egg-info/dependency_links.txt +0 -0
  34. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol_python.egg-info/entry_points.txt +0 -0
  35. {visqol_python-3.6.0 → visqol_python-3.7.0}/visqol_python.egg-info/top_level.txt +0 -0
@@ -4,6 +4,45 @@ 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
+ ## [3.7.0] - 2026-05-30
8
+
9
+ ### Fixed
10
+ - **Audio-mode C++ parity in fine realignment** (`finely_align_and_recreate_patches`):
11
+ the previous "B2 optimisation" skipped the per-patch spectrogram rebuild
12
+ whenever the fine-alignment lag was zero, on the assumption that the rebuilt
13
+ spectrogram would be identical to the original patch. That assumption is
14
+ wrong. The original patch is sliced out of the **full-signal** Gammatone
15
+ spectrogram (warm IIR filter state, carrying history), whereas the rebuild
16
+ runs Gammatone over the **sliced** audio from a cold filter state — so the
17
+ leading frames differ even when no shift is applied. C++ ViSQOL always
18
+ rebuilds and keeps whichever scores higher, so skipping it silently dropped
19
+ that improvement. The skip is removed; every matched patch is now rebuilt and
20
+ compared, exactly as C++ does.
21
+ - **Impact:** audio MOS-LQO now matches the C++ binary to ULP. Max diff over
22
+ the 10 conformance cases dropped from **2.41 × 10⁻² → 1.17 × 10⁻⁴**; 9 of 10
23
+ cases are bit-exact (~10⁻¹⁴). The lone residual (`contrabassoon_24aac`,
24
+ 1.17 × 10⁻⁴) is an unrelated sub-sample boundary-patch alignment rounding,
25
+ now smaller than the speech-mode parity gap.
26
+ - Existing audio scores shift by up to ~0.024 MOS where they were previously
27
+ too low; this is a move **toward** the C++ ground truth, not away from it.
28
+ - Speech polynomial and lattice scores are unaffected (bit-identical before
29
+ and after).
30
+
31
+ ### Performance
32
+ - Fine realignment now rebuilds the spectrogram for every patch (including
33
+ `lag == 0`), so it no longer benefits from the zero-lag skip. Net effect is
34
+ roughly **1.4–1.5× slower realignment** (e.g. `guitar48_stereo` 0.44 s → 0.65 s
35
+ end-to-end with Numba + pyFFTW). This is the cost of exact C++ parity and is
36
+ consistent with the project's precision-first policy. Audio-mode RTF rises
37
+ from ~0.036 (3.6.0) to ~0.054 — still ~1.7× faster than the C++ binary
38
+ (~0.093) and faster than 3.5.0 (~0.067).
39
+
40
+ ### Changed
41
+ - **Dropped Python 3.9 support** (`requires-python = ">=3.10"`). Downstream
42
+ projects still on 3.9 should pin `visqol-python<3.7`.
43
+ - CI: bumped GitHub Actions to Node-24-compatible major versions
44
+ (checkout v5, setup-python v6, upload/download-artifact v5).
45
+
7
46
  ## [3.6.0] - 2026-05-27
8
47
 
9
48
  ### Added
@@ -218,6 +257,7 @@ All v3.5.0 conformance baselines preserved within ULP precision:
218
257
  - Bundled SVR model (`libsvm_nu_svr_model.txt`)
219
258
  - GitHub Actions workflow for auto-publish to PyPI via Trusted Publisher
220
259
 
260
+ [3.7.0]: https://github.com/talker93/visqol-python/compare/v3.6.0...v3.7.0
221
261
  [3.6.0]: https://github.com/talker93/visqol-python/compare/v3.5.0...v3.6.0
222
262
  [3.5.0]: https://github.com/talker93/visqol-python/compare/v3.4.0...v3.5.0
223
263
  [3.4.0]: https://github.com/talker93/visqol-python/compare/v3.3.6...v3.4.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: visqol-python
3
- Version: 3.6.0
3
+ Version: 3.7.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.9
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; python_version >= "3.10" and extra == "lattice"
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; python_version >= "3.10" and extra == "all"
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; python_version >= "3.10" and extra == "dev"
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
 
@@ -8,7 +8,7 @@ dynamic = ["version"]
8
8
  description = "ViSQOL - Virtual Speech Quality Objective Listener (Pure Python)"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
11
- requires-python = ">=3.9"
11
+ requires-python = ">=3.10"
12
12
  authors = [
13
13
  {name = "Shan Jiang"},
14
14
  ]
@@ -22,7 +22,6 @@ classifiers = [
22
22
  "Intended Audience :: Developers",
23
23
  "Intended Audience :: Science/Research",
24
24
  "Programming Language :: Python :: 3",
25
- "Programming Language :: Python :: 3.9",
26
25
  "Programming Language :: Python :: 3.10",
27
26
  "Programming Language :: Python :: 3.11",
28
27
  "Programming Language :: Python :: 3.12",
@@ -40,11 +39,11 @@ dependencies = [
40
39
 
41
40
  [project.optional-dependencies]
42
41
  accel = ["numba>=0.57"]
43
- lattice = ["ai-edge-litert>=2.1.5; python_version >= '3.10'"]
42
+ lattice = ["ai-edge-litert>=2.1.5"]
44
43
  fftw = ["pyfftw>=0.13"]
45
44
  all = [
46
45
  "numba>=0.57",
47
- "ai-edge-litert>=2.1.5; python_version >= '3.10'",
46
+ "ai-edge-litert>=2.1.5",
48
47
  "pyfftw>=0.13",
49
48
  ]
50
49
  test = ["pytest>=7.0"]
@@ -53,7 +52,7 @@ dev = [
53
52
  "ruff>=0.4",
54
53
  "mypy>=1.8",
55
54
  "numba>=0.57",
56
- "ai-edge-litert>=2.1.5; python_version >= '3.10'",
55
+ "ai-edge-litert>=2.1.5",
57
56
  "pyfftw>=0.13",
58
57
  ]
59
58
 
@@ -80,7 +79,7 @@ visqol = ["model/*.txt", "model/*.tflite", "py.typed"]
80
79
  testpaths = ["tests"]
81
80
 
82
81
  [tool.ruff]
83
- target-version = "py39"
82
+ target-version = "py310"
84
83
  line-length = 95
85
84
 
86
85
  [tool.ruff.lint]
@@ -13,7 +13,7 @@ Usage:
13
13
  print(f"MOS-LQO: {result.moslqo}")
14
14
  """
15
15
 
16
- __version__: str = "3.6.0"
16
+ __version__: str = "3.7.0"
17
17
 
18
18
  from visqol.api import ProgressCallback, VisqolApi
19
19
  from visqol.audio_utils import AudioSignal
@@ -10,9 +10,8 @@ from __future__ import annotations
10
10
 
11
11
  import logging
12
12
  import os
13
- from collections.abc import Sequence
13
+ from collections.abc import Callable, Sequence
14
14
  from concurrent.futures import ProcessPoolExecutor, as_completed
15
- from typing import Callable
16
15
 
17
16
  import numpy as np
18
17
  from numpy.typing import NDArray
@@ -469,14 +469,19 @@ def finely_align_and_recreate_patches(
469
469
 
470
470
  1. Extract audio sub-signals
471
471
  2. Re-align at fine granularity
472
- 3. **If lag == 0, skip spectrogram rebuild** (B2 optimisation)
473
- 4. Rebuild spectrograms only when alignment changed
474
- 5. Recompute NSIM
475
- 6. Keep the better result (original or re-aligned)
476
-
477
- The B2 optimisation avoids redundant Gammatone filtering when the
478
- fine alignment produces zero shift the rebuilt spectrograms would
479
- be identical to the originals, so skipping saves **~2-3 s** per file.
472
+ 3. Rebuild spectrograms from the (re)aligned audio slices
473
+ 4. Recompute NSIM
474
+ 5. Keep the better result (original or re-aligned)
475
+
476
+ The rebuild is performed for **every** patch, including those with
477
+ ``lag == 0``. This mirrors C++ exactly: the original patch was sliced
478
+ out of the *full-signal* spectrogram (its Gammatone IIR state is warm,
479
+ carrying history from earlier samples), whereas the rebuilt patch runs
480
+ Gammatone over the *sliced* audio from a cold filter state. The two
481
+ therefore differ in their leading frames even when no time shift is
482
+ applied, and C++ keeps whichever scores higher. Skipping the rebuild
483
+ on ``lag == 0`` (a former "optimisation") silently dropped this
484
+ improvement and broke audio-mode C++ parity by up to ~0.024 MOS.
480
485
  """
481
486
  realigned_results: list[PatchSimilarityResult] = list(sim_results)
482
487
 
@@ -506,17 +511,13 @@ def finely_align_and_recreate_patches(
506
511
  except Exception:
507
512
  continue
508
513
 
509
- # B2 optimisation: if lag is zero, alignment didn't change anything.
510
- # The rebuilt spectrogram would be identical to the original, so the
511
- # recomputed NSIM would also be identical — skip the expensive rebuild.
512
- if lag == 0.0:
513
- continue
514
-
515
514
  # Check we have enough samples
516
515
  if len(ref_aligned.data) <= window.size or len(deg_aligned.data) <= window.size:
517
516
  continue
518
517
 
519
- # 3. Rebuild spectrograms (only when lag != 0)
518
+ # 3. Rebuild spectrograms from the aligned slices (always see docstring:
519
+ # cold vs warm Gammatone state makes this differ from the original
520
+ # patch even when lag == 0, so it is not a redundant recompute).
520
521
  try:
521
522
  ref_spec = spect_builder.build(ref_aligned, window)
522
523
  deg_spec = spect_builder.build(deg_aligned, window)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: visqol-python
3
- Version: 3.6.0
3
+ Version: 3.7.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.9
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; python_version >= "3.10" and extra == "lattice"
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; python_version >= "3.10" and extra == "all"
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; python_version >= "3.10" and extra == "dev"
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
 
@@ -8,27 +8,21 @@ numba>=0.57
8
8
 
9
9
  [all]
10
10
  numba>=0.57
11
- pyfftw>=0.13
12
-
13
- [all:python_version >= "3.10"]
14
11
  ai-edge-litert>=2.1.5
12
+ pyfftw>=0.13
15
13
 
16
14
  [dev]
17
15
  pytest>=7.0
18
16
  ruff>=0.4
19
17
  mypy>=1.8
20
18
  numba>=0.57
21
- pyfftw>=0.13
22
-
23
- [dev:python_version >= "3.10"]
24
19
  ai-edge-litert>=2.1.5
20
+ pyfftw>=0.13
25
21
 
26
22
  [fftw]
27
23
  pyfftw>=0.13
28
24
 
29
25
  [lattice]
30
-
31
- [lattice:python_version >= "3.10"]
32
26
  ai-edge-litert>=2.1.5
33
27
 
34
28
  [test]
File without changes
File without changes
File without changes
File without changes