From c4d8692b71eb3b9c27e9b460adfc114f092ee0a2 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 1 Jul 2024 16:23:39 +0300 Subject: [PATCH] fix timing by cleaning it --- src/core/time.rs | 134 ++++++++++++++------------------- src/device/launcher/mod.rs | 17 ++--- src/device/sequencer/mod.rs | 2 +- src/device/sequencer/phrase.rs | 11 ++- src/main.rs | 2 +- 5 files changed, 69 insertions(+), 97 deletions(-) diff --git a/src/core/time.rs b/src/core/time.rs index 234ace3b..a7c4364d 100644 --- a/src/core/time.rs +++ b/src/core/time.rs @@ -9,20 +9,6 @@ pub struct Timebase { pub ppq: AtomicUsize, } -enum QuantizeMode { - Forward, - Backward, - Nearest, -} - -struct Loop { - repeat: bool, - pre_start: T, - start: T, - end: T, - post_end: T, -} - /// NoteDuration in musical terms. Has definite usec value /// for given bpm and sample rate. pub enum NoteDuration { @@ -32,79 +18,84 @@ pub enum NoteDuration { } impl Timebase { - #[inline] pub fn rate (&self) -> usize { + #[inline] fn rate (&self) -> usize { self.rate.load(Ordering::Relaxed) } - #[inline] pub fn bpm (&self) -> usize { - self.bpm.load(Ordering::Relaxed) - } - #[inline] pub fn ppq (&self) -> usize { - self.ppq.load(Ordering::Relaxed) - } - #[inline] pub fn pulse_to_frame (&self, pulses: usize) -> usize { - let beat = self.bpm() / 60; - let quaver = beat / 4; - let pulse = quaver / self.ppq(); - pulse * pulses - } - #[inline] pub fn frame_to_pulse (&self, frames: usize) -> usize { - let beat = self.bpm() / 60; - let quaver = beat / 4; - let pulse = quaver / self.ppq(); - frames / pulse - } - #[inline] fn beats_per_second (&self) -> f64 { - self.bpm() as f64 / 60000.0 - } - #[inline] fn frames_per_second (&self) -> usize { - self.rate() - } - #[inline] pub fn frames_per_beat (&self) -> f64 { - self.frames_per_second() as f64 / self.beats_per_second() - } - #[inline] pub fn frames_per_tick (&self) -> f64 { - self.frames_per_second() as f64 / self.ticks_per_second() - } - #[inline] fn frames_per_loop (&self, steps: f64, steps_per_beat: f64) -> f64 { - self.frames_per_beat() * steps / steps_per_beat - } - #[inline] fn ticks_per_beat (&self) -> f64 { - self.ppq.load(Ordering::Relaxed) as f64 - } - #[inline] fn ticks_per_second (&self) -> f64 { - self.beats_per_second() * self.ticks_per_beat() + #[inline] fn frame_usec (&self) -> usize { + 1_000_000 / self.rate() } #[inline] pub fn frame_to_usec (&self, frame: usize) -> usize { frame * 1000000 / self.rate() } - #[inline] pub fn usec_to_frame (&self, usec: usize) -> usize { + + #[inline] pub fn bpm (&self) -> usize { + self.bpm.load(Ordering::Relaxed) + } + #[inline] fn beat_usec (&self) -> usize { + 60_000_000_000_000 / self.bpm() + } + #[inline] fn beats_per_second (&self) -> f64 { + self.bpm() as f64 / 60000000.0 + } + + #[inline] pub fn ppq (&self) -> usize { + self.ppq.load(Ordering::Relaxed) + } + #[inline] fn pulse_usec (&self) -> usize { + self.beat_usec() / self.ppq() + } + #[inline] fn pulse_frame (&self) -> usize { + self.pulse_usec() / self.frame_usec() + } + #[inline] pub fn pulses_frames (&self, pulses: usize) -> usize { + self.pulse_frame() * pulses + } + #[inline] pub fn frames_pulses (&self, frames: usize) -> usize { + frames / self.pulse_frame() + } + #[inline] pub fn frames_per_tick (&self) -> f64 { + self.rate() as f64 / self.ticks_per_second() + } + #[inline] fn ticks_per_second (&self) -> f64 { + self.beats_per_second() * self.ppq() as f64 + } + #[inline] fn usec_to_frame (&self, usec: usize) -> usize { usec * self.rate() / 1000 } - #[inline] pub fn usec_per_bar (&self, beats_per_bar: usize) -> usize { - self.usec_per_beat() * beats_per_bar - } - #[inline] pub fn usec_per_beat (&self) -> usize { - 60_000_000_000 / self.bpm() - } #[inline] pub fn usec_per_step (&self, divisor: usize) -> usize { - self.usec_per_beat() / divisor - } - #[inline] pub fn usec_per_tick (&self) -> usize { - self.usec_per_beat() / self.ppq() + self.beat_usec() / divisor } + #[inline] pub fn note_to_usec (&self, note: &NoteDuration) -> usize { match note { NoteDuration::Nth(time, flies) => - self.usec_per_beat() * *time / *flies, + self.beat_usec() * *time / *flies, NoteDuration::Dotted(note) => self.note_to_usec(note) * 3 / 2, NoteDuration::Tuplet(n, note) => self.note_to_usec(note) * 2 / *n, } } + #[inline] pub fn note_to_frame (&self, note: &NoteDuration) -> usize { self.usec_to_frame(self.note_to_usec(note)) } + + #[inline] pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) { + let step = self.note_to_usec(step); + let time = time / step; + let offset = time % step; + (time, offset) + } + + #[inline] pub fn quantize_into (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)> + where U: std::iter::Iterator + Sized + { + events + .map(|(time, event)|(self.quantize(step, time).0, event)) + .collect() + } + pub fn frames_to_ticks (&self, start: usize, end: usize, quant: usize) -> Vec<(usize, usize)> { let start_frame = start % quant; let end_frame = end % quant; @@ -142,18 +133,5 @@ impl Timebase { } ticks } - pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) { - let step = self.note_to_usec(step); - let time = time / step; - let offset = time % step; - (time, offset) - } - pub fn quantize_into (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)> - where U: std::iter::Iterator + Sized - { - events - .map(|(time, event)|(self.quantize(step, time).0, event)) - .collect() - } } diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index 8626ec65..f7dbdfc9 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -19,12 +19,7 @@ pub struct Launcher { show_help: bool, view: LauncherView, } -pub enum LauncherView { - Tracks, - Sequencer, - Chains, - Modal(Box), -} +pub enum LauncherView { Tracks, Sequencer, Chains, Modal(Box) } impl LauncherView { fn is_tracks (&self) -> bool { match self { Self::Tracks => true, _ => false } @@ -118,25 +113,25 @@ impl Launcher { } } - fn track <'a> (&'a self) -> Option<(usize, &'a Track)> { + pub fn track <'a> (&'a self) -> Option<(usize, &'a Track)> { match self.col() { 0 => None, _ => { let id = self.col() as usize - 1; self.tracks.get(id).map(|t|(id, t)) } } } - fn scene <'a> (&'a self) -> Option<(usize, &'a Scene)> { + pub fn scene <'a> (&'a self) -> Option<(usize, &'a Scene)> { match self.row() { 0 => None, _ => { let id = self.row() as usize - 1; self.scenes.get(id).map(|t|(id, t)) } } } - fn sequencer <'a> (&'a self) -> Option> { + pub fn sequencer <'a> (&'a self) -> Option> { Some(self.track()?.1.sequencer.state()) } - fn chain <'a> (&'a self) -> Option> { + pub fn chain <'a> (&'a self) -> Option> { Some(self.track()?.1.chain.state()) } - fn phrase_id (&self) -> Option { + pub fn phrase_id (&self) -> Option { let (track_id, _) = self.track()?; let (_, scene) = self.scene()?; *scene.clips.get(track_id)? diff --git a/src/device/sequencer/mod.rs b/src/device/sequencer/mod.rs index 1066f66d..bad538ef 100644 --- a/src/device/sequencer/mod.rs +++ b/src/device/sequencer/mod.rs @@ -174,7 +174,7 @@ pub fn contains_note_on (sequence: &Phrase, k: u7, start: u32, end: u32) -> bool let mut s = sequencer.state.lock().unwrap(); s.rate = Hz(48000); s.tempo = Tempo(240_000); - println!("F/S = {:.03}", s.frames_per_second()); + 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()); diff --git a/src/device/sequencer/phrase.rs b/src/device/sequencer/phrase.rs index fb35860e..b31b8897 100644 --- a/src/device/sequencer/phrase.rs +++ b/src/device/sequencer/phrase.rs @@ -15,10 +15,10 @@ impl Phrase { Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) } } pub fn frames (&self, timebase: &Arc) -> usize { - timebase.pulse_to_frame(self.length as usize) + timebase.pulses_frames(self.length as usize) } pub fn frame_to_pulse (&self, timebase: &Arc, frame: usize) -> usize { - timebase.frame_to_pulse(frame) % self.length as usize + timebase.frames_pulses(frame) % self.length as usize } /** Write a chunk of MIDI events to an output port. */ pub fn chunk_out ( @@ -29,8 +29,7 @@ impl Phrase { frame0: usize, frames: usize, ) { - let quant = self.frames(timebase); - let ticks = timebase.frames_to_ticks(frame0, frame0 + frames, quant); + let ticks = timebase.frames_to_ticks(frame0, frame0 + frames, self.frames(timebase)); for (time, tick) in ticks.iter() { let events = self.notes.get(&(*tick as u32)); if events.is_none() { @@ -56,7 +55,7 @@ impl Phrase { } } } - /** React a chunk of MIDI events from an input port. */ + /** Read a chunk of MIDI events from an input port. */ pub fn chunk_in ( &mut self, input: ::jack::MidiIter, @@ -68,7 +67,7 @@ impl Phrase { ) { for RawMidi { time, bytes } in input { let time = time as usize; - let pulse = timebase.frame_to_pulse(frame0 + time) as u32; + let pulse = timebase.frames_pulses(frame0 + time) as u32; if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { if let MidiMessage::NoteOn { key, vel: _ } = message { notes_on[key.as_int() as usize] = true; diff --git a/src/main.rs b/src/main.rs index 12aa14e4..797f19a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ fn main () -> Result<(), Box> { let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?; let timebase = Arc::new(Timebase { rate: AtomicUsize::new(client.sample_rate()), - bpm: AtomicUsize::new(99000), + bpm: AtomicUsize::new(125000000), ppq: AtomicUsize::new(96), }); let ppq = timebase.ppq() as u32;