refactor: atomic timebase

This commit is contained in:
🪞👃🪞 2024-06-20 19:15:26 +03:00
parent 87c5e47b43
commit f77c84a99c
5 changed files with 116 additions and 99 deletions

View file

@ -1,10 +1,12 @@
use crate::prelude::*;
pub struct Timebase {
/// Frames per second
pub rate: Option<usize>,
pub rate: AtomicUsize,
/// Beats per minute
pub tempo: Option<usize>,
pub tempo: AtomicUsize,
/// Ticks per beat
pub ppq: usize,
pub ppq: AtomicUsize,
}
enum QuantizeMode {
@ -24,22 +26,32 @@ struct Loop<T> {
/// NoteDuration in musical terms. Has definite usec value
/// for given bpm and sample rate.
pub enum NoteDuration {
Nth(u16, u16),
Nth(usize, usize),
Dotted(Box<Self>),
Tuplet(u16, Box<Self>),
Tuplet(usize, Box<Self>),
}
impl Timebase {
pub fn rate (&self) -> usize {
self.rate.load(Ordering::Relaxed)
}
pub fn tempo (&self) -> usize {
self.tempo.load(Ordering::Relaxed)
}
pub fn ppq (&self) -> usize {
self.ppq.load(Ordering::Relaxed)
}
/// Beats per second
#[inline] fn bps (&self) -> f64 {
self.tempo.0 as f64 / 60000.0
self.tempo() as f64 / 60000.0
}
/// Frames per second
#[inline] fn fps (&self) -> u64 {
self.rate.0 as u64
#[inline] fn fps (&self) -> usize {
self.rate()
}
/// Frames per beat
#[inline] fn fpb (&self) -> f64 {
#[inline] pub fn fpb (&self) -> f64 {
self.fps() as f64 / self.bps()
}
/// Frames per tick FIXME double times
@ -47,19 +59,18 @@ impl Timebase {
self.fps() as f64 / self.tps()
}
/// Frames per loop
#[inline] fn fpl (&self) -> f64 {
self.fpb() * self.steps as f64 / self.steps_per_beat as f64
#[inline] fn fpl (&self, steps: f64, steps_per_beat: f64) -> f64 {
self.fpb() * steps / steps_per_beat
}
/// Ticks per beat
#[inline] fn tpb (&self) -> f64 {
self.ticks_per_beat as f64
self.ppq.load(Ordering::Relaxed) as f64
}
/// Ticks per second
#[inline] fn tps (&self) -> f64 {
self.bps() * self.tpb()
}
fn frames_to_ticks (&self, start: u64, end: u64) -> Vec<(u64, u64)> {
let fpl = self.fpl() as u64;
pub fn frames_to_ticks (&self, start: usize, end: usize, fpl: usize) -> Vec<(usize, usize)> {
let start_frame = start % fpl;
let end_frame = end % fpl;
let fpt = self.fpt();
@ -69,7 +80,7 @@ impl Timebase {
let last_jitter = (frame - 1.0).max(0.0) % fpt;
let next_jitter = frame + 1.0 % fpt;
if jitter <= last_jitter && jitter <= next_jitter {
ticks.push((frame as u64 % (end-start), (frame / fpt) as u64));
ticks.push((frame as usize % (end-start), (frame / fpt) as usize));
};
};
if start_frame < end_frame {
@ -99,11 +110,11 @@ impl Timebase {
#[inline]
pub fn frame_to_usec (&self, frame: usize) -> usize {
frame * 1000000 / self.rate
frame * 1000000 / self.rate()
}
#[inline]
pub fn usec_to_frame (&self, usec: usize) -> usize {
frame * self.rate / 1000
usec * self.rate() / 1000
}
#[inline]
pub fn usec_per_bar (&self, beats_per_bar: usize) -> usize {
@ -111,7 +122,7 @@ impl Timebase {
}
#[inline]
pub fn usec_per_beat (&self) -> usize {
60_000_000_000 / self.tempo
60_000_000_000 / self.tempo()
}
#[inline]
pub fn usec_per_step (&self, divisor: usize) -> usize {
@ -119,7 +130,7 @@ impl Timebase {
}
#[inline]
pub fn usec_per_tick (&self) -> usize {
self.usec_per_beat() / self.ppq
self.usec_per_beat() / self.ppq()
}
#[inline]
pub fn usec_per_note (&self, note: &NoteDuration) -> usize {
@ -132,6 +143,10 @@ impl Timebase {
self.usec_per_note(note) * 2 / *n,
}
}
#[inline]
pub fn frame_per_note (&self, note: &NoteDuration) -> usize {
self.usec_to_frame(self.usec_per_note(note))
}
pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) {
let step = self.usec_per_note(step);
let time = time / step;
@ -146,19 +161,3 @@ impl Timebase {
.collect()
}
}
impl NoteDuration {
fn to_usec (&self, bpm: &Tempo) -> Usec {
Usec(match self {
Self::Nth(time, flies) =>
bpm.usec_per_beat().0 * *time as usize / *flies as usize,
Self::Dotted(duration) =>
duration.to_usec(bpm).0 * 3 / 2,
Self::Tuplet(n, duration) =>
duration.to_usec(bpm).0 * 2 / *n as usize,
})
}
fn to_frame (&self, bpm: &Tempo, rate: &Hz) -> Frame {
self.to_usec(bpm).to_frame(rate)
}
}