Mikkeli Matlock d7782bb9d9 Speed up analysis: drop BPM, vectorise loudness/true-peak, share short-term
Profiled hot spots on a 4-min track and cut the worst offenders:

- Remove BPM: librosa.beat.beat_track ran on every load (~3.7s) for a number no
  better than tapping by hand. Dropped from AudioFile + the metadata panel.
- LUFS short-term: replace 474 per-window pyloudnorm.integrated_loudness calls
  with one K-weighting pass (reusing pyloudnorm's own filter coefficients) + a
  vectorised sliding mean-square. This is true *ungated* EBU R128 short-term
  (the old loop wrongly gated each 3s window). Integrated + LRA still use
  pyloudnorm's gated calls. ~3.8s -> ~1.9s.
- PSR: reuse LUFS's short-term series (memoised on the AudioFile) + vectorised
  sample-peak. ~3.0s -> ~0.2s.
- True Peak: oversample the whole signal once, then an O(N) running max over
  windows instead of per-window resample_poly. Bit-identical to the old loop
  (max|diff| 0.0000 dB). ~2.1s -> ~1.1s.
- Crest Factor: peaks via the same O(N) running max (last per-window loop gone).

lufs+psr+true_peak: ~9.2s -> ~3.2s, plus ~3.7s of BPM removed from every load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 01:36:08 +09:00
2026-05-28 14:05:34 +09:00
2026-05-28 14:05:34 +09:00
2024-04-10 20:14:02 +09:00

uj-mastering-master

Custom mastering toolkit providing visual metrics for evaluating audio masterings. Developed with Claude Code assistance.

Features

Current

  • PyQt5 GUI: drag-and-drop or file-dialog ingest of .mp3, .wav, .flac
  • Switchable metrics via a dropdown, all sharing one analysis cache:
    • RMS Power — 10 s rolling window with adaptive colour scale
    • Waveform — min/max envelope, fixed ±1.1 scale
    • LUFS — BS.1770 short-term (3 s) + integrated + loudness range (LRA)
    • Crest Factor — peak-to-RMS spread over time
    • PSR — peak-to-short-term-loudness ratio ("is it still breathing?")
    • True Peak — 4× oversampled dBTP, catches inter-sample peaks
    • Spectrogram — log-frequency STFT power heatmap over time
  • Always-labelled axis extremes: every plot forces its exact min/max onto the ticks, so you can read the true range even on a log axis (e.g. the spectrogram's 22 kHz top, which otherwise falls between decade ticks)
  • Native sample rate: audio is loaded without resampling, so the full band (up to the file's own nyquist, e.g. ~22 kHz for 44.1 kHz files) is analysed
  • BPM detection via librosa
  • CJK-safe font system with custom fonts loaded from fonts/ (gitignored), system fallbacks, and a live font selector
  • Background analysis thread so the UI stays responsive; metric switches compute off the GUI thread and cache, so re-selecting a metric is instant
  • Embedded matplotlib canvas with auto-regenerated plots on font change

Roadmap

See CLAUDE.md for the full development roadmap. Near-term: dynamic range (DR meter), plot-style controls, interactive axis controls.

Quick start

This project uses uv. With uv installed:

uv sync
uv run ujm

uv run ujm is the only supported entry point — it boots the GUI.

Logging flags

uv run ujm --log-level DEBUG       # ERROR | WARN | INFO | DEBUG | TRACE
uv run ujm --log-file               # also write audio_analysis.log

Fonts

Drop .ttf / .otf / .ttc files into fonts/ to get them in the font selector. The directory is gitignored to avoid bundling licensed font data. See CJK_FONTS.md for details.

Dependencies

librosa, numpy, matplotlib, mutagen, pyloudnorm, PyQt5 — all pinned through uv.lock. Python 3.10+.

Architecture

Module Responsibility
main.py MainWindow + the ujm entry point
analysis_results_manager.py Background QThread worker, result + metric-data cache
master_core.py AudioFile: native-rate librosa loading, RMS rolling window, BPM
metrics.py Pluggable Metric ABC + registry (RMS, Waveform, LUFS, Crest, PSR, True Peak, Spectrogram)
audio_visualization_widget.py Embedded FigureCanvasQTAgg host
font_manager.py Custom + system CJK font discovery, matplotlib/Qt config
font_control_widget.py Font picker + size slider
plot_control_widget.py Metric selector + refresh-plot button
logger_setup.py CLI log-level parsing + custom TRACE level
setup_fonts.py Diagnostic utility (run standalone)

Adding a metric

Subclass Metric in metrics.py, implement compute(audio_file) -> data (the heavy part, runs on the worker thread) and render(data, file_path) -> Figure (cheap, runs on the GUI thread). Register the instance in the METRICS dict at the bottom of the file — it shows up in the dropdown automatically.

S
Description
Mastering helper
Readme GPL-3.0 478 KiB
Languages
Python 100%