diff --git a/app/tek.rs b/app/tek.rs index 121360e7..70d0dcd3 100644 --- a/app/tek.rs +++ b/app/tek.rs @@ -883,163 +883,209 @@ pub mod glue { /// /// TODO: Replace these with scripted configurations. #[command(subcommand)] action: Action, - /// 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, Default)] enum Action { /// Continue where you left off #[default] Resume, - /// Show version. - Version, - /// Show configuration. - Config, - /// Show status of current session. - Status, /// Run headlessly in current session. Headless, - /// Create a new session instead of loading the previous one. - New, - /// Create new session from importable file. + /// Show status of current session. + Status, + /// List known sessions. + List, + /// Continue work in a copy of the current session. + Fork, + /// Create a new empty session. + New { + /// Name of JACK client + #[arg(short='n', long)] name: Option, + /// Whether to attempt to become transport master + #[arg(short='Y', long, default_value_t = false)] sync_lead: bool, + /// Whether to sync to external transport master + #[arg(short='y', 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='c', 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, + /// Tracks to create + #[arg(short='t', long)] tracks: Option, + /// Scenes to create + #[arg(short='s', long)] scenes: Option, + }, + /// Import media as new session. Import, + /// Show configuration. + Config, + /// Show version. + Version, } impl Cli { - fn midi_froms (&self) -> Vec { - Connect::collect(&self.midi_from, &[] as &[&str], &self.midi_from_re) - } - fn midi_tos (&self) -> Vec { - Connect::collect(&self.midi_to, &[] as &[&str], &self.midi_to_re) - } pub fn run (&self) -> Usually<()> { - if matches!(self.action, Action::Version) { - println!("todo version"); - } else { - let mut config = Config::new(None); - config.init()?; - if matches!(self.action, Action::Config) { - use ::ansi_term::Color::*; - println!("{:?}", config.dirs); - for (k, v) in config.views.read().unwrap().iter() { - println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}"))); - } - for (k, v) in config.binds.read().unwrap().iter() { - println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}"))); - for (k, v) in v.0.iter() { - print!("{} ", &Yellow.paint(match &k.0 { - Event::Key(KeyEvent { modifiers, .. }) => - format!("{:>16}", format!("{modifiers}")), - _ => unimplemented!() - })); - print!("{}", &Yellow.bold().paint(match &k.0 { - Event::Key(KeyEvent { code, .. }) => - format!("{:<10}", format!("{code}")), - _ => unimplemented!() - })); - for v in v.iter() { - print!(" => {:?}", v.commands); - print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default()); - println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default()); - //println!(" {:?}", v.source); - } - } - } - for (k, v) in config.modes.read().unwrap().iter() { - println!("{} {} {:?} {:?}", Green.paint("\nTOOL "), - Green.bold().paint(format!("{k:<16}")), - v.name, v.info); - print!("{}", Green.paint(" VIEW")); - for v in v.view.iter() { print!(" {}", Yellow.paint(format!("{v}"))); } - println!(); - print!("{}", Green.paint(" KEYS")); - for v in v.keys.iter() { print!(" {}", Yellow.paint(format!("{v}"))); } - println!(); - for (k, v) in v.modes.read().unwrap().iter() { - print!("{} {} {:?}", - Green.paint(" MODE"), - Green.bold().paint(format!("{k:<16}")), - v.name); - print!(" INFO={:?}", - v.info); - print!(" VIEW={:?}", - v.view); - println!(" KEYS={:?}", - v.keys); - } - } + if let Action::Version = self.action { + return Ok(self.show_version()) + } + let mut config = Config::new(None); + config.init()?; + if let Action::Config = self.action { + self.show_config(&config); + } else if let Action::List = self.action { + todo!("list sessions") + } else if let Action::Resume = self.action { + todo!("resume session") + } else if let Action::New { + name, bpm, tracks, scenes, sync_lead, sync_follow, + midi_from, midi_from_re, midi_to, midi_to_re, + left_from, right_from, left_to, right_to, .. + } = &self.action { + let name = name.as_ref().map_or("tek", |x|x.as_str()); + let jack = Jack::new(&name)?; + let empty = &[] as &[&str]; + let left_froms = Connect::collect(&left_from, empty, empty); + let left_tos = Connect::collect(&left_to, empty, empty); + let right_froms = Connect::collect(&right_from, empty, empty); + let right_tos = Connect::collect(&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()]; + let mut midi_ins = vec![]; + let mut midi_outs = vec![]; + for (index, connect) in Connect::collect(&midi_from, &[] as &[&str], &midi_from_re).iter().enumerate() { + midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?); + } + for (index, connect) in Connect::collect(&midi_to, &[] as &[&str], &midi_to_re).iter().enumerate() { + midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?); + }; + let clock = Clock::new( + &jack, *bpm + )?; + let mut project = Arrangement::new( + &jack, None, clock, vec![], vec![], midi_ins, midi_outs + ); + project.tracks_add(tracks.unwrap_or(0), None, &[], &[])?; + project.scenes_add(scenes.unwrap_or(0))?; + if matches!(self.action, Action::Status) { + self.show_status(&project); } else { - let name = self.name.as_ref().map_or("tek", |x|x.as_str()); - let jack = Jack::new(&name)?; - let tracks = vec![]; - let scenes = vec![]; - let empty = &[] as &[&str]; - 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()]; - let mut midi_ins = vec![]; - let mut midi_outs = vec![]; - for (index, connect) in self.midi_froms().iter().enumerate() { - midi_ins.push(jack.midi_in(&format!("M/{index}"), &[connect.clone()])?); - } - for (index, connect) in self.midi_tos().iter().enumerate() { - midi_outs.push(jack.midi_out(&format!("{index}/M"), &[connect.clone()])?); - }; - let clock = Clock::new(&jack, self.bpm)?; - let project = Arrangement::new( - &jack, None, clock, tracks, scenes, midi_ins, midi_outs - ); - if matches!(self.action, Action::Status) { - println!("{project:?}"); - // TODO git integration - } else { - let app = App::new(&jack, project, config, ":menu"); - let client = jack.run(move|jack|{ - 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) + let app = App::new(&jack, project, config, ":menu"); + let client = jack.run(move|jack|{ + jack.sync_lead(*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 })?; - if matches!(self.action, Action::Headless) { - println!("todo headless"); - } else { - return Tui::new()?.run(&client) - } + jack.sync_follow(*sync_follow)?; + Ok(app) + })?; + if matches!(self.action, Action::Headless) { + println!("todo headless"); + } else { + return Tui::new()?.run(&client) } } } Ok(()) } + fn show_version (&self) { + println!("todo version"); + } + fn show_config (&self, config: &Config) { + use ::ansi_term::Color::*; + println!("{:?}", config.dirs); + for (k, v) in config.views.read().unwrap().iter() { + println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}"))); + } + for (k, v) in config.binds.read().unwrap().iter() { + println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}"))); + for (k, v) in v.0.iter() { + print!("{} ", &Yellow.paint(match &k.0 { + Event::Key(KeyEvent { modifiers, .. }) => + format!("{:>16}", format!("{modifiers}")), + _ => unimplemented!() + })); + print!("{}", &Yellow.bold().paint(match &k.0 { + Event::Key(KeyEvent { code, .. }) => + format!("{:<10}", format!("{code}")), + _ => unimplemented!() + })); + for v in v.iter() { + print!(" => {:?}", v.commands); + print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default()); + println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default()); + //println!(" {:?}", v.source); + } + } + } + for (k, v) in config.modes.read().unwrap().iter() { + println!("{} {} {:?} {:?}", Green.paint("\nTOOL "), + Green.bold().paint(format!("{k:<16}")), + v.name, v.info); + print!("{}", Green.paint(" VIEW")); + for v in v.view.iter() { print!(" {}", Yellow.paint(format!("{v}"))); } + println!(); + print!("{}", Green.paint(" KEYS")); + for v in v.keys.iter() { print!(" {}", Yellow.paint(format!("{v}"))); } + println!(); + for (k, v) in v.modes.read().unwrap().iter() { + print!("{} {} {:?}", + Green.paint(" MODE"), + Green.bold().paint(format!("{k:<16}")), + v.name); + print!(" INFO={:?}", + v.info); + print!(" VIEW={:?}", + v.view); + println!(" KEYS={:?}", + v.keys); + } + } + } + fn show_status (&self, project: &Arrangement) { + println!("Name: {:?}", &project.name); + println!("JACK: {:?}", &project.jack); + println!("Buffer: {:?}", &project.clock.chunk); + println!("Sample rate: {:?}", &project.clock.timebase.sr); + println!("MIDI PPQ: {:?}", &project.clock.timebase.ppq); + println!("Tempo: {:?}", &project.clock.timebase.bpm); + println!("Quantize: {:?}", &project.clock.quant); + println!("Launch: {:?}", &project.clock.sync); + println!("Playhead: {:?}us", &project.clock.playhead.usec); + println!("Playhead: {:?}s", &project.clock.playhead.sample); + println!("Playhead: {:?}p", &project.clock.playhead.pulse); + println!("Started: {:?}", &project.clock.started); + println!("Tracks:"); + for (i, t) in project.tracks.iter().enumerate() { + println!(" Track {i}: {} {} {:?} {:?}", t.name, t.width, + &t.sequencer.play_clip, &t.sequencer.next_clip); + } + println!("Scenes:"); + for (i, t) in project.scenes.iter().enumerate() { + println!(" Scene {i}: {} {:?}", &t.name, &t.clips); + } + println!("MIDI Ins: {:?}", &project.midi_ins); + println!("MIDI Outs: {:?}", &project.midi_outs); + println!("Audio Ins: {:?}", &project.audio_ins); + println!("Audio Outs: {:?}", &project.audio_outs); + // TODO git integration + // TODO dawvert integration + } } } /// Command-line entrypoint.