1
0
Fork 0
mirror of https://github.com/alemidev/scope-tui.git synced 2024-11-14 18:59:19 +01:00

fix: updated readme, catch errors

This commit is contained in:
əlemi 2022-12-24 06:57:56 +01:00
parent e2269ef6d6
commit 00f27b7f83
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
5 changed files with 84 additions and 57 deletions

View file

@ -3,9 +3,7 @@ name = "scope-tui"
version = "0.1.1" version = "0.1.1"
edition = "2021" edition = "2021"
authors = [ "alemi <me@alemi.dev>" ] authors = [ "alemi <me@alemi.dev>" ]
description = """ description = "A simple audio visualization tool for the terminal built with tui-rs and libpulse-simple-binding, inspired from cava"
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"] keywords = ["tui", "terminal", "audio", "visualization", "scope", "dashboard"]
repository = "https://github.com/alemidev/scope-tui" repository = "https://github.com/alemidev/scope-tui"
readme = "README.md" readme = "README.md"

View file

@ -13,17 +13,17 @@ the first version of `scope-tui` was developed, with very minimal settings given
# Usage # Usage
``` ```
$ scope-tui [OPTIONS] <WIDTH> $ scope-tui [OPTIONS] [DEVICE]
Arguments: Arguments:
<WIDTH> Size of audio buffer, and width of scope [DEVICE] Audio device to attach to
Options: Options:
-d, --device <DEVICE> Audio device to attach to -b, --buffer <BUFFER> Size of audio buffer, and width of scope [default: 8192]
-s, --scale <SCALE> Max value on Amplitude scale [default: 20000] -r, --range <RANGE> Max value, positive and negative, on amplitude scale [default: 20000]
--no-reference Don't draw reference line --no-reference Don't draw reference line
--no-braille Don't use braille dots for drawing lines --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 --vectorscope Combine left and right channels into vectorscope view
-h, --help Print help information -h, --help Print help information
-V, --version Print version 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` 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 ## 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 `<SPACE>` to pause and resume display * Use `<SPACE>` 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. * Decrease/increase terminal font size to increase/decrease scope resolution.

View file

@ -101,9 +101,11 @@ impl AppConfig {
} }
pub fn update_scale(&mut self, increment: i32) { pub fn update_scale(&mut self, increment: i32) {
if increment > 0 || increment.abs() < self.scale as i32 {
self.scale = ((self.scale as i32) + increment) as u32; self.scale = ((self.scale as i32) + increment) as u32;
self.update_values(); self.update_values();
} }
}
pub fn set_scatter(&mut self, scatter: bool) { pub fn set_scatter(&mut self, scatter: bool) {
self.graph_type = if scatter { GraphType::Scatter } else { GraphType::Line }; self.graph_type = if scatter { GraphType::Scatter } else { GraphType::Line };
@ -111,8 +113,8 @@ impl AppConfig {
} }
impl From::<crate::Args> for AppConfig { impl From::<&crate::Args> for AppConfig {
fn from(args: crate::Args) -> Self { fn from(args: &crate::Args) -> Self {
let marker_type = if args.no_braille { symbols::Marker::Dot } else { symbols::Marker::Braille }; 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 }; let graph_type = if args.scatter { GraphType::Scatter } else { GraphType::Line };
@ -121,8 +123,8 @@ impl From::<crate::Args> for AppConfig {
primary_color: Color::Red, primary_color: Color::Red,
secondary_color: Color::Yellow, secondary_color: Color::Yellow,
axis_color: Color::DarkGray, axis_color: Color::DarkGray,
scale: args.scale, scale: args.range,
width: args.width / 4, // TODO It's 4 because 2 channels and 2 bytes per sample! width: args.buffer / 4, // TODO It's 4 because 2 channels and 2 bytes per sample!
vectorscope: args.vectorscope, vectorscope: args.vectorscope,
references: !args.no_reference, references: !args.no_reference,
bounds: ChartBounds::default(), bounds: ChartBounds::default(),

View file

@ -1,9 +1,9 @@
mod parser; mod parser;
mod app; mod app;
use std::{io, time::Duration}; use std::{io::{self, ErrorKind}, time::Duration};
use tui::{ use tui::{
backend::CrosstermBackend, backend::{CrosstermBackend, Backend},
widgets::{Block, Chart, Axis, Dataset, GraphType}, widgets::{Block, Chart, Axis, Dataset, GraphType},
// layout::{Layout, Constraint, Direction}, // layout::{Layout, Constraint, Direction},
Terminal, text::Span, style::{Style, Color, Modifier}, symbols Terminal, text::Span, style::{Style, Color, Modifier}, symbols
@ -32,11 +32,11 @@ struct Args {
/// Size of audio buffer, and width of scope /// Size of audio buffer, and width of scope
#[arg(short, long, default_value_t = 8192)] #[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)] #[arg(short, long, default_value_t = 20000)]
scale: u32, range: u32,
/// Don't draw reference line /// Don't draw reference line
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
@ -46,7 +46,7 @@ struct Args {
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
no_braille: bool, no_braille: bool,
/// Use vintage looking scatter mode /// Use vintage looking scatter mode instead of line mode
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
scatter: bool, scatter: bool,
@ -78,10 +78,45 @@ fn data_set<'a>(
.data(&data) .data(&data)
} }
fn main() -> Result<(), io::Error> { fn main() -> Result<(), io::Error> {
let args = Args::parse(); 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<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io::Error> {
// prepare globals
let mut buffer : Vec<u8> = 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 // setup audio capture
let spec = Spec { let spec = Spec {
format: Format::S16NE, format: Format::S16NE,
@ -95,8 +130,7 @@ fn main() -> Result<(), io::Error> {
None => None, None => None,
}; };
let s = match Simple::new(
let s = Simple::new(
None, // Use the default server None, // Use the default server
"ScopeTUI", // Our applications name "ScopeTUI", // Our applications name
Direction::Record, // We want a record stream Direction::Record, // We want a record stream
@ -105,29 +139,27 @@ fn main() -> Result<(), io::Error> {
&spec, // Our sample format &spec, // Our sample format
None, // Use default channel map None, // Use default channel map
Some(&BufferAttr { Some(&BufferAttr {
maxlength: 32 * args.width, maxlength: 32 * args.buffer,
fragsize: args.width, fragsize: args.buffer,
..Default::default() ..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<u8> = 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 { 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 { if !pause {
let mut datasets = vec![]; let mut datasets = vec![];
@ -138,7 +170,7 @@ fn main() -> Result<(), io::Error> {
let mut ref_data_y = Vec::new(); let mut ref_data_y = Vec::new();
if cfg.references { if cfg.references {
// TODO find a proper way to put these references...
if cfg.vectorscope() { if cfg.vectorscope() {
for x in -(cfg.scale() as i64)..(cfg.scale() as i64) { for x in -(cfg.scale() as i64)..(cfg.scale() as i64) {
ref_data_x.push((x as f64, 0 as f64)); ref_data_x.push((x as f64, 0 as f64));
@ -182,7 +214,7 @@ fn main() -> Result<(), io::Error> {
.x_axis(Axis::default() .x_axis(Axis::default()
.title(Span::styled(cfg.name(app::Axis::X), Style::default().fg(Color::Cyan))) .title(Span::styled(cfg.name(app::Axis::X), Style::default().fg(Color::Cyan)))
.style(Style::default().fg(cfg.axis_color)) .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() .y_axis(Axis::default()
.title(Span::styled(cfg.name(app::Axis::Y), Style::default().fg(Color::Cyan))) .title(Span::styled(cfg.name(app::Axis::Y), Style::default().fg(Color::Cyan)))
.style(Style::default().fg(cfg.axis_color)) .style(Style::default().fg(cfg.axis_color))
@ -196,8 +228,6 @@ fn main() -> Result<(), io::Error> {
KeyModifiers::CONTROL => { KeyModifiers::CONTROL => {
match key.code { match key.code {
KeyCode::Char('c') => break, 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 { match key.code {
KeyCode::Char('q') => break, KeyCode::Char('q') => break,
KeyCode::Char(' ') => pause = !pause, 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('v') => cfg.set_vectorscope(!cfg.vectorscope()),
KeyCode::Char('s') => cfg.set_scatter(!cfg.scatter()), 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(()) Ok(())
} }

View file

@ -14,6 +14,7 @@ pub trait SampleParser {
pub struct Signed16PCM {} pub struct Signed16PCM {}
/// TODO these are kinda inefficient, can they be faster?
impl SampleParser for Signed16PCM { impl SampleParser for Signed16PCM {
fn oscilloscope(&self, data: &mut [u8]) -> (Vec<(f64, f64)>, Vec<(f64, f64)>) { fn oscilloscope(&self, data: &mut [u8]) -> (Vec<(f64, f64)>, Vec<(f64, f64)>) {
let mut left = Vec::new(); // TODO does left really come first? let mut left = Vec::new(); // TODO does left really come first?