mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
refactor tick timer to make sense
This commit is contained in:
parent
b3e6206b08
commit
597c3fa903
4 changed files with 82 additions and 80 deletions
|
|
@ -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
|
||||
|
|
|
|||
42
src/main.rs
42
src/main.rs
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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,72 +77,48 @@ 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|{
|
||||
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);
|
||||
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())
|
||||
}
|
||||
struct Ticks(f64);
|
||||
|
||||
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);
|
||||
impl Ticks {
|
||||
fn between_frames (&self, start: usize, end: usize) -> TicksIterator {
|
||||
TicksIterator(self.0, start, start, end)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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 crossing:
|
||||
let time = (frame as usize) % (end as usize-start as usize);
|
||||
let tick = (frame / fpt) as usize;
|
||||
return Some((time, tick))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
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::<Vec<_>>())
|
||||
let ticks = Ticks(12.3).between_frames(0, 100).collect::<Vec<_>>();
|
||||
println!("{ticks:?}");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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![];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue