mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
separate TimeInteger/TimeFloat
This commit is contained in:
parent
89dcc2afe2
commit
063706017e
4 changed files with 91 additions and 59 deletions
|
|
@ -1,113 +1,148 @@
|
|||
use crate::*;
|
||||
use std::iter::Iterator;
|
||||
|
||||
pub trait TimeUnit: Mul<Self, Output=Self> + Div<Self, Output=Self>
|
||||
+ Rem<Self, Output=Self> + From<f64> + Copy {}
|
||||
impl<T> TimeUnit for T where T: Mul<Self, Output=Self> + Div<Self, Output=Self>
|
||||
+ Rem<Self, Output=Self> + From<f64> + Copy {}
|
||||
/// Any numeric type that represents time
|
||||
pub trait TimeUnit: PartialEq + Copy
|
||||
+ Add<Self, Output=Self> + Mul<Self, Output=Self>
|
||||
+ 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)
|
||||
pub trait SampleRate<T: TimeUnit> {
|
||||
pub trait SampleRate<U: TimeUnit> {
|
||||
/// Get the sample rate
|
||||
fn sr (&self) -> T;
|
||||
fn sr (&self) -> U;
|
||||
/// Set the sample rate
|
||||
fn set_sr (&self, sr: T);
|
||||
/// Return the duration of a sample in microseconds
|
||||
#[inline] fn usec_per_sample (&self) -> T { T::from(1_000_000f64) / self.sr() }
|
||||
/// Convert a number of samples to microseconds
|
||||
#[inline] fn samples_to_usec (&self, samples: T) -> T { samples * self.usec_per_sample() }
|
||||
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<T: TimeUnit> {
|
||||
pub trait BeatsPerMinute<U: TimeFloat> {
|
||||
/// Get the tempo
|
||||
fn bpm (&self) -> T;
|
||||
fn bpm (&self) -> U;
|
||||
/// Set the tempo
|
||||
fn set_bpm (&self, bpm: T);
|
||||
fn set_bpm (&self, bpm: U);
|
||||
/// 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
|
||||
#[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
|
||||
#[inline] fn note_to_usec (&self, (num, den): (T, T)) -> T {
|
||||
T::from(4.0) * self.usec_per_beat() * num / den
|
||||
#[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: (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))
|
||||
}
|
||||
/// 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> {
|
||||
usec * self.sr() / T::from(1000f64)
|
||||
#[inline] fn usec_to_frame (&self, usec: U) -> U where Self: SampleRate<U> {
|
||||
usec * self.sr() / U::from(1000f64)
|
||||
}
|
||||
/// 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);
|
||||
(time / step, time % step)
|
||||
}
|
||||
/// Quantize a collection of events
|
||||
#[inline] fn quantize_into <E: Iterator<Item=(T, U)> + Sized, U> (
|
||||
&self, step: (T, T), events: E
|
||||
) -> Vec<(T, U)> {
|
||||
let step = (step.0.into(), step.1.into());
|
||||
let quantize = |(time, event)|(self.quantize(step, time).0, event);
|
||||
events.map(quantize).collect()
|
||||
#[inline] fn quantize_into <E: Iterator<Item=(U, U)> + 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<T: TimeUnit> {
|
||||
const DEFAULT_PPQ: T;
|
||||
pub trait PulsesPerQuaver<U: TimeUnit> {
|
||||
const DEFAULT_PPQ: U;
|
||||
/// Get the PPQ
|
||||
fn ppq (&self) -> T;
|
||||
fn ppq (&self) -> U;
|
||||
/// Set the PPQ
|
||||
fn set_ppq (&self, ppq: T);
|
||||
fn set_ppq (&self, ppq: U);
|
||||
/// 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()
|
||||
}
|
||||
/// 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()
|
||||
}
|
||||
/// 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()
|
||||
}
|
||||
/// 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
|
||||
}
|
||||
/// 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()
|
||||
}
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FramePosition<T> {
|
||||
fn frame (&self) -> T;
|
||||
fn set_frame (&self, frame: T);
|
||||
pub trait FramePosition<U: TimeUnit> {
|
||||
fn frame (&self) -> U;
|
||||
fn set_frame (&self, frame: U);
|
||||
}
|
||||
|
||||
pub trait PulsePosition<T> {
|
||||
fn pulse (&self) -> T;
|
||||
fn set_pulse (&self, pulse: T);
|
||||
pub trait PulsePosition<U: TimeUnit> {
|
||||
fn pulse (&self) -> U;
|
||||
fn set_pulse (&self, pulse: U);
|
||||
}
|
||||
|
||||
pub trait UsecPosition<T> {
|
||||
fn usec (&self) -> T;
|
||||
fn set_usec (&self, usec: T);
|
||||
pub trait UsecPosition<U: TimeUnit> {
|
||||
fn usec (&self) -> U;
|
||||
fn set_usec (&self, usec: U);
|
||||
}
|
||||
|
||||
pub trait LaunchSync<T> {
|
||||
fn sync (&self) -> T;
|
||||
fn set_sync (&self, sync: T);
|
||||
#[inline] fn next_launch (&self) -> T where Self: FramePosition<T> {
|
||||
todo!("next_launch")
|
||||
pub trait LaunchSync<U: TimeUnit> {
|
||||
fn sync (&self) -> U;
|
||||
fn set_sync (&self, sync: U);
|
||||
#[inline] fn next_launch_frame (&self) -> U where U: TimeInteger, Self: FramePosition<U> {
|
||||
let sync = self.sync();
|
||||
let frame = self.frame();
|
||||
if frame % sync == U::from(0) {
|
||||
frame
|
||||
} else {
|
||||
(frame / sync) * sync + U::from(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,12 +237,12 @@ impl<E: Engine> Arrangement<E> {
|
|||
match self.selected {
|
||||
ArrangementFocus::Scene(s) => {
|
||||
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());
|
||||
}
|
||||
},
|
||||
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());
|
||||
},
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -173,8 +173,8 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
|||
.flatten()
|
||||
.map(|t|format!("▎{t:>}"))
|
||||
.unwrap_or(String::from("▎"));
|
||||
let time2 = track.player.switch_at.as_ref()
|
||||
.map(|t|format!("▎{:>}", t.load(Ordering::Relaxed)))
|
||||
let time2 = track.player.next_phrase.as_ref()
|
||||
.map(|(t, _)|format!("▎{:>}", t.load(Ordering::Relaxed)))
|
||||
.unwrap_or(String::from("▎"));
|
||||
col!(name, time1, time2)
|
||||
.min_xy(w as u16, title_h)
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@ pub struct PhrasePlayer<E: Engine> {
|
|||
pub phrase: Option<(AtomicUsize, Option<Arc<RwLock<Phrase>>>)>,
|
||||
/// Start time and next 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.
|
||||
pub monitoring: bool,
|
||||
/// Write input to sequence.
|
||||
|
|
@ -334,7 +332,6 @@ impl<E: Engine> PhrasePlayer<E> {
|
|||
clock: clock.clone(),
|
||||
phrase: None,
|
||||
next_phrase: None,
|
||||
switch_at: None,
|
||||
notes_in: Arc::new(RwLock::new([false;128])),
|
||||
notes_out: Arc::new(RwLock::new([false;128])),
|
||||
monitoring: false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue