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

chore: separated lib from app code

so various display modes can be imported as deps, but by default it
still compiles a binary target
This commit is contained in:
əlemi 2024-04-04 23:53:09 +02:00
parent f62586d1d1
commit 58a084e154
Signed by: alemi
GPG key ID: A4895B84D311642C
9 changed files with 72 additions and 61 deletions

View file

@ -10,12 +10,22 @@ keywords = ["cli", "tui", "audio", "visualization", "scope"]
repository = "https://git.alemi.dev/scope-tui.git" repository = "https://git.alemi.dev/scope-tui.git"
readme = "README.md" readme = "README.md"
[lib]
name = "scope"
path = "src/lib.rs"
[[bin]]
name = "scope-tui"
path = "src/main.rs"
required-features = ["app"]
[dependencies] [dependencies]
clap = { version = "4.0.32", features = ["derive"] }
derive_more = "0.99.17" derive_more = "0.99.17"
thiserror = "1.0.48" thiserror = "1.0.48"
rustfft = "6.1.0" rustfft = "6.1.0"
# for TUI app
clap = { version = "4.0.32", features = ["derive"], optional = true }
# cross platform audio library backend # cross platform audio library backend
cpal = { version = "0.15.3", optional = true } cpal = { version = "0.15.3", optional = true }
# for TUI backend # for TUI backend
@ -26,9 +36,11 @@ libpulse-binding = { version = "2.0", optional = true }
libpulse-simple-binding = { version = "2.25", optional = true } libpulse-simple-binding = { version = "2.25", optional = true }
[features] [features]
default = ["tui", "file", "cpal"] default = ["tui"]
file = [] #default = ["app", "file", "cpal"]
app = ["dep:clap", "tui"]
tui = ["dep:ratatui", "dep:crossterm"] tui = ["dep:ratatui", "dep:crossterm"]
file = []
cpal = ["dep:cpal"] cpal = ["dep:cpal"]
pulseaudio = ["dep:libpulse-binding", "dep:libpulse-simple-binding"] pulseaudio = ["dep:libpulse-binding", "dep:libpulse-simple-binding"]

View file

@ -47,9 +47,9 @@ impl App {
}, },
}; };
let oscilloscope = Oscilloscope::from_args(source); let oscilloscope = Oscilloscope::default();
let vectorscope = Vectorscope::from_args(source); let vectorscope = Vectorscope::default();
let spectroscope = Spectroscope::from_args(source); let spectroscope = Spectroscope::from(source);
App { App {
graph, oscilloscope, vectorscope, spectroscope, graph, oscilloscope, vectorscope, spectroscope,
@ -175,32 +175,6 @@ impl App {
// TODO can these be removed or merged somewhere else? // TODO can these be removed or merged somewhere else?
pub fn update_value_f(val: &mut f64, base: f64, magnitude: f64, range: Range<f64>) {
let delta = base * magnitude;
if *val + delta > range.end {
*val = range.end
} else if *val + delta < range.start {
*val = range.start
} else {
*val += delta;
}
}
pub fn update_value_i(val: &mut u32, inc: bool, base: u32, magnitude: f64, range: Range<u32>) {
let delta = (base as f64 * magnitude) as u32;
if inc {
if range.end - delta < *val {
*val = range.end
} else {
*val += delta
}
} else if range.start + delta > *val {
*val = range.start
} else {
*val -= delta
}
}
fn make_header<'a>(cfg: &GraphConfig, module_header: &'a str, kind_o_scope: &'static str, fps: usize, pause: bool) -> Table<'a> { fn make_header<'a>(cfg: &GraphConfig, module_header: &'a str, kind_o_scope: &'static str, fps: usize, pause: bool) -> Table<'a> {
Table::new( Table::new(
vec![ vec![

View file

@ -11,7 +11,7 @@ pub enum Dimension {
X, Y X, Y
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct GraphConfig { pub struct GraphConfig {
pub pause: bool, pub pause: bool,
pub samples: u32, pub samples: u32,
@ -36,7 +36,6 @@ impl GraphConfig {
#[allow(clippy::ptr_arg)] // TODO temporarily! it's a shitty solution #[allow(clippy::ptr_arg)] // TODO temporarily! it's a shitty solution
pub trait DisplayMode { pub trait DisplayMode {
// MUST define // MUST define
fn from_args(args: &crate::cfg::SourceOptions) -> Self where Self : Sized;
fn axis(&self, cfg: &GraphConfig, dimension: Dimension) -> Axis; // TODO simplify this fn axis(&self, cfg: &GraphConfig, dimension: Dimension) -> Axis; // TODO simplify this
fn process(&mut self, cfg: &GraphConfig, data: &Matrix<f64>) -> Vec<DataSet>; fn process(&mut self, cfg: &GraphConfig, data: &Matrix<f64>) -> Vec<DataSet>;
fn mode_str(&self) -> &'static str; fn mode_str(&self) -> &'static str;
@ -83,3 +82,29 @@ impl DataSet {
} }
} }
pub(crate) fn update_value_f(val: &mut f64, base: f64, magnitude: f64, range: std::ops::Range<f64>) {
let delta = base * magnitude;
if *val + delta > range.end {
*val = range.end
} else if *val + delta < range.start {
*val = range.start
} else {
*val += delta;
}
}
pub(crate) fn update_value_i(val: &mut u32, inc: bool, base: u32, magnitude: f64, range: std::ops::Range<u32>) {
let delta = (base as f64 * magnitude) as u32;
if inc {
if range.end - delta < *val {
*val = range.end
} else {
*val += delta
}
} else if range.start + delta > *val {
*val = range.start
} else {
*val -= delta
}
}

View file

@ -1,9 +1,9 @@
use crossterm::event::{Event, KeyModifiers, KeyCode}; use crossterm::event::{Event, KeyModifiers, KeyCode};
use ratatui::{widgets::{Axis, GraphType}, style::Style, text::Span}; use ratatui::{widgets::{Axis, GraphType}, style::Style, text::Span};
use crate::{app::{update_value_f, update_value_i}, input::Matrix}; use crate::input::Matrix;
use super::{DisplayMode, GraphConfig, DataSet, Dimension}; use super::{update_value_f, update_value_i, DataSet, Dimension, DisplayMode, GraphConfig};
#[derive(Default)] #[derive(Default)]
pub struct Oscilloscope { pub struct Oscilloscope {
@ -15,10 +15,6 @@ pub struct Oscilloscope {
} }
impl DisplayMode for Oscilloscope { impl DisplayMode for Oscilloscope {
fn from_args(_opts: &crate::cfg::SourceOptions) -> Self {
Oscilloscope::default()
}
fn mode_str(&self) -> &'static str { fn mode_str(&self) -> &'static str {
"oscillo" "oscillo"
} }

View file

@ -3,9 +3,9 @@ use std::collections::VecDeque;
use crossterm::event::{Event, KeyCode}; use crossterm::event::{Event, KeyCode};
use ratatui::{widgets::{Axis, GraphType}, style::Style, text::Span}; use ratatui::{widgets::{Axis, GraphType}, style::Style, text::Span};
use crate::{app::update_value_i, input::Matrix}; use crate::input::Matrix;
use super::{DisplayMode, GraphConfig, DataSet, Dimension}; use super::{update_value_i, DataSet, Dimension, DisplayMode, GraphConfig};
use rustfft::{FftPlanner, num_complex::Complex}; use rustfft::{FftPlanner, num_complex::Complex};
@ -37,17 +37,20 @@ pub fn hann_window(samples: &[f64]) -> Vec<f64> {
windowed_samples windowed_samples
} }
impl DisplayMode for Spectroscope { #[cfg(feature = "app")]
fn from_args(opts: &crate::cfg::SourceOptions) -> Self { impl From<&crate::cfg::SourceOptions> for Spectroscope {
fn from(value: &crate::cfg::SourceOptions) -> Self {
Spectroscope { Spectroscope {
sampling_rate: opts.sample_rate, sampling_rate: value.sample_rate,
buffer_size: opts.buffer, buffer_size: value.buffer,
average: 1, buf: Vec::new(), average: 1, buf: Vec::new(),
window: false, window: false,
log_y: true, log_y: true,
} }
} }
}
impl DisplayMode for Spectroscope {
fn mode_str(&self) -> &'static str { fn mode_str(&self) -> &'static str {
"spectro" "spectro"
} }
@ -80,7 +83,7 @@ impl DisplayMode for Spectroscope {
Dimension::X => ("frequency -", [20.0f64.ln(), ((cfg.samples as f64 / cfg.width as f64) * 20000.0).ln()]), Dimension::X => ("frequency -", [20.0f64.ln(), ((cfg.samples as f64 / cfg.width as f64) * 20000.0).ln()]),
Dimension::Y => ( Dimension::Y => (
if self.log_y { "| level" } else { "| amplitude" }, if self.log_y { "| level" } else { "| amplitude" },
[if self.log_y { 0. } else { 0.0 }, cfg.scale * 7.5] // very arbitrary but good default [0.0, cfg.scale * 7.5] // very arbitrary but good default
), ),
// TODO super arbitraty! wtf! also ugly inline ifs, get this thing together! // TODO super arbitraty! wtf! also ugly inline ifs, get this thing together!
}; };

View file

@ -8,10 +8,6 @@ use super::{DisplayMode, GraphConfig, DataSet, Dimension};
pub struct Vectorscope {} pub struct Vectorscope {}
impl DisplayMode for Vectorscope { impl DisplayMode for Vectorscope {
fn from_args(_opts: &crate::cfg::SourceOptions) -> Self {
Vectorscope::default()
}
fn mode_str(&self) -> &'static str { fn mode_str(&self) -> &'static str {
"vector" "vector"
} }

View file

@ -3,8 +3,10 @@ pub mod format;
#[cfg(feature = "pulseaudio")] #[cfg(feature = "pulseaudio")]
pub mod pulse; pub mod pulse;
#[cfg(feature = "file")]
pub mod file; pub mod file;
#[cfg(feature = "cpal")]
pub mod cpal; pub mod cpal;
pub type Matrix<T> = Vec<Vec<T>>; pub type Matrix<T> = Vec<Vec<T>>;

9
src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
pub mod music;
pub mod input;
pub mod display;
#[cfg(feature = "app")]
pub mod cfg;
#[cfg(feature = "app")]
pub mod app;

View file

@ -1,11 +1,5 @@
mod app; use scope::app::App;
mod cfg; use scope::cfg::{ScopeArgs, ScopeSource};
mod music;
mod input;
mod display;
use app::App;
use cfg::{ScopeArgs, ScopeSource};
use clap::Parser; use clap::Parser;
use ratatui::{backend::CrosstermBackend, Terminal}; use ratatui::{backend::CrosstermBackend, Terminal};
use crossterm::{execute, terminal::{ use crossterm::{execute, terminal::{
@ -25,12 +19,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "file")] #[cfg(feature = "file")]
ScopeSource::File { path, limit_rate } => { ScopeSource::File { path, limit_rate } => {
input::file::FileSource::new(&path, &args.opts, limit_rate)? scope::input::file::FileSource::new(&path, &args.opts, limit_rate)?
}, },
#[cfg(feature = "cpal")] #[cfg(feature = "cpal")]
ScopeSource::Audio { device, timeout } => { ScopeSource::Audio { device, timeout } => {
input::cpal::DefaultAudioDeviceWithCPAL::new(device.as_deref(), &args.opts, timeout)? scope::input::cpal::DefaultAudioDeviceWithCPAL::new(device.as_deref(), &args.opts, timeout)?
} }
}; };