Skip to main content

speequal

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

speequal.visqolViSQOL 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.patchbuild --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.patchsetup.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.patchWORKSPACE 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.patchMANIFEST.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).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

speequal-3.3.3.post1.tar.gz (1.4 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.39+ x86-64

speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.39+ x86-64

File details

Details for the file speequal-3.3.3.post1.tar.gz.

File metadata

  • Download URL: speequal-3.3.3.post1.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for speequal-3.3.3.post1.tar.gz
Algorithm Hash digest
SHA256 fed6e592a1c4eb8f462d34ae62467ae540452d9875aa8050be5fc6969a98d795
MD5 a2525cfea6d499cbe83092419829ece8
BLAKE2b-256 c9463f62572fcf8fa5312fd44fee6da1544ed05d49845de2dae3d2d40a430d4e

See more details on using hashes here.

File details

Details for the file speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for speequal-3.3.3.post1-cp314-cp314-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 ba53bc72512a11f0cdbaf5fb9e4f28166d16086a043e4a4df91efc7c30151ff5
MD5 662e8054c2d0b0e19e162f93b53295fb
BLAKE2b-256 15212e7989ea9f20d69bff209287e4f4554ed7f6dbd1a929b619a7e690ff8bd7

See more details on using hashes here.

File details

Details for the file speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for speequal-3.3.3.post1-cp312-cp312-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 edaaf864018c7751dc6571ca874eeb4b2de1b4ec39cfbcb87871d27c8df28fb9
MD5 4f4d9c4fd797382a7fb9bc7edb5a2f81
BLAKE2b-256 b18f8e09f9853db6ecbf9d8c0d8f617e9d195d14226082fa7570cd613045d450

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

3.3.3.post1 This release

3 files

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