Skip to main content

speequal

Speech/audio quality metrics as prebuilt Python wheels — pip install speequal. One metric today:

speequal.visqol — ViSQOL v3, Google's objective, full-reference perceptual audio/speech quality metric (MOS-LQO), plus a small numpy API. This is a packaging of Google's code, not a reimplementation: the C++ library, the pybind11 bindings, the SVR and TFLite lattice models and all default mappings are the ones in google/visqol at the commit recorded in UPSTREAM.md, built with Bazel and redistributed under the same Apache-2.0 license (see LICENSE, NOTICE). Upstream is not on PyPI (the name is prohibited there) and needs Bazel plus a TensorFlow Lite source build to install; this wheel needs neither.

pip install speequal

Wheels: Linux x86_64, CPython 3.12 and 3.14, tagged manylinux_2_39 (built on Ubuntu 24.04 with GCC 13; they need glibc ≥ 2.39 and a GCC-13-era libstdc++). Other platforms/interpreters fall back to the sdist, which needs Bazel (see below). Runtime dependencies: numpy, protobuf (≥ 4.21 — the bindings use message_factory.GetMessageClass; tested with 6.33 and 7.36).

API

from speequal import visqol

visqol.speech(ref, deg, fs=16000)          # MOS-LQO, speech mode (wideband, 16 kHz)
visqol.audio(ref, deg, fs=48000)           # MOS-LQO, audio mode (SVR model, 48 kHz)
visqol.measure(ref, deg, mode="speech", fs=16000)  # upstream's raw SimilarityResultMsg
visqol.selftest()                          # the install gate (see below); speequal.selftest() runs every metric's

ref / deg are 1-d float arrays (numpy or torch, any float dtype, values in [-1, 1]) or paths to 16-bit PCM WAV files. speech() raises unless fs == 16000 and audio() unless fs == 48000 — nothing is resampled for you; get to the right rate first. Lengths may differ (ViSQOL aligns and handles the mismatch itself; nothing is padded or cropped here). Model files are located inside the package, never relative to the working directory. speequal.__version__ is the wheel version (<upstream visqol version>.postN), speequal.visqol.UPSTREAM_COMMIT the vendored git sha.

Options, all mirroring VisqolConfig fields: speech(..., lattice=True) maps NSIM→MOS with the TFLite deep-lattice model (upstream's binary default; its ceiling for an identical pair is ≈ 4.51), lattice=False uses the scaled polynomial mapping (identical pair → 5.0); unscaled_mos_mapping=True (requires lattice=False, as upstream ignores it otherwise) leaves a perfect score at ≈ 4.0. measure() additionally exposes allow_unsupported_sample_rates and search_window_radius, and returns moslqo, vnsim, fvnsim, fvnsim10, fstdnsim, fvdegenergy, center_freq_bands, patch_sims, alignment_lag_s.

Upstream's own modules are untouched and still importable, one level down: from speequal.visqol import visqol_lib_py, from speequal.visqol.pb2 import visqol_config_pb2, similarity_result_pb2.

CLI

python -m speequal.visqol ref.wav deg.wav            # speech mode, prints the MOS-LQO
python -m speequal.visqol ref.wav deg.wav --audio    # audio mode
python -m speequal.visqol ref.wav deg.wav --verbose  # + VNSIM, per-band NSIM, patch count

The sample rate is read from the files (any rate in speech mode, 48 kHz in audio mode, like upstream's binary). --no-lattice, --unscaled, --allow-unsupported-sample-rates as above.

Self-test

python -c "import speequal; speequal.selftest()"

For ViSQOL: (a) speech mode at 16 kHz: a clip vs itself hits the mapper ceiling (≈ 4.5 lattice / 5.0 polynomial) and a −20 dB-noise copy scores lower; (b) audio mode at 48 kHz: a clip vs itself ≥ 4.7; (c) conformance: the 20 reference/degraded/flag cases of upstream's tests/conformance_test.cc with the expected MOS-LQO values of src/include/conformance.h (copied into speequal/visqol/conformance.json) reproduce to 1e-3. The two clean-speech clips ship in the wheel; the other conformance WAVs (~55 MB) are downloaded once from google/visqol at the pinned commit into ~/.cache/visqol/ (VISQOL_TESTDATA=/path/to/upstream/testdata uses a local copy). visqol.selftest(conformance=False) skips the download.

Building from source

The sdist on PyPI is upstream's tree plus the wrapper; pip install from it runs upstream's setup.py, which needs Bazel 5.3.2 (bazelisk picks it up from .bazelversion), a C++17 compiler, and fetches TensorFlow 2.11 sources — so on an unsupported platform expect a long first build. From this repository:

git clone https://github.com/danjacobellis/speequal && cd speequal
pip install numpy setuptools wheel auditwheel patchelf build   # in the target interpreter
./build.sh                       # installs bazelisk into ~/.local/bin if absent; wheel lands in dist/
./build.sh --python=/path/to/other/python   # e.g. a 3.14
./install_for_debugging.sh       # build + install into ~/g + selftest

build.sh copies upstream/ to build/upstream/, applies patches/, runs Bazel there, builds the wheel with upstream's setup.py, then auditwheel repairs it to the most specific manylinux policy the build host allows. Bazel's cache makes rebuilds fast; --clean expunges it.

What changed vs upstream

Nothing in the metric. Every deviation from google/visqol is a file in patches/:

  • 0001-bazelrc-gcc13-cstdint.patch — build --cxxopt=-include --cxxopt=cstdint: TF 2.11's tensorflow/lite/kernels/internal/spectrogram.cc uses uint32_t without including <cstdint>, which GCC 13 no longer tolerates.
  • 0002-setup-py-version-wrapper-metadata.patch — setup.py: distribution speequal, version 3.3.3.postN, Google's visqol package mapped to speequal.visqol (.model, .pb2 beneath it), PyPI metadata (readme, license, python_requires, install_requires = numpy, protobuf), a Distribution.has_ext_modules override so the wheel is tagged cpXY-<platform> instead of py3-none-any, and copying the wrapper (python_wrapper/) plus the two clean-speech WAVs into the package.
  • 0003-workspace-pybind11_protobuf-getmessageclass.patch — WORKSPACE applies a one-hunk patch to the pinned pybind11_protobuf (2022): protobuf ≥ 5 removed MessageFactory.GetPrototype, which made every proto conversion silently fail; use message_factory.GetMessageClass when it exists.
  • 0004-manifest-in-sdist.patch — MANIFEST.in so the sdist carries the whole Bazel tree (src/, python/, model/, WORKSPACE, BUILD, .bazelrc, .bazelversion, the patches) and never a previous build's bazel-bin.

Added on top (not patches): the speequal package from src/speequal/ (__init__.py, visqol/{__init__,api,__main__,selftest}.py, visqol/conformance.json).

Metadata

Release files for speequal 3.3.3.post1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for speequal 3.3.3.post1
File Size Uploaded Provenance
speequal-3.3.3.post1.tar.gz 1.4 MB Without provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for speequal 3.3.3.post1
File Interpreter ABI Platform
speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.39+ x86-64 Details
speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.39+ x86-64 Details

Total release size: 11.6 MB

Release files / speequal-3.3.3.post1.tar.gz

Download URL speequal-3.3.3.post1.tar.gz
Size 1.4 MB
Tags Source
SHA-256 checksum
How to use checksums
fed6e592a1c4eb8f462d34ae62467ae540452d9875aa8050be5fc6969a98d795
BLAKE2b-256 checksum
How to use checksums
c9463f62572fcf8fa5312fd44fee6da1544ed05d49845de2dae3d2d40a430d4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.3

Release files / speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl

Download URL speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl
Size 5.1 MB
Tags CPython 3.14 Linux glibc 2.39+ x86-64
SHA-256 checksum
How to use checksums
ba53bc72512a11f0cdbaf5fb9e4f28166d16086a043e4a4df91efc7c30151ff5
BLAKE2b-256 checksum
How to use checksums
15212e7989ea9f20d69bff209287e4f4554ed7f6dbd1a929b619a7e690ff8bd7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.3

Release files / speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl

Download URL speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl
Size 5.1 MB
Tags CPython 3.12 Linux glibc 2.39+ x86-64
SHA-256 checksum
How to use checksums
edaaf864018c7751dc6571ca874eeb4b2de1b4ec39cfbcb87871d27c8df28fb9
BLAKE2b-256 checksum
How to use checksums
b18f8e09f9853db6ecbf9d8c0d8f617e9d195d14226082fa7570cd613045d450
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.3

Security for speequal 3.3.3.post1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

3.3.3.post1 This release

3 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page