fix view diff

This commit is contained in:
2025-04-10 13:59:42 +02:00
parent bc4c3d1fd3
commit 2ade4ef543
+78 -20
View File
@@ -1,23 +1,46 @@
use clap::Parser; use clap::Parser;
use ncurses::*; use ncurses::*;
use ncurses::{LcCategory, setlocale};
use rustfft::{algorithm::Radix4, num_complex::Complex, Fft, FftDirection}; 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::io::{stdin, Read};
use std::fs::File; use std::fs::File;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::thread::sleep;
// Create screen buffer struct to track cell states // Create screen buffer struct to track cell states
// Each cell stores a character and its state (whether it's filled or not) // Each cell stores a character and its state (whether it's filled or not)
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)]
struct Cell { struct Cell {
character: String, character: String,
filled: bool, 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 // Function to handle terminal resizing
fn handle_terminal_resize( fn handle_terminal_resize(
rows: &mut i32, rows: &mut i32,
@@ -99,7 +122,7 @@ fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
// Initialize ncurses with proper locale for UTF-8 support // 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(); initscr();
noecho(); noecho();
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
@@ -114,14 +137,15 @@ fn main() {
let sample_rate: usize = 48000; let sample_rate: usize = 48000;
let bit_depth: usize = 16; 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 samples_per_frame: usize = sample_rate / usize::from(cli.fps);
let fft_size: usize = 1024; let fft_size: usize = 1024;
// Set up FFT processor // Set up FFT processor
let fft = Radix4::new(fft_size, FftDirection::Forward); let fft = Radix4::new(fft_size, FftDirection::Forward);
// Set up audio input source // Set up audio input source - account for num_channels in buffer size
let mut input_buffer = vec![0; samples_per_frame * (bit_depth / 8)]; 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]; let mut complex_buffer = vec![Complex { re: 0.0, im: 0.0 }; fft_size];
// Set up input reader based on command line arguments // Set up input reader based on command line arguments
@@ -137,8 +161,9 @@ fn main() {
// Set up audio source // Set up audio source
let mut audio_source = if cli.pipewire { let mut audio_source = if cli.pipewire {
// Launch PipeWire capture process with raw output format // 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") 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()) .stdout(Stdio::piped())
.spawn() .spawn()
.expect("Failed to start pw-record"); .expect("Failed to start pw-record");
@@ -264,14 +289,28 @@ fn main() {
// Resize input buffer to match actual bytes read // Resize input buffer to match actual bytes read
let actual_input = &input_buffer[0..n]; 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); let mut cursor = std::io::Cursor::new(actual_input);
// Read as many complete samples as possible // Read as many complete sample frames as possible
while cursor.position() < (n as u64 - 1) { while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) {
match cursor.read_i16::<LittleEndian>() { let mut frame_sum = 0.0;
Ok(sample) => all_samples.push(sample as f32 / 32768.0), let mut valid_channels = 0;
Err(_) => break,
// Read all channels and average them
for _ in 0..num_channels {
match cursor.read_i16::<LittleEndian>() {
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 y in 0..safe_rows as usize {
for x in 0..safe_cols as usize { for x in 0..safe_cols as usize {
if current_buffer[y][x] != next_buffer[y][x] { 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 // Swap buffers
std::mem::swap(&mut current_buffer, &mut next_buffer); std::mem::swap(&mut current_buffer, &mut next_buffer);
@@ -362,6 +407,7 @@ fn main() {
complex_buffer[i].re *= window; complex_buffer[i].re *= window;
} }
// Process FFT using standard rustfft implementation (already optimized)
fft.process(&mut complex_buffer); fft.process(&mut complex_buffer);
// Calculate frequency bands using our mapping - with safe dimensions // 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 y in 0..safe_rows as usize {
for x in 0..safe_cols as usize { for x in 0..safe_cols as usize {
if current_buffer[y][x] != next_buffer[y][x] { 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 // Swap buffers for next frame
std::mem::swap(&mut current_buffer, &mut next_buffer); 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 y in 0..safe_rows as usize {
for x in 0..safe_cols as usize { for x in 0..safe_cols as usize {
if current_buffer[y][x] != next_buffer[y][x] { 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 // Swap buffers
std::mem::swap(&mut current_buffer, &mut next_buffer); std::mem::swap(&mut current_buffer, &mut next_buffer);