From 063706017ecc562c475f67339ab51812a8b6f037 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 26 Oct 2024 20:07:36 +0300 Subject: [PATCH] separate TimeInteger/TimeFloat --- crates/tek_core/src/time.rs | 139 ++++++++++++++--------- crates/tek_sequencer/src/arranger.rs | 4 +- crates/tek_sequencer/src/arranger_tui.rs | 4 +- crates/tek_sequencer/src/sequencer.rs | 3 - 4 files changed, 91 insertions(+), 59 deletions(-) diff --git a/crates/tek_core/src/time.rs b/crates/tek_core/src/time.rs index 506d7835..69ac840e 100644 --- a/crates/tek_core/src/time.rs +++ b/crates/tek_core/src/time.rs @@ -1,113 +1,148 @@ use crate::*; use std::iter::Iterator; -pub trait TimeUnit: Mul + Div - + Rem + From + Copy {} -impl TimeUnit for T where T: Mul + Div - + Rem + From + Copy {} +/// Any numeric type that represents time +pub trait TimeUnit: PartialEq + Copy + + Add + Mul + + Div + Rem {} +impl TimeUnit for T where T: PartialEq + Copy + + Add + Mul + + Div + Rem {} + +/// Integer time unit, such as frames, pulses, or microseconds +pub trait TimeInteger: TimeUnit + From + Into + Copy {} +impl TimeInteger for T where T: TimeUnit + From + Into + Copy {} + +/// Floating time unit, such as beats or seconds +pub trait TimeFloat: TimeUnit + From + Into + Copy {} +impl TimeFloat for T where T: TimeUnit + From + Into + Copy {} /// Trait for struct that defines a sample rate in hertz (samples per second) -pub trait SampleRate { +pub trait SampleRate { /// 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 { +pub trait BeatsPerMinute { /// 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 { + #[inline] fn note_to_frame (&self, note: (U, U)) -> U where Self: SampleRate { 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 { - usec * self.sr() / T::from(1000f64) + #[inline] fn usec_to_frame (&self, usec: U) -> U where Self: SampleRate { + 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 + 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 + 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 { - const DEFAULT_PPQ: T; +pub trait PulsesPerQuaver { + 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 { + #[inline] fn usec_per_pulse (&self) -> U + where U: TimeFloat, Self: BeatsPerMinute + { 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 { + #[inline] fn pulses_per_second (&self) -> U + where U: TimeFloat, Self: BeatsPerMinute + { 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 + BeatsPerMinute { + #[inline] fn pulses_per_sample (&self) -> U + where U: TimeFloat, Self: SampleRate + BeatsPerMinute + { 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 + BeatsPerMinute { + #[inline] fn pulses_to_sample (&self, p: U) -> U + where U: TimeFloat, Self: SampleRate + BeatsPerMinute + { 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 + BeatsPerMinute { + #[inline] fn samples_to_pulse (&self, s: U) -> U + where U: TimeFloat, Self: SampleRate + BeatsPerMinute + { 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 + BeatsPerMinute { + #[inline] fn samples_per_pulse (&self) -> U + where U: TimeFloat, Self: SampleRate + BeatsPerMinute + { self.sr() / self.pulses_per_second() } } -pub trait FramePosition { - fn frame (&self) -> T; - fn set_frame (&self, frame: T); +pub trait FramePosition { + fn frame (&self) -> U; + fn set_frame (&self, frame: U); } -pub trait PulsePosition { - fn pulse (&self) -> T; - fn set_pulse (&self, pulse: T); +pub trait PulsePosition { + fn pulse (&self) -> U; + fn set_pulse (&self, pulse: U); } -pub trait UsecPosition { - fn usec (&self) -> T; - fn set_usec (&self, usec: T); +pub trait UsecPosition { + fn usec (&self) -> U; + fn set_usec (&self, usec: U); } -pub trait LaunchSync { - fn sync (&self) -> T; - fn set_sync (&self, sync: T); - #[inline] fn next_launch (&self) -> T where Self: FramePosition { - todo!("next_launch") +pub trait LaunchSync { + fn sync (&self) -> U; + fn set_sync (&self, sync: U); + #[inline] fn next_launch_frame (&self) -> U where U: TimeInteger, Self: FramePosition { + let sync = self.sync(); + let frame = self.frame(); + if frame % sync == U::from(0) { + frame + } else { + (frame / sync) * sync + U::from(1) + } } } diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index ea8e3f82..19e0450d 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -237,12 +237,12 @@ impl Arrangement { 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()); }, _ => {} diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index f0a1b304..74ab2d9b 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -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) diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 1f2cc93b..3490475a 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -119,8 +119,6 @@ pub struct PhrasePlayer { pub phrase: Option<(AtomicUsize, Option>>)>, /// Start time and next phrase pub next_phrase: Option<(AtomicUsize, Option>>)>, - /// Frames remaining until switch to next phrase - pub switch_at: Option, /// Play input through output. pub monitoring: bool, /// Write input to sequence. @@ -334,7 +332,6 @@ impl PhrasePlayer { 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,