tek/src/model/transport.rs

126 lines
3.8 KiB
Rust

use crate::core::*;
#[derive(PartialEq)]
/// Which section of the transport is focused
pub enum TransportFocus { BPM, Quant, Sync }
impl TransportFocus {
pub fn prev (&mut self) {
*self = match self {
Self::BPM => Self::Sync,
Self::Quant => Self::BPM,
Self::Sync => Self::Quant,
}
}
pub fn next (&mut self) {
*self = match self {
Self::BPM => Self::Quant,
Self::Quant => Self::Sync,
Self::Sync => Self::BPM,
}
}
}
/// 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<Timebase>,
/// JACK transport handle.
transport: Option<Transport>,
/// Quantization factor
pub quant: u16,
/// Global sync quant
pub sync: u16,
/// Current transport state
pub playing: Option<TransportState>,
/// Current position according to transport
playhead: usize,
/// Global frame and usec at which playback started
pub started: Option<(usize, usize)>,
}
impl TransportToolbar {
pub fn new (transport: Option<Transport>) -> 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 u16 * 4,
transport,
timebase,
}
}
pub fn toggle_play (&mut self) -> Usually<()> {
self.playing = match self.playing.expect("1st frame has not been processed yet") {
TransportState::Stopped => {
self.transport.as_ref().unwrap().start()?;
Some(TransportState::Starting)
},
_ => {
self.transport.as_ref().unwrap().stop()?;
self.transport.as_ref().unwrap().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
}
}