mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: correct cycle timings (jitter eats notes)
This commit is contained in:
parent
e83802e1fd
commit
665885f6ff
5 changed files with 90 additions and 76 deletions
|
|
@ -35,6 +35,10 @@ impl Timebase {
|
||||||
#[inline] pub fn frames_usecs (&self, frame: f64) -> f64 {
|
#[inline] pub fn frames_usecs (&self, frame: f64) -> f64 {
|
||||||
frame * self.frame_usec()
|
frame * self.frame_usec()
|
||||||
}
|
}
|
||||||
|
/// Frames to usecs
|
||||||
|
#[inline] pub fn usecs_frames (&self, usec: f64) -> f64 {
|
||||||
|
usec / self.frame_usec()
|
||||||
|
}
|
||||||
|
|
||||||
/// Beats per minute
|
/// Beats per minute
|
||||||
#[inline] pub fn bpm (&self) -> f64 {
|
#[inline] pub fn bpm (&self) -> f64 {
|
||||||
|
|
@ -53,6 +57,10 @@ impl Timebase {
|
||||||
#[inline] fn pulse_usec (&self) -> f64 {
|
#[inline] fn pulse_usec (&self) -> f64 {
|
||||||
self.beat_usec() / self.ppq() as f64
|
self.beat_usec() / self.ppq() as f64
|
||||||
}
|
}
|
||||||
|
/// Usecs to pulses
|
||||||
|
#[inline] pub fn pulses_usecs (&self, pulses: f64) -> f64 {
|
||||||
|
self.pulse_usec() * pulses
|
||||||
|
}
|
||||||
/// Pulses per frame
|
/// Pulses per frame
|
||||||
#[inline] pub fn pulse_frame (&self) -> f64 {
|
#[inline] pub fn pulse_frame (&self) -> f64 {
|
||||||
self.pulse_usec() / self.frame_usec() as f64
|
self.pulse_usec() / self.frame_usec() as f64
|
||||||
|
|
@ -110,49 +118,6 @@ impl Timebase {
|
||||||
#[inline] pub fn frames_per_tick (&self) -> f64 {
|
#[inline] pub fn frames_per_tick (&self) -> f64 {
|
||||||
self.rate() as f64 / self.ticks_per_second()
|
self.rate() as f64 / self.ticks_per_second()
|
||||||
}
|
}
|
||||||
pub fn frames_to_ticks (
|
|
||||||
&self,
|
|
||||||
start: f64,
|
|
||||||
end: f64,
|
|
||||||
repeat: f64,
|
|
||||||
) -> Vec<(usize, usize)> {
|
|
||||||
let start_frame = start % repeat;
|
|
||||||
let end_frame = end % repeat;
|
|
||||||
let fpt = self.pulse_frame();
|
|
||||||
//panic!("{start_frame} {end_frame} {fpt}");
|
|
||||||
let mut ticks = vec![];
|
|
||||||
let mut add_frame = |frame: f64|{
|
|
||||||
let jitter = frame.rem_euclid(fpt);
|
|
||||||
let last_jitter = (frame - 1.0).max(0.0) % fpt;
|
|
||||||
let next_jitter = frame + 1.0 % fpt;
|
|
||||||
if jitter <= last_jitter && jitter <= next_jitter {
|
|
||||||
ticks.push((frame as usize % (end as usize-start as usize), (frame / fpt) as usize));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if start_frame < end_frame {
|
|
||||||
for frame in start_frame as usize..end_frame as usize {
|
|
||||||
add_frame(frame as f64);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut frame = start_frame as usize;
|
|
||||||
loop {
|
|
||||||
add_frame(frame as f64);
|
|
||||||
frame = frame + 1;
|
|
||||||
if frame >= repeat as usize {
|
|
||||||
frame = 0;
|
|
||||||
loop {
|
|
||||||
add_frame(frame as f64);
|
|
||||||
frame = frame + 1;
|
|
||||||
if frame >= (end_frame as usize).saturating_sub(1) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ticks
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(test)] mod test {
|
#[cfg(test)] mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ pub use ::_jack::{
|
||||||
ClientStatus,
|
ClientStatus,
|
||||||
ClosureProcessHandler,
|
ClosureProcessHandler,
|
||||||
Control,
|
Control,
|
||||||
|
CycleTimes,
|
||||||
Frames,
|
Frames,
|
||||||
MidiIn,
|
MidiIn,
|
||||||
MidiIter,
|
MidiIter,
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -237,17 +237,23 @@ process!(App |self, _client, scope| {
|
||||||
}
|
}
|
||||||
self.playing = Some(transport.state);
|
self.playing = Some(transport.state);
|
||||||
self.playhead = transport.pos.frame() as usize;
|
self.playhead = transport.pos.frame() as usize;
|
||||||
let frame = scope.last_frame_time() as usize;
|
|
||||||
let frames = scope.n_frames() as usize;
|
let frames = scope.n_frames() as usize;
|
||||||
|
let CycleTimes {
|
||||||
|
current_frames,
|
||||||
|
current_usecs,
|
||||||
|
next_usecs,
|
||||||
|
period_usecs
|
||||||
|
} = scope.cycle_times().unwrap();
|
||||||
for track in self.tracks.iter_mut() {
|
for track in self.tracks.iter_mut() {
|
||||||
track.process(
|
track.process(
|
||||||
self.midi_in.as_ref().unwrap().iter(scope),
|
self.midi_in.as_ref().unwrap().iter(scope),
|
||||||
&self.timebase,
|
&self.timebase,
|
||||||
self.playing,
|
self.playing,
|
||||||
&scope,
|
|
||||||
self.playhead,
|
|
||||||
frames,
|
|
||||||
panic,
|
panic,
|
||||||
|
&scope,
|
||||||
|
(current_frames as usize, frames),
|
||||||
|
(current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize),
|
||||||
|
period_usecs as f64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Control::Continue
|
Control::Continue
|
||||||
|
|
|
||||||
|
|
@ -44,20 +44,15 @@ impl Phrase {
|
||||||
output: &mut MIDIChunk,
|
output: &mut MIDIChunk,
|
||||||
notes_on: &mut Vec<bool>,
|
notes_on: &mut Vec<bool>,
|
||||||
timebase: &Arc<Timebase>,
|
timebase: &Arc<Timebase>,
|
||||||
frame0: usize,
|
(usec0, usecs, _): (usize, usize, f64),
|
||||||
frames: usize,
|
|
||||||
) {
|
) {
|
||||||
let start = frame0 as f64;
|
let start = timebase.usecs_frames(usec0 as f64);
|
||||||
let end = start + frames as f64;
|
let end = timebase.usecs_frames((usec0 + usecs) as f64);
|
||||||
let repeat = timebase.pulses_frames(self.length as f64);
|
let repeat = timebase.pulses_frames(self.length as f64);
|
||||||
let ticks = timebase.frames_to_ticks(start, end, repeat);
|
let ticks = timebase.frames_to_ticks(start, end, repeat);
|
||||||
//panic!("{start} {end} {repeat} {ticks:?}");
|
|
||||||
for (time, tick) in ticks.iter() {
|
for (time, tick) in ticks.iter() {
|
||||||
let events = self.notes.get(&(*tick as usize));
|
if let Some(events) = self.notes.get(&(*tick as usize)) {
|
||||||
if events.is_none() {
|
for message in events.iter() {
|
||||||
continue
|
|
||||||
}
|
|
||||||
for message in events.unwrap().iter() {
|
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
let channel = 0.into();
|
let channel = 0.into();
|
||||||
let message = *message;
|
let message = *message;
|
||||||
|
|
@ -73,6 +68,53 @@ impl Phrase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timebase {
|
||||||
|
pub fn frames_to_ticks (
|
||||||
|
&self,
|
||||||
|
start: f64,
|
||||||
|
end: f64,
|
||||||
|
repeat: f64,
|
||||||
|
) -> Vec<(usize, usize)> {
|
||||||
|
let start_frame = start % repeat;
|
||||||
|
let end_frame = end % repeat;
|
||||||
|
let fpt = self.pulse_frame();
|
||||||
|
//panic!("{start_frame} {end_frame} {fpt}");
|
||||||
|
let mut ticks = vec![];
|
||||||
|
let mut add_frame = |frame: f64|{
|
||||||
|
let jitter = frame.rem_euclid(fpt);
|
||||||
|
let last_jitter = (frame - 1.0).max(0.0) % fpt;
|
||||||
|
let next_jitter = frame + 1.0 % fpt;
|
||||||
|
if jitter <= last_jitter && jitter <= next_jitter {
|
||||||
|
ticks.push((frame as usize % (end as usize-start as usize), (frame / fpt) as usize));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if start_frame < end_frame {
|
||||||
|
for frame in start_frame as usize..end_frame as usize {
|
||||||
|
add_frame(frame as f64);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut frame = start_frame as usize;
|
||||||
|
loop {
|
||||||
|
add_frame(frame as f64);
|
||||||
|
frame = frame + 1;
|
||||||
|
if frame >= repeat as usize {
|
||||||
|
frame = 0;
|
||||||
|
loop {
|
||||||
|
add_frame(frame as f64);
|
||||||
|
frame = frame + 1;
|
||||||
|
if frame >= (end_frame as usize).saturating_sub(1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ticks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! phrase {
|
#[macro_export] macro_rules! phrase {
|
||||||
($($t:expr => $msg:expr),* $(,)?) => {{
|
($($t:expr => $msg:expr),* $(,)?) => {{
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,11 @@ impl Track {
|
||||||
input: MidiIter,
|
input: MidiIter,
|
||||||
timebase: &Arc<Timebase>,
|
timebase: &Arc<Timebase>,
|
||||||
playing: Option<TransportState>,
|
playing: Option<TransportState>,
|
||||||
scope: &ProcessScope,
|
|
||||||
frame0: usize,
|
|
||||||
frames: usize,
|
|
||||||
panic: bool,
|
panic: bool,
|
||||||
|
scope: &ProcessScope,
|
||||||
|
(frame0, frames): (usize, usize),
|
||||||
|
(usec0, usecs): (usize, usize),
|
||||||
|
period: f64,
|
||||||
) {
|
) {
|
||||||
// Need to be borrowed outside the conditionals?
|
// Need to be borrowed outside the conditionals?
|
||||||
let recording = self.recording;
|
let recording = self.recording;
|
||||||
|
|
@ -132,14 +133,14 @@ impl Track {
|
||||||
all_notes_off(&mut self.midi_out_buf);
|
all_notes_off(&mut self.midi_out_buf);
|
||||||
}
|
}
|
||||||
// Play from phrase into output buffer
|
// Play from phrase into output buffer
|
||||||
if let Some(Some(phrase)) = self.sequence.map(|id|self.phrases.get(id)) {
|
let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten();
|
||||||
if playing == Some(TransportState::Rolling) {
|
if playing == Some(TransportState::Rolling) {
|
||||||
|
if let Some(phrase) = phrase {
|
||||||
phrase.process_out(
|
phrase.process_out(
|
||||||
&mut self.midi_out_buf,
|
&mut self.midi_out_buf,
|
||||||
&mut self.notes_on,
|
&mut self.notes_on,
|
||||||
timebase,
|
timebase,
|
||||||
frame0,
|
(usec0, usecs, period)
|
||||||
frames
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +148,6 @@ impl Track {
|
||||||
if self.recording || self.monitoring {
|
if self.recording || self.monitoring {
|
||||||
// For highlighting keys
|
// For highlighting keys
|
||||||
let notes_on = &mut self.notes_on;
|
let notes_on = &mut self.notes_on;
|
||||||
let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id));
|
|
||||||
for (time, event, bytes) in parse_midi_input(input) {
|
for (time, event, bytes) in parse_midi_input(input) {
|
||||||
match event {
|
match event {
|
||||||
LiveEvent::Midi { message, .. } => {
|
LiveEvent::Midi { message, .. } => {
|
||||||
|
|
@ -155,7 +155,7 @@ impl Track {
|
||||||
self.midi_out_buf[time].push(bytes.to_vec())
|
self.midi_out_buf[time].push(bytes.to_vec())
|
||||||
}
|
}
|
||||||
if recording {
|
if recording {
|
||||||
if let Some(Some(phrase)) = phrase {
|
if let Some(phrase) = phrase {
|
||||||
let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize;
|
let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize;
|
||||||
let pulse = pulse % phrase.length;
|
let pulse = pulse % phrase.length;
|
||||||
let contains = phrase.notes.contains_key(&pulse);
|
let contains = phrase.notes.contains_key(&pulse);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue