mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +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 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue