From b3e6206b08731e90ed202441bf7cbbc4e10c6d85 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 6 Jul 2024 09:17:39 +0300 Subject: [PATCH] wip: simplifying timebase --- src/core/time.rs | 102 +++++++++++------------------------------- src/model/phrase.rs | 26 ++++++++--- src/model/track.rs | 5 +-- src/view.rs | 2 +- src/view/transport.rs | 4 +- 5 files changed, 50 insertions(+), 89 deletions(-) diff --git a/src/core/time.rs b/src/core/time.rs index df0de2ca..3579b24e 100644 --- a/src/core/time.rs +++ b/src/core/time.rs @@ -3,11 +3,11 @@ use atomic_float::AtomicF64; #[derive(Debug)] pub struct Timebase { /// Frames per second - pub rate: AtomicF64, + rate: AtomicF64, /// Beats per minute - pub bpm: AtomicF64, + bpm: AtomicF64, /// Ticks per beat - pub ppq: AtomicF64, + ppq: AtomicF64, } impl Default for Timebase { fn default () -> Self { @@ -27,61 +27,48 @@ impl Timebase { #[inline] fn rate (&self) -> f64 { self.rate.load(Ordering::Relaxed) } - /// Usec per frame - #[inline] fn frame_usec (&self) -> f64 { + #[inline] fn usec_per_frame (&self) -> f64 { 1_000_000 as f64 / self.rate() as f64 } - /// Frames to usecs - #[inline] pub fn frames_usecs (&self, frame: f64) -> f64 { - frame * self.frame_usec() - } - /// Frames to usecs - #[inline] pub fn usecs_frames (&self, usec: f64) -> f64 { - usec / self.frame_usec() + #[inline] pub fn frame_to_usec (&self, frame: f64) -> f64 { + frame * self.usec_per_frame() } /// Beats per minute #[inline] pub fn bpm (&self) -> f64 { self.bpm.load(Ordering::Relaxed) } - /// Usec per beat - #[inline] fn beat_usec (&self) -> f64 { + #[inline] fn usec_per_beat (&self) -> f64 { 60_000_000f64 / self.bpm() as f64 } + #[inline] fn beat_per_second (&self) -> f64 { + self.bpm() as f64 / 60000000.0 + } /// Pulses per beat #[inline] pub fn ppq (&self) -> f64 { self.ppq.load(Ordering::Relaxed) } - /// Usec per pulse - #[inline] fn pulse_usec (&self) -> f64 { - self.beat_usec() / self.ppq() as f64 + #[inline] fn usec_per_pulse (&self) -> f64 { + self.usec_per_beat() / self.ppq() as f64 } - /// Usecs to pulses - #[inline] pub fn pulses_usecs (&self, pulses: f64) -> f64 { - self.pulse_usec() * pulses + #[inline] pub fn pulse_per_frame (&self) -> f64 { + self.usec_per_pulse() / self.usec_per_frame() as f64 } - /// Pulses per frame - #[inline] pub fn pulse_frame (&self) -> f64 { - self.pulse_usec() / self.frame_usec() as f64 + #[inline] pub fn frame_to_pulse (&self, pulses: f64) -> f64 { + self.pulse_per_frame() * pulses } - /// Frames per pulse - #[inline] pub fn frame_pulse (&self) -> f64 { - self.frame_usec() as f64 / self.pulse_usec() - } - /// Frames to pulses - #[inline] pub fn pulses_frames (&self, pulses: f64) -> f64 { - self.pulse_frame() * pulses - } - /// Pulses to frames - #[inline] pub fn frames_pulses (&self, frames: f64) -> f64 { - frames / self.pulse_frame() - } - #[inline] pub fn usec_per_step (&self, divisor: f64) -> f64 { - self.beat_usec() / divisor + #[inline] pub fn pulse_to_frame (&self, frames: f64) -> f64 { + frames / self.pulse_per_frame() } #[inline] pub fn note_to_usec (&self, (num, den): (f64, f64)) -> f64 { - 4.0 * self.beat_usec() * num / den + 4.0 * self.usec_per_beat() * num / den + } + #[inline] fn pulses_per_second (&self) -> f64 { + self.beat_per_second() * self.ppq() as f64 + } + #[inline] pub fn frames_per_pulse (&self) -> f64 { + self.rate() as f64 / self.pulses_per_second() } #[inline] fn usec_to_frame (&self, usec: f64) -> f64 { @@ -109,43 +96,4 @@ impl Timebase { .collect() } - #[inline] fn beats_per_second (&self) -> f64 { - self.bpm() as f64 / 60000000.0 - } - #[inline] fn ticks_per_second (&self) -> f64 { - self.beats_per_second() * self.ppq() as f64 - } - #[inline] pub fn frames_per_tick (&self) -> f64 { - self.rate() as f64 / self.ticks_per_second() - } -} -#[cfg(test)] mod test { - use super::*; - - #[test] - fn test_timebase () -> Usually<()> { - let timebase = Timebase::new(48000.0, 240.0, 24.0); - println!("F/S = {:.03}", s.rate()); - println!("B/S = {:.03}", s.beats_per_secon()); - println!("F/B = {:.03}", s.frames_per_beat()); - println!("T/B = {:.03}", s.ticks_per_beat()); - println!("F/T = {:.03}", s.frames_per_tick()); - println!("F/L = {:.03}", s.frames_per_loop()); - println!("T/L = {:.03}", s.ticks_per_loop()); - let fpt = s.fpt(); - let frames_per_chunk = 240; - let chunk = |chunk: usize| s.frames_to_ticks( - chunk * frames_per_chunk, - (chunk + 1) * frames_per_chunk - ); - //for i in 0..2000 { - //println!("{i} {:?}", chunk(i)); - //} - assert_eq!(chunk(0), vec![(0, 0), (125, 1)]); - assert_eq!(chunk(1), vec![(10, 2), (135, 3)]); - assert_eq!(chunk(12), vec![(120, 24)]); - assert_eq!(chunk(412), vec![(120, 24)]); - assert_eq!(chunk(413), vec![(5, 25), (130, 26)]); - Ok(()) - } } diff --git a/src/model/phrase.rs b/src/model/phrase.rs index 0bec4024..cc2fe434 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -47,8 +47,8 @@ impl Phrase { (frame0, frames, _): (usize, usize, f64), ) { for (time, tick) in frames_to_ticks( - timebase.pulse_frame(), - timebase.pulses_frames(self.length as f64), + timebase.pulse_per_frame(), + timebase.frame_to_pulse(self.length as f64), frame0 as f64, (frame0 + frames) as f64, ) { @@ -76,10 +76,9 @@ fn frames_to_ticks (fpt: f64, repeat: f64, start: f64, end: f64) -> Box next_jitter { // at head of ramp crossing - ticks.push(( - frame as usize % (end as usize-start as usize), - (frame / fpt) as usize) - ); + let time = frame as usize % (end as usize-start as usize); + let tick = (frame / fpt) as usize; + ticks.push((time, tick)); }; }; let start_frame = start % repeat; @@ -125,3 +124,18 @@ fn frames_to_ticks_2 (add_frame: &mut impl FnMut(f64), start_frame: f64, end_fra phrase }} } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_frames_to_ticks () { + let fpt = Timebase::default().pulse_per_frame(); + let repeat = 0.0; + let start = 0.0; + let end = 0.0; + println!("{:?}", frames_to_ticks(fpt, repeat, start, end).collect::>()) + } + +} diff --git a/src/model/track.rs b/src/model/track.rs index 7d71c66a..98173c3c 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -138,8 +138,7 @@ impl Track { if self.reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; - } - else if reset { + } else if reset { all_notes_off(&mut self.midi_out_buf); } // Play from phrase into output buffer @@ -168,7 +167,7 @@ impl Track { } if recording { if let Some(phrase) = phrase { - let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize; + let pulse = timebase.pulse_to_frame((frame0 + time) as f64) as usize; let pulse = pulse % phrase.length; let pulse = (pulse / quant) * quant; let contains = phrase.notes.contains_key(&pulse); diff --git a/src/view.rs b/src/view.rs index 4f215a50..c3991a34 100644 --- a/src/view.rs +++ b/src/view.rs @@ -59,7 +59,7 @@ render!(App |self, buf, area| { phrase, focused: self.section == 2, ppq: self.timebase.ppq() as usize, - now: self.timebase.frames_pulses(self.playhead as f64) as usize, + now: self.timebase.pulse_to_frame(self.playhead as f64) as usize, time_cursor: self.time_cursor, time_start: self.time_start, time_zoom: self.time_zoom, diff --git a/src/view/transport.rs b/src/view/transport.rs index 8596b31c..da7ff61d 100644 --- a/src/view/transport.rs +++ b/src/view/transport.rs @@ -26,11 +26,11 @@ impl<'a> Render for TransportView<'a> { pub fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc, frame: usize) { let ppq = timebase.ppq() as usize; - let pulse = timebase.frames_pulses(frame as f64) as usize; + let pulse = timebase.pulse_to_frame(frame as f64) as usize; let (beats, pulses) = (pulse / ppq, pulse % ppq); let (bars, beats) = (beats / 4, beats % 4); - let usecs = timebase.frames_usecs(frame as f64) as usize; + let usecs = timebase.frame_to_usec(frame as f64) as usize; let (seconds, msecs) = (usecs / 1000000, usecs / 1000 % 1000); let (minutes, seconds) = (seconds / 60, seconds % 60);