use std::sync::{Arc, RwLock}; use tek::*; #[allow(unused_imports)] use clap::{self, Parser, Subcommand, ValueEnum}; #[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct TekCli { /// Which app to initialize #[command(subcommand)] mode: TekMode, /// 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, } #[derive(Debug, Clone, Subcommand)] pub enum TekMode { /// A standalone transport clock. Clock, /// A MIDI sequencer. Sequencer, /// A MIDI-controlled audio sampler. Sampler, /// Sequencer and sampler together.12 Groovebox, /// Multi-track MIDI sequencer. Arranger { /// Number of scenes #[arg(short = 'y', long, default_value_t = 1)] scenes: usize, /// Number of tracks #[arg(short = 'x', long, default_value_t = 1)] tracks: usize, /// Width of tracks #[arg(short = 'w', long, default_value_t = 9)] track_width: usize, }, /// TODO: A MIDI-controlled audio mixer Mixer, /// TODO: A customizable channel strip Track, /// TODO: An audio plugin host Plugin, } /// Application entrypoint. pub fn main () -> Usually<()> { let cli = TekCli::parse(); let name = cli.name.as_ref().map_or("tek", |x|x.as_str()); let color = ItemPalette::random(); let jack = JackConnection::new(name)?; let engine = Tui::new()?; let empty = &[] as &[&str]; let midi_froms = PortConnection::collect(&cli.midi_from, &cli.midi_from_re, empty); let midi_tos = PortConnection::collect(&cli.midi_to, &cli.midi_to_re, empty); let left_froms = PortConnection::collect(&cli.left_from, empty, empty); let left_tos = PortConnection::collect(&cli.left_to, empty, empty); let right_froms = PortConnection::collect(&cli.right_from, empty, empty); let right_tos = PortConnection::collect(&cli.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 default_clip = ||Arc::new(RwLock::new(MidiClip::new( "Clip", true, 384usize, None, Some(ItemColor::random().into())))); let default_player = |jack: &Arc>, clip: Option<&Arc>>| MidiPlayer::new(&jack, name, clip, &midi_froms, &midi_tos); let default_sampler = |jack: &Arc>| Sampler::new(jack, &"sampler", &midi_froms, &[&left_froms, &right_froms], &[&left_tos, &right_tos]); let default_bpm = |clock: Clock|{ if let Some(bpm) = cli.bpm { clock.timebase.bpm.set(bpm); } clock }; let default_clock = |jack: &Arc>|default_bpm(Clock::from(jack)); // TODO: enable sync master/follow //let sync_clock = |jack: &Arc>, app|{ //if cli.sync_lead { //jack.read().unwrap().client().register_timebase_callback(false, |mut state|{ //app.clock().playhead.update_from_sample(state.position.frame() as f64); //state.position.bbt = Some(app.clock().bbt()); //state.position //}) //} else if cli.sync_follow { //jack.read().unwrap().client().register_timebase_callback(false, |state|{ //app.clock().playhead.update_from_sample(state.position.frame() as f64); //state.position //}) //} else { //Ok(()) //} //}; Ok(match cli.mode { TekMode::Clock => engine.run(&jack.activate_with(|jack|Ok(ClockTui { jack: jack.clone(), clock: default_clock(jack), }))?)?, TekMode::Sampler => engine.run(&jack.activate_with(|jack|Ok( SamplerTui { cursor: (0, 0), editing: None, mode: None, note_lo: 36.into(), note_pt: 36.into(), state: default_sampler(jack)?, color, size: Measure::new(), } ))?)?, TekMode::Sequencer => engine.run(&jack.activate_with(|jack|Ok({ let clip = default_clip(); let mut player = default_player(jack, Some(&clip))?; player.clock = default_bpm(player.clock); App::sequencer( jack, (&clip).into(), (&clip).into(), Some(player), &midi_froms, &midi_tos, ) }))?)?, TekMode::Groovebox => engine.run(&jack.activate_with(|jack|Ok({ let clip = default_clip(); let mut player = default_player(jack, Some(&clip))?; player.clock = default_bpm(player.clock); let sampler = default_sampler(jack)?; jack.connect_ports(&player.midi_outs[0].port, &sampler.midi_in.as_ref().unwrap().port)?; App::groovebox( jack, (&clip).into(), (&clip).into(), Some(player), &midi_froms, &midi_tos, sampler, audio_froms, audio_tos, ) }))?)?, TekMode::Arranger { scenes, tracks, track_width, .. } => engine.run(&jack.activate_with(|jack|Ok({ App::arranger( jack, PoolModel::default(), MidiEditor::default(), &midi_froms, &midi_tos, default_sampler(jack)?, audio_froms, audio_tos, scenes, tracks, track_width ) }))?)?, //let clock = default_clock(jack); //let mut app = Arranger { //jack: jack.clone(), //midi_ins: vec![JackPort::::new(jack, format!("M/{name}"), &midi_froms)?,], //midi_outs: vec![JackPort::::new(jack, format!("{name}/M"), &midi_tos)?, ], //clock, //pool: PoolModel::default(),//(&clip).into(), //editor: MidiEditor::default(),//(&clip).into(), //selected: ArrangerSelection::Clip(0, 0), //scenes: vec![], //tracks: vec![], //splits: [12, 20], //midi_buf: vec![vec![];65536], //note_buf: vec![], //compact: false, //editing: true.into(), //color, //perf, //size, //}; //app.tracks_add(tracks, track_width, &midi_froms, &midi_tos)?; //app.scenes_add(scenes)?; //app //}))?)?, _ => todo!() }) } #[test] fn verify_cli () { use clap::CommandFactory; TekCli::command().debug_assert(); }