From 58a084e154cad6abbe2829aea38d824a5eafea9a Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 4 Apr 2024 23:53:09 +0200 Subject: [PATCH] chore: separated lib from app code so various display modes can be imported as deps, but by default it still compiles a binary target --- Cargo.toml | 18 +++++++++++++++--- src/app.rs | 32 +++----------------------------- src/display/mod.rs | 29 +++++++++++++++++++++++++++-- src/display/oscilloscope.rs | 8 ++------ src/display/spectroscope.rs | 17 ++++++++++------- src/display/vectorscope.rs | 4 ---- src/input/mod.rs | 2 ++ src/lib.rs | 9 +++++++++ src/main.rs | 14 ++++---------- 9 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 3146407..0d21a3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,22 @@ keywords = ["cli", "tui", "audio", "visualization", "scope"] repository = "https://git.alemi.dev/scope-tui.git" readme = "README.md" +[lib] +name = "scope" +path = "src/lib.rs" + +[[bin]] +name = "scope-tui" +path = "src/main.rs" +required-features = ["app"] + [dependencies] -clap = { version = "4.0.32", features = ["derive"] } derive_more = "0.99.17" thiserror = "1.0.48" rustfft = "6.1.0" +# for TUI app +clap = { version = "4.0.32", features = ["derive"], optional = true } # cross platform audio library backend cpal = { version = "0.15.3", optional = true } # for TUI backend @@ -26,9 +36,11 @@ libpulse-binding = { version = "2.0", optional = true } libpulse-simple-binding = { version = "2.25", optional = true } [features] -default = ["tui", "file", "cpal"] -file = [] +default = ["tui"] +#default = ["app", "file", "cpal"] +app = ["dep:clap", "tui"] tui = ["dep:ratatui", "dep:crossterm"] +file = [] cpal = ["dep:cpal"] pulseaudio = ["dep:libpulse-binding", "dep:libpulse-simple-binding"] diff --git a/src/app.rs b/src/app.rs index 7e66595..28f7d40 100644 --- a/src/app.rs +++ b/src/app.rs @@ -47,9 +47,9 @@ impl App { }, }; - let oscilloscope = Oscilloscope::from_args(source); - let vectorscope = Vectorscope::from_args(source); - let spectroscope = Spectroscope::from_args(source); + let oscilloscope = Oscilloscope::default(); + let vectorscope = Vectorscope::default(); + let spectroscope = Spectroscope::from(source); App { graph, oscilloscope, vectorscope, spectroscope, @@ -175,32 +175,6 @@ impl App { // TODO can these be removed or merged somewhere else? -pub fn update_value_f(val: &mut f64, base: f64, magnitude: f64, range: Range) { - 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) { - 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> { Table::new( vec![ diff --git a/src/display/mod.rs b/src/display/mod.rs index 27d9482..abff4be 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -11,7 +11,7 @@ pub enum Dimension { X, Y } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct GraphConfig { pub pause: bool, pub samples: u32, @@ -36,7 +36,6 @@ impl GraphConfig { #[allow(clippy::ptr_arg)] // TODO temporarily! it's a shitty solution pub trait DisplayMode { // 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 process(&mut self, cfg: &GraphConfig, data: &Matrix) -> Vec; 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) { + 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) { + 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 + } +} diff --git a/src/display/oscilloscope.rs b/src/display/oscilloscope.rs index 624009e..178576c 100644 --- a/src/display/oscilloscope.rs +++ b/src/display/oscilloscope.rs @@ -1,9 +1,9 @@ use crossterm::event::{Event, KeyModifiers, KeyCode}; 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)] pub struct Oscilloscope { @@ -15,10 +15,6 @@ pub struct Oscilloscope { } impl DisplayMode for Oscilloscope { - fn from_args(_opts: &crate::cfg::SourceOptions) -> Self { - Oscilloscope::default() - } - fn mode_str(&self) -> &'static str { "oscillo" } diff --git a/src/display/spectroscope.rs b/src/display/spectroscope.rs index 22bd994..3a6f30f 100644 --- a/src/display/spectroscope.rs +++ b/src/display/spectroscope.rs @@ -3,9 +3,9 @@ use std::collections::VecDeque; use crossterm::event::{Event, KeyCode}; 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}; @@ -37,17 +37,20 @@ pub fn hann_window(samples: &[f64]) -> Vec { windowed_samples } -impl DisplayMode for Spectroscope { - fn from_args(opts: &crate::cfg::SourceOptions) -> Self { +#[cfg(feature = "app")] +impl From<&crate::cfg::SourceOptions> for Spectroscope { + fn from(value: &crate::cfg::SourceOptions) -> Self { Spectroscope { - sampling_rate: opts.sample_rate, - buffer_size: opts.buffer, + sampling_rate: value.sample_rate, + buffer_size: value.buffer, average: 1, buf: Vec::new(), window: false, log_y: true, } } +} +impl DisplayMode for Spectroscope { fn mode_str(&self) -> &'static str { "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::Y => ( 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! }; diff --git a/src/display/vectorscope.rs b/src/display/vectorscope.rs index 6d72a9a..2b703a2 100644 --- a/src/display/vectorscope.rs +++ b/src/display/vectorscope.rs @@ -8,10 +8,6 @@ use super::{DisplayMode, GraphConfig, DataSet, Dimension}; pub struct Vectorscope {} impl DisplayMode for Vectorscope { - fn from_args(_opts: &crate::cfg::SourceOptions) -> Self { - Vectorscope::default() - } - fn mode_str(&self) -> &'static str { "vector" } diff --git a/src/input/mod.rs b/src/input/mod.rs index 4e4e61a..776f8f9 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -3,8 +3,10 @@ pub mod format; #[cfg(feature = "pulseaudio")] pub mod pulse; +#[cfg(feature = "file")] pub mod file; +#[cfg(feature = "cpal")] pub mod cpal; pub type Matrix = Vec>; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8908bdf --- /dev/null +++ b/src/lib.rs @@ -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; diff --git a/src/main.rs b/src/main.rs index 36bf5a2..f348637 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,5 @@ -mod app; -mod cfg; -mod music; -mod input; -mod display; - -use app::App; -use cfg::{ScopeArgs, ScopeSource}; +use scope::app::App; +use scope::cfg::{ScopeArgs, ScopeSource}; use clap::Parser; use ratatui::{backend::CrosstermBackend, Terminal}; use crossterm::{execute, terminal::{ @@ -25,12 +19,12 @@ fn main() -> Result<(), Box> { #[cfg(feature = "file")] 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")] 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)? } };