separate TimeInteger/TimeFloat

This commit is contained in:
🪞👃🪞 2024-10-26 20:07:36 +03:00
parent 89dcc2afe2
commit 063706017e
4 changed files with 91 additions and 59 deletions

View file

@ -1,113 +1,148 @@
use crate::*; use crate::*;
use std::iter::Iterator; use std::iter::Iterator;
pub trait TimeUnit: Mul<Self, Output=Self> + Div<Self, Output=Self> /// Any numeric type that represents time
+ Rem<Self, Output=Self> + From<f64> + Copy {} pub trait TimeUnit: PartialEq + Copy
impl<T> TimeUnit for T where T: Mul<Self, Output=Self> + Div<Self, Output=Self> + Add<Self, Output=Self> + Mul<Self, Output=Self>
+ Rem<Self, Output=Self> + From<f64> + Copy {} + Div<Self, Output=Self> + Rem<Self, Output=Self> {}
impl<T> TimeUnit for T where T: PartialEq + Copy
+ Add<Self, Output=Self> + Mul<Self, Output=Self>
+ Div<Self, Output=Self> + Rem<Self, Output=Self> {}
/// Integer time unit, such as frames, 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 {}
/// Trait for struct that defines a sample rate in hertz (samples per second) /// Trait for struct that defines a sample rate in hertz (samples per second)
pub trait SampleRate<T: TimeUnit> { pub trait SampleRate<U: TimeUnit> {
/// Get the sample rate /// Get the sample rate
fn sr (&self) -> T; fn sr (&self) -> U;
/// Set the sample rate /// Set the sample rate
fn set_sr (&self, sr: T); fn set_sr (&self, sr: U);
/// Return the duration of a sample in microseconds /// Return the duration of a sample in microseconds (floating)
#[inline] fn usec_per_sample (&self) -> T { T::from(1_000_000f64) / self.sr() } #[inline] fn usec_per_sample (&self) -> U where U: TimeFloat {
/// Convert a number of samples to microseconds U::from(1_000_000f64 / self.sr().into())
#[inline] fn samples_to_usec (&self, samples: T) -> T { samples * self.usec_per_sample() } }
/// 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 /// Trait for struct that defines a tempo in beats per minute
pub trait BeatsPerMinute<T: TimeUnit> { pub trait BeatsPerMinute<U: TimeFloat> {
/// Get the tempo /// Get the tempo
fn bpm (&self) -> T; fn bpm (&self) -> U;
/// Set the tempo /// Set the tempo
fn set_bpm (&self, bpm: T); fn set_bpm (&self, bpm: U);
/// Return the duration fo a beat in microseconds /// Return the duration fo a beat in microseconds
#[inline] fn usec_per_beat (&self) -> T { T::from(60_000_000f64) / self.bpm() } #[inline] fn usec_per_beat (&self) -> U {
U::from(60_000_000f64) / self.bpm()
}
/// Return the number of beats in a second /// Return the number of beats in a second
#[inline] fn beat_per_second (&self) -> T { self.bpm() / T::from(60_000_000f64) } #[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 /// Return the number of microseconds corresponding to a note of the given duration
#[inline] fn note_to_usec (&self, (num, den): (T, T)) -> T { #[inline] fn note_to_usec (&self, (num, den): (U, U)) -> U {
T::from(4.0) * self.usec_per_beat() * num / den U::from(4.0) * self.usec_per_beat() * num / den
} }
/// Return the number of frames corresponding to a note of the given duration /// Return the number of frames corresponding to a note of the given duration
#[inline] fn note_to_frame (&self, note: (T, T)) -> T where Self: SampleRate<T> { #[inline] fn note_to_frame (&self, note: (U, U)) -> U where Self: SampleRate<U> {
self.usec_to_frame(self.note_to_usec(note)) self.usec_to_frame(self.note_to_usec(note))
} }
/// Return the number of frames corresponding to the given number of microseconds /// Return the number of frames corresponding to the given number of microseconds
#[inline] fn usec_to_frame (&self, usec: T) -> T where Self: SampleRate<T> { #[inline] fn usec_to_frame (&self, usec: U) -> U where Self: SampleRate<U> {
usec * self.sr() / T::from(1000f64) usec * self.sr() / U::from(1000f64)
} }
/// Return the quantized position of a moment in time given a step /// Return the quantized position of a moment in time given a step
#[inline] fn quantize (&self, step: (T, T), time: T) -> (T, T) { #[inline] fn quantize (&self, step: (U, U), time: U) -> (U, U) {
let step = self.note_to_usec(step); let step = self.note_to_usec(step);
(time / step, time % step) (time / step, time % step)
} }
/// Quantize a collection of events /// Quantize a collection of events
#[inline] fn quantize_into <E: Iterator<Item=(T, U)> + Sized, U> ( #[inline] fn quantize_into <E: Iterator<Item=(U, U)> + Sized, T> (
&self, step: (T, T), events: E &self, step: (U, U), events: E
) -> Vec<(T, U)> { ) -> Vec<(U, U)> {
let step = (step.0.into(), step.1.into()); events.map(|(time, event)|(self.quantize(step, time).0, event)).collect()
let quantize = |(time, event)|(self.quantize(step, time).0, event);
events.map(quantize).collect()
} }
} }
/// Trait for struct that defines a MIDI resolution in pulses per quaver (beat) /// Trait for struct that defines a MIDI resolution in pulses per quaver (beat)
pub trait PulsesPerQuaver<T: TimeUnit> { pub trait PulsesPerQuaver<U: TimeUnit> {
const DEFAULT_PPQ: T; const DEFAULT_PPQ: U;
/// Get the PPQ /// Get the PPQ
fn ppq (&self) -> T; fn ppq (&self) -> U;
/// Set the PPQ /// Set the PPQ
fn set_ppq (&self, ppq: T); fn set_ppq (&self, ppq: U);
/// Return duration of a pulse in microseconds (BPM-dependent) /// Return duration of a pulse in microseconds (BPM-dependent)
#[inline] fn usec_per_pulse (&self) -> T where Self: BeatsPerMinute<T> { #[inline] fn usec_per_pulse (&self) -> U
where U: TimeFloat, Self: BeatsPerMinute<U>
{
self.usec_per_beat() / self.ppq() self.usec_per_beat() / self.ppq()
} }
/// Return number of pulses in a second (BPM-dependent) /// Return number of pulses in a second (BPM-dependent)
#[inline] fn pulses_per_second (&self) -> T where Self: BeatsPerMinute<T> { #[inline] fn pulses_per_second (&self) -> U
where U: TimeFloat, Self: BeatsPerMinute<U>
{
self.beat_per_second() * self.ppq() self.beat_per_second() * self.ppq()
} }
/// Return fraction of a pulse to which a sample corresponds (SR- and BPM-dependent) /// Return fraction of a pulse to which a sample corresponds (SR- and BPM-dependent)
#[inline] fn pulses_per_sample (&self) -> T where Self: SampleRate<T> + BeatsPerMinute<T> { #[inline] fn pulses_per_sample (&self) -> U
where U: TimeFloat, Self: SampleRate<U> + BeatsPerMinute<U>
{
self.usec_per_pulse() / self.usec_per_sample() self.usec_per_pulse() / self.usec_per_sample()
} }
/// Convert a number of pulses to a sample number (SR- and BPM-dependent) /// Convert a number of pulses to a sample number (SR- and BPM-dependent)
#[inline] fn pulses_to_sample (&self, p: T) -> T where Self: SampleRate<T> + BeatsPerMinute<T> { #[inline] fn pulses_to_sample (&self, p: U) -> U
where U: TimeFloat, Self: SampleRate<U> + BeatsPerMinute<U>
{
self.pulses_per_sample() * p self.pulses_per_sample() * p
} }
/// Convert a number of samples to a pulse number (SR- and BPM-dependent) /// Convert a number of samples to a pulse number (SR- and BPM-dependent)
#[inline] fn samples_to_pulse (&self, s: T) -> T where Self: SampleRate<T> + BeatsPerMinute<T> { #[inline] fn samples_to_pulse (&self, s: U) -> U
where U: TimeFloat, Self: SampleRate<U> + BeatsPerMinute<U>
{
s / self.pulses_per_sample() s / self.pulses_per_sample()
} }
/// Return number of samples in a pulse (SR- and BPM-dependent) /// Return number of samples in a pulse (SR- and BPM-dependent)
#[inline] fn samples_per_pulse (&self) -> T where Self: SampleRate<T> + BeatsPerMinute<T> { #[inline] fn samples_per_pulse (&self) -> U
where U: TimeFloat, Self: SampleRate<U> + BeatsPerMinute<U>
{
self.sr() / self.pulses_per_second() self.sr() / self.pulses_per_second()
} }
} }
pub trait FramePosition<T> { pub trait FramePosition<U: TimeUnit> {
fn frame (&self) -> T; fn frame (&self) -> U;
fn set_frame (&self, frame: T); fn set_frame (&self, frame: U);
} }
pub trait PulsePosition<T> { pub trait PulsePosition<U: TimeUnit> {
fn pulse (&self) -> T; fn pulse (&self) -> U;
fn set_pulse (&self, pulse: T); fn set_pulse (&self, pulse: U);
} }
pub trait UsecPosition<T> { pub trait UsecPosition<U: TimeUnit> {
fn usec (&self) -> T; fn usec (&self) -> U;
fn set_usec (&self, usec: T); fn set_usec (&self, usec: U);
} }
pub trait LaunchSync<T> { pub trait LaunchSync<U: TimeUnit> {
fn sync (&self) -> T; fn sync (&self) -> U;
fn set_sync (&self, sync: T); fn set_sync (&self, sync: U);
#[inline] fn next_launch (&self) -> T where Self: FramePosition<T> { #[inline] fn next_launch_frame (&self) -> U where U: TimeInteger, Self: FramePosition<U> {
todo!("next_launch") let sync = self.sync();
let frame = self.frame();
if frame % sync == U::from(0) {
frame
} else {
(frame / sync) * sync + U::from(1)
}
} }
} }

View file

@ -237,12 +237,12 @@ impl<E: Engine> Arrangement<E> {
match self.selected { match self.selected {
ArrangementFocus::Scene(s) => { ArrangementFocus::Scene(s) => {
for (t, track) in self.tracks.iter_mut().enumerate() { for (t, track) in self.tracks.iter_mut().enumerate() {
let start = self.clock.next_launch(); let start = self.clock.next_launch_frame();
track.player.enqueue_next(start, self.scenes[s].clips[t].as_ref()); track.player.enqueue_next(start, self.scenes[s].clips[t].as_ref());
} }
}, },
ArrangementFocus::Clip(t, s) => { ArrangementFocus::Clip(t, s) => {
let start = self.clock.next_launch(); let start = self.clock.next_launch_frame();
self.tracks[t].player.enqueue_next(start, self.scenes[s].clips[t].as_ref()); self.tracks[t].player.enqueue_next(start, self.scenes[s].clips[t].as_ref());
}, },
_ => {} _ => {}

View file

@ -173,8 +173,8 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
.flatten() .flatten()
.map(|t|format!("{t:>}")) .map(|t|format!("{t:>}"))
.unwrap_or(String::from("")); .unwrap_or(String::from(""));
let time2 = track.player.switch_at.as_ref() let time2 = track.player.next_phrase.as_ref()
.map(|t|format!("{:>}", t.load(Ordering::Relaxed))) .map(|(t, _)|format!("{:>}", t.load(Ordering::Relaxed)))
.unwrap_or(String::from("")); .unwrap_or(String::from(""));
col!(name, time1, time2) col!(name, time1, time2)
.min_xy(w as u16, title_h) .min_xy(w as u16, title_h)

View file

@ -119,8 +119,6 @@ pub struct PhrasePlayer<E: Engine> {
pub phrase: Option<(AtomicUsize, Option<Arc<RwLock<Phrase>>>)>, pub phrase: Option<(AtomicUsize, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase /// Start time and next phrase
pub next_phrase: Option<(AtomicUsize, Option<Arc<RwLock<Phrase>>>)>, pub next_phrase: Option<(AtomicUsize, Option<Arc<RwLock<Phrase>>>)>,
/// Frames remaining until switch to next phrase
pub switch_at: Option<AtomicUsize>,
/// Play input through output. /// Play input through output.
pub monitoring: bool, pub monitoring: bool,
/// Write input to sequence. /// Write input to sequence.
@ -334,7 +332,6 @@ impl<E: Engine> PhrasePlayer<E> {
clock: clock.clone(), clock: clock.clone(),
phrase: None, phrase: None,
next_phrase: None, next_phrase: None,
switch_at: None,
notes_in: Arc::new(RwLock::new([false;128])), notes_in: Arc::new(RwLock::new([false;128])),
notes_out: Arc::new(RwLock::new([false;128])), notes_out: Arc::new(RwLock::new([false;128])),
monitoring: false, monitoring: false,