Introduce a Metric ABC (compute on worker thread, render on GUI thread) with a METRICS registry, and refactor the analysis pipeline around it. RMS power (ported), raw waveform, and BS.1770 LUFS (via pyloudnorm) ship as the initial three; new metrics drop in by appending to METRICS. - metrics.py: Metric ABC + RMSPowerMetric, WaveformMetric (locked to +/-1.1 y-range for float headroom), LUFSMetric (short-term 3 s window + integrated value, with streaming-target reference line). - plot_control_widget.py: metric selector dropdown + Refresh Plot button (moved out of FontControlWidget). - analysis_results_manager.py: AnalysisResult caches the AudioFile and a per-metric data dict; new MetricComputeWorker runs metric switches off the GUI thread via metricComputeStarted/metricReady/metricComputeError signals, so LUFS on a 12-minute track no longer stalls the UI. - main.py: all redraw paths funnel through one _render_or_request helper; stale-result guards keep slow computes from overwriting fresh selections. - plotting_engine.py removed (metadata text moved onto AnalysisResult; figure construction lives in each Metric). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2.5 KiB
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 - RMS power analysis on a 10 s rolling window with adaptive colour scale
- 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
- Embedded matplotlib canvas with auto-regenerated plots on font change
Roadmap
See CLAUDE.md for the full development roadmap. Near-term: dynamic range, 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: librosa loading, RMS rolling window, BPM |
metrics.py |
Pluggable Metric ABC + registry (RMS Power, Waveform, …) |
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.