From 00f27b7f83080fb9143b71669f702e8f9e44dec5 Mon Sep 17 00:00:00 2001 From: alemidev Date: Sat, 24 Dec 2022 06:57:56 +0100 Subject: [PATCH] fix: updated readme, catch errors --- Cargo.toml | 4 +- README.md | 15 ++++--- src/app.rs | 14 ++++--- src/main.rs | 107 ++++++++++++++++++++++++++++++-------------------- src/parser.rs | 1 + 5 files changed, 84 insertions(+), 57 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8306045..f6e14b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,7 @@ name = "scope-tui" version = "0.1.1" edition = "2021" authors = [ "alemi " ] -description = """ -A simple audio visualization tool for the terminal built with tui-rs and libpulse-simple-binding, inspired from cava -""" +description = "A simple audio visualization tool for the terminal built with tui-rs and libpulse-simple-binding, inspired from cava" keywords = ["tui", "terminal", "audio", "visualization", "scope", "dashboard"] repository = "https://github.com/alemidev/scope-tui" readme = "README.md" diff --git a/README.md b/README.md index 03ea30f..beeec06 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ the first version of `scope-tui` was developed, with very minimal settings given # Usage ``` -$ scope-tui [OPTIONS] +$ scope-tui [OPTIONS] [DEVICE] Arguments: - Size of audio buffer, and width of scope + [DEVICE] Audio device to attach to Options: - -d, --device Audio device to attach to - -s, --scale Max value on Amplitude scale [default: 20000] + -b, --buffer Size of audio buffer, and width of scope [default: 8192] + -r, --range Max value, positive and negative, on amplitude scale [default: 20000] --no-reference Don't draw reference line --no-braille Don't use braille dots for drawing lines - --scatter Use vintage looking scatter mode + --scatter Use vintage looking scatter mode instead of line mode --vectorscope Combine left and right channels into vectorscope view -h, --help Print help information -V, --version Print version information @@ -32,6 +32,9 @@ Options: The audio buffer size directly impacts resource usage, latency and refresh rate and its limits are given by the audio refresh rate. Larger buffers are slower but less resource intensive. A good starting value might be `8192` ## Controls -* Use `q` or `CTRL+C` to exit. Not all keypresses are caught, so keep trying... (wip!) +* Use `q` or `CTRL+C` to exit * Use `` to pause and resume display +* Use `-` and `=` to decrease or increase range (`_` and `+` for smaller steps) +* Use `v` to toggle vectorscope mode +* Use `s` to toggle scatter mode * Decrease/increase terminal font size to increase/decrease scope resolution. diff --git a/src/app.rs b/src/app.rs index 24fd850..2d7339e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -101,8 +101,10 @@ impl AppConfig { } pub fn update_scale(&mut self, increment: i32) { - self.scale = ((self.scale as i32) + increment) as u32; - self.update_values(); + if increment > 0 || increment.abs() < self.scale as i32 { + self.scale = ((self.scale as i32) + increment) as u32; + self.update_values(); + } } pub fn set_scatter(&mut self, scatter: bool) { @@ -111,8 +113,8 @@ impl AppConfig { } -impl From:: for AppConfig { - fn from(args: crate::Args) -> Self { +impl From::<&crate::Args> for AppConfig { + fn from(args: &crate::Args) -> Self { let marker_type = if args.no_braille { symbols::Marker::Dot } else { symbols::Marker::Braille }; let graph_type = if args.scatter { GraphType::Scatter } else { GraphType::Line }; @@ -121,8 +123,8 @@ impl From:: for AppConfig { primary_color: Color::Red, secondary_color: Color::Yellow, axis_color: Color::DarkGray, - scale: args.scale, - width: args.width / 4, // TODO It's 4 because 2 channels and 2 bytes per sample! + scale: args.range, + width: args.buffer / 4, // TODO It's 4 because 2 channels and 2 bytes per sample! vectorscope: args.vectorscope, references: !args.no_reference, bounds: ChartBounds::default(), diff --git a/src/main.rs b/src/main.rs index b56f5b5..554bd3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ mod parser; mod app; -use std::{io, time::Duration}; +use std::{io::{self, ErrorKind}, time::Duration}; use tui::{ - backend::CrosstermBackend, + backend::{CrosstermBackend, Backend}, widgets::{Block, Chart, Axis, Dataset, GraphType}, // layout::{Layout, Constraint, Direction}, Terminal, text::Span, style::{Style, Color, Modifier}, symbols @@ -32,11 +32,11 @@ struct Args { /// Size of audio buffer, and width of scope #[arg(short, long, default_value_t = 8192)] - width: u32, + buffer: u32, - /// Max value on Amplitude scale + /// Max value, positive and negative, on amplitude scale #[arg(short, long, default_value_t = 20000)] - scale: u32, + range: u32, /// Don't draw reference line #[arg(long, default_value_t = false)] @@ -46,7 +46,7 @@ struct Args { #[arg(long, default_value_t = false)] no_braille: bool, - /// Use vintage looking scatter mode + /// Use vintage looking scatter mode instead of line mode #[arg(long, default_value_t = false)] scatter: bool, @@ -78,10 +78,45 @@ fn data_set<'a>( .data(&data) } - fn main() -> Result<(), io::Error> { let args = Args::parse(); + // setup terminal + enable_raw_mode()?; + let mut stdout = io::stdout(); + execute!(stdout, EnterAlternateScreen)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + terminal.hide_cursor()?; + + match run_app(args, &mut terminal) { + Ok(()) => {}, + Err(e) => { + println!("[!] Error executing app: {:?}", e); + } + } + + // restore terminal + disable_raw_mode()?; + execute!( + terminal.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + )?; + terminal.show_cursor()?; + + Ok(()) +} + + +fn run_app(args: Args, terminal: &mut Terminal) -> Result<(), io::Error> { + // prepare globals + let mut buffer : Vec = vec![0; args.buffer as usize]; + let mut cfg = AppConfig::from(&args); + let fmt = Signed16PCM{}; // TODO some way to choose this? + + let mut pause = false; + // setup audio capture let spec = Spec { format: Format::S16NE, @@ -95,8 +130,7 @@ fn main() -> Result<(), io::Error> { None => None, }; - - let s = Simple::new( + let s = match Simple::new( None, // Use the default server "ScopeTUI", // Our application’s name Direction::Record, // We want a record stream @@ -105,29 +139,27 @@ fn main() -> Result<(), io::Error> { &spec, // Our sample format None, // Use default channel map Some(&BufferAttr { - maxlength: 32 * args.width, - fragsize: args.width, + maxlength: 32 * args.buffer, + fragsize: args.buffer, ..Default::default() }), - ).unwrap(); + ) { + Ok(s) => s, + Err(e) => { + println!("[!] Could not connect to pulseaudio : {:?}", e); + return Err(io::Error::new(ErrorKind::Other, "could not connect to pulseaudio")); + }, + }; - // setup terminal - enable_raw_mode()?; - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen)?; - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - terminal.hide_cursor().unwrap(); - - // prepare globals - let mut buffer : Vec = vec![0; args.width as usize]; - let mut cfg = AppConfig::from(args); - let fmt = Signed16PCM{}; // TODO some way to choose this? - - let mut pause = false; loop { - s.read(&mut buffer).unwrap(); + match s.read(&mut buffer) { + Ok(()) => {}, + Err(e) => { + println!("[!] Could not read data from pulseaudio : {:?}", e); + return Err(io::Error::new(ErrorKind::Other, "could not read from pulseaudio")); + }, + } if !pause { let mut datasets = vec![]; @@ -138,7 +170,7 @@ fn main() -> Result<(), io::Error> { let mut ref_data_y = Vec::new(); if cfg.references { - + // TODO find a proper way to put these references... if cfg.vectorscope() { for x in -(cfg.scale() as i64)..(cfg.scale() as i64) { ref_data_x.push((x as f64, 0 as f64)); @@ -182,7 +214,7 @@ fn main() -> Result<(), io::Error> { .x_axis(Axis::default() .title(Span::styled(cfg.name(app::Axis::X), Style::default().fg(Color::Cyan))) .style(Style::default().fg(cfg.axis_color)) - .bounds(cfg.bounds(app::Axis::X))) + .bounds(cfg.bounds(app::Axis::X))) // TODO allow to have axis sometimes? .y_axis(Axis::default() .title(Span::styled(cfg.name(app::Axis::Y), Style::default().fg(Color::Cyan))) .style(Style::default().fg(cfg.axis_color)) @@ -196,8 +228,6 @@ fn main() -> Result<(), io::Error> { KeyModifiers::CONTROL => { match key.code { KeyCode::Char('c') => break, - KeyCode::Char('+') => cfg.update_scale(100), - KeyCode::Char('-') => cfg.update_scale(-100), _ => {}, } }, @@ -205,8 +235,10 @@ fn main() -> Result<(), io::Error> { match key.code { KeyCode::Char('q') => break, KeyCode::Char(' ') => pause = !pause, - KeyCode::Char('+') => cfg.update_scale(1000), - KeyCode::Char('-') => cfg.update_scale(-1000), + KeyCode::Char('=') => cfg.update_scale(-1000), + KeyCode::Char('-') => cfg.update_scale(1000), + KeyCode::Char('+') => cfg.update_scale(-100), + KeyCode::Char('_') => cfg.update_scale(100), KeyCode::Char('v') => cfg.set_vectorscope(!cfg.vectorscope()), KeyCode::Char('s') => cfg.set_scatter(!cfg.scatter()), _ => {}, @@ -216,14 +248,5 @@ fn main() -> Result<(), io::Error> { } } - // restore terminal - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index ce16892..c0abfa8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,6 +14,7 @@ pub trait SampleParser { pub struct Signed16PCM {} +/// TODO these are kinda inefficient, can they be faster? impl SampleParser for Signed16PCM { fn oscilloscope(&self, data: &mut [u8]) -> (Vec<(f64, f64)>, Vec<(f64, f64)>) { let mut left = Vec::new(); // TODO does left really come first?