From 2ade4ef54303ec2b6ce22004f5e0544c6c093733 Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Thu, 10 Apr 2025 13:59:42 +0200 Subject: [PATCH] fix view diff --- src/main.rs | 98 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 17e6efa..0c3adb6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,46 @@ use clap::Parser; use ncurses::*; +use ncurses::{LcCategory, setlocale}; use rustfft::{algorithm::Radix4, num_complex::Complex, Fft, FftDirection}; -use std::collections::VecDeque; +use std::collections::{VecDeque, HashMap}; use std::io::{stdin, Read}; use std::fs::File; use std::time::{Duration, Instant}; use byteorder::{LittleEndian, ReadBytesExt}; - use std::process::{Command, Stdio}; -use std::thread::sleep; // Create screen buffer struct to track cell states // Each cell stores a character and its state (whether it's filled or not) -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] struct Cell { character: String, filled: bool, } +// ViewDiff struct for efficient terminal updates +struct ViewDiff { + changes: HashMap<(usize, usize), String>, +} + +impl ViewDiff { + fn new() -> Self { + ViewDiff { + changes: HashMap::new(), + } + } + + fn add_change(&mut self, y: usize, x: usize, character: String) { + self.changes.insert((y, x), character); + } + + fn apply(&self) { + for ((y, x), character) in &self.changes { + let _ = mvprintw(*y as i32, *x as i32, character); + } + } +} + + // Function to handle terminal resizing fn handle_terminal_resize( rows: &mut i32, @@ -99,7 +122,7 @@ fn main() { let cli = Cli::parse(); // Initialize ncurses with proper locale for UTF-8 support - setlocale(LcCategory::all, ""); // Set locale before ncurses init for UTF-8 support + let _ = setlocale(LcCategory::all, ""); // Set locale before ncurses init for UTF-8 support initscr(); noecho(); curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); @@ -114,14 +137,15 @@ fn main() { let sample_rate: usize = 48000; let bit_depth: usize = 16; + let num_channels: usize = 2; // Default to assume stereo for better handling let samples_per_frame: usize = sample_rate / usize::from(cli.fps); let fft_size: usize = 1024; // Set up FFT processor let fft = Radix4::new(fft_size, FftDirection::Forward); - // Set up audio input source - let mut input_buffer = vec![0; samples_per_frame * (bit_depth / 8)]; + // Set up audio input source - account for num_channels in buffer size + let mut input_buffer = vec![0; samples_per_frame * num_channels * (bit_depth / 8)]; let mut complex_buffer = vec![Complex { re: 0.0, im: 0.0 }; fft_size]; // Set up input reader based on command line arguments @@ -137,8 +161,9 @@ fn main() { // Set up audio source let mut audio_source = if cli.pipewire { // Launch PipeWire capture process with raw output format + // Using stereo (2 channels) for consistent behavior with other input sources let process = Command::new("pw-record") - .args(["--format=s16", "--rate=48000", "--channels=1", "--raw", "-"]) + .args(["--format=s16", "--rate=48000", "--channels=2", "--raw", "-"]) .stdout(Stdio::piped()) .spawn() .expect("Failed to start pw-record"); @@ -264,14 +289,28 @@ fn main() { // Resize input buffer to match actual bytes read let actual_input = &input_buffer[0..n]; - // Convert raw bytes to audio samples + // Convert raw bytes to audio samples, handling multichannel input let mut cursor = std::io::Cursor::new(actual_input); - // Read as many complete samples as possible - while cursor.position() < (n as u64 - 1) { - match cursor.read_i16::() { - Ok(sample) => all_samples.push(sample as f32 / 32768.0), - Err(_) => break, + // Read as many complete sample frames as possible + while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) { + let mut frame_sum = 0.0; + let mut valid_channels = 0; + + // Read all channels and average them + for _ in 0..num_channels { + match cursor.read_i16::() { + Ok(sample) => { + frame_sum += sample as f32; + valid_channels += 1; + }, + Err(_) => break, + } + } + + // Average the channels (if any valid channels were read) + if valid_channels > 0 { + all_samples.push((frame_sum / valid_channels as f32) / 32768.0); } } @@ -302,15 +341,21 @@ fn main() { } } - // Render only the changed cells - using safe dimensions + // Create a view diff for error display + let mut view_diff = ViewDiff::new(); + + // Record only the changed cells - using safe dimensions for y in 0..safe_rows as usize { for x in 0..safe_cols as usize { if current_buffer[y][x] != next_buffer[y][x] { - let _ = mvprintw(y as i32, x as i32, &next_buffer[y][x].character); + view_diff.add_change(y, x, next_buffer[y][x].character.clone()); } } } + // Apply all changes in one batch operation + view_diff.apply(); + // Swap buffers std::mem::swap(&mut current_buffer, &mut next_buffer); @@ -362,6 +407,7 @@ fn main() { complex_buffer[i].re *= window; } + // Process FFT using standard rustfft implementation (already optimized) fft.process(&mut complex_buffer); // Calculate frequency bands using our mapping - with safe dimensions @@ -440,15 +486,21 @@ fn main() { } } - // Render only the cells that changed (diff and patch) - using safe dimensions + // Create a view diff for efficient terminal updates + let mut view_diff = ViewDiff::new(); + + // Record only the changed cells (diff and patch) - using safe dimensions for y in 0..safe_rows as usize { for x in 0..safe_cols as usize { if current_buffer[y][x] != next_buffer[y][x] { - let _ = mvprintw(y as i32, x as i32, &next_buffer[y][x].character); + view_diff.add_change(y, x, next_buffer[y][x].character.clone()); } } } + // Apply changes efficiently in one batch + view_diff.apply(); + // Swap buffers for next frame std::mem::swap(&mut current_buffer, &mut next_buffer); @@ -483,15 +535,21 @@ fn main() { } } - // Render only the cells that changed - using safe dimensions + // Create a view diff for efficient terminal updates + let mut view_diff = ViewDiff::new(); + + // Record only the changed cells - using safe dimensions for y in 0..safe_rows as usize { for x in 0..safe_cols as usize { if current_buffer[y][x] != next_buffer[y][x] { - let _ = mvprintw(y as i32, x as i32, &next_buffer[y][x].character); + view_diff.add_change(y, x, next_buffer[y][x].character.clone()); } } } + // Apply all changes in one batch operation + view_diff.apply(); + // Swap buffers std::mem::swap(&mut current_buffer, &mut next_buffer);