time: clock: add next_launch_instant, modularize

This commit is contained in:
🪞👃🪞 2025-04-26 12:51:14 +03:00
parent 13444dc59a
commit 24bc33d3d0
4 changed files with 81 additions and 61 deletions

18
crates/time/src/clock.rs Normal file
View file

@ -0,0 +1,18 @@
use crate::*;
mod clock_api; pub use self::clock_api::*;
mod clock_model; pub use self::clock_model::*;
pub trait HasClock: Send + Sync {
fn clock (&self) -> &Clock;
fn clock_mut (&mut self) -> &mut Clock;
}
#[macro_export] macro_rules! has_clock {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? {
fn clock (&$self) -> &Clock { &$cb }
fn clock_mut (&mut $self) -> &mut Clock { &mut $cb }
}
}
}

View file

@ -0,0 +1,55 @@
use crate::*;
#[derive(Clone, Debug, PartialEq)]
pub enum ClockCommand {
Play(Option<u32>),
Pause(Option<u32>),
SeekUsec(f64),
SeekSample(f64),
SeekPulse(f64),
SetBpm(f64),
SetQuant(f64),
SetSync(f64),
}
provide_num!(u32: |self: Clock| {});
provide!(f64: |self: Clock| {});
atom_command!(ClockCommand: |state: Clock| {
("play" [] Some(Self::Play(None)))
("play" [t: u32] Some(Self::Play(t)))
("pause" [] Some(Self::Pause(None)))
("pause" [t: u32] Some(Self::Pause(t)))
("toggle" [] Some(if state.is_rolling() { Self::Pause(None) } else { Self::Play(None) }))
("toggle" [t: u32] Some(if state.is_rolling() { Self::Pause(t) } else { Self::Play(t) }))
("seek/usec" [t: f64] Some(Self::SeekUsec(t.expect("no usec"))))
("seek/pulse" [t: f64] Some(Self::SeekPulse(t.expect("no pulse"))))
("seek/sample" [t: f64] Some(Self::SeekSample(t.expect("no sample"))))
("set/bpm" [t: f64] Some(Self::SetBpm(t.expect("no bpm"))))
("set/sync" [t: f64] Some(Self::SetSync(t.expect("no sync"))))
("set/quant" [t: f64] Some(Self::SetQuant(t.expect("no quant"))))
});
impl<T: HasClock> Command<T> for ClockCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
self.execute(state.clock_mut())
}
}
impl Command<Clock> for ClockCommand {
fn execute (self, state: &mut Clock) -> Perhaps<Self> {
use ClockCommand::*;
match self {
Play(start) => state.play_from(start)?,
Pause(pause) => state.pause_at(pause)?,
SeekUsec(usec) => state.playhead.update_from_usec(usec),
SeekSample(sample) => state.playhead.update_from_sample(sample),
SeekPulse(pulse) => state.playhead.update_from_pulse(pulse),
SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))),
SetQuant(quant) => return Ok(Some(SetQuant(state.quant.set(quant)))),
SetSync(sync) => return Ok(Some(SetSync(state.sync.set(sync)))),
};
Ok(None)
}
}

View file

@ -1,64 +1,4 @@
use crate::*;
pub trait HasClock: Send + Sync {
fn clock (&self) -> &Clock;
fn clock_mut (&mut self) -> &mut Clock;
}
#[macro_export] macro_rules! has_clock {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? {
fn clock (&$self) -> &Clock { &$cb }
fn clock_mut (&mut $self) -> &mut Clock { &mut $cb }
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ClockCommand {
Play(Option<u32>),
Pause(Option<u32>),
SeekUsec(f64),
SeekSample(f64),
SeekPulse(f64),
SetBpm(f64),
SetQuant(f64),
SetSync(f64),
}
provide_num!(u32: |self: Clock| {});
provide!(f64: |self: Clock| {});
atom_command!(ClockCommand: |state: Clock| {
("play" [] Some(Self::Play(None)))
("play" [t: u32] Some(Self::Play(t)))
("pause" [] Some(Self::Pause(None)))
("pause" [t: u32] Some(Self::Pause(t)))
("toggle" [] Some(if state.is_rolling() { Self::Pause(None) } else { Self::Play(None) }))
("toggle" [t: u32] Some(if state.is_rolling() { Self::Pause(t) } else { Self::Play(t) }))
("seek/usec" [t: f64] Some(Self::SeekUsec(t.expect("no usec"))))
("seek/pulse" [t: f64] Some(Self::SeekPulse(t.expect("no pulse"))))
("seek/sample" [t: f64] Some(Self::SeekSample(t.expect("no sample"))))
("set/bpm" [t: f64] Some(Self::SetBpm(t.expect("no bpm"))))
("set/sync" [t: f64] Some(Self::SetSync(t.expect("no sync"))))
("set/quant" [t: f64] Some(Self::SetQuant(t.expect("no quant"))))
});
impl<T: HasClock> Command<T> for ClockCommand {
fn execute (self, state: &mut T) -> Perhaps<Self> {
self.execute(state.clock_mut())
}
}
impl Command<Clock> for ClockCommand {
fn execute (self, state: &mut Clock) -> Perhaps<Self> {
use ClockCommand::*;
match self {
Play(start) => state.play_from(start)?,
Pause(pause) => state.pause_at(pause)?,
SeekUsec(usec) => state.playhead.update_from_usec(usec),
SeekSample(sample) => state.playhead.update_from_sample(sample),
SeekPulse(pulse) => state.playhead.update_from_pulse(pulse),
SetBpm(bpm) => return Ok(Some(SetBpm(state.timebase().bpm.set(bpm)))),
SetQuant(quant) => return Ok(Some(SetQuant(state.quant.set(quant)))),
SetSync(sync) => return Ok(Some(SetSync(state.sync.set(sync)))),
};
Ok(None)
}
}
#[derive(Clone, Default)]
pub struct Clock {
@ -87,6 +27,7 @@ pub struct Clock {
/// For emitting a metronome
pub click_out: Arc<RwLock<Option<JackAudioOut>>>,
}
impl std::fmt::Debug for Clock {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("Clock")
@ -100,6 +41,7 @@ impl std::fmt::Debug for Clock {
.finish()
}
}
impl Clock {
pub fn new (jack: &Jack, bpm: Option<f64>) -> Usually<Self> {
let (chunk, transport) = jack.with_client(|c|(c.buffer_size(), c.transport()));
@ -231,4 +173,8 @@ impl Clock {
ticks_per_beat: ppq as f64
}
}
pub fn next_launch_instant (&self) -> Moment {
Moment::from_pulse(self.timebase(), self.next_launch_pulse() as f64)
}
}

View file

@ -1,4 +1,5 @@
mod time_clock; pub use self::time_clock::*;
mod clock; pub use self::clock::*;
mod time_moment; pub use self::time_moment::*;
mod time_note; pub use self::time_note::*;
mod time_perf; pub use self::time_perf::*;