fix(viz): log/dB scaling for bar visualisers#57
Merged
Conversation
Raw linear FFT magnitudes map bars to ~5-10% of pane height at typical listening volumes, leaving the top of the viewport black. Replace the `(1 + mag/1000).ln()` soft-compress with a two-layer mapping shared by bars_dot, bars_outline, and classic_peak: 1. dB conversion (`20 * log10`) with a -60 dB floor and 40 dB display range so perceived loudness maps linearly to bar height. 2. Envelope-follower AGC on the loudest band: ~50 ms attack, ~2.5 s release, with a -30 dB minimum peak so silence doesn't auto-gain. The shared helper lives in `visualiser::scaling::SpectrumScaler`. Per-viz tests assert ≥60% bar fill once the AGC converges on a typical-listening input. Existing tint/peak-cap/gutter behaviour is preserved. Closes clitunes-kfj / CLI-89
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
vxcozy
added a commit
that referenced
this pull request
Apr 18, 2026
Users report the entire visualiser suite feels sluggish — not just the bar family. The shared sluggishness is the one-pole EnergyTracker used by ~20 viz with release=0.88 (tau ~258ms at 30fps), plus the PR #57 AGC whose 2.5s release was tuned for safety over feel. Tightening time constants where the viz is meant to track the beat (not slowly evolve textures) restores the "it's moving with the music" quality. scaling.rs (SpectrumScaler AGC, bars family): attack: 0.48 -> 0.735 (tau 50ms -> 25ms; kick snaps same frame) release: 0.013 -> 0.027 (tau 2.5s -> 1.2s; ceiling tracks sections) Per-viz EnergyTracker release retained 0.88 -> 0.75 (tau ~258ms -> 115ms): heartbeat, wave, scope, matrix, terrain, retro, rain, binary, butterfly, scatter, fire, sakura, ripples, bars_dot, bars_outline, classic_peak. bars_outline per-bar release: 0.85 -> 0.8 (tau 203ms -> 148ms, matches bars_dot). Deliberately untouched: - pulse, firework (0.3, 0.85) — delta-based transient detection needs a smoother baseline; tightening release defeats the spike trigger. - tunnel, vortex, plasma, metaballs, moire (>=0.9 retention) — these drive smoothly-evolving textures where jitter would read as noise, not snappiness. Existing scaling.rs unit tests (attack_faster_than_release, typical_listening_peak_fills_majority_of_pane, etc.) all pass under the new coefficients — the attack:release ratio is still ~27:1. Closes clitunes-lc4 / CLI-91
6 tasks
vxcozy
added a commit
that referenced
this pull request
Apr 18, 2026
Users report the entire visualiser suite feels sluggish — not just the bar family. The shared sluggishness is the one-pole EnergyTracker used by ~20 viz with release=0.88 (tau ~258ms at 30fps), plus the PR #57 AGC whose 2.5s release was tuned for safety over feel. Tightening time constants where the viz is meant to track the beat (not slowly evolve textures) restores the "it's moving with the music" quality. scaling.rs (SpectrumScaler AGC, bars family): attack: 0.48 -> 0.735 (tau 50ms -> 25ms; kick snaps same frame) release: 0.013 -> 0.027 (tau 2.5s -> 1.2s; ceiling tracks sections) Per-viz EnergyTracker release retained 0.88 -> 0.75 (tau ~258ms -> 115ms): heartbeat, wave, scope, matrix, terrain, retro, rain, binary, butterfly, scatter, fire, sakura, ripples, bars_dot, bars_outline, classic_peak. bars_outline per-bar release: 0.85 -> 0.8 (tau 203ms -> 148ms, matches bars_dot). Deliberately untouched: - pulse, firework (0.3, 0.85) — delta-based transient detection needs a smoother baseline; tightening release defeats the spike trigger. - tunnel, vortex, plasma, metaballs, moire (>=0.9 retention) — these drive smoothly-evolving textures where jitter would read as noise, not snappiness. Existing scaling.rs unit tests (attack_faster_than_release, typical_listening_peak_fills_majority_of_pane, etc.) all pass under the new coefficients — the attack:release ratio is still ~27:1. Closes clitunes-lc4 / CLI-91
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
visualiser::scaling::SpectrumScaler— dB conversion + envelope-follower AGC shared by bars_dot, bars_outline, classic_peak.Why
Raw FFT magnitudes span a huge dynamic range and are perceived logarithmically. Mapping them linearly to bar height leaves bars pinned to the bottom 5-10% of the pane at typical listening volumes (
peak_sample≈ 0.05-0.15), which reads as "visualiser doesn't fill the window".Two layers of fix:
20 * log10(mag)with a -60 dB floor and a 40 dB display range. Loudness → row-height becomes linear.The shared module keeps the math in one place — the three bar viz all call
SpectrumScaler::update+normalise. Non-bar viz (pulse, wave, scope, terrain, heartbeat, etc.) are untouched.Test plan
scaling::typical_listening_peak_fills_majority_of_pane,scaling::quiet_passage_stays_visible_but_not_pinned,scaling::loud_transient_clamps_at_one,scaling::silence_does_not_auto_gain,scaling::attack_faster_than_release,scaling::no_nan_on_zero_input, plustypical_listening_fills_paneper bar viz