mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
142 lines
4.3 KiB
Rust
142 lines
4.3 KiB
Rust
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<Timebase>,
|
|
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
|
pub jack: Option<JackClient>,
|
|
/// JACK transport handle.
|
|
pub transport: Option<Transport>,
|
|
/// Quantization factor
|
|
pub quant: usize,
|
|
/// Global sync quant
|
|
pub sync: usize,
|
|
/// Current transport state
|
|
pub playing: Option<TransportState>,
|
|
/// 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<Arc<RwLock<Self>>> {
|
|
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<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 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
|
|
}
|
|
|
|
}
|