use crate::*; #[derive(Clone, Debug, PartialEq)] pub enum PlayheadCommand { Play(Option), Pause(Option), SeekUsec(f64), SeekSample(f64), SeekPulse(f64), } impl Command for PlayheadCommand { fn execute (self, state: &mut T) -> Perhaps { use PlayheadCommand::*; match self { Play(_start) => { todo!() }, Pause(_start) => { todo!() }, SeekUsec(usec) => { state.current().update_from_usec(usec); }, SeekSample(sample) => { state.current().update_from_sample(sample); }, SeekPulse(pulse) => { state.current().update_from_pulse(pulse); }, }; Ok(None) } } pub trait PlayheadApi: ClockApi { /// Current moment in time fn current (&self) -> &Instant; /// Handle to JACK transport fn transport (&self) -> &jack::Transport; /// Playback state fn playing (&self) -> &RwLock>; /// Global sample and usec at which playback started fn started (&self) -> &RwLock>; fn is_stopped (&self) -> bool { *self.playing().read().unwrap() == Some(TransportState::Stopped) } fn is_rolling (&self) -> bool { *self.playing().read().unwrap() == Some(TransportState::Rolling) } /// Current pulse fn pulse (&self) -> f64 { self.current().pulse.get() } fn next_launch_pulse (&self) -> usize { let sync = self.sync().get() as usize; let pulse = self.pulse() as usize; if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync } } fn toggle_play (&self) -> Usually<()> { let playing = self.playing().read().unwrap().expect("1st sample has not been processed yet"); let playing = match playing { TransportState::Stopped => { self.transport().start()?; Some(TransportState::Starting) }, _ => { self.transport().stop()?; self.transport().locate(0)?; Some(TransportState::Stopped) }, }; *self.playing().write().unwrap() = playing; Ok(()) } } /// Hosts the JACK callback for updating the temporal pointer and playback status. pub struct PlayheadAudio<'a, T: PlayheadApi>(pub &'a mut T); impl<'a, T: PlayheadApi> Audio for PlayheadAudio<'a, T> { #[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { let state = &mut self.0; 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 = state.transport().query().unwrap(); state.current().sample.set(transport.pos.frame() as f64); let mut playing = state.playing().write().unwrap(); let mut started = state.started().write().unwrap(); if *playing != Some(transport.state) { match transport.state { TransportState::Rolling => { *started = Some((current_frames as usize, current_usecs as usize)) }, TransportState::Stopped => { *started = None }, _ => {} } }; *playing = Some(transport.state); if *playing == Some(TransportState::Stopped) { *started = None; } state.current().update_from_usec(match *started { Some((_, usecs)) => current_usecs as f64 - usecs as f64, None => 0. }); Control::Continue } }