fix view diff
This commit is contained in:
+76
-18
@@ -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,17 +289,31 @@ 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) {
|
||||||
|
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::<LittleEndian>() {
|
match cursor.read_i16::<LittleEndian>() {
|
||||||
Ok(sample) => all_samples.push(sample as f32 / 32768.0),
|
Ok(sample) => {
|
||||||
|
frame_sum += sample as f32;
|
||||||
|
valid_channels += 1;
|
||||||
|
},
|
||||||
Err(_) => break,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we've collected enough samples, we can stop to avoid too much processing
|
// If we've collected enough samples, we can stop to avoid too much processing
|
||||||
if all_samples.len() >= fft_size * 2 {
|
if all_samples.len() >= fft_size * 2 {
|
||||||
break;
|
break;
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user