use crate::*; use std::iter::Iterator; /// Any numeric type that represents time pub trait TimeUnit: PartialEq + Copy + Add + Mul + Div + Rem {} impl TimeUnit for T where T: PartialEq + Copy + Add + Mul + Div + Rem {} /// Integer time unit, such as frames, pulses, or microseconds pub trait TimeInteger: TimeUnit + From + Into + Copy {} impl TimeInteger for T where T: TimeUnit + From + Into + Copy {} /// Floating time unit, such as beats or seconds pub trait TimeFloat: TimeUnit + From + Into + Copy {} impl TimeFloat for T where T: TimeUnit + From + Into + Copy {} /// Trait for struct that defines a sample rate in hertz (samples per second) pub trait SampleRate { /// Get the sample rate fn sr (&self) -> U; /// Set the sample rate fn set_sr (&self, sr: U); /// Return the duration of a sample in microseconds (floating) #[inline] fn usec_per_sample (&self) -> U where U: TimeFloat { U::from(1_000_000f64 / self.sr().into()) } /// Convert a number of samples to microseconds (floating) #[inline] fn samples_to_usec (&self, samples: U) -> U where U: TimeFloat { samples * self.usec_per_sample() } } /// Trait for struct that defines a tempo in beats per minute pub trait BeatsPerMinute { /// Get the tempo fn bpm (&self) -> U; /// Set the tempo fn set_bpm (&self, bpm: U); /// Return the duration fo a beat in microseconds #[inline] fn usec_per_beat (&self) -> U { U::from(60_000_000f64) / self.bpm() } /// Return the number of beats in a second #[inline] fn beat_per_second (&self) -> U { self.bpm() / U::from(60_000_000f64) } /// Return the number of microseconds corresponding to a note of the given duration #[inline] fn note_to_usec (&self, (num, den): (U, U)) -> U { U::from(4.0) * self.usec_per_beat() * num / den } /// Return the number of frames corresponding to a note of the given duration #[inline] fn note_to_frame (&self, note: (U, U)) -> U where Self: SampleRate { self.usec_to_frame(self.note_to_usec(note)) } /// Return the number of frames corresponding to the given number of microseconds #[inline] fn usec_to_frame (&self, usec: U) -> U where Self: SampleRate { usec * self.sr() / U::from(1000f64) } /// Return the quantized position of a moment in time given a step #[inline] fn quantize (&self, step: (U, U), time: U) -> (U, U) { let step = self.note_to_usec(step); (time / step, time % step) } /// Quantize a collection of events #[inline] fn quantize_into + Sized, T> ( &self, step: (U, U), events: E ) -> Vec<(U, U)> { events.map(|(time, event)|(self.quantize(step, time).0, event)).collect() } } /// Trait for struct that defines a MIDI resolution in pulses per quaver (beat) pub trait PulsesPerQuaver { const DEFAULT_PPQ: U; /// Get the PPQ fn ppq (&self) -> U; /// Set the PPQ fn set_ppq (&self, ppq: U); /// Return duration of a pulse in microseconds (BPM-dependent) #[inline] fn usec_per_pulse (&self) -> U where U: TimeFloat, Self: BeatsPerMinute { self.usec_per_beat() / self.ppq() } /// Return number of pulses in a second (BPM-dependent) #[inline] fn pulses_per_second (&self) -> U where U: TimeFloat, Self: BeatsPerMinute { 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) -> U where U: TimeFloat, Self: SampleRate + BeatsPerMinute { 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, p: U) -> U where U: TimeFloat, Self: SampleRate + BeatsPerMinute { self.pulses_per_sample() * p } /// Convert a number of samples to a pulse number (SR- and BPM-dependent) #[inline] fn samples_to_pulse (&self, s: U) -> U where U: TimeFloat, Self: SampleRate + BeatsPerMinute { s / self.pulses_per_sample() } /// Return number of samples in a pulse (SR- and BPM-dependent) #[inline] fn samples_per_pulse (&self) -> U where U: TimeFloat, Self: SampleRate + BeatsPerMinute { self.sr() / self.pulses_per_second() } } pub trait FramePosition { fn frame (&self) -> U; fn set_frame (&self, frame: U); } pub trait PulsePosition { fn pulse (&self) -> U; fn set_pulse (&self, pulse: U); } pub trait UsecPosition { fn usec (&self) -> U; fn set_usec (&self, usec: U); } pub trait LaunchSync { fn sync (&self) -> U; fn set_sync (&self, sync: U); #[inline] fn next_launch_frame (&self) -> U where U: TimeInteger, Self: FramePosition { let sync = self.sync(); let frame = self.frame(); if frame % sync == U::from(0) { frame } else { (frame / sync) * sync + U::from(1) } } } pub trait Quantize { fn quant (&self) -> T; fn set_quant (&self, quant: T); } #[derive(Debug)] /// Keeps track of global time units. pub struct Timebase { /// Samples per second pub sr: AtomicF64, /// Beats per minute pub bpm: AtomicF64, /// Ticks per beat pub ppq: AtomicF64, } impl Default for Timebase { fn default () -> Self { Self::new(48000f64, 150f64, 96f64) } } impl Timebase { pub fn new (s: impl Into, b: impl Into, p: impl Into) -> Self { Self { sr: s.into(), bpm: b.into(), ppq: p.into() } } } impl SampleRate 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 BeatsPerMinute 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 PulsesPerQuaver 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); } } /// (pulses, name) pub const NOTE_DURATIONS: [(usize, &str);26] = [ (1, "1/384"), (2, "1/192"), (3, "1/128"), (4, "1/96"), (6, "1/64"), (8, "1/48"), (12, "1/32"), (16, "1/24"), (24, "1/16"), (32, "1/12"), (48, "1/8"), (64, "1/6"), (96, "1/4"), (128, "1/3"), (192, "1/2"), (256, "2/3"), (384, "1/1"), (512, "4/3"), (576, "3/2"), (768, "2/1"), (1152, "3/1"), (1536, "4/1"), (2304, "6/1"), (3072, "8/1"), (3456, "9/1"), (6144, "16/1"), ]; /// Returns the next shorter length pub fn prev_note_length (pulses: usize) -> usize { for i in 1..=16 { let length = NOTE_DURATIONS[16-i].0; if length < pulses { return length } } pulses } /// Returns the next longer length pub fn next_note_length (pulses: usize) -> usize { for (length, _) in &NOTE_DURATIONS { if *length > pulses { return *length } } pulses } pub fn pulses_to_name (pulses: usize) -> &'static str { for (length, name) in &NOTE_DURATIONS { if *length == pulses { return name } } "" } /// 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:?}"); } }