diff --git a/cbd-tui/src/app/now_playing.rs b/cbd-tui/src/app/now_playing.rs index 1bc2615..d02cb62 100644 --- a/cbd-tui/src/app/now_playing.rs +++ b/cbd-tui/src/app/now_playing.rs @@ -100,14 +100,16 @@ impl NowPlaying { } pub fn render(&self, f: &mut Frame, area: Rect) { - // Info block, progress row, then (when enabled) a spectrum row. + // Info block fills, then a fixed progress row and (when enabled) + // a fixed spectrum row. Length(1) is hard-fixed so the rows are + // never squeezed out by the block above them. let spectrum_row = if self.spectrum_enabled { 1 } else { 0 }; let now_playing_layout = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Min(6), - Constraint::Max(1), - Constraint::Max(spectrum_row), + Constraint::Min(3), + Constraint::Length(1), + Constraint::Length(spectrum_row), ]) .split(area); diff --git a/crabidy-server/src/lib.rs b/crabidy-server/src/lib.rs index 5fee402..5e7656c 100644 --- a/crabidy-server/src/lib.rs +++ b/crabidy-server/src/lib.rs @@ -172,11 +172,15 @@ fn spawn_spectrum_task( let count = tap.frame_count(); if count != last_count { last_count = count; + if !was_active { + debug!("spectrum: audio flowing, streaming bars"); + } was_active = true; let bins = analyzer.analyze(&tap.snapshot()); let _ = update_tx.send(Update::Spectrum(SpectrumFrame { bins })); } else if was_active { // Playback just went idle: drop the bars to the floor once. + debug!("spectrum: audio idle, bars to zero"); was_active = false; let _ = update_tx.send(Update::Spectrum(SpectrumFrame { bins: vec![0.0; spectrum::SPECTRUM_BINS], diff --git a/crabidy-server/src/spectrum.rs b/crabidy-server/src/spectrum.rs index 3263cb1..36f5bbf 100644 --- a/crabidy-server/src/spectrum.rs +++ b/crabidy-server/src/spectrum.rs @@ -60,14 +60,22 @@ impl SpectrumAnalyzer { return vec![0.0; SPECTRUM_BINS]; } let n = self.window.len() as f32; - let magnitude = |c: &Complex| c.norm() / n; + // Single-sided amplitude (2/N): a full-scale tone reads near 1.0 + // at its bin, so the bars fill for ordinary listening levels. + let magnitude = |c: &Complex| c.norm() * 2.0 / n; (0..SPECTRUM_BINS) .map(|bar| { let lo = self.edges[bar]; let hi = self.edges[bar + 1].max(lo + 1).min(self.output.len()); - let avg = self.output[lo..hi].iter().map(magnitude).sum::() / (hi - lo) as f32; + // Peak within the band, not the mean: a strong component + // must light its bar instead of being diluted by the + // quiet bins around it (which averaging does). + let peak = self.output[lo..hi] + .iter() + .map(magnitude) + .fold(0.0_f32, f32::max); // Log-compress: dBFS mapped onto [0, 1]. - let db = 20.0 * (avg + 1e-9).log10(); + let db = 20.0 * (peak + 1e-9).log10(); ((db - MIN_DB) / -MIN_DB).clamp(0.0, 1.0) }) .collect()