Skip to main content

Module gain

Module gain 

Source
Expand description

Running normalization: turns raw magnitudes into “loud relative to this track’s recent past” (ADR-0049).

Raw band means on real music sit at 0.006-0.040 while the authoring stimuli reached 0.187-0.8, so every threshold in the shipped library was a magic number against a table that moved three times in one week. Dividing each signal by its own slowly-decaying running peak makes > 0.5 mean the same thing on every track, at every gain setting, on every stimulus.

Three properties, each pinned by a test rather than by a comment:

  • Instant attack. A new peak is adopted on the hop it arrives, so a hit reads high immediately instead of fading in.
  • Slow release. The peak decays with a seconds-scale time constant, so a quiet passage lifts gradually rather than pumping bar to bar.
  • Silence floor. Below a floor the output is zero, not amplified noise — the difference between a quiet room and a loud one must not be a full-scale visual.

The ceiling is reached routinely, and that is what these properties buy. The reading is raw / peak against the signal’s own peak, so it is exactly 1.0 on any hop that is the loudest since the peak last released — on periodic material, every kick, at any input level. It is scale-invariant for the same reason: halve the input and both terms halve, so no input gain moves it. A consumer that wants a magnitude rather than an excitation must read the raw value beside it; one that reads a levelled scalar as a dimmer sees a term pinned at its ceiling and no gain control that can unpin it.

Pure and allocation-free after construction: state is a fixed set of floats and every step is arithmetic on the input, so the same sequence always yields the same output (NFR section 6).

What is levelled here, each against its own running peak: the 64-band spectrum array (BandNormalizer, one shared peak so the array’s internal ratios survive), the bass/mid/treb scalars and the onset envelope (PeakNormalizer), and the waveform trace (TraceNormalizer, ADR-0139). The trace is the odd one of the set on two counts: it is a signal rather than a magnitude, so it is divided rather than rectified and clamps to -1..=1; and its divisor is published as AnalysisFrame::waveform_gain rather than discarded, making it the one levelled output whose raw amplitude a consumer can reconstruct. Levelling it is what makes the two frontends agree, since the plugin taps its stream before the output volume and the standalone taps loopback after it.

Where this sits matters. Normalization is applied at the published frame boundary only. The onset detector, the tempo tracker and the novelty detector all keep reading raw values, because each is tuned against raw magnitudes and would be actively harmed by an AGC: autocorrelating a peak-normalized envelope distorts the very periodicity the tempo tracker looks for, and per-band normalization flattens exactly the spectral-shape difference novelty exists to measure.

Structs§

BandNormalizer
The 64-band array’s normalizer: one running peak, tracking the loudest band, applied as a uniform gain across the whole array.
PeakNormalizer
One signal’s running peak, and the normalized reading it produces.
TraceNormalizer
The waveform’s normalizer: one running peak of the trace’s magnitude, applied as a uniform gain to the whole trace.

Constants§

BAND_FLOOR
Silence floor for the band scalars and the 64-band array.
ONSET_FLOOR
Silence floor for onset. An order of magnitude lower because spectral flux is an order of magnitude smaller: the same groove peaks at 0.0167 with a 0.0016 mean, so this keeps the same ~1000x margin.
WAVE_FLOOR
Silence floor for the waveform trace, in signal amplitude — a different quantity from BAND_FLOOR, which is a band mean, and deliberately an order of magnitude above it.