mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
fix timing by cleaning it
This commit is contained in:
parent
55a8b67bfb
commit
c4d8692b71
5 changed files with 69 additions and 97 deletions
134
src/core/time.rs
134
src/core/time.rs
|
|
@ -9,20 +9,6 @@ pub struct Timebase {
|
|||
pub ppq: AtomicUsize,
|
||||
}
|
||||
|
||||
enum QuantizeMode {
|
||||
Forward,
|
||||
Backward,
|
||||
Nearest,
|
||||
}
|
||||
|
||||
struct Loop<T> {
|
||||
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 <T, U> (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)>
|
||||
where U: std::iter::Iterator<Item=(usize, T)> + 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 <T, U> (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)>
|
||||
where U: std::iter::Iterator<Item=(usize, T)> + Sized
|
||||
{
|
||||
events
|
||||
.map(|(time, event)|(self.quantize(step, time).0, event))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,7 @@ pub struct Launcher {
|
|||
show_help: bool,
|
||||
view: LauncherView,
|
||||
}
|
||||
pub enum LauncherView {
|
||||
Tracks,
|
||||
Sequencer,
|
||||
Chains,
|
||||
Modal(Box<dyn Device>),
|
||||
}
|
||||
pub enum LauncherView { Tracks, Sequencer, Chains, Modal(Box<dyn Device>) }
|
||||
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<MutexGuard<Sequencer>> {
|
||||
pub fn sequencer <'a> (&'a self) -> Option<MutexGuard<Sequencer>> {
|
||||
Some(self.track()?.1.sequencer.state())
|
||||
}
|
||||
fn chain <'a> (&'a self) -> Option<MutexGuard<Chain>> {
|
||||
pub fn chain <'a> (&'a self) -> Option<MutexGuard<Chain>> {
|
||||
Some(self.track()?.1.chain.state())
|
||||
}
|
||||
fn phrase_id (&self) -> Option<usize> {
|
||||
pub fn phrase_id (&self) -> Option<usize> {
|
||||
let (track_id, _) = self.track()?;
|
||||
let (_, scene) = self.scene()?;
|
||||
*scene.clips.get(track_id)?
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ impl Phrase {
|
|||
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
|
||||
}
|
||||
pub fn frames (&self, timebase: &Arc<Timebase>) -> usize {
|
||||
timebase.pulse_to_frame(self.length as usize)
|
||||
timebase.pulses_frames(self.length as usize)
|
||||
}
|
||||
pub fn frame_to_pulse (&self, timebase: &Arc<Timebase>, 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;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ fn main () -> Result<(), Box<dyn Error>> {
|
|||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue