Open Audio Analyzer

github.com/JonasGrunau/open_audio_analyzer

Open Audio Analyzer

A free and open-source loudness and spectrum analyzer. Fourteen meter modules on a canvas you arrange — on the desktop, inside your DAW, and on a tablet beside the desk — measuring against ITU-R BS.1770-4 and EBU R 128, held to the reference cases on every push.

macOS · Windows · Linux · iPadOS · Android VST3 · Audio Unit · CLI Free, GPL-3.0

Eight meter modules on one canvas: loudness bars, three concentric arcs, a spectrum analyser, a minute of loudness against time, a delivery verdict reading NOT READY, per-channel peak bars, a waveform and a latched true-peak alert.

Measured from Apathy And Resignation Are The Sources Of Our Misfortune by Citizens Of The Empire, CC BY 3.0. The readings are the engine's own, recorded from that track — not a simulation.

Modules

Fourteen meter modules

Every module is one ModuleFrame plus a painter, reads the same MeterSource, and repaints from the same clock. That is the whole reuse strategy: a module that also owned its border and title treatment is a module that would drift from the other thirteen. Independent tickers drift too — and two meters showing the same quantity that disagree inside one frame is a correctness bug, not a cosmetic one.

Measurement

Every quantity, and how
it is defined

Quantity Definition
K-weighting ITU-R BS.1770-4 stage-1 shelf plus stage-2 RLB, with the coefficients computed from the analog prototype at the actual sample rate — not from a hardcoded 48 kHz table.
Gating EBU R128: 400 ms blocks at 75% overlap, absolute gate −70 LUFS, relative gate −10 LU.
Momentary / Short-term 400 ms and 3 s windows.
LRA Gated at −20 LU relative, 10th to 95th percentile, through a 0.01 LU-bin histogram that stores exact energy sums — O(1) per update, constant memory.
True peak BS.1770-4 Annex 2: 4× oversampling through the specified 48-tap polyphase FIR, at every sample rate.
Spectrum A 4096-point Hann window at a 1024-sample hop, zero-padded to a 16384-point transform and mapped onto 512 log-spaced bands with peak-per-bin, so narrow peaks survive. A full-scale sine reads 0.0 dBFS on a bin centre and within 0.3 dB off it.
Correlation Running Pearson over a sliding window.
Crest Sample peak minus RMS over the same block — the block’s own values, not the held peak and the smoothed RMS the meters draw, whose difference drifts on its own. Exactly 3.0103 dB for a sine, 0 for DC.
Clip Longest run of consecutive samples at or above 0.999 since the reset, per channel. Latched, so a clip that lasted three samples is still visible when you look back.

Dynamics

What it reports
instead of TrueDyn

Decibel reports a dynamics figure called TrueDyn. It is proprietary and undocumented, so any claim to match it would be a guess presented as a measurement. Open Audio Analyzer does not implement it.

Instead it reports DR-S and DR-I, defined as TruePeak − LUFS-S and TruePeakMax − LUFS-I, published in the metrics document and reproducible from the definition by anybody who wants to check. PSR and PLR are the same two quantities under the names in wider use, and are offered as well — four names for two numbers, each computed once so the pair cannot drift, and printed once in a report rather than twice under different headings.

Read the metrics document

A quantity nobody measured
report
jsonnull
csv(empty)
never0
Zero is a legitimate reading for correlation, balance and several dB quantities. It cannot double as “no data”.

Architecture

Three tiers, and the
boundary between each

Measurements never cross an isolate boundary, never allocate per frame, and never rebuild a widget.

Capture

audio callback, realtime-safe

Copy input into a lock-free ring. No malloc, no locks, no syscalls.

Analysis

dedicated thread, high priority

Run the DSP graph. Publish results into a seqlock-protected snapshot.

Display

the UI thread

One FFI call per frame, then paint. The widget tree is never involved.

Once per frame the clock makes a single leaf FFI call — an atomic load, a memcpy, and a second atomic load, with no VM state transition. Then every painter reads views that were built once at startup over native memory that never moves. A frame costs one FFI call plus N paint calls, and allocates nothing.

The snapshot is a seqlock precisely because it is wait-free for the analysis thread: the reader retries, never the writer. A mutex would let a descheduled UI thread stall the thread that must never stall — and when that thread falls behind, the ring overruns and signal is lost for good.

Plugin, tablet and CLI

The same engine,
in three more places

In a DAW

A VST3 and an Audio Unit that draw nothing. Insert one on a track, a bus or the master, and the desktop app meters what your DAW is playing — through the same engine, on the same canvas, with the same painters as a live input. The plugin measures and streams; the app displays. That split is not a compromise around a UI toolkit, it is what stops there being two implementations of every meter drifting apart.

On a tablet

Flip PUBLISH in the desktop’s status bar and a tablet mirrors the canvas over Wi-Fi. It finds the host by itself, or reads a pairing code off the desktop’s screen with its camera, or takes an address typed by hand — three routes, because the first one is what a venue’s Wi-Fi blocks. A preset stores grid cells rather than fractions of a window, so the same layout opens on a 32-inch monitor and an 11-inch tablet with nobody writing responsive code.

On the command line

oaa decodes a file and pushes the blocks through the same analysis a capture device drives — there is no second DSP path, and a test asserts that an offline reading and a live reading of the same audio are identical rather than merely close. Nothing resamples or remixes: a converter in front of a measurement changes the measurement.

oaa — the command-line analyser 0 pass · 2 missed target · 1 unreadable
$ oaa master.wav                              # human-readable report
$ oaa --target streaming-14 master.wav        # …and a delivery verdict
$ oaa --format json --timeline master.wav     # every measurement, for scripts
$ oaa --format csv -o loudness.csv master.wav  # the loudness timeline
$ oaa --list-targets                          # what you can measure against

# fail a release build on a master that is 2 LU too loud,
# instead of shipping it
$ oaa --target streaming-14 master.wav || exit 1

Configuration

Settings, presets, targets
and skins

Settings, presets, delivery targets and skins are plain JSON in a documented directory — open them, edit them, copy them between machines, keep them in version control. A skin names as many of thirteen semantic colour roles as it likes and inherits the rest, so changing one colour is a three-line file. The roles are semantic rather than literal — over is “the colour that means over a limit”, used for nothing else — which is what lets a skin apply to a module written after it.

There is an editor for them too. Every role gets a colour picker, the canvas repaints as you drag — and so does any tablet mirroring the session — and each role shows its contrast ratio against the surface it has to be read on, so a palette that puts a meter track 1.1:1 against its own panel says so before you ship it. The two skins that come with Open Audio Analyzer are fixed; copying one takes a press.

A delivery target whose id matches one that ships replaces it everywhere, including in presets that already name it. So if you disagree with our reading of a published spec, your number wins, and it reaches the CLI’s exit code and not only the window. Delete the file and the original comes back.

calibrations/house-standard.json
{
  "id": "house-standard",
  "name": "House standard",
  "lufs_target": -12.0,
  "lufs_tolerance": 0.5,
  "true_peak_max": -1.0,
  "lra_max": 14.0,
  "vu_reference": -18.0
}

Known gaps

What is not built

The list in the README is honest rather than short, and it is kept there rather than here so it stays next to the code. Six of the twelve are below.

All twelve, in the README

Installing

Artefacts per platform

Platform Artefact Plugin Note
macOS 14.2+ .pkg VST3 + AU Universal — Apple silicon and Intel
Windows 10 1809+ .exe VST3 Uninstaller in Installed apps
Linux .tar.gz VST3 install.sh, no root
Linux .AppImage One file, no root, GTK from the host
Linux .flatpak Sandboxed, carries its own runtime
Any oaa CLI The analyser. No Flutter runtime.

The first three install the plugin as well, and the checkbox starts ticked. Metering your own system’s output needs nothing installed on Windows, on macOS 14.2 and later, or on Linux — no driver on any platform, and on macOS the audio still reaches your speakers while it is measured.