use crate::*; /// Stores and displays time-related state. pub struct TransportToolbar { _engine: PhantomData, /// Enable metronome? pub metronome: bool, /// 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 /// Global frame and usec at which playback started pub started: Option<(usize, usize)>, pub focused: bool, pub focus: TransportToolbarFocus, pub playing: Option, pub bpm: f64, pub quant: usize, pub sync: usize, pub frame: usize, pub pulse: usize, pub ppq: usize, pub usecs: usize, } impl TransportToolbar { pub fn new (transport: Option) -> Self { let timebase = Arc::new(Timebase::default()); Self { _engine: Default::default(), focused: false, focus: TransportToolbarFocus::PlayPause, playing: Some(TransportState::Stopped), bpm: timebase.bpm(), quant: 24, sync: timebase.ppq() as usize * 4, frame: 0, pulse: 0, ppq: 0, usecs: 0, metronome: false, started: None, 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 times = scope.cycle_times().unwrap(); let CycleTimes { current_frames, current_usecs, next_usecs, period_usecs } = times; let chunk_size = scope.n_frames() as usize; let transport = self.transport.as_ref().unwrap().query().unwrap(); self.frame = 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.frame as f64) as usize } pub fn usecs (&self) -> usize { self.timebase.frame_to_usec(self.frame as f64) as usize } pub fn quant (&self) -> usize { self.quant } pub fn sync (&self) -> usize { self.sync } } impl Audio for TransportToolbar { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { self.update(&scope); Control::Continue } } /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Clone, Copy, PartialEq)] pub enum TransportToolbarFocus { PlayPause, Bpm, Quant, Sync, Clock, } impl TransportToolbarFocus { pub fn next (&mut self) { *self = match self { Self::PlayPause => Self::Bpm, Self::Bpm => Self::Quant, Self::Quant => Self::Sync, Self::Sync => Self::Clock, Self::Clock => Self::PlayPause, } } pub fn prev (&mut self) { *self = match self { Self::PlayPause => Self::Clock, Self::Bpm => Self::PlayPause, Self::Quant => Self::Bpm, Self::Sync => Self::Quant, Self::Clock => Self::Sync, } } }