pub struct AnalysisFrame {Show 22 fields
pub spectrum: [f32; 64],
pub waveform: [f32; 512],
pub waveform_gain: f32,
pub onset: f32,
pub beat: bool,
pub bass: f32,
pub mid: f32,
pub treb: f32,
pub bass_raw: f32,
pub mid_raw: f32,
pub treb_raw: f32,
pub onset_raw: f32,
pub bpm: f32,
pub bar: f32,
pub beat_index: u32,
pub time_since_beat: f32,
pub beat_in_bar: u32,
pub bar_index: u32,
pub bar_phase: f32,
pub downbeat_confidence: f32,
pub downbeat_locked: bool,
pub novelty: f32,
}Expand description
One hop’s worth of analysis.
The four headline levels are normalized (ADR-0049): bass, mid, treb
and onset are each a 0..1 fraction of that signal’s own slowly-decaying
recent peak, so > 0.5 means “loud for this track” rather than naming an
absolute magnitude that depended on the gain staging. The absolute values
remain as *_raw for looks that genuinely want them, and for harness
continuity. spectrum normalizes against one peak shared by the whole
array, so every ratio inside it — and therefore every bin() contrast — comes
through untouched; see gain::BandNormalizer for why per-band would not.
beat flags an onset event this hop; bpm/bar come from the tempo tracker,
which reads the raw onset — see gain for why the internal consumers
are deliberately left on raw values. The bar-position trio comes from
downbeat, and falls back to plain counters whenever its estimate is not
confident (ADR-0050).
Fields§
§spectrum: [f32; 64]Per-band energy, the whole array normalized against one shared recent
peak — so every ratio inside it, and therefore every bin() contrast,
comes through untouched. Not a per-band normalization: that was the draft
ADR-0049 rejected, because it flattens the very shape a spectrum is for.
waveform: [f32; 512]The most recent WAVE_SAMPLES of the mono signal, in time order —
the oscilloscope trace, not a spectrum (Plan 0100 Phase 4 / ADR-0113).
Nothing in the engine’s own vocabulary reads this: the expression grammar
is scalar and reaches the band array through bin() alone (ADR-0036), and
widening it to an array type is exactly what ADR-0002’s purity refuses.
It is here for the one consumer that genuinely needs a waveform — the
warp mesh’s wave_mode draw, which is what MilkDrop’s presets use as
their light source, and which no amount of spectrum can reconstruct.
Levelled against its own recent peak (ADR-0139), like every other
headline value on this struct: the whole trace is divided by one
slowly-released running peak of its magnitude, so it reads -1..=1 at any
fader position. That is what makes the two frontends agree — the plugin
taps the decoded stream before the output volume, the standalone taps
loopback after it, and only the absolute level differs between them.
Dynamics within a track survive the seconds-scale release; a quiet
track reads like a loud one, which is the price of cancelling a volume
knob nothing else can see. The consumer still scales what it gets
(MilkDrop’s wave_scale does exactly that).
waveform_gain is the divisor, so an absolute
amplitude is one multiply away and a true oscilloscope stays reachable.
This is the array that made this struct big. AnalysisFrame is Copy
and copied per frame, and 512 floats take it from ~340 bytes to ~2.4 kB —
about 100 ns of memcpy at 60 Hz, which is why it was acceptable. It is
deliberately not in Variables, which
carries the band array by borrow for precisely this reason.
waveform_gain: f32The divisor waveform was levelled by: waveform[i] * waveform_gain is the raw amplitude the analyzer read.
0.0 while the tracked peak sits under gain::WAVE_FLOOR, where the
trace is zeroed rather than amplified — so reconstructing from a silent
frame gives silence rather than noise.
onset: f32Spectral-flux onset envelope, normalized against its recent peak.
beat: boolWhether a beat (onset event) fired this hop.
bass: f32Bass-band level (~20-250 Hz), normalized against its recent peak.
mid: f32Mid-band level (~250-4000 Hz), normalized against its recent peak.
treb: f32Treble-band level (~4-18 kHz), normalized against its recent peak.
bass_raw: f32Raw mean magnitude in the bass band — the pre-ADR-0049 bass, unchanged.
mid_raw: f32Raw mean magnitude in the mid band — the pre-ADR-0049 mid, unchanged.
treb_raw: f32Raw mean magnitude in the treble band — the pre-ADR-0049 treb, unchanged.
onset_raw: f32Raw spectral-flux envelope — the pre-ADR-0049 onset, unchanged.
bpm: f32Tempo estimate in BPM (hop-clock autocorrelation; 0 until warm).
bar: f32Beat phase in [0, 1): 0 on each beat, ramping to the next.
The name is a documented misnomer — this is beat phase, not bar phase.
Too widely bound to rename (ADR-0050); bar_phase is the true quantity.
beat_index: u32Monotone count of onset detections since the stream started, 0 before the first (ADR-0050 Layer 1, corrected by ADR-0109). Unconditional and deterministic — no confidence gate. Not a musical period: the detector fires 1.2x-2.3x per musical beat depending on the material and wanders inside a single track, so no fixed multiplier converts this to beats.
time_since_beat: f32Seconds since the last onset detection; exactly 0 on a detection hop.
beat_in_bar: u32Which beat of the bar this is, 0..4 (ADR-0050 Layer 2). Estimated when
the downbeat tracker is confident, and the fold’s own counter modulo 4
otherwise — see bar_index for what that counter is.
bar_index: u32Bar counter, on the same gated-or-counted basis. Monotone except across
an alignment change — it is (beat count - alignment) / 4, where the
beat count is the grid’s tempo-driven one, and beat_index only
while the grid warms up. So the beat the estimator locks, drops back, or
moves its alignment can repeat or skip a bar. Hysteresis makes that rare
(a challenger must lead for three bars), and a repeated bar is a far
softer failure than a wrong downbeat — but mod(bar_index, 8) will see
it. The warmup handover is not a second source of it: the grid’s count
carries a whole-bar offset that keeps this moving forward across it.
bar_phase: f32Position across the bar in [0, 1) — the true bar phase, as against
bar, which is beat phase under a historical name.
downbeat_confidence: f32Downbeat-alignment confidence in 0..1. Diagnostics only — not a
grammar variable, so authors get behavior rather than homework.
downbeat_locked: boolWhether the bar trio above came from the estimator rather than the counter fallback. Diagnostics only, as with the confidence.
novelty: f32Experimental spectral track-change novelty (Plan 0009 Phase 4): ~0 within a steady segment, spiking at a spectral boundary. Native-API only — not exposed across the C ABI.
Implementations§
Source§impl AnalysisFrame
impl AnalysisFrame
Sourcepub fn fully_driven() -> Self
pub fn fully_driven() -> Self
The one definition of “fully driven”: every headline level and the whole log-band array at full scale, with the beat flag set.
Two harnesses hold a differential against this frame — --report’s
drive column and its step stimulus (ADR-0134), and the animation gate’s
driven branch (ADR-0136). A second construction site would let them
measure two different stimuli while reading as the same word, which is a
disagreement no capture could show.
Three fields are deliberately not at full scale, and each for its own mechanism:
- The four
*_rawlevels stay0. The headline four are peak-normalized (ADR-0049), so1.0is the documented top of their range; a raw magnitude has no top to name, and any value picked for one would be a gain-staging assumption. A binding readingbass_rawsees silence here. bpmstays0, the tracker’s own not-yet-warm value. There is no “full scale” tempo.baris a phase in[0, 1), not a level, so it takes0.5— the middle of a beat rather than either edge, so abar-driven binding reads a typical position instead of sitting on the wrap.
Trait Implementations§
Source§impl Clone for AnalysisFrame
impl Clone for AnalysisFrame
Source§fn clone(&self) -> AnalysisFrame
fn clone(&self) -> AnalysisFrame
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more