mirror of
https://github.com/alemidev/scope-tui.git
synced 2024-11-23 22:24:48 +01:00
feat: key to toggle UI, hide title when no ui
This commit is contained in:
parent
d77c27087f
commit
ae493cd221
4 changed files with 70 additions and 53 deletions
|
@ -3,7 +3,7 @@ name = "scope-tui"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = [ "alemi <me@alemi.dev>" ]
|
authors = [ "alemi <me@alemi.dev>" ]
|
||||||
description = "A simple audio visualization tool for the terminal built with tui-rs and libpulse-simple-binding, inspired from cava"
|
description = "A simple oscilloscope/vectorscope for the terminal, inspired by 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"
|
||||||
|
|
|
@ -39,4 +39,5 @@ To change audio buffer size, the PulseAudio client must be restarted. Because of
|
||||||
* Use `-` and `=` to decrease or increase range (`_` and `+` for smaller steps)
|
* Use `-` and `=` to decrease or increase range (`_` and `+` for smaller steps)
|
||||||
* Use `v` to toggle vectorscope mode
|
* Use `v` to toggle vectorscope mode
|
||||||
* Use `s` to toggle scatter mode
|
* Use `s` to toggle scatter mode
|
||||||
|
* Use `h` to toggle interface
|
||||||
* Decrease/increase terminal font size to increase/decrease scope resolution.
|
* Decrease/increase terminal font size to increase/decrease scope resolution.
|
||||||
|
|
18
src/app.rs
18
src/app.rs
|
@ -2,7 +2,7 @@ use tui::{style::Color, widgets::GraphType, symbols};
|
||||||
|
|
||||||
// use crate::parser::SampleParser;
|
// use crate::parser::SampleParser;
|
||||||
|
|
||||||
pub enum Axis {
|
pub enum Dimension {
|
||||||
X, Y
|
X, Y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,14 +63,14 @@ pub struct App {
|
||||||
impl App {
|
impl App {
|
||||||
fn update_values(&mut self) {
|
fn update_values(&mut self) {
|
||||||
if self.cfg.vectorscope {
|
if self.cfg.vectorscope {
|
||||||
self.names.x = "- left".into();
|
self.names.x = "left -".into();
|
||||||
self.names.y = "| right".into();
|
self.names.y = "| right".into();
|
||||||
self.bounds.x = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
self.bounds.x = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
||||||
self.bounds.y = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
self.bounds.y = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
||||||
self.references.x = vec![(-(self.cfg.scale as f64), 0.0), (self.cfg.scale as f64, 0.0)];
|
self.references.x = vec![(-(self.cfg.scale as f64), 0.0), (self.cfg.scale as f64, 0.0)];
|
||||||
self.references.y = vec![(0.0, -(self.cfg.scale as f64)), (0.0, self.cfg.scale as f64)];
|
self.references.y = vec![(0.0, -(self.cfg.scale as f64)), (0.0, self.cfg.scale as f64)];
|
||||||
} else {
|
} else {
|
||||||
self.names.x = "- time".into();
|
self.names.x = "time -".into();
|
||||||
self.names.y = "| amplitude".into();
|
self.names.y = "| amplitude".into();
|
||||||
self.bounds.x = [0.0, self.cfg.width as f64];
|
self.bounds.x = [0.0, self.cfg.width as f64];
|
||||||
self.bounds.y = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
self.bounds.y = [-(self.cfg.scale as f64), self.cfg.scale as f64];
|
||||||
|
@ -80,17 +80,17 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bounds(&self, axis: Axis) -> [f64;2] {
|
pub fn bounds(&self, axis: &Dimension) -> [f64;2] {
|
||||||
match axis {
|
match axis {
|
||||||
Axis::X => self.bounds.x,
|
Dimension::X => self.bounds.x,
|
||||||
Axis::Y => self.bounds.y,
|
Dimension::Y => self.bounds.y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self, axis: Axis) -> &str {
|
pub fn name(&self, axis: &Dimension) -> &str {
|
||||||
match axis {
|
match axis {
|
||||||
Axis::X => self.names.x.as_str(),
|
Dimension::X => self.names.x.as_str(),
|
||||||
Axis::Y => self.names.y.as_str(),
|
Dimension::Y => self.names.y.as_str(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
102
src/main.rs
102
src/main.rs
|
@ -7,7 +7,7 @@ use tui::{
|
||||||
backend::{CrosstermBackend, Backend},
|
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, layout::Alignment
|
||||||
};
|
};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, DisableMouseCapture, Event, KeyCode, KeyModifiers},
|
event::{self, DisableMouseCapture, Event, KeyCode, KeyModifiers},
|
||||||
|
@ -70,29 +70,6 @@ struct Args {
|
||||||
no_braille: bool,
|
no_braille: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_event() -> Result<Option<Event>, std::io::Error> {
|
|
||||||
if event::poll(Duration::from_millis(0))? {
|
|
||||||
Ok(Some(event::read()?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_set<'a>(
|
|
||||||
name: &'a str,
|
|
||||||
data: &'a [(f64, f64)],
|
|
||||||
marker_type: symbols::Marker,
|
|
||||||
graph_type: GraphType,
|
|
||||||
axis_color: Color
|
|
||||||
) -> Dataset<'a> {
|
|
||||||
Dataset::default()
|
|
||||||
.name(name)
|
|
||||||
.marker(marker_type)
|
|
||||||
.graph_type(graph_type)
|
|
||||||
.style(Style::default().fg(axis_color))
|
|
||||||
.data(&data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), io::Error> {
|
fn main() -> Result<(), io::Error> {
|
||||||
let mut args = Args::parse();
|
let mut args = Args::parse();
|
||||||
|
|
||||||
|
@ -216,7 +193,6 @@ fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io
|
||||||
datasets.push(data_set("L", &left, app.cfg.marker_type, app.graph_type(), app.cfg.primary_color));
|
datasets.push(data_set("L", &left, app.cfg.marker_type, app.graph_type(), app.cfg.primary_color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fps += 1;
|
fps += 1;
|
||||||
|
|
||||||
if last_poll.elapsed().as_secs() >= 1 {
|
if last_poll.elapsed().as_secs() >= 1 {
|
||||||
|
@ -228,24 +204,9 @@ fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.size();
|
||||||
let chart = Chart::new(datasets)
|
let chart = Chart::new(datasets)
|
||||||
.block(Block::default().title(
|
.block(block(&app, args.sample_rate as f32, framerate))
|
||||||
Span::styled(
|
.x_axis(axis(&app, app::Dimension::X)) // TODO allow to have axis sometimes?
|
||||||
format!(
|
.y_axis(axis(&app, app::Dimension::Y));
|
||||||
"TUI {} <me@alemi.dev> -- {} mode -- range {} -- {} samples -- {:.1} kHz -- {} fps",
|
|
||||||
if app.vectorscope() { "Vectorscope" } else { "Oscilloscope" },
|
|
||||||
if app.scatter() { "scatter" } else { "line" },
|
|
||||||
app.scale(), app.width(), args.sample_rate as f32 / 1000.0, framerate,
|
|
||||||
),
|
|
||||||
Style::default().add_modifier(Modifier::BOLD).fg(Color::Yellow))
|
|
||||||
))
|
|
||||||
.x_axis(Axis::default()
|
|
||||||
.title(Span::styled(app.name(app::Axis::X), Style::default().fg(Color::Cyan)))
|
|
||||||
.style(Style::default().fg(app.cfg.axis_color))
|
|
||||||
.bounds(app.bounds(app::Axis::X))) // TODO allow to have axis sometimes?
|
|
||||||
.y_axis(Axis::default()
|
|
||||||
.title(Span::styled(app.name(app::Axis::Y), Style::default().fg(Color::Cyan)))
|
|
||||||
.style(Style::default().fg(app.cfg.axis_color))
|
|
||||||
.bounds(app.bounds(app::Axis::Y)));
|
|
||||||
f.render_widget(chart, size)
|
f.render_widget(chart, size)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -267,6 +228,7 @@ fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io
|
||||||
KeyCode::Char('_') => app.update_scale(100),
|
KeyCode::Char('_') => app.update_scale(100),
|
||||||
KeyCode::Char('v') => app.set_vectorscope(!app.vectorscope()),
|
KeyCode::Char('v') => app.set_vectorscope(!app.vectorscope()),
|
||||||
KeyCode::Char('s') => app.set_scatter(!app.scatter()),
|
KeyCode::Char('s') => app.set_scatter(!app.scatter()),
|
||||||
|
KeyCode::Char('h') => app.cfg.references = !app.cfg.references,
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,3 +238,57 @@ fn run_app<T : Backend>(args: Args, terminal: &mut Terminal<T>) -> Result<(), io
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO these functions probably shouldn't be here
|
||||||
|
|
||||||
|
fn poll_event() -> Result<Option<Event>, std::io::Error> {
|
||||||
|
if event::poll(Duration::from_millis(0))? {
|
||||||
|
Ok(Some(event::read()?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_set<'a>(
|
||||||
|
name: &'a str,
|
||||||
|
data: &'a [(f64, f64)],
|
||||||
|
marker_type: symbols::Marker,
|
||||||
|
graph_type: GraphType,
|
||||||
|
axis_color: Color
|
||||||
|
) -> Dataset<'a> {
|
||||||
|
Dataset::default()
|
||||||
|
.name(name)
|
||||||
|
.marker(marker_type)
|
||||||
|
.graph_type(graph_type)
|
||||||
|
.style(Style::default().fg(axis_color))
|
||||||
|
.data(&data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axis(app: &App, dim: app::Dimension) -> Axis {
|
||||||
|
let mut a = Axis::default();
|
||||||
|
if app.cfg.references {
|
||||||
|
a = a.title(Span::styled(app.name(&dim), Style::default().fg(Color::Cyan)));
|
||||||
|
}
|
||||||
|
a.style(Style::default().fg(app.cfg.axis_color))
|
||||||
|
.bounds(app.bounds(&dim))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(app: &App, sample_rate: f32, framerate: u32) -> Block {
|
||||||
|
let mut b = Block::default();
|
||||||
|
|
||||||
|
if app.cfg.references {
|
||||||
|
b = b.title(
|
||||||
|
Span::styled(
|
||||||
|
format!(
|
||||||
|
"TUI {} -- {} mode -- range {} -- {} samples -- {:.1} kHz -- {} fps",
|
||||||
|
if app.vectorscope() { "Vectorscope" } else { "Oscilloscope" },
|
||||||
|
if app.scatter() { "scatter" } else { "line" },
|
||||||
|
app.scale(), app.width(), sample_rate / 1000.0, framerate,
|
||||||
|
),
|
||||||
|
Style::default().add_modifier(Modifier::BOLD).fg(Color::Yellow))
|
||||||
|
).title_alignment(Alignment::Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue