Kouncil: Algorithm Reference

Overview

Kouncil is a desktop mastering analyzer written in Python (NumPy, SciPy, Matplotlib, Tkinter, ffmpeg). This post lists every measurement it makes and how each one is computed. Window sizes, thresholds and formulas are the ones in the code.

Thresholds that decide whether a value is flagged were set from measured masters, not from rules of thumb: the reference library behind the genre profiles, 2,499 released tracks across 100 genres. Where this page gives a “typical” range, that is where it comes from.

Kouncil Overview tab

Loudness

All loudness follows ITU-R BS.1770-4 / EBU R128, the measurement streaming services normalize to. It is tested against EBU Tech 3341 (a stereo 997 Hz sine at −23 dBFS reads −23.0 LUFS) and checked against ffmpeg’s ebur128 filter on real tracks.

K-Weighting

Two biquads per channel: a high shelf (1681.97 Hz, +4.0 dB, Q 0.7072) and a high-pass (38.135 Hz, Q 0.5003). The standard only publishes coefficients at 48 kHz, so Kouncil takes the analog prototypes behind them through the bilinear transform at the file’s own rate. That reproduces the published values exactly at 48 kHz and the same curve at 44.1, 96 or anything else. The weighted signal is computed once and shared by every loudness measure.

Integrated Loudness (LUFS-I)

  1. 400 ms blocks, 100 ms hop
  2. Block power = mean square over time, summed over channels
  3. Absolute gate at −70 LUFS
  4. Relative gate 10 LU below the mean of what survived
  5. LUFS = −0.691 + 10·log10(mean(gated blocks))

Short-term and Momentary

The same block power at 3 s / 1 s hop (short-term) and 400 ms / 100 ms hop (momentary), ungated, as time series. The momentary series also carries a gate flag per block (silent, audible but excluded, counted) for the loudness distribution chart.

Loudness Range (LRA)

Short-term values, absolute gate at −70 LUFS, relative gate 20 LU below the mean, then the 95th minus the 10th percentile.

Loudness distribution with the relative gate, integrated level and target marked

Peak and Level

Every level figure is measured on the file as delivered, before any analysis normalization is applied to the spectral and stereo stages.

Dynamic Range

Dynamics

These describe the shape of the dynamics rather than the level.

Crest Factor Timeline

Peak-to-RMS per 500 ms window, so you can see where transients survive and where they were flattened.

Band Dynamics

STFT (4096-point Hann, 50% overlap) summed into 7 bands from Sub to Air. Per band: crest as max minus mean of the band’s dB level, and micro-dynamics as the standard deviation of the frame-to-frame change. Shows which part of the spectrum lost its movement, which is what you tune a multiband limiter against.

Micro-Dynamics

RMS envelope in 50 ms hops, in dB, absolute difference between neighboring frames, smoothed with a 0.5 s rolling mean. A low average means short-term amplitude movement (articulation, groove) has been limited out.

Transient Profile and Density

Positive spectral flux on a 2048-point STFT (hop 1024): flux = Σ max(0, |X[n]| − |X[n−1]|), normalized to its maximum. Peaks above 0.3 are onsets, and onsets per second is the transient density.

Attack Slopes

For each onset (deduplicated within 50 ms), a 2 ms-hop RMS envelope over a 20 ms window (5 ms before, 15 ms after), and slope = (max dB − min dB) / window ms. The median is reported in dB/ms. Two tracks can have the same crest factor and very different attacks; this tells them apart.

Section Contrast

K-weighted loudness of 8 s sections, silence below −60 LUFS dropped, then P95 minus P5. The verse-to-chorus arc in one number.

Envelope Modulation Spectrum

RMS envelope at 10 ms hops, in dB, mean removed, then an FFT of the envelope itself, kept over 0.5 to 20 Hz. A flat result means the limiter took the movement out. A narrow peak at 2 to 4 Hz is usually a compressor pumping on the kick. Modulation depth is the standard deviation of this spectrum.

Crest factor timeline
Envelope modulation spectrum

Spectral

Average Spectrum

Mono, 4096-point Hann FFT, 50% overlap, power averaged over all frames and normalized by FFT size. 10.8 Hz bins are too coarse for the low end, so below a join frequency the curve comes from a 32768-point FFT of the same signal (1.35 Hz bins instead of 10.8) and is crossfaded into the coarse one. Both are density-matched, so broadband content sits at the same level in each.

Tonal Balance

8192-point FFT, power summed into 8 mastering bands, in dB, normalized so the Bass band (120 to 250 Hz) is 0 dB. Compared against the active profile’s range for each band.

Spectral Tilt

Least-squares fit of dB = slope·log2(f) + b over 100 Hz to 16 kHz, in dB/octave. Pink noise is −3. The fitted line is drawn over the spectrum.

Resonance Detection

  1. Up to 200 frames of 8192-point spectra across the track, 60 Hz to 18 kHz
  2. Each frame against its own baseline: a running median one octave wide on a log-frequency grid, so the window is the same musical width at 100 Hz as at 10 kHz
  3. Prominence per frame = frame minus baseline, then the median over frames
  4. Peaks above 7 dB
  5. Drop a peak that is line narrow and part of a harmonic series with two or more peaks of similar strength
  6. Top 10

Step 3 is what makes this usable on music. A resonance (an EQ boost, a room mode, a ringing drum) raises its frequency in every frame. A note’s partials stand up only while the note plays, so the median over time removes them. Step 5 catches the notes that sound most of the track, like a pad on the key center: their partials are spectral lines in a harmonic series, and a resonance is a lone bell.

The prominence isn’t weighted by hearing sensitivity, so it reads close to the boost’s own gain in dB. Tested by multiplying peaking-EQ boosts into the spectra of the reference library’s tracks, a +12 dB boost is found 81% of the time, and 87 to 100% of the time from 2 kHz up, at about 0.8 false reports per released master.

Resonance Map

The same detection per 2 s window (0.5 s hop) with a hard 6 dB gate, plus the tilt per window. Drawn as a frequency-by-time heatmap with the tilt line over it, so a resonance can be found in the arrangement, not just in the average.

Average spectrum with tilt line
Tonal balance against profile range

Resonance map with spectral tilt over time

Perceptual

Sensitivity Curve

An A-weighting approximation of the equal-loudness contours:

Ra(f) = 12194²·f⁴ / ((f² + 20.6²)·sqrt((f² + 107.7²)(f² + 737.9²))·(f² + 12194²))
weight_dB = 20·log10(Ra(f) / Ra(1 kHz))

For the resonance map it becomes a multiplier, 1 + clip(weight_dB, −10, 10)·0.05. For listening-level comparisons the curve is scaled by (85 − SPL) / 45, since the contours flatten as playback gets louder.

Harshness

Weighted power in 2 to 5 kHz over weighted power in 80 Hz to 16 kHz, mapped from its typical range (0.12 to 0.45) onto 0 to 100. Tilt is broadband; this targets the band where the ear canal adds about 6 dB of its own.

Low-Volume Translation

The average spectrum weighted at 85 dB SPL and at 65 dB SPL, each normalized to its own mean, and score = 100 − 12.5·RMS(difference). A mix that depends on sub and air drops out at low volume and scores low.

Perceptual Dynamic Range

Weighted level per 3 s chunk (FFT, weighted, summed), silence dropped, P95 minus P5. A track whose dynamics all live in the sub reads as DR8 but sounds flat; this number agrees with the ear.

Stereo

Stereo width by frequency
Phase correlation by band

Codec Simulation

Rather than model codec damage, Kouncil runs the track through real encode and decode round-trips with a bundled ffmpeg and measures the difference.

Version Stands in for
AAC 128 web players, free tiers
AAC 256 Apple Music, YouTube Music, Spotify Premium
Vorbis 160 Spotify’s apps at normal quality
Opus 128 YouTube, Discord
MP3 320 SoundCloud, downloads, CDJs
SBC 192 baseline Bluetooth
Vorbis 160 → SBC 192 Spotify over Bluetooth
AAC 256 → AAC 256 streaming AAC re-encoded for AirPods

Chains feed the decoded output of the first codec into the second and are measured against the original, so the error is the whole listener path.

Alignment

Every figure below is a difference, so the decoded audio has to line up with the source to the sample. The lag is found by cross-correlating a stretch from the middle of the track (time-delay estimation, as in sonar), then the decode is shifted. ffmpeg already strips the priming delay of AAC, MP3 and Opus; raw SBC comes back 73 samples late and short, which this catches.

Error Measurements

With error = original − decoded:

Consistency

For each of six figures a master can actually change (transient fidelity, stereo collapse, true peak delta, noise floor, centroid shift, width shift), the spread across the six single codecs scored as 100·(1 − spread / reference range), averaged into one 0 to 100 score. How much top end a codec keeps is not in it: that is the codec’s bandwidth, the same for every master.

Codec error spectrum against the masking threshold Codec artifact timeline

Render QC

Checks for mistakes in the bounce itself rather than in the master.

Methods From Outside Audio

A few of these came from other fields where the problem turned out to be the same one.

Performance

Related Posts