refactoring time representation into multuple traits

This commit is contained in:
🪞👃🪞 2024-10-26 13:38:14 +03:00
parent a25548c39c
commit 5a18a2023d
4 changed files with 149 additions and 72 deletions

View file

@ -1,87 +1,158 @@
use crate::*;
pub trait TimeUnit: Mul<Self, Output=Self> + Div<Self, Output=Self> + From<f64> + Sized {}
impl<T> TimeUnit for T
where T: Mul<Self, Output=Self> + Div<Self, Output=Self> + From<f64> + Sized {}
/// Trait for struct that defines a sample rate in hertz (samples per second)
pub trait TimeSR<T: TimeUnit> {
/// Get the sample rate
fn sr (&self) -> T;
/// Set the sample rate
fn set_sr (&self, sr: T);
/// Return the duration of a sample in microseconds
#[inline] fn usec_per_sample (&self) -> T { T::from(1_000_000f64) / self.sr() }
/// Convert a number of samples to microseconds
#[inline] fn samples_to_usec (&self, samples: T) -> T { samples * self.usec_per_sample() }
}
/// Trait for struct that defines a tempo in beats per minute
pub trait TimeBPM<T: TimeUnit> {
/// Get the tempo
fn bpm (&self) -> T;
/// Set the tempo
fn set_bpm (&self, bpm: T);
/// Return the duration fo a beat in microseconds
#[inline] fn usec_per_beat (&self) -> T { T::from(60_000_000f64) / self.bpm() }
/// Return the number of beats in a second
#[inline] fn beat_per_second (&self) -> T { self.bpm() / T::from(60_000_000f64) }
}
/// Trait for struct that defines a MIDI resolution in pulses per quaver (beat)
pub trait TimePPQ<T: TimeUnit> {
const DEFAULT_PPQ: T;
/// Get the PPQ
fn ppq (&self) -> T;
/// Set the PPQ
fn set_ppq (&self, ppq: T);
/// Return duration of a pulse in microseconds (BPM-dependent)
#[inline] fn usec_per_pulse (&self) -> T where Self: TimeBPM<T> {
self.usec_per_beat() / self.ppq()
}
/// Return number of pulses in a second (BPM-dependent)
#[inline] fn pulses_per_second (&self) -> T where Self: TimeBPM<T> {
self.beat_per_second() * self.ppq()
}
/// Return fraction of a pulse to which a sample corresponds (SR- and BPM-dependent)
#[inline] fn pulses_per_sample (&self) -> T where Self: TimeSR<T> + TimeBPM<T> {
self.usec_per_pulse() / self.usec_per_sample()
}
/// Convert a number of pulses to a sample number (SR- and BPM-dependent)
#[inline] fn pulses_to_sample (&self, pulses: T) -> T where Self: TimeSR<T> + TimeBPM<T> {
self.pulses_per_sample() * pulses
}
/// Convert a number of samples to a pulse number (SR- and BPM-dependent)
#[inline] fn samples_to_pulse (&self, samples: T) -> T where Self: TimeSR<T> + TimeBPM<T> {
samples / self.pulses_per_sample()
}
/// Return number of samples in a pulse (SR- and BPM-dependent)
#[inline] fn samples_per_pulse (&self) -> T where Self: TimeSR<T> + TimeBPM<T> {
self.sr() / self.pulses_per_second()
}
}
#[derive(Debug)]
/// Keeps track of global time units.
pub struct Timebase {
/// Frames per second
pub rate: AtomicF64,
/// Samples per second
pub sr: AtomicF64,
/// Beats per minute
pub bpm: AtomicF64,
pub bpm: AtomicF64,
/// Ticks per beat
pub ppq: AtomicF64,
pub ppq: AtomicF64,
}
#[derive(Debug)]
pub struct TransportTime {
/// Current sample sr, tempo, and PPQ.
pub timebase: Arc<Timebase>,
/// Note quantization factor
pub quant: usize,
/// Launch quantization factor
pub sync: usize,
/// Current time in frames
pub frame: usize,
/// Current time in pulses
pub pulse: usize,
/// Current time in microseconds
pub usecs: usize,
/// Pulses per quarter note
pub ppq: usize,
}
impl TimeSR<f64> for Timebase {
#[inline] fn sr (&self) -> f64 { self.sr.load(Ordering::Relaxed) }
#[inline] fn set_sr (&self, sr: f64) { self.sr.store(sr, Ordering::Relaxed); }
}
impl TimeSR<f64> for TransportTime {
#[inline] fn sr (&self) -> f64 { self.timebase.sr() }
#[inline] fn set_sr (&self, sr: f64) { self.timebase.set_sr(sr); }
}
impl TimeBPM<f64> for Timebase {
#[inline] fn bpm (&self) -> f64 { self.bpm.load(Ordering::Relaxed) }
#[inline] fn set_bpm (&self, bpm: f64) { self.bpm.store(bpm, Ordering::Relaxed); }
}
impl TimeBPM<f64> for TransportTime {
#[inline] fn bpm (&self) -> f64 { self.timebase.bpm() }
#[inline] fn set_bpm (&self, bpm: f64) { self.timebase.set_bpm(bpm); }
}
impl TimePPQ<f64> for Timebase {
const DEFAULT_PPQ: f64 = 96f64;
#[inline] fn ppq (&self) -> f64 { self.ppq.load(Ordering::Relaxed) }
#[inline] fn set_ppq (&self, ppq: f64) { self.ppq.store(ppq, Ordering::Relaxed); }
}
impl TimePPQ<f64> for TransportTime {
const DEFAULT_PPQ: f64 = Timebase::DEFAULT_PPQ;
#[inline] fn ppq (&self) -> f64 { self.timebase.ppq() }
#[inline] fn set_ppq (&self, ppq: f64) { self.timebase.set_ppq(ppq); }
}
/// Trait defining temporal resolution in different modes.
pub trait TimeBase<T: TimeUnit>: TimeSR<T> + TimeBPM<T> + TimePPQ<T> {}
pub trait TimeFrame<T> { fn frame (&self) -> T; fn set_frame (&self, frame: T); }
pub trait TimePulse<T> { fn pulse (&self) -> T; fn set_pulse (&self, pulse: T); }
pub trait TimeUsec<T> { fn usec (&self) -> T; fn set_usec (&self, usec: T); }
/// Trait defining a moment in time in different modes.
pub trait TimePoint<T>: TimeFrame<T> + TimePulse<T> + TimeUsec<T> {}
pub trait TimeSync<T> { fn sync (&self) -> T; fn set_sync (&self, sync: T) -> T; }
pub trait TimeQuant<T> { fn quant (&self) -> T; fn set_quant (&self, quant: T); }
impl Default for Timebase {
fn default () -> Self {
Self {
rate: 48000f64.into(),
bpm: 150f64.into(),
ppq: 96f64.into(),
}
}
fn default () -> Self { Self::new(48000f64, 150f64, 96f64) }
}
impl Timebase {
pub fn new (rate: f64, bpm: f64, ppq: f64) -> Self {
Self { rate: rate.into(), bpm: bpm.into(), ppq: ppq.into() }
pub fn new (
sr: impl Into<AtomicF64>, bpm: impl Into<AtomicF64>, ppq: impl Into<AtomicF64>
) -> Self {
Self { sr: sr.into(), bpm: bpm.into(), ppq: ppq.into() }
}
/// Frames per second
#[inline] fn rate (&self) -> f64 {
self.rate.load(Ordering::Relaxed)
}
#[inline] fn usec_per_frame (&self) -> f64 {
1_000_000f64 / self.rate()
}
#[inline] pub fn frame_to_usec (&self, frame: f64) -> f64 {
frame * self.usec_per_frame()
}
/// Beats per minute
#[inline] pub fn bpm (&self) -> f64 {
self.bpm.load(Ordering::Relaxed)
}
#[inline] pub fn set_bpm (&self, bpm: f64) {
self.bpm.store(bpm, Ordering::Relaxed)
}
#[inline] fn usec_per_beat (&self) -> f64 {
60_000_000f64 / self.bpm()
}
#[inline] fn beat_per_second (&self) -> f64 {
self.bpm() / 60000000.0
}
/// Pulses per beat
#[inline] pub fn ppq (&self) -> f64 {
self.ppq.load(Ordering::Relaxed)
}
#[inline] pub fn pulse_per_frame (&self) -> f64 {
self.usec_per_pulse() / self.usec_per_frame()
}
#[inline] pub fn usec_per_pulse (&self) -> f64 {
self.usec_per_beat() / self.ppq()
}
#[inline] pub fn pulse_to_frame (&self, pulses: f64) -> f64 {
self.pulse_per_frame() * pulses
}
#[inline] pub fn frame_to_pulse (&self, frames: f64) -> f64 {
frames / self.pulse_per_frame()
}
#[inline] pub fn note_to_usec (&self, (num, den): (f64, f64)) -> f64 {
4.0 * self.usec_per_beat() * num / den
}
#[inline] pub fn frames_per_pulse (&self) -> f64 {
self.rate() / self.pulses_per_second()
}
#[inline] fn pulses_per_second (&self) -> f64 {
self.beat_per_second() * self.ppq()
}
#[inline] pub fn note_to_frame (&self, note: (f64, f64)) -> f64 {
self.usec_to_frame(self.note_to_usec(note))
}
#[inline] fn usec_to_frame (&self, usec: f64) -> f64 {
usec * self.rate() / 1000.0
usec * self.sr() / 1000.0
}
#[inline] pub fn quantize (