Stereo & phase

HF Stereo

Does the treble seem much more centred than the rest of the sound? HF Stereo compares the left/right difference in treble with that in a reference band.

Reading the result

A more negative number means less stereo difference relative to the centre signal. This cue remains experimental: mixing choices can resemble codec-related narrowing.

Understanding the measurementSchematic example: treble much more centred than the reference band. Bars illustrate the labelled levels, not a measurement of music.Side energy relative to Mid1.5–5 kHz6–20 kHz0 dB−30 dB
Schematic example: treble much more centred than the reference band. Bars illustrate the labelled levels, not a measurement of music.
Go deeperMethod, limits, verification and sources

The HF Stereo column measures how much Side energy remains in the high-frequency band compared with Mid energy. It reports a conservative cue when a stereo programme is wide in the upper-mid reference band but remains much narrower above 6 kHz. This experimental pattern can result from intensity stereo or deliberate mixing, but it does not identify a codec and does not prove an audible defect.

Principle#

For each stereo frame, FlacCompagnon derives:

Mid  = (Left + Right) / 2
Side = (Left − Right) / 2

A high Side/Mid ratio indicates a wide stereo difference; a more negative ratio indicates a narrower image. The meter filters Mid and Side through matching filters, using eighth-order Butterworth high-pass and low-pass sections at each band corner, then measures their energies in non-overlapping 500 ms blocks. The high band ends at 20 kHz; when Nyquist is at or below 20 kHz, no upper filter is needed. Filter transitions are gradual, so these are nominal bands, not exact spectral cutouts. The coefficients follow the LPF/HPF equations in the W3C Audio EQ Cookbook.

BandRole
1.5–5 kHzReference: establishes that the programme has meaningful stereo width
6–20 kHzHigh band, limited by Nyquist at lower sample rates

A block is eligible only when Mid RMS exceeds −60 dBFS in both bands. A block is called narrowed when high-band Side/Mid is at or below −20 dB, the reference Side/Mid is at or above −12 dB, and the difference between them is at least 12 dB. The file receives the narrowing cue only when at least 70% of three or more eligible blocks satisfy the rule and the ratios of summed eligible energies satisfy the same three thresholds. Blocks need not be consecutive; the fraction describes eligible audio, not a continuous duration.

Here the gate means normalized-sample RMS strictly greater than 0.001, using 20 log10(RMS) relative to sample magnitude one. This explicitly defines the project's floor rather than an audibility threshold or a standardized RMS-meter calibration.

The displayed number is 10 log10(sum(Side energy) / sum(Mid energy)) across eligible complete blocks, in dB. Quiet blocks and the trailing partial block are excluded. Zero or extremely weak Side is reported at a relative floor of −120 dB, which is a display convention, not a measured noise floor. Hovering also shows the reference ratio and the fraction of qualifying blocks.

All thresholds are project heuristics, not values prescribed by MPEG, ITU or EBU. Synthetic tests validate their implementation; sensitivity and false-positive rates on a labelled corpus of music have not been established.

Interpretation and limits#

The ITU overview explains that intensity stereo reduces directional information while retaining energy envelopes. M/S coding is a separate sum/difference representation and does not inherently remove Side energy. ITU HSTP-MCTA, §§7.4 and 7.5.2

A low Side/Mid ratio is only an indirect cue. Unequal channel gains or polarity can leave substantial Side energy even when the high-band channels share one signal. A negative result cannot rule out intensity stereo; identifying a codec tool requires evidence beyond this measurement.

For proportional channels R = kL, Side/Mid = ((1 − k)/(1 + k))²: a panned common signal can have Side energy with no independent ambience. For balanced channel energies only, the relationship to correlation is Side/Mid = (1 − correlation)/(1 + correlation). Thus the −20 dB rule corresponds to correlation about +0.9802 in that balanced special case; it is not a general stereo-width or perception scale. A mono average is exactly Mid, while a pure opposed pair has no Mid and receives no eligible reading. One silent channel gives equal Mid/Side energies and no frequency-dependent narrowing cue.

Common gain, channel swapping and simultaneous polarity reversal preserve the ratios when the same blocks remain eligible. Independent channel gains change M/S, and the absolute Mid gate can change coverage after a gain change. Missing data does not mean a clean stereo image. The interaural experiments discussed for local phase do not validate these decoded-signal thresholds.

Naturally centre-heavy mixes, deliberately narrow high-frequency material, mono ambience and later mastering can have the same shape. The reference-band requirement prevents a uniformly narrow recording from being labelled, but cannot eliminate every artistic case. A negative result means only that this defined persistent pattern was not measured.

At startup, the meter primes the first high-pass stage's input history with the initial M/S values, assuming those values preceded the excerpt. The remaining stages start at their zero DC output. Computing the high-pass numerator as sample differences rejects constant DC exactly, without an artificial filter-start step or a permanent subtraction that could swallow ordinary audio after an extreme first sample. Events at the first frame depend on this assumed, unknown prehistory; later real steps and DC changes still produce transients. Audio lost to float quantization cannot be recovered. The filter histories continue across block boundaries; blocks divide the decisions, not the audio filtering.

The measurement is available only for valid two-channel streams at 24–768 kHz with at least three qualifying half-second blocks. Mono, multichannel, silent, very short, invalid and old saved reports show —.

Alternatives and cost#

The matched IIR paths are retained for bounded memory and one-pass operation. At 48–768 kHz the four M/S band paths contain 32 biquad sections in total; when the high-band upper edge reaches Nyquist, its two low-pass paths are omitted, leaving 24. Work grows linearly with sample count and memory stays constant with duration. The eighth-order skirts reduce neighbouring-band leakage but cannot eliminate it: a very strong reference or ultrasonic component can still affect the measured ratio.

Gain-normalized correlation or averaged coherence could describe shared HF signals despite panning, but would answer a different question from the relative energies of Mid and Side. Per-block coherence would also need multiple spectral segments to avoid a trivial one-bin result. Neither is substituted for this explicit M/S cue. More auditory bands, resampling or retuned thresholds require independent music-corpus calibration and new frequency/time-resolution choices; the existing thresholds are not made normative by passing synthetic tests.

Tests and manual fixture#

cargo test -p flaccompagnon-core analysis::intensity_stereo
node --test tests/analysis-cells.test.mjs tests/search.test.mjs

The tests also cover bass leakage into the reference, ultrasonic contamination, constant channel offsets at startup, recovery of eligible tone blocks after an extreme first sample, common gain and polarity, channel swapping, panned common signals, pure Side, mono ratio invariance, the exact complete-block minimum, persistence, invalid tails, seven sample rates from 24 to 768 kHz, and decoding a WAV through report export. The Rust tests construct Mid and Side tones independently: a 3 kHz reference with equal Mid and Side energy, paired with either a 9 kHz Side signal 40 dB below Mid or a wide 9 kHz control. This gives ground truth without using the meter’s own result as its oracle.

For a manual fixture, create a wide upper-mid reference and a strongly narrowed high band, then compare it with a wide-band control in the app:

ffmpeg -n -f lavfi -i 'aevalsrc=0.1*sin(2*PI*3000*t)+0.1*cos(2*PI*3000*t)+0.101*sin(2*PI*9000*t)|0.1*sin(2*PI*3000*t)-0.1*cos(2*PI*3000*t)+0.099*sin(2*PI*9000*t):s=48000:d=4' -c:a pcm_s24le hf-narrow.wav
ffmpeg -n -f lavfi -i 'aevalsrc=0.1*sin(2*PI*3000*t)+0.1*cos(2*PI*3000*t)+0.1*sin(2*PI*9000*t)+0.1*cos(2*PI*9000*t)|0.1*sin(2*PI*3000*t)-0.1*cos(2*PI*3000*t)+0.1*sin(2*PI*9000*t)-0.1*cos(2*PI*9000*t):s=48000:d=4' -c:a pcm_s24le hf-wide.wav

hf-narrow.wav is a controlled signal for the column and should show a markedly more negative HF Stereo value and the narrowing cue. It is not a realistic music corpus or a reference for codec quality.

Integrating the engine in your software?

Browse the Rust API in Rustdoc ↗