Feat: fix scope / spectrum / vumeter
This commit is contained in:
@@ -64,6 +64,10 @@ fn render_horizontal(data: &[f32], area: Rect, buf: &mut Buffer, color: Color, g
|
||||
let fine_width = width * 2;
|
||||
let fine_height = height * 4;
|
||||
|
||||
// Auto-scale: find peak amplitude and normalize to fill height
|
||||
let peak = data.iter().map(|s| s.abs()).fold(0.0f32, f32::max);
|
||||
let auto_gain = if peak > 0.001 { gain / peak } else { gain };
|
||||
|
||||
PATTERNS.with(|p| {
|
||||
let mut patterns = p.borrow_mut();
|
||||
let size = width * height;
|
||||
@@ -72,7 +76,7 @@ fn render_horizontal(data: &[f32], area: Rect, buf: &mut Buffer, color: Color, g
|
||||
|
||||
for fine_x in 0..fine_width {
|
||||
let sample_idx = (fine_x * data.len()) / fine_width;
|
||||
let sample = (data.get(sample_idx).copied().unwrap_or(0.0) * gain).clamp(-1.0, 1.0);
|
||||
let sample = (data.get(sample_idx).copied().unwrap_or(0.0) * auto_gain).clamp(-1.0, 1.0);
|
||||
|
||||
let fine_y = ((1.0 - sample) * 0.5 * (fine_height - 1) as f32).round() as usize;
|
||||
let fine_y = fine_y.min(fine_height - 1);
|
||||
@@ -117,6 +121,10 @@ fn render_vertical(data: &[f32], area: Rect, buf: &mut Buffer, color: Color, gai
|
||||
let fine_width = width * 2;
|
||||
let fine_height = height * 4;
|
||||
|
||||
// Auto-scale: find peak amplitude and normalize to fill width
|
||||
let peak = data.iter().map(|s| s.abs()).fold(0.0f32, f32::max);
|
||||
let auto_gain = if peak > 0.001 { gain / peak } else { gain };
|
||||
|
||||
PATTERNS.with(|p| {
|
||||
let mut patterns = p.borrow_mut();
|
||||
let size = width * height;
|
||||
@@ -125,7 +133,7 @@ fn render_vertical(data: &[f32], area: Rect, buf: &mut Buffer, color: Color, gai
|
||||
|
||||
for fine_y in 0..fine_height {
|
||||
let sample_idx = (fine_y * data.len()) / fine_height;
|
||||
let sample = (data.get(sample_idx).copied().unwrap_or(0.0) * gain).clamp(-1.0, 1.0);
|
||||
let sample = (data.get(sample_idx).copied().unwrap_or(0.0) * auto_gain).clamp(-1.0, 1.0);
|
||||
|
||||
let fine_x = ((sample + 1.0) * 0.5 * (fine_width - 1) as f32).round() as usize;
|
||||
let fine_x = fine_x.min(fine_width - 1);
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::thread::{self, JoinHandle};
|
||||
use super::AudioCommand;
|
||||
|
||||
pub struct ScopeBuffer {
|
||||
pub samples: [AtomicU32; 64],
|
||||
pub samples: [AtomicU32; 256],
|
||||
peak_left: AtomicU32,
|
||||
peak_right: AtomicU32,
|
||||
}
|
||||
@@ -29,12 +29,19 @@ impl ScopeBuffer {
|
||||
let mut peak_l: f32 = 0.0;
|
||||
let mut peak_r: f32 = 0.0;
|
||||
|
||||
// Calculate peaks from ALL input frames for accurate VU metering
|
||||
for chunk in data.chunks(2) {
|
||||
if let [left, right] = chunk {
|
||||
peak_l = peak_l.max(left.abs());
|
||||
peak_r = peak_r.max(right.abs());
|
||||
}
|
||||
}
|
||||
|
||||
// Downsample for scope display
|
||||
let frames = data.len() / 2;
|
||||
for (i, atom) in self.samples.iter().enumerate() {
|
||||
let idx = i * 2;
|
||||
let left = data.get(idx).copied().unwrap_or(0.0);
|
||||
let right = data.get(idx + 1).copied().unwrap_or(0.0);
|
||||
peak_l = peak_l.max(left.abs());
|
||||
peak_r = peak_r.max(right.abs());
|
||||
let frame_idx = (i * frames) / self.samples.len();
|
||||
let left = data.get(frame_idx * 2).copied().unwrap_or(0.0);
|
||||
atom.store(left.to_bits(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
@@ -42,7 +49,7 @@ impl ScopeBuffer {
|
||||
self.peak_right.store(peak_r.to_bits(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn read(&self) -> [f32; 64] {
|
||||
pub fn read(&self) -> [f32; 256] {
|
||||
std::array::from_fn(|i| f32::from_bits(self.samples[i].load(Ordering::Relaxed)))
|
||||
}
|
||||
|
||||
@@ -146,7 +153,7 @@ impl SpectrumAnalyzer {
|
||||
let hi = self.band_edges[band + 1].max(lo + 1);
|
||||
let sum: f32 = self.fft_buf[lo..hi].iter().map(|c| c.norm()).sum();
|
||||
let avg = sum / (hi - lo) as f32;
|
||||
let amplitude = avg / (FFT_SIZE as f32 / 2.0);
|
||||
let amplitude = avg / (FFT_SIZE as f32 / 4.0);
|
||||
let db = 20.0 * amplitude.max(1e-10).log10();
|
||||
*mag = ((db + 60.0) / 60.0).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ pub struct Metrics {
|
||||
pub peak_voices: usize,
|
||||
pub cpu_load: f32,
|
||||
pub schedule_depth: usize,
|
||||
pub scope: [f32; 64],
|
||||
pub scope: [f32; 256],
|
||||
pub peak_left: f32,
|
||||
pub peak_right: f32,
|
||||
pub spectrum: [f32; 32],
|
||||
@@ -190,7 +190,7 @@ impl Default for Metrics {
|
||||
peak_voices: 0,
|
||||
cpu_load: 0.0,
|
||||
schedule_depth: 0,
|
||||
scope: [0.0; 64],
|
||||
scope: [0.0; 256],
|
||||
peak_left: 0.0,
|
||||
peak_right: 0.0,
|
||||
spectrum: [0.0; 32],
|
||||
|
||||
@@ -245,7 +245,6 @@ fn preprocess_underscores(md: &str) -> String {
|
||||
|
||||
fn parse_markdown(md: &str) -> Vec<RLine<'static>> {
|
||||
let processed = preprocess_underscores(md);
|
||||
eprintln!("DEBUG parse_markdown: processed={:?}", &processed[..100.min(processed.len())]);
|
||||
let text = minimad::Text::from(processed.as_str());
|
||||
let mut lines = Vec::new();
|
||||
|
||||
@@ -316,11 +315,6 @@ fn compound_to_spans(compound: Compound, base: Style, out: &mut Vec<Span<'static
|
||||
let theme = theme::get();
|
||||
let mut style = base;
|
||||
|
||||
// DEBUG: Check if bold flag is being set
|
||||
if compound.bold || compound.src.contains("**") {
|
||||
eprintln!("compound: bold={} src={:?}", compound.bold, compound.src);
|
||||
}
|
||||
|
||||
if compound.bold {
|
||||
style = style.add_modifier(Modifier::BOLD);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user