pub(crate) use tek::*; pub(crate) use clap::{self, Parser, Subcommand}; /// Application entrypoint. pub fn main () -> Usually<()> { Cli::parse().run() } #[derive(Debug, Parser)] #[command(name = "tek", version, about = Some(HEADER), long_about = Some(HEADER))] pub struct Cli { /// Pre-defined configuration modes. /// /// TODO: Replace these with scripted configurations. #[command(subcommand)] mode: Option, /// Name of JACK client #[arg(short='n', long)] name: Option, /// Whether to attempt to become transport master #[arg(short='S', long, default_value_t = false)] sync_lead: bool, /// Whether to sync to external transport master #[arg(short='s', long, default_value_t = true)] sync_follow: bool, /// Initial tempo in beats per minute #[arg(short='b', long, default_value = None)] bpm: Option, /// Whether to include a transport toolbar (default: true) #[arg(short='t', long, default_value_t = true)] show_clock: bool, /// MIDI outs to connect to (multiple instances accepted) #[arg(short='I', long)] midi_from: Vec, /// MIDI outs to connect to (multiple instances accepted) #[arg(short='i', long)] midi_from_re: Vec, /// MIDI ins to connect to (multiple instances accepted) #[arg(short='O', long)] midi_to: Vec, /// MIDI ins to connect to (multiple instances accepted) #[arg(short='o', long)] midi_to_re: Vec, /// Audio outs to connect to left input #[arg(short='l', long)] left_from: Vec, /// Audio outs to connect to right input #[arg(short='r', long)] right_from: Vec, /// Audio ins to connect from left output #[arg(short='L', long)] left_to: Vec, /// Audio ins to connect from right output #[arg(short='R', long)] right_to: Vec, } /// Application modes #[derive(Debug, Clone, Subcommand)] pub enum LaunchMode { /// Create a new session instead of loading the previous one. New, } impl Cli { pub fn run (&self) -> Usually<()> { let name = self.name.as_ref().map_or("tek", |x|x.as_str()); let config = Config::init()?; let empty = &[] as &[&str]; let mut midi_ins = vec![]; let mut midi_outs = vec![]; let tracks = vec![]; let scenes = vec![]; let midi_froms = Connect::collect(&self.midi_from, empty, &self.midi_from_re); let midi_tos = Connect::collect(&self.midi_to, empty, &self.midi_to_re); let left_froms = Connect::collect(&self.left_from, empty, empty); let left_tos = Connect::collect(&self.left_to, empty, empty); let right_froms = Connect::collect(&self.right_from, empty, empty); let right_tos = Connect::collect(&self.right_to, empty, empty); let _audio_froms = &[left_froms.as_slice(), right_froms.as_slice()]; let _audio_tos = &[left_tos.as_slice(), right_tos.as_slice()]; Tui::new()?.run(&Jack::new_run(&name, move|jack|{ for (index, connect) in midi_froms.iter().enumerate() { midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?); } for (index, connect) in midi_tos.iter().enumerate() { midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?); }; let clock = Clock::new(&jack, self.bpm)?; let mode = config.modes.clone().read().unwrap().get(":menu").cloned().unwrap(); let app = App { jack: jack.clone(), color: ItemTheme::random(), dialog: Dialog::Menu(0), mode, config, project: Arrangement { name: Default::default(), color: ItemTheme::random(), jack: jack.clone(), clock, tracks, scenes, selection: Selection::TrackClip { track: 0, scene: 0 }, midi_ins, midi_outs, ..Default::default() }, ..Default::default() }; //if let LaunchMode::Arranger { scenes, tracks, track_width, .. } = self.mode { //app.project.arranger = Default::default(); //app.project.selection = Selection::TrackClip { track: 1, scene: 1 }; //app.project.scenes_add(scenes)?; //app.project.tracks_add(tracks, Some(track_width), &[], &[])?; //} jack.sync_lead(self.sync_lead, |mut state|{ let clock = app.clock(); clock.playhead.update_from_sample(state.position.frame() as f64); state.position.bbt = Some(clock.bbt()); state.position })?; jack.sync_follow(self.sync_follow)?; Ok(app) })?) } } /// CLI header const HEADER: &'static str = r#" ~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~~~~~~ ~ ~ ║ ~ ╟─╌ ~╟─< ~ v0.3.0-rc.0 "no, i insist that i am not a dj ~ ~ ~ ╨ ~ ╙──╜ ╨ ╜ ~ 2025, summer, the nose of the cat. J ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On first run, Tek will create configuration and state dirs. ~ On subsequent runs, Tek should resume from where you left off. ~"#; #[cfg(test)] #[test] fn test_cli () { use clap::CommandFactory; Cli::command().debug_assert(); //let jack = Jack::default(); }