Skip to main content

Differentiable Distortion Residual Level (DRL) metric for audio processing

Project description

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

Project details


Download files

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

Source Distribution

distortion_residual-0.1.0.tar.gz (76.9 kB view details)

Uploaded Source

Built Distribution

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

distortion_residual-0.1.0-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file distortion_residual-0.1.0.tar.gz.

File metadata

  • Download URL: distortion_residual-0.1.0.tar.gz
  • Upload date:
  • Size: 76.9 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for distortion_residual-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e043f1b4ccc514130949c4c3329e295d5e70a2c520935b2f309b2e4390ee3913
MD5 e94de06fc556ef599e50cd74993e6cbe
BLAKE2b-256 b763017b69ee80ab9087e76dba1dfcb084d773408ab34142a2491bf8cb2f1573

See more details on using hashes here.

File details

Details for the file distortion_residual-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: distortion_residual-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for distortion_residual-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6e0f3124d30daeffd1bbff8dff66f8736a2855609634059caea8ebef9b6f9e9a
MD5 522a31d07ed42bb2b0bd32cca937819d
BLAKE2b-256 bf809eedde035766b46d7c88584145465b0430ebb6a6e841e3ac826e90f08de2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page