diff --git a/src/core/time.rs b/src/core/time.rs index 3579b24e..b5f96b3c 100644 --- a/src/core/time.rs +++ b/src/core/time.rs @@ -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 diff --git a/src/main.rs b/src/main.rs index 74dac61d..0c358a74 100644 --- a/src/main.rs +++ b/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(()) diff --git a/src/model/phrase.rs b/src/model/phrase.rs index cc2fe434..c72c2aa6 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -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, (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> { - 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 { + 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::>()) + let ticks = Ticks(12.3).between_frames(0, 100).collect::>(); + println!("{ticks:?}"); } } diff --git a/src/model/plugin/lv2.rs b/src/model/plugin/lv2.rs index 83f35cee..1e7dc9c5 100644 --- a/src/model/plugin/lv2.rs +++ b/src/model/plugin/lv2.rs @@ -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![];