Kouncil: Optimizing Resolution with Split FFTs

Overview

Every spectrum is a tradeoff between resolution in frequency and resolution in time. Most analyzers pick one FFT size and live with it.

Kouncil’s average spectrum uses two. The low end comes from an FFT eight times longer than the rest, and the two are joined into one curve.

The Resolution Problem

An FFT of N samples splits the spectrum into bins spaced sample rate / N apart. At 44.1 kHz, a 4096-point FFT has a bin every 10.8 Hz.

That’s plenty at the top of the spectrum. At 5 kHz, 10.8 Hz is a fraction of a semitone. At the bottom it’s a disaster. The octave from 20 to 40 Hz, where a kick’s fundamental and the lowest bass notes live, gets two bins. A bass line becomes a smear, and two notes a few hertz apart become one hump.

The low end of a mastered track drawn from a 4096-point FFT and from a 32768-point FFT The low end of a loud drum and bass master. The 4096-point curve (purple) has two points between 20 and 40 Hz. The 32768-point curve (gold) has fifteen, and finds a sub peak 5 dB taller than the coarse curve shows.

For mastering, the low end is exactly where precision matters. A sub that sits a few hertz off the kick, or a single bass note ringing louder than the rest, is the kind of thing you open a spectrum to find.

Options

The usual fixes each give something up.

The key observation is that this is a long-term average. Kouncil’s average spectrum describes the whole track. Time resolution lost by a long FFT in the low frequencies doesn’t matter here at all.

Two FFTs

Kouncil runs two FFTs on each track:

A 32768-point frame for every hop of a track is several hundred megabytes at once, so this pass runs one frame at a time instead of the usual vectorized batch.

Below 150 Hz the chart draws the fine curve. Above 300 Hz it draws the coarse one. In between it crossfades linearly from one to the other.

The join sits where the coarse curve stops needing help. By 150 Hz it has about 14 bins an octave, enough to draw a smooth line at a chart’s width, against the two it has between 20 and 40 Hz. By 300 Hz the fine curve has about 220 an octave, more than one per pixel, so going higher would only add points nobody can see.

Density Matching

Broadband sound, like noise or a dense mix, spreads its energy across every bin. With eight times as many bins, each one gets an eighth of the energy. So the long FFT reads broadband content 10·log₁₀(8) ≈ 9 dB lower per bin.

A steady tone behaves differently. It sits in one bin however long the window is, so it reads at the same level in both.

The correction is to add 10·log₁₀(32768 / 4096) back to the fine curve. That turns it into a density, matched to the coarse curve’s scale.

A test signal of noise plus 55 Hz and 82.4 Hz tones, through both FFT sizes, with and without the density correction A test signal: white noise plus steady tones at 55 and 82.4 Hz. Without the correction, the fine curve’s noise sits exactly 9 dB under the coarse curve’s. With it, the noise lines up and the tones stand 9 dB taller.

After the correction, broadband content sits at the same level in both curves, so the join is seamless on real music. Tones now stand taller in the fine curve than in the coarse one. That’s intentional. It’s what any analyzer does when you turn its resolution up: the noise floor stays put and the tones resolve out of it.

The figure also shows what the coarse curve does to two notes 27 Hz apart. It draws one wide hump that doesn’t peak at either note.

Other Notes

Related Posts