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. Groovebox, /// Multi-track MIDI sequencer. Arranger { /// Number of tracks #[arg(short = 'x', long, default_value_t = 16)] tracks: usize, /// Width of tracks #[arg(short = 'w', long, default_value_t = 6)] track_width: usize, /// Number of scenes #[arg(short = 'y', long, default_value_t = 8)] scenes: 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 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 perf = PerfModel::default(); let size = Measure::new(); let default_clip = ||{ let len = 384usize; let color = ItemColor::random().into(); Arc::new(RwLock::new(MidiClip::new("Clip", true, len, None, Some(color)))) }; Ok(match cli.mode { TekMode::Clock => engine.run(&jack.activate_with(|jack|Ok(TransportTui { jack: jack.clone(), clock: Clock::from(jack), }))?)?, TekMode::Sequencer => engine.run(&jack.activate_with(|jack|Ok({ let clip = default_clip(); let player = MidiPlayer::new(&jack, name, Some(&clip), &midi_froms, &midi_tos)?; Sequencer { _jack: jack.clone(), clock: player.clock.clone(), player, editor: MidiEditor::from(&clip), pool: PoolModel::from(&clip), compact: true, transport: true, selectors: true, midi_buf: vec![vec![];65536], note_buf: vec![], status: true, perf, size, } }))?)?, 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(), color: ItemPalette::from(Color::Rgb(64, 128, 32)), state: Sampler::new(jack, &"sampler", &midi_froms, &[&left_froms, &right_froms], &[&left_tos, &right_tos])?, size, } ))?)?, TekMode::Groovebox => engine.run(&jack.activate_with(|jack|Ok({ let clip = default_clip(); let player = MidiPlayer::new(jack, &"sequencer", Some(&clip), &midi_froms, &midi_tos)?; let sampler = Sampler::new(jack, &"sampler", &midi_froms, &[&left_froms, &right_froms], &[&left_tos, &right_tos])?; jack.read().unwrap().client().connect_ports(&player.midi_outs[0].port, &sampler.midi_in.port)?; let app = Groovebox { _jack: jack.clone(), player, sampler, pool: PoolModel::from(&clip), editor: MidiEditor::from(&clip), compact: true, status: true, midi_buf: vec![vec![];65536], note_buf: vec![], perf, size, }; if let Some(bpm) = cli.bpm { app.clock().timebase.bpm.set(bpm); } 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 })? } app }))?)?, TekMode::Arranger { scenes, tracks, track_width, .. } => engine.run(&jack.activate_with(|jack|Ok({ let clock = Clock::from(jack); let clip = default_clip(); let mut app = Arranger { jack: jack.clone(), clock, pool: (&clip).into(), editor: (&clip).into(), selected: ArrangerSelection::Clip(0, 0), scenes: vec![], tracks: vec![], color: ItemPalette::random(), splits: [12, 20], midi_buf: vec![vec![];65536], note_buf: vec![], compact: true, 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(); }