use crate::*; use tek_core::Direction; use tek_mixer::Mixer; use tek_sequencer::{Arranger, TransportToolbar}; /// Root of application state. pub struct App { /// Whether the currently focused section has input priority pub entered: bool, /// Currently focused section pub section: AppFocus, /// Transport model and view. pub transport: TransportToolbar, /// Arranger (contains sequencers) pub arranger: Arranger, /// Mixer (contains tracks) pub mixer: Mixer, /// Main JACK client. pub jack: Option, /// Map of external MIDI outs in the jack graph /// to internal MIDI ins of this app. pub midi_in: Option>>, /// Names of ports to connect to main MIDI IN. pub midi_ins: Vec, /// Display mode of chain section pub chain_mode: bool, /// Main audio outputs. pub audio_outs: Vec>>, /// Number of frames requested by process callback chunk_size: usize, /// Paths to user directories _xdg: Option>, } impl App { pub fn new() -> Usually { let xdg = Arc::new(XdgApp::new("tek")?); let first_run = AppPaths::new(&xdg)?.should_create(); let jack = JackClient::Inactive(Client::new("tek", ClientOptions::NO_START_SERVER)?.0); *MODAL.lock().unwrap() = first_run.then(|| ExitableComponent::boxed(SetupModal(Some(xdg.clone()), false))); Ok(Self { entered: true, section: AppFocus::default(), transport: TransportToolbar::new(Some(jack.transport())), arranger: Arranger::new(""), mixer: Mixer::new("")?, jack: Some(jack), audio_outs: vec![], chain_mode: false, chunk_size: 0, midi_in: None, midi_ins: vec![], _xdg: Some(xdg), }) } pub fn client(&self) -> &Client { self.jack.as_ref().unwrap().client() } pub fn audio_out(&self, index: usize) -> Option>> { self.audio_outs.get(index).map(|x| x.clone()) } pub fn with_midi_ins(mut self, names: &[&str]) -> Usually { self.midi_ins = names.iter().map(|x| x.to_string()).collect(); Ok(self) } pub fn with_audio_outs(mut self, names: &[&str]) -> Usually { let client = self.client(); self.audio_outs = names .iter() .map(|name| { client .ports(Some(name), None, PortFlags::empty()) .get(0) .map(|name| client.port_by_name(name)) }) .flatten() .filter_map(|x| x) .map(Arc::new) .collect(); Ok(self) } pub fn activate( mut self, init: Option>) -> Usually<()>>, ) -> Usually>> { let jack = self.jack.take().expect("no jack client"); let app = Arc::new(RwLock::new(self)); app.write().unwrap().jack = Some(jack.activate(&app.clone(), |state, client, scope| { state.write().unwrap().process(client, scope) })?); if let Some(init) = init { init(&app)?; } Ok(app) } } render!( App | self, buf, area | { Stack::down() .add_ref(&self.transport) .add_ref(&self.arranger) .add(If( self.arranger.selected.is_clip(), &Stack::right() .add(tek_mixer::TrackView { direction: Direction::Down, entered: self.entered, focused: self.section == AppFocus::Chain, chain: self.mixer.track(), }) .add_ref(&self.arranger.sequencer()), )) .render(buf, area)?; if let Some(ref modal) = *MODAL.lock().unwrap() { modal.render(buf, area)?; } Ok(area) } ); process!( App | self, _client, scope | { let (reset, current_frames, chunk_size, current_usecs, next_usecs, period_usecs) = self.transport.update(&scope); self.chunk_size = chunk_size; for track in self.arranger.tracks.iter_mut() { track.process( self.midi_in.as_ref().map(|p| p.iter(&scope)), &self.transport.timebase, self.transport.playing, self.transport.started, self.transport.quant as usize, reset, &scope, (current_frames as usize, self.chunk_size), ( current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize, ), period_usecs as f64, ); } Control::Continue } );