1
0
Fork 0
mirror of https://github.com/alemidev/scope-tui.git synced 2024-11-14 10:49:20 +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"
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"]

View file

@ -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<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> {
Table::new(
vec![

View file

@ -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<f64>) -> Vec<DataSet>;
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 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"
}

View file

@ -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<f64> {
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!
};

View file

@ -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"
}

View file

@ -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<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;
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<dyn std::error::Error>> {
#[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)?
}
};