use clap::{Parser, ValueEnum, Args}; use crate::outputers::{ColourScheme, Outputers, SimpleOutputer, LineLayout, TuiOutputer}; use derive_more::Display; use crate::notes::Note; use std::path::{PathBuf, Path}; use std::error::Error; use std::fs::File; use std::io::BufReader; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Display)] pub enum OutputerTypes { #[display(fmt="simple")] Simple, #[display(fmt="line-layout")] LineLayout, #[display(fmt="tui")] Tui, } // TODO: it might make sense to add a step and quanitize values (they're quanitized to 1/i16::MAX // anyway because it's 16bit signed ints from alsa), but i think just scaling by // max_threshold will work for now #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct SimpleArgs { /// outputter to use #[arg(short, long, default_value_t = OutputerTypes::Simple)] pub outputer: OutputerTypes, /// threshold for displaying the value, the values have a range of /// 0 - 1 for the most part, a square wave at the max volume /// of a single note will be ~1 (pure sine will be ~sqrt(0.5) or ~0.707ish) /// but in practice most sounds won't get close to that, normally being in /// the range of 0.005-0.10 will give you something usable without being too noisy, /// highly depending on your input though #[arg(short, long, default_value_t = 0.00526532149076897)] pub threshold: f32, /// max (saturating) threshold, for colour output in LineLayout and maybe with /// certain other outputers this is used as a value for which any values higher /// then this is considered a max value and displayed at full brightness. /// /// For LineLayout this contols the brightness of the colour output, /// anything >= max_threshold will display at full brightness and anything below /// will be scaled linearly, like brightness = min(1, note_val / max_threshold) and of course /// r = r_max * brightness, g = g_max * brightness, b = b_max * brightness. /// /// it's recommended to set this somewhat close to what you /// expect your highest value is going to be (you can play with that by experimenting /// with the Simple output, in Simple output each value is scaled by 10,000 so 2000 for /// example would be 0.2) /// /// because the max sine wave volume is sqrt(0.5) for now this defaults to sqrt(0.5), but /// it's recommended to set it lower especially if the brightness of the bars seems too low for /// your input. /// /// 0.2 - 0.3 seems like an actual good value in practice, but it's worth playing with #[arg(short, long, default_value_t = 0.7071067811865476)] pub max_threshold: f32, /// don't use colour to output for outputers that support it /// (currently LineLayout) #[arg(long, default_value_t = false)] pub plain: bool, /*/// max brightness colour for LineLayout and maybe other outputers, /// this is the r/g/b colour at >= max_threshold, values /// threshold > val > max_threshold will scale linearly from /// brightness = threshold to brightness = 1, currently /// threshold just determines if the value is displayed and not /// its colour, that's purely from the max threshold /// (though that could be something to change, make the calculation something like /// diff = note_val - threshold /// max_diff = max_threshold - threshold /// brightness = diff / max_diff) /// /// These should have a range of (0.0 - 255.0) because each value gets converted /// to a u8 to determine the final rgb colour #[arg(long, default_value_t = String::from("78ffdc"))] pub colour: String,*/ /// File that contains the colourscheme as JSON, see the colourschemes/ directory for examples /// we'll use adam neely's as the default for the moment, but this should be changed /// TODO /// NOTE (The default here is set at compile time, so you have to recompile if you change the /// dir you're in) #[arg(short, long)] colours: Option, /// Output device #[arg(long)] pub outdev: Option> } impl SimpleArgs { pub fn get_outputer(&self, notes: &[Note]) -> Outputers { match self.outputer { OutputerTypes::Simple => Outputers::Simple(SimpleOutputer), // TODO: change this unwrap to a ? and a Result return value OutputerTypes::LineLayout => Outputers::LineLayout(LineLayout::new(self.max_threshold, !self.plain, self.get_colours().unwrap(), ¬es)), OutputerTypes::Tui => Outputers::Tui(TuiOutputer::new().unwrap()), } } pub fn get_outdev(&self) -> Option { match &self.outdev { Some(outdev) => outdev.clone(), None => Some(String::from("default")), } } pub fn get_colours(&self) -> Result> { match (self.plain, &self.colours) { (true, _) => Ok(ColourScheme::default()), (false, None) => Ok(ColourScheme::default()), (false, Some(cf)) => { // first check if the file exists either as an absolute path or in the current // directory, if it does try to load it let mut fres = File::open(&cf); // otherwise try to load the colourscheme from colourschemes/ in the project directory if fres.is_err() { //let pth: PathBuf = [env!("CARGO_MANIFEST_DIR"), "colourschemes", cf.as_path()].iter().collect(); let mut pth: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pth.push("colourschemes"); pth.push(&cf); fres = File::open(pth); } let file = fres?; let reader = BufReader::new(file); let colours: ColourScheme = serde_json::from_reader(reader)?; // if either loaded successfully, make sure all of the colours are Rgb for later Ok(colours) } } } }