Skip to main content

audio-diag

Standalone Python CLI and library for stereo-master audio diagnostics with explicit reliability tiers. It operates completely offline, requires no external services or telemetry, and classifies measurements into three clear levels of trustworthiness.

  • Level 1 (reliable): BS.1770-4 / EBU R128 loudness (integrated, momentary max, short-term max, LRA), sample peak, true peak (4x polyphase Kaiser sinc oversampling), sample-level clipping runs, DC offset, leading and trailing silence, 8-band energy distribution, spectral slope, per-band stereo correlation and mono compatibility.
  • Level 2 (heuristic hints): Beat-window energy drops (dropouts vs. sustained breaks), kick-onset deviation from a BPM grid, section energy contrast, crest factor (global and FFT band projections). Always reported with confidence: "heuristic" and a warning; never treated as an automatic defect.
  • Level 3 (not determinable from a stereo master): Kick fundamental frequency, kick envelope, kick/rumble ratio, sub masking, musical key. Always reports {"status": "not_determinable", "reason": ...}. No estimators exist for these from mixed stereo audio.

Requirements

  • Python >= 3.12
  • numpy
  • ffmpeg and ffprobe installed and available on PATH

System ffmpeg is used solely to decode audio into float32 PCM; all loudness, true-peak, spectral, and time-domain calculations are performed directly by audio-diag's internal NumPy implementation.

Installation

Install via pip from source:

pip install .

For editable development:

pip install -e .

CLI Usage

The package installs the audio-diag executable and can also be run via python3 -m audio_diag.

# Analyze an audio file
audio-diag analyze track.wav

# Pass tempo and declare intentional arrangement decisions
audio-diag analyze master.wav --bpm 155 --intent "break at bars 33-40" --intent "mono bass"

# Export machine-readable JSON and human-readable Markdown
audio-diag analyze master.wav --json report.json --md report.md

# Inspect the bundled JSON Schema
audio-diag schema

Options

usage: audio_diag analyze [-h] [--bpm BPM] [--json OUT] [--md OUT]
                          [--intent TEXT] [--target-lufs TARGET_LUFS]
                          [--tolerance-lu TOLERANCE_LU] [--tp-max TP_MAX]
                          [--silence-dbfs SILENCE_DBFS] [--drop-db DROP_DB]
                          file
  • file: Path to input audio file (opened strictly read-only).
  • --bpm N: Tempo in beats per minute; enables grid-aligned Level 2 onset hints.
  • --json OUT: Write formatted JSON report to OUT instead of stdout.
  • --md OUT: Write Markdown report to OUT instead of stderr.
  • --intent TEXT: Declare an intentional artistic choice (repeatable). Echoed in intentional_decisions and never reported as a defect.
  • --target-lufs N: Integrated loudness target in LUFS (default: -8.0).
  • --tolerance-lu N: Loudness tolerance in LU (default: 2.0).
  • --tp-max N: Maximum true peak in dBTP (default: -0.3).
  • --silence-dbfs N: Threshold for digital silence detection (default: -60.0).
  • --drop-db N: Energy drop threshold for dropout/break detection (default: 6.0).

Path Safety and Safeguards

  • The input file is opened strictly read-only (O_RDONLY).
  • The CLI refuses any output path that matches the input file (resolving symlinks and hardlinks).
  • The CLI refuses to write output inside protected directories:
    • Standard user music directories (~/Music, ~/Música).
    • Directories configured via the AUDIO_DIAG_PROTECTED_DIRS environment variable (colon-separated).
  • Exit codes: 0 on success, 2 on invalid arguments, decoding failure, or unsafe output paths.

Python API

from audio_diag import analyze, load_schema, to_markdown, DecodeError

# Analyze an audio file
try:
    report = analyze("master.wav", bpm=158, intents=["break at bars 33-40"])
except DecodeError as err:
    print(f"Decoding failed: {err}")

# Access Level 1 metrics
loudness = report["level1"]["loudness"]
print(f"Integrated LUFS: {loudness['integrated_lufs']}")
print(f"True Peak dBTP:  {report['level1']['peaks']['true_peak_dbtp']}")

# Render Markdown report
md_text = to_markdown(report)

# Load the JSON Schema definition
schema = load_schema()

Direct PCM Measurement

Measure in-memory float32 PCM samples without decoding from disk:

import numpy as np
from audio_diag.loudness import measure

# samples: 2D numpy array of shape (num_samples, channels)
stats = measure(samples, sample_rate=48000)
print(stats["integrated_lufs"], stats["true_peak_dbtp"])

Spectral Profiles and Dispersion

Compare relative spectral balance across tracks using gain-normalized power profiles:

from audio_diag.profiles import profile, compare

p1 = profile(analyze("track1.wav"))
p2 = profile(analyze("track2.wav"))
dispersion = compare([p1, p2])

Profiles normalize by total spectral power ($P_{band} / P_{total}$), canceling gain differences. Comparisons produce observed minimum and maximum envelopes per band rather than an artificial average or arbitrary pass/fail gate.

Schema & Report Invariants

  • Schema definition is bundled as report.schema.json and accessible via audio_diag.load_schema().
  • Standard keys: tool, version, file, thresholds, level1, level2, level3, intentional_decisions, warnings, verdicts.
  • Finite JSON: values are never NaN or inf. Missing or invalid numbers are represented as null with an accompanying <metric>_reason explanation.
  • Lossy inputs (e.g. MP3, Opus, AAC) set file.lossy_source: true. True peak, crest factor, and absolute loudness are explicitly marked "not valid for calibration (lossy source)" because lossy codecs alter peaks and levels dynamically.

Metric Summary

  • Loudness: ITU-R BS.1770-4 / EBU R128 biquad K-weighting, 400 ms blocks with 75% overlap, -70 LUFS absolute and -10 LU relative gating. Maxima evaluated on a 10 ms grid. LRA uses 3 s windows, -20 LU relative gate, P10-P95 span.
  • True Peak: 4x polyphase Kaiser-windowed sinc oversampling filter (32 taps).
  • Clipping: Identifies consecutive runs of full-scale samples (|x| >= 1.0 - 1e-6) per channel.
  • Bands: Relative energy across 8 sub-bands computed via Hann-windowed periodogram (8192-point FFT, 50% overlap).
  • Mono Compatibility: Inter-channel correlation and mono energy ratio $10 \log_{10} \frac{E[((L+R)/2)^2]}{E[(L^2+R^2)/2]}$ per frequency band.

Testing

The test suite runs with Python's standard unittest:

# Run the entire test suite
python3 -m unittest discover

# Run specific test modules
python3 -m unittest tests.test_cli_robustness
python3 -m unittest tests.test_corpus_expected
python3 -m unittest tests.test_ebu_tech3341
  • test_corpus_expected: Verifies measurements against analytically calculated mathematical ground truths (sine LUFS via K-weighting filter transfer function, pink noise LUFS via Parseval's theorem, exact clipping runs, synthesized click trains).
  • test_ebu_tech3341: Reconstructs EBU Tech 3341 compliance signals and asserts tolerances according to the specification ($\pm 0.1$ LU).
  • test_ffmpeg_crosscheck: Cross-checks loudness and true-peak results against system ffmpeg -af ebur128=peak=true.
  • test_cli_robustness: Verifies CLI exit codes, error handling, path traversal and overwrite protection, memory limits, and schema output.

Test audio signals are generated deterministically in memory or temporary directories and are not committed to source control.

License

MIT License. Copyright (c) 2026 Adrián Cancio / contributors. See LICENSE for details.

Metadata

Release files for audio-diag 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 audio-diag 0.1.0
File Size Uploaded Provenance Actions
audio_diag-0.1.0.tar.gz 41.7 kB Without provenance Details for audio_diag-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for audio-diag 0.1.0
File Interpreter ABI Platform Actions
audio_diag-0.1.0-py3-none-any.whl Python 3 none any Details for audio_diag-0.1.0-py3-none-any.whl

Total release size: 74.2 kB

Release files / audio_diag-0.1.0.tar.gz

Download URL audio_diag-0.1.0.tar.gz
Size 41.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a8c08cc298b077259d68af78231d62a67554ec0f883534f184476682059a74fb
BLAKE2b-256 checksum
How to use checksums
a261a84b4c1dc65a81909d72e0e29fed9219ac31fa239fd2c341fe015cc06958
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

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

Download URL audio_diag-0.1.0-py3-none-any.whl
Size 32.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
295ff2257f44fefd3f42e288e6efa7655cfbfb322eb0522d080a698af0e48d7a
BLAKE2b-256 checksum
How to use checksums
d3c75e28e9ff9494777649b78b23b189fd3224230470203bc7e69270e5bf15b6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Security for audio-diag 0.1.0

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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