Skip to main content

distortion-residual

A differentiable Distortion Residual Level (DRL) metric for PyTorch.

DRL measures the nonlinear distortion introduced by audio processors (limiters, compressors, saturators, etc.) using the nulling method:

  1. Level-match the reference to the processed signal via least-squares projection, cancelling any linear gain difference.
  2. Subtract the matched reference from the processed signal to isolate the distortion residual.
  3. Measure the power ratio of the residual to the signal:

$$ \text{DRL} = 10 \log_{10} \frac{\lVert d \rVert^2}{\lVert \hat{g},x \rVert^2}, \qquad \hat{g} = \frac{\langle x, y \rangle}{\langle x, x \rangle}, \qquad d = y - \hat{g},x $$

Every operation is differentiable, so DRL can be used directly as a loss function for gradient-based optimisation of audio processing parameters.

Installation

pip install distortion-residual

Or from source:

git clone https://github.com/agrathwohl/distortion-residual.git
cd distortion-residual
pip install -e .

Optional: audio file I/O

pip install "distortion-residual[audio]"

Quick start

import torch
from distortion_residual import DRL

drl = DRL(sample_rate=44100)

reference = torch.randn(44100)              # 1 s of audio
processed = torch.clamp(reference, -0.5, 0.5)  # hard-clip at -6 dBFS

result = drl(reference, processed)
print(result["total_drl_db"])    # e.g. tensor(-18.42)
print(result["total_drl_percent"])  # e.g. tensor(12.0)

As a loss function

gain = torch.tensor(1.0, requires_grad=True)
processed = torch.tanh(reference * gain)

result = drl(reference, processed)
loss = result["total_drl_db"]
loss.backward()
print(gain.grad)  # gradient flows through

Band-wise analysis

By default, DRL is decomposed into three frequency bands (20-200 Hz, 200-2000 Hz, 2000-20000 Hz). You can customise or disable this:

# Custom bands
drl = DRL(sample_rate=44100, frequency_bands=[(100, 1000), (1000, 10000)])

# Broadband only (faster, no FIR filtering)
drl = DRL(sample_rate=44100, frequency_bands=None)

Output dictionary

DRL.forward() returns a dict with:

Key Type Description
total_drl_db Tensor (scalar) Broadband DRL in dB
total_drl_percent Tensor (scalar) DRL as a percentage
band_drl_db dict[str, Tensor] Per-band DRL in dB
band_drl_percent dict[str, Tensor] Per-band DRL as percentage
residual Tensor The distortion residual signal
residual_rms Tensor (scalar) RMS of the residual
signal_rms Tensor (scalar) RMS of the level-matched reference

How it works

The level-matching step (g_hat = <x,y>/<x,x>) projects out any linear gain component, so DRL is invariant to makeup gain. Only the nonlinear distortion component remains in the residual.

This makes DRL ideal for optimising dynamics processors: the loss function measures what the processor does to the waveform shape, not how loud it makes the output.

Gradient properties

  • Through the residual: linear subtraction, gradients pass directly.
  • Through level matching: quotient rule on the inner-product ratio.
  • Through band filters: FIR convolution is a linear operation.

All paths are fully differentiable. No straight-through estimators or surrogate gradients required.

Development

git clone https://github.com/agrathwohl/distortion-residual.git
cd distortion-residual
uv sync --extra dev
uv run pytest

License

MIT

Metadata

Release files for distortion-residual 0.1.0

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

Source distribution (sdist)

Source distribution for distortion-residual 0.1.0
File Size Uploaded
distortion_residual-0.1.0.tar.gz 76.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for distortion-residual 0.1.0
File Interpreter ABI Platform
distortion_residual-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 84.5 kB

Release files / distortion_residual-0.1.0.tar.gz

Download URL distortion_residual-0.1.0.tar.gz
Size 76.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e043f1b4ccc514130949c4c3329e295d5e70a2c520935b2f309b2e4390ee3913
BLAKE2b-256 checksum
How to use checksums
b763017b69ee80ab9087e76dba1dfcb084d773408ab34142a2491bf8cb2f1573
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / distortion_residual-0.1.0-py3-none-any.whl

Download URL distortion_residual-0.1.0-py3-none-any.whl
Size 7.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6e0f3124d30daeffd1bbff8dff66f8736a2855609634059caea8ebef9b6f9e9a
BLAKE2b-256 checksum
How to use checksums
bf809eedde035766b46d7c88584145465b0430ebb6a6e841e3ac826e90f08de2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release 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