use crate::core::*; use atomic_float::AtomicF64; #[derive(Debug)] /// Keeps track of global time units. pub struct Timebase { /// Frames per second pub rate: AtomicF64, /// Beats per minute pub bpm: AtomicF64, /// Ticks per beat pub ppq: AtomicF64, } impl Default for Timebase { fn default () -> Self { Self { rate: 48000f64.into(), bpm: 150f64.into(), ppq: 96f64.into(), } } } impl Timebase { pub fn new (rate: f64, bpm: f64, ppq: f64) -> Self { Self { rate: rate.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_000 as f64 / self.rate() as f64 } #[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() as f64 } #[inline] fn beat_per_second (&self) -> f64 { self.bpm() as f64 / 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() as f64 } #[inline] pub fn usec_per_pulse (&self) -> f64 { self.usec_per_beat() / self.ppq() as f64 } #[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() as f64 / self.pulses_per_second() } #[inline] fn pulses_per_second (&self) -> f64 { self.beat_per_second() * self.ppq() as f64 } #[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 } #[inline] pub fn quantize ( &self, step: (f64, f64), time: f64 ) -> (f64, f64) { let step = self.note_to_usec(step); (time / step, time % step) } #[inline] pub fn quantize_into ( &self, step: (f64, f64), events: E ) -> Vec<(f64, T)> where E: std::iter::Iterator + Sized { let step = (step.0.into(), step.1.into()); events .map(|(time, event)|(self.quantize(step, time).0, event)) .collect() } } /// Defines frames per tick. pub struct Ticks(pub f64); impl Ticks { /// Iterate over ticks between start and end. pub fn between_frames (&self, start: usize, end: usize) -> TicksIterator { TicksIterator(self.0, start, start, end) } } /// Iterator that emits subsequent ticks within a range. pub struct TicksIterator(f64, usize, usize, usize); impl Iterator for TicksIterator { type Item = (usize, usize); fn next (&mut self) -> Option { loop { if self.1 > self.3 { return None } let fpt = self.0; let frame = self.1 as f64; let start = self.2; let end = self.3; self.1 = self.1 + 1; //println!("{fpt} {frame} {start} {end}"); let jitter = frame.rem_euclid(fpt); // ramps let next_jitter = (frame + 1.0).rem_euclid(fpt); if jitter > next_jitter { // at crossing: let time = (frame as usize) % (end as usize-start as usize); let tick = (frame / fpt) as usize; return Some((time, tick)) } } } } #[cfg(test)] mod test { use super::*; #[test] fn test_frames_to_ticks () { let ticks = Ticks(12.3).between_frames(0, 100).collect::>(); println!("{ticks:?}"); } }