mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: simplify time traits (f64-first)
This commit is contained in:
parent
66f9afe500
commit
98073dd40c
1 changed files with 168 additions and 214 deletions
|
|
@ -1,30 +1,94 @@
|
|||
use crate::*;
|
||||
use std::iter::Iterator;
|
||||
/// Any numeric type that represents time
|
||||
pub trait TimeUnit: Copy + Display + PartialOrd + PartialEq
|
||||
+ Add<Self, Output=Self> + Mul<Self, Output=Self>
|
||||
+ Div<Self, Output=Self> + Rem<Self, Output=Self> {}
|
||||
impl<T> TimeUnit for T where T: Copy + Display + PartialOrd + PartialEq
|
||||
+ Add<Self, Output=Self> + Mul<Self, Output=Self>
|
||||
+ Div<Self, Output=Self> + Rem<Self, Output=Self> {}
|
||||
/// Integer time unit, such as samples, pulses, or microseconds
|
||||
pub trait TimeInteger: TimeUnit + From<usize> + Into<usize> + Copy {}
|
||||
impl<T> TimeInteger for T where T: TimeUnit + From<usize> + Into<usize> + Copy {}
|
||||
/// Floating time unit, such as beats or seconds
|
||||
pub trait TimeFloat: TimeUnit + From<f64> + Into<f64> + Copy {}
|
||||
impl<T> TimeFloat for T where T: TimeUnit + From<f64> + Into<f64> + Copy {}
|
||||
/// Defines global temporal resolutions.
|
||||
#[derive(Debug)] pub struct Timebase {
|
||||
|
||||
pub const DEFAULT_PPQ: f64 = 96.0;
|
||||
|
||||
/// The unit of time, an atomic 64-bit float.
|
||||
///
|
||||
/// According to https://stackoverflow.com/a/873367, as per IEEE754,
|
||||
/// every integer between 1 and 2^53 can be represented exactly.
|
||||
/// This should mean that, even at 192kHz sampling rate, over 1 year of audio
|
||||
/// can be clocked in microseconds with f64 without losing precision.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TimeUnit(AtomicF64);
|
||||
/// Temporal resolutions: sample rate, tempo, MIDI pulses per quaver (beat)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Timebase {
|
||||
/// Audio samples per second
|
||||
sr: AtomicF64,
|
||||
pub sr: TimeUnit,
|
||||
/// MIDI beats per minute
|
||||
bpm: AtomicF64,
|
||||
pub bpm: TimeUnit,
|
||||
/// MIDI ticks per beat
|
||||
ppq: AtomicF64,
|
||||
pub ppq: TimeUnit,
|
||||
}
|
||||
/// A point in time in all time scales (microsecond, sample, MIDI pulse)
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Instant {
|
||||
pub timebase: Arc<Timebase>,
|
||||
/// Current time in microseconds
|
||||
pub usec: TimeUnit,
|
||||
/// Current time in audio samples
|
||||
pub sample: TimeUnit,
|
||||
/// Current time in MIDI pulses
|
||||
pub pulse: TimeUnit,
|
||||
}
|
||||
impl From<f64> for TimeUnit {
|
||||
fn from (value: f64) -> Self { Self(value.into()) }
|
||||
}
|
||||
impl From<usize> for TimeUnit {
|
||||
fn from (value: usize) -> Self { Self((value as f64).into()) }
|
||||
}
|
||||
pub trait TimeUnitMethods<T> {
|
||||
fn get (&self) -> T;
|
||||
fn set (&self, value: T);
|
||||
}
|
||||
impl TimeUnitMethods<f64> for TimeUnit {
|
||||
fn get (&self) -> f64 { self.0.load(Ordering::Relaxed) }
|
||||
fn set (&self, value: f64) { self.0.store(value, Ordering::Relaxed) }
|
||||
}
|
||||
impl TimeUnitMethods<usize> for TimeUnit {
|
||||
fn get (&self) -> usize { self.0.load(Ordering::Relaxed) as usize }
|
||||
fn set (&self, value: usize) { self.0.store(value as f64, Ordering::Relaxed) }
|
||||
}
|
||||
impl Clone for TimeUnit {
|
||||
fn clone (&self) -> Self { Self(TimeUnitMethods::<f64>::get(self).into()) }
|
||||
}
|
||||
macro_rules! impl_op {
|
||||
($Op:ident, $method:ident, |$a:ident,$b:ident|{$impl:expr}) => {
|
||||
impl $Op<Self> for TimeUnit {
|
||||
type Output = Self;
|
||||
#[inline] fn $method (self, other: Self) -> Self {
|
||||
let $a = TimeUnitMethods::<f64>::get(&self);
|
||||
let $b = TimeUnitMethods::<f64>::get(&other);
|
||||
Self($impl.into())
|
||||
}
|
||||
}
|
||||
impl $Op<usize> for TimeUnit {
|
||||
type Output = Self;
|
||||
#[inline] fn $method (self, other: usize) -> Self {
|
||||
let $a = TimeUnitMethods::<f64>::get(&self);
|
||||
let $b = other as f64;
|
||||
Self($impl.into())
|
||||
}
|
||||
}
|
||||
impl $Op<f64> for TimeUnit {
|
||||
type Output = Self;
|
||||
#[inline] fn $method (self, other: f64) -> Self {
|
||||
let $a = TimeUnitMethods::<f64>::get(&self);
|
||||
let $b = other;
|
||||
Self($impl.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl_op!(Add, add, |a, b|{a + b});
|
||||
impl_op!(Sub, sub, |a, b|{a - b});
|
||||
impl_op!(Mul, mul, |a, b|{a * b});
|
||||
impl_op!(Div, div, |a, b|{a / b});
|
||||
impl_op!(Rem, rem, |a, b|{a % b});
|
||||
impl Timebase {
|
||||
/// Specify sample rate, BPM and PPQ
|
||||
pub fn new (s: impl Into<AtomicF64>, b: impl Into<AtomicF64>, p: impl Into<AtomicF64>) -> Self {
|
||||
pub fn new (s: impl Into<TimeUnit>, b: impl Into<TimeUnit>, p: impl Into<TimeUnit>) -> Self {
|
||||
Self { sr: s.into(), bpm: b.into(), ppq: p.into() }
|
||||
}
|
||||
/// Iterate over ticks between start and end.
|
||||
|
|
@ -32,16 +96,7 @@ impl Timebase {
|
|||
TicksIterator { fpt: self.samples_per_pulse(), sample: start, start, end }
|
||||
}
|
||||
}
|
||||
/// Represents a point in time in all scales
|
||||
#[derive(Debug, Default)] pub struct Instant {
|
||||
pub timebase: Arc<Timebase>,
|
||||
/// Current time in microseconds
|
||||
usec: AtomicUsize,
|
||||
/// Current time in audio samples
|
||||
sample: AtomicUsize,
|
||||
/// Current time in MIDI pulses
|
||||
pulse: AtomicF64,
|
||||
}
|
||||
impl Default for Timebase { fn default () -> Self { Self::new(48000f64, 150f64, 96f64) } }
|
||||
impl Instant {
|
||||
pub fn from_usec (timebase: &Arc<Timebase>, usec: usize) -> Self {
|
||||
Self {
|
||||
|
|
@ -68,22 +123,22 @@ impl Instant {
|
|||
}
|
||||
}
|
||||
pub fn update_from_usec (&self, usec: usize) {
|
||||
self.set_usec(usec);
|
||||
self.set_pulse(self.timebase.usecs_to_pulse(usec as f64));
|
||||
self.set_sample(self.timebase.usecs_to_sample(usec as f64) as usize);
|
||||
self.usec.set(usec);
|
||||
self.pulse.set(self.timebase.usecs_to_pulse(usec as f64));
|
||||
self.sample.set(self.timebase.usecs_to_sample(usec as f64));
|
||||
}
|
||||
pub fn update_from_sample (&self, sample: usize) {
|
||||
self.set_usec(self.timebase.samples_to_usec(sample as f64) as usize);
|
||||
self.set_pulse(self.timebase.samples_to_pulse(sample as f64));
|
||||
self.set_sample(sample);
|
||||
self.usec.set(self.timebase.samples_to_usec(sample as f64));
|
||||
self.pulse.set(self.timebase.samples_to_pulse(sample as f64));
|
||||
self.sample.set(sample);
|
||||
}
|
||||
pub fn update_from_pulse (&self, pulse: f64) {
|
||||
self.set_usec(self.timebase.pulses_to_usec(pulse) as usize);
|
||||
self.set_pulse(pulse);
|
||||
self.set_sample(self.timebase.pulses_to_sample(pulse) as usize);
|
||||
self.usec.set(self.timebase.pulses_to_usec(pulse));
|
||||
self.pulse.set(pulse);
|
||||
self.sample.set(self.timebase.pulses_to_sample(pulse));
|
||||
}
|
||||
pub fn format_beat (&self) -> String {
|
||||
self.format_beats_float(self.pulse())
|
||||
self.format_beats(self.pulse().get())
|
||||
}
|
||||
}
|
||||
/// Iterator that emits subsequent ticks within a range.
|
||||
|
|
@ -114,229 +169,128 @@ impl Iterator for TicksIterator {
|
|||
}
|
||||
}
|
||||
}
|
||||
/// Trait for struct that defines a sample rate in hertz (samples per second)
|
||||
pub trait SampleRate<U: TimeUnit> {
|
||||
/// Something 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);
|
||||
fn sr (&self) -> &TimeUnit;
|
||||
/// 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())
|
||||
#[inline] fn usec_per_sample (&self) -> TimeUnit {
|
||||
TimeUnit::from(1_000_000f64) / self.sr().get()
|
||||
}
|
||||
/// Return the duration of a sample in microseconds (floating)
|
||||
#[inline] fn sample_per_usec (&self) -> U where U: TimeFloat {
|
||||
U::from(self.sr().into() / 1_000_000f64)
|
||||
#[inline] fn sample_per_usec (&self) -> TimeUnit {
|
||||
TimeUnit::from(1_000_000f64) / self.sr().get()
|
||||
}
|
||||
/// 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()
|
||||
#[inline] fn samples_to_usec (&self, samples: impl Into<TimeUnit>) -> f64 {
|
||||
samples.into() * self.usec_per_sample()
|
||||
}
|
||||
/// Convert a number of microseconds to samples (floating)
|
||||
#[inline] fn usecs_to_sample (&self, usecs: U) -> U where U: TimeFloat, Self: SampleRate<U> {
|
||||
usecs * self.sample_per_usec()
|
||||
#[inline] fn usecs_to_sample (&self, usecs: impl Into<TimeUnit>) -> f64 {
|
||||
usecs.into() * self.sample_per_usec()
|
||||
}
|
||||
}
|
||||
impl SampleRate<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 SampleRate<f64> for Instant {
|
||||
#[inline] fn sr (&self) -> f64 { self.timebase.sr() }
|
||||
#[inline] fn set_sr (&self, sr: f64) { self.timebase.set_sr(sr); }
|
||||
}
|
||||
/// Trait for struct that defines a tempo in beats per minute
|
||||
pub trait BeatsPerMinute<U: TimeFloat> {
|
||||
impl SampleRate for Timebase { #[inline] fn sr (&self) -> &TimeUnit { &self.sr } }
|
||||
impl SampleRate for Instant { #[inline] fn sr (&self) -> &TimeUnit { self.timebase.sr() } }
|
||||
/// Something 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);
|
||||
fn bpm (&self) -> &TimeUnit;
|
||||
/// Return the duration fo a beat in microseconds
|
||||
#[inline] fn usec_per_beat (&self) -> U {
|
||||
U::from(60_000_000f64) / self.bpm()
|
||||
}
|
||||
#[inline] fn usec_per_beat (&self) -> f64 { 60_000_000f64 / self.bpm().get() }
|
||||
/// Return the number of beats in a second
|
||||
#[inline] fn beat_per_second (&self) -> U {
|
||||
self.bpm() / U::from(60_000_000f64)
|
||||
}
|
||||
#[inline] fn beat_per_second (&self) -> f64 { self.bpm().get() / 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
|
||||
#[inline] fn note_to_usec (&self, (num, den): (f64, f64)) -> f64 {
|
||||
4.0 * self.usec_per_beat() * num / den
|
||||
}
|
||||
/// Return the number of samples corresponding to a note of the given duration
|
||||
#[inline] fn note_to_samples (&self, note: (U, U)) -> U where Self: SampleRate<U> {
|
||||
#[inline] fn note_to_samples (&self, note: (f64, f64)) -> TimeUnit {
|
||||
self.usec_to_sample(self.note_to_usec(note))
|
||||
}
|
||||
/// Return the number of samples corresponding to the given number of microseconds
|
||||
#[inline] fn usec_to_sample (&self, usec: U) -> U where Self: SampleRate<U> {
|
||||
usec * self.sr() / U::from(1000f64)
|
||||
#[inline] fn usec_to_sample (&self, usec: f64) -> TimeUnit {
|
||||
usec * self.sr() / TimeUnit::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) {
|
||||
#[inline] fn quantize (&self, step: (f64, f64), time: f64) -> (f64, f64) {
|
||||
let step = self.note_to_usec(step);
|
||||
(time / step, time % step)
|
||||
}
|
||||
/// Quantize a collection of events
|
||||
#[inline] fn quantize_into <E: Iterator<Item=(U, U)> + Sized, T> (
|
||||
&self, step: (U, U), events: E
|
||||
) -> Vec<(U, U)> {
|
||||
#[inline] fn quantize_into <E: Iterator<Item=(TimeUnit, TimeUnit)> + Sized, T> (
|
||||
&self, step: (TimeUnit, TimeUnit), events: E
|
||||
) -> Vec<(TimeUnit, TimeUnit)> {
|
||||
events.map(|(time, event)|(self.quantize(step, time).0, event)).collect()
|
||||
}
|
||||
}
|
||||
impl BeatsPerMinute<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 BeatsPerMinute<f64> for Instant {
|
||||
#[inline] fn bpm (&self) -> f64 { self.timebase.bpm() }
|
||||
#[inline] fn set_bpm (&self, bpm: f64) { self.timebase.set_bpm(bpm); }
|
||||
}
|
||||
/// Trait for struct that defines a MIDI resolution in pulses per quaver (beat)
|
||||
pub trait PulsesPerQuaver<U: TimeUnit> {
|
||||
const DEFAULT_PPQ: U;
|
||||
/// Get the PPQ
|
||||
fn ppq (&self) -> U;
|
||||
/// Set the PPQ
|
||||
fn set_ppq (&self, ppq: U);
|
||||
impl BeatsPerMinute for Timebase { #[inline] fn bpm (&self) -> &TimeUnit { &self.bpm } }
|
||||
impl BeatsPerMinute for Instant { #[inline] fn bpm (&self) -> &TimeUnit { self.timebase.bpm() } }
|
||||
/// Something that defines a MIDI resolution in pulses per quaver (beat)
|
||||
pub trait PulsesPerQuaver {
|
||||
// Get the PPQ
|
||||
fn ppq (&self) -> &TimeUnit;
|
||||
/// Return duration of a pulse in microseconds (BPM-dependent)
|
||||
#[inline] fn pulse_per_usec (&self) -> U
|
||||
where U: TimeFloat, Self: BeatsPerMinute<U>
|
||||
{
|
||||
self.ppq() / self.usec_per_beat()
|
||||
}
|
||||
#[inline] fn pulse_per_usec (&self) -> f64 { self.ppq().get() / self.usec_per_beat() }
|
||||
/// Return duration of a pulse in microseconds (BPM-dependent)
|
||||
#[inline] fn usec_per_pulse (&self) -> U
|
||||
where U: TimeFloat, Self: BeatsPerMinute<U>
|
||||
{
|
||||
self.usec_per_beat() / self.ppq()
|
||||
}
|
||||
#[inline] fn usec_per_pulse (&self) -> f64 { self.usec_per_beat() / self.ppq().get() }
|
||||
/// Return number of pulses to which a number of microseconds corresponds (BPM-dependent)
|
||||
#[inline] fn usecs_to_pulse (&self, usec: U) -> U
|
||||
where U: TimeFloat, Self: BeatsPerMinute<U>
|
||||
{
|
||||
usec * self.pulse_per_usec()
|
||||
}
|
||||
#[inline] fn usecs_to_pulse (&self, usec: f64) -> f64 { usec * self.pulse_per_usec() }
|
||||
/// Convert a number of pulses to a sample number (SR- and BPM-dependent)
|
||||
#[inline] fn pulses_to_usec (&self, pulse: U) -> U
|
||||
where U: TimeFloat, Self: SampleRate<U> + BeatsPerMinute<U>
|
||||
{
|
||||
pulse / self.usec_per_pulse()
|
||||
}
|
||||
#[inline] fn pulses_to_usec (&self, pulse: f64) -> f64 { pulse / self.usec_per_pulse() }
|
||||
/// Return number of pulses in a second (BPM-dependent)
|
||||
#[inline] fn pulses_per_second (&self) -> U
|
||||
where U: TimeFloat, Self: BeatsPerMinute<U>
|
||||
{
|
||||
self.beat_per_second() * self.ppq()
|
||||
}
|
||||
#[inline] fn pulses_per_second (&self) -> f64 { self.beat_per_second() * self.ppq().get() }
|
||||
/// 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<U> + BeatsPerMinute<U>
|
||||
{
|
||||
self.usec_per_pulse() / self.usec_per_sample()
|
||||
}
|
||||
#[inline] fn pulses_per_sample (&self) -> f64 { 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<U> + BeatsPerMinute<U>
|
||||
{
|
||||
self.pulses_per_sample() * p
|
||||
}
|
||||
#[inline] fn pulses_to_sample (&self, p: f64) -> f64 { 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<U> + BeatsPerMinute<U>
|
||||
{
|
||||
s / self.pulses_per_sample()
|
||||
}
|
||||
#[inline] fn samples_to_pulse (&self, s: f64) -> f64 { 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<U> + BeatsPerMinute<U>
|
||||
{
|
||||
self.sr() / self.pulses_per_second()
|
||||
}
|
||||
#[inline] fn format_beats_int (&self, pulse: U) -> String where U: TimeInteger {
|
||||
let ppq = self.ppq();
|
||||
let (beats, pulses) = if ppq > U::from(0) {
|
||||
(pulse / ppq, pulse % ppq)
|
||||
} else {
|
||||
(U::from(0), U::from(0))
|
||||
};
|
||||
let bars = (beats / U::from(4)) + U::from(1);
|
||||
let beats = (beats % U::from(4)) + U::from(1);
|
||||
format!("{bars}.{beats}.{pulses:02}")
|
||||
}
|
||||
#[inline] fn format_beats_float (&self, pulse: U) -> String where U: TimeFloat {
|
||||
let ppq = self.ppq();
|
||||
let (beats, pulses) = if ppq > U::from(0.) {
|
||||
(pulse / ppq, pulse % ppq)
|
||||
} else {
|
||||
(U::from(0.), U::from(0.))
|
||||
};
|
||||
let bars = (beats / U::from(4.)) + U::from(1.);
|
||||
let beats = (beats % U::from(4.)) + U::from(1.);
|
||||
#[inline] fn samples_per_pulse (&self) -> f64 { self.sr() / self.pulses_per_second() }
|
||||
/// Format a number of pulses into Beat.Bar.Pulse
|
||||
#[inline] fn format_beats (&self, pulse: f64) -> String {
|
||||
let ppq: usize = self.ppq().get();
|
||||
let (beats, pulses) = if ppq > 0 { (pulse / ppq, pulse % ppq) } else { (0, 0) };
|
||||
let bars = ((beats / 4) + 1) as usize;
|
||||
let beats = ((beats % 4) + 1) as usize;
|
||||
format!("{bars}.{beats}.{pulses:02}")
|
||||
}
|
||||
}
|
||||
impl PulsesPerQuaver<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 PulsesPerQuaver<f64> for Instant {
|
||||
const DEFAULT_PPQ: f64 = 96f64;
|
||||
#[inline] fn ppq (&self) -> f64 { self.timebase.ppq() }
|
||||
#[inline] fn set_ppq (&self, ppq: f64) { self.timebase.set_ppq(ppq); }
|
||||
}
|
||||
pub trait SamplePosition<U: TimeUnit> {
|
||||
fn sample (&self) -> U;
|
||||
fn set_sample (&self, sample: U);
|
||||
}
|
||||
impl SamplePosition<usize> for Instant {
|
||||
#[inline] fn sample (&self) -> usize { self.sample.load(Ordering::Relaxed) }
|
||||
#[inline] fn set_sample (&self, s: usize) { self.sample.store(s, Ordering::Relaxed); }
|
||||
}
|
||||
pub trait PulsePosition<U: TimeUnit> {
|
||||
fn pulse (&self) -> U;
|
||||
fn set_pulse (&self, pulse: U);
|
||||
}
|
||||
impl PulsePosition<f64> for Instant {
|
||||
#[inline] fn pulse (&self) -> f64 { self.pulse.load(Ordering::Relaxed) }
|
||||
#[inline] fn set_pulse (&self, p: f64) { self.pulse.store(p, Ordering::Relaxed); }
|
||||
}
|
||||
impl PulsePosition<usize> for Instant {
|
||||
#[inline] fn pulse (&self) -> usize { self.pulse.load(Ordering::Relaxed) as usize }
|
||||
#[inline] fn set_pulse (&self, p: usize) { PulsePosition::<f64>::set_pulse(self, p as f64) }
|
||||
}
|
||||
pub trait UsecPosition<U: TimeUnit> {
|
||||
fn usec (&self) -> U;
|
||||
fn set_usec (&self, usec: U);
|
||||
#[inline] fn format_current_usec (&self) -> String where U: From<usize> {
|
||||
let usecs = self.usec();
|
||||
let (seconds, msecs) = (usecs / U::from(1000000), usecs / U::from(1000) % U::from(1000));
|
||||
let (minutes, seconds) = (seconds / U::from(60), seconds % U::from(60));
|
||||
impl PulsesPerQuaver for Timebase { #[inline] fn ppq (&self) -> &TimeUnit { &self.ppq } }
|
||||
impl PulsesPerQuaver for Instant { #[inline] fn ppq (&self) -> &TimeUnit { self.timebase.ppq() } }
|
||||
/// Something that refers to a point in time in samples
|
||||
pub trait SamplePosition { fn sample (&self) -> &TimeUnit; }
|
||||
impl SamplePosition for Instant { #[inline] fn sample (&self) -> &TimeUnit { &self.sample } }
|
||||
/// Something that refers to a point in time in MIDI pulses
|
||||
pub trait PulsePosition { fn pulse (&self) -> &TimeUnit; }
|
||||
impl PulsePosition for Instant { #[inline] fn pulse (&self) -> &TimeUnit { &self.pulse } }
|
||||
/// Something that refers to a point in time in microseconds
|
||||
pub trait UsecPosition {
|
||||
fn usec (&self) -> &TimeUnit;
|
||||
#[inline] fn format_current_usec (&self) -> String {
|
||||
let usecs: usize = self.usec().get();
|
||||
let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000);
|
||||
let (minutes, seconds) = (seconds / 60, seconds % 60);
|
||||
format!("{minutes}:{seconds:02}:{msecs:03}")
|
||||
}
|
||||
}
|
||||
impl UsecPosition<usize> for Instant {
|
||||
#[inline] fn usec (&self) -> usize { self.usec.load(Ordering::Relaxed) }
|
||||
#[inline] fn set_usec (&self, u: usize) { self.usec.store(u, Ordering::Relaxed); }
|
||||
}
|
||||
pub trait LaunchSync<U: TimeUnit> {
|
||||
fn sync (&self) -> U;
|
||||
fn set_sync (&self, sync: U);
|
||||
#[inline] fn next_launch_pulse (&self) -> U where U: TimeInteger, Self: PulsePosition<U> {
|
||||
let sync = self.sync();
|
||||
let pulse = self.pulse();
|
||||
if pulse % sync == U::from(0) {
|
||||
impl UsecPosition for Instant { #[inline] fn usec (&self) -> &TimeUnit { &self.usec } }
|
||||
/// Something that defines launch quantization
|
||||
pub trait LaunchSync {
|
||||
fn sync (&self) -> &TimeUnit;
|
||||
#[inline] fn next_launch_pulse (&self) -> f64 where Self: PulsePosition {
|
||||
let sync: f64 = self.sync().get();
|
||||
let pulse = self.pulse().get();
|
||||
if pulse % sync == 0. {
|
||||
pulse
|
||||
} else {
|
||||
(pulse / sync + U::from(1)) * sync
|
||||
(pulse / sync + 1.) * sync
|
||||
}
|
||||
}
|
||||
}
|
||||
pub trait Quantize<T> {
|
||||
fn quant (&self) -> T;
|
||||
fn set_quant (&self, quant: T);
|
||||
}
|
||||
impl Default for Timebase { fn default () -> Self { Self::new(48000f64, 150f64, 96f64) } }
|
||||
/// Something that defines note quantization
|
||||
pub trait Quantize<T> { fn quant (&self) -> &TimeUnit; }
|
||||
/// (pulses, name), assuming 96 PPQ
|
||||
pub const NOTE_DURATIONS: [(usize, &str);26] = [
|
||||
(1, "1/384"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue