use crate::*; /// Stores and displays time-related state. pub struct TransportToolbar { /// Enable metronome? pub metronome: bool, pub focused: bool, pub entered: bool, pub selected: TransportFocus, /// Current sample rate, tempo, and PPQ. pub timebase: Arc, /// JACK client handle (needs to not be dropped for standalone mode to work). pub jack: Option, /// JACK transport handle. pub transport: Option, /// Quantization factor pub quant: usize, /// Global sync quant pub sync: usize, /// Current transport state pub playing: Option, /// Current position according to transport pub playhead: usize, /// Global frame and usec at which playback started pub started: Option<(usize, usize)>, } process!(TransportToolbar |self, _client, scope| { self.update(&scope); Control::Continue }); impl TransportToolbar { pub fn standalone () -> Usually>> { let mut transport = Self::new(None); transport.focused = true; transport.entered = true; let jack = JackClient::Inactive( Client::new("tek_transport", ClientOptions::NO_START_SERVER)?.0 ); transport.transport = Some(jack.transport()); let transport = Arc::new(RwLock::new(transport)); transport.write().unwrap().jack = Some( jack.activate( &transport.clone(), |state, client, scope| { state.write().unwrap().process(client, scope) } )? ); Ok(transport) } pub fn new (transport: Option) -> Self { let timebase = Arc::new(Timebase::default()); Self { selected: TransportFocus::BPM, metronome: false, focused: false, entered: false, playhead: 0, playing: Some(TransportState::Stopped), started: None, quant: 24, sync: timebase.ppq() as usize * 4, jack: None, transport, timebase, } } pub fn toggle_play (&mut self) -> Usually<()> { let transport = self.transport.as_ref().unwrap(); self.playing = match self.playing.expect("1st frame has not been processed yet") { TransportState::Stopped => { transport.start()?; Some(TransportState::Starting) }, _ => { transport.stop()?; transport.locate(0)?; Some(TransportState::Stopped) }, }; Ok(()) } pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) { let CycleTimes { current_frames, current_usecs, next_usecs, period_usecs } = scope.cycle_times().unwrap(); let chunk_size = scope.n_frames() as usize; let transport = self.transport.as_ref().unwrap().query().unwrap(); self.playhead = transport.pos.frame() as usize; let mut reset = false; if self.playing != Some(transport.state) { match transport.state { TransportState::Rolling => { self.started = Some(( current_frames as usize, current_usecs as usize, )); }, TransportState::Stopped => { self.started = None; reset = true; }, _ => {} } } self.playing = Some(transport.state); ( reset, current_frames as usize, chunk_size as usize, current_usecs as usize, next_usecs as usize, period_usecs as f64 ) } pub fn bpm (&self) -> usize { self.timebase.bpm() as usize } pub fn ppq (&self) -> usize { self.timebase.ppq() as usize } pub fn pulse (&self) -> usize { self.timebase.frame_to_pulse(self.playhead as f64) as usize } pub fn usecs (&self) -> usize { self.timebase.frame_to_usec(self.playhead as f64) as usize } }