refactor tick timer to make sense

This commit is contained in:
🪞👃🪞 2024-07-06 14:13:31 +03:00
parent b3e6206b08
commit 597c3fa903
4 changed files with 82 additions and 80 deletions

View file

@ -13,7 +13,7 @@ impl Default for Timebase {
fn default () -> Self {
Self {
rate: 48000f64.into(),
bpm: 125f64.into(),
bpm: 100f64.into(),
ppq: 96f64.into(),
}
}
@ -49,12 +49,12 @@ impl Timebase {
#[inline] pub fn ppq (&self) -> f64 {
self.ppq.load(Ordering::Relaxed)
}
#[inline] fn usec_per_pulse (&self) -> f64 {
self.usec_per_beat() / self.ppq() as f64
}
#[inline] pub fn pulse_per_frame (&self) -> f64 {
self.usec_per_pulse() / self.usec_per_frame() as f64
}
#[inline] fn usec_per_pulse (&self) -> f64 {
self.usec_per_beat() / self.ppq() as f64
}
#[inline] pub fn frame_to_pulse (&self, pulses: f64) -> f64 {
self.pulse_per_frame() * pulses
}
@ -64,19 +64,19 @@ impl Timebase {
#[inline] pub fn note_to_usec (&self, (num, den): (f64, f64)) -> f64 {
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 {
usec * self.rate() / 1000.0
#[inline] fn pulses_per_second (&self) -> f64 {
self.beat_per_second() * self.ppq() as f64
}
#[inline] pub fn note_to_frame (&self, note: (f64, f64)) -> f64 {
self.usec_to_frame(self.note_to_usec(note))
}
#[inline] fn usec_to_frame (&self, usec: f64) -> f64 {
usec * self.rate() / 1000.0
}
#[inline] pub fn quantize (
&self, step: (f64, f64), time: f64

View file

@ -126,8 +126,24 @@ pub fn main () -> Usually<()> {
12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
}));
track.add_phrase("Garage", ppq * 4, Some(phrase! {
00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
01 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
02 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
03 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
04 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
06 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
07 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
09 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
10 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
12 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
14 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
00 * ppq/4 => MidiMessage::NoteOn { key: 35.into(), vel: 100.into() },
02 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
07 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
11 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
11 * ppq/4 => MidiMessage::NoteOn { key: 35.into(), vel: 100.into() },
12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
}));
@ -164,19 +180,19 @@ pub fn main () -> Usually<()> {
})?;
state.add_track_with_cb(Some("Lead"), |_, track|{
track.add_device_with_cb(Plugin::lv2(
"Helm",
"file:///home/user/.lv2/Helm.lv2"
)?, |track, device|{
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
if let Some(Some(left)) = audio_outs.get(0) {
device.connect_audio_out(0, left)?;
}
if let Some(Some(right)) = audio_outs.get(0) {
device.connect_audio_out(1, right)?;
}
Ok(())
})?;
//track.add_device_with_cb(Plugin::lv2(
//"Helm",
//"file:///home/user/.lv2/Helm.lv2"
//)?, |track, device|{
//device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
//if let Some(Some(left)) = audio_outs.get(0) {
//device.connect_audio_out(0, left)?;
//}
//if let Some(Some(right)) = audio_outs.get(0) {
//device.connect_audio_out(1, right)?;
//}
//Ok(())
//})?;
track.sequence = Some(0); // FIXME
track.add_phrase("Custom", ppq * 4, None);
Ok(())

View file

@ -1,5 +1,14 @@
use crate::core::*;
#[macro_export] macro_rules! phrase {
($($t:expr => $msg:expr),* $(,)?) => {{
let mut phrase = BTreeMap::new();
$(phrase.insert($t, vec![]);)*
$(phrase.get_mut(&$t).unwrap().push($msg);)*
phrase
}}
}
pub struct Phrase {
pub name: String,
pub length: usize,
@ -46,12 +55,10 @@ impl Phrase {
timebase: &Arc<Timebase>,
(frame0, frames, _): (usize, usize, f64),
) {
for (time, tick) in frames_to_ticks(
timebase.pulse_per_frame(),
timebase.frame_to_pulse(self.length as f64),
frame0 as f64,
(frame0 + frames) as f64,
for (time, tick) in Ticks(timebase.pulse_per_frame()).between_frames(
frame0, frame0 + frames
) {
let tick = tick % self.length;
if let Some(events) = self.notes.get(&(tick as usize)) {
for message in events.iter() {
let mut buf = vec![];
@ -70,59 +77,38 @@ impl Phrase {
}
}
fn frames_to_ticks (fpt: f64, repeat: f64, start: f64, end: f64) -> Box<dyn Iterator<Item=(usize, usize)>> {
let mut ticks = vec![];
let mut add_frame = |frame: f64|{
struct Ticks(f64);
impl Ticks {
fn between_frames (&self, start: usize, end: usize) -> TicksIterator {
TicksIterator(self.0, start, start, end)
}
}
struct TicksIterator(f64, usize, usize, usize);
impl Iterator for TicksIterator {
type Item = (usize, usize);
fn next (&mut self) -> Option<Self::Item> {
loop {
if self.1 > self.3 {
return None
}
let fpt = self.0;
let frame = self.1 as f64;
let start = self.2;
let end = self.3;
self.1 = self.1 + 1;
//println!("{fpt} {frame} {start} {end}");
let jitter = frame.rem_euclid(fpt); // ramps
let next_jitter = (frame + 1.0).rem_euclid(fpt);
if jitter > next_jitter { // at head of ramp crossing
let time = frame as usize % (end as usize-start as usize);
if jitter > next_jitter { // at crossing:
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;
let end_frame = end % repeat;
if start_frame < end_frame {
frames_to_ticks_1(&mut add_frame, start % repeat, end % repeat);
} else {
frames_to_ticks_2(&mut add_frame, start % repeat, end % repeat, repeat);
}
Box::new(ticks.into_iter())
}
fn frames_to_ticks_1 (add_frame: &mut impl FnMut(f64), start_frame: f64, end_frame: f64) {
for frame in start_frame as usize..end_frame as usize {
add_frame(frame as f64);
}
}
fn frames_to_ticks_2 (add_frame: &mut impl FnMut(f64), start_frame: f64, end_frame: f64, repeat: f64) {
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
return Some((time, tick))
}
}
}
#[macro_export] macro_rules! phrase {
($($t:expr => $msg:expr),* $(,)?) => {{
let mut phrase = BTreeMap::new();
$(phrase.insert($t, vec![]);)*
$(phrase.get_mut(&$t).unwrap().push($msg);)*
phrase
}}
}
#[cfg(test)]
@ -131,11 +117,8 @@ mod test {
#[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::<Vec<_>>())
let ticks = Ticks(12.3).between_frames(0, 100).collect::<Vec<_>>();
println!("{ticks:?}");
}
}

View file

@ -20,12 +20,15 @@ impl LV2Plugin {
break
}
let plugin = plugin.unwrap();
let err = &format!("init {uri}");
// Instantiate
Ok(Self {
world,
instance: unsafe {
plugin.instantiate(features.clone(), 48000.0).expect("boop")
plugin
.instantiate(features.clone(), 48000.0)
.expect(&err)
},
port_list: {
let mut port_list = vec![];