diff --git a/src/main.rs b/src/main.rs index f5d8e8a6..8f6f98a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,6 +107,13 @@ pub fn main () -> Usually<()> { 08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, 12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, })); + track.add_phrase("5 kicks", ppq * 4, Some(phrase! { + 00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 04 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + 14 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, + })); track.add_phrase("D-Beat", ppq * 4, Some(phrase! { 00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, 00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, @@ -175,9 +182,9 @@ pub fn main () -> Usually<()> { state.scenes = vec![ Scene::new("Intro", vec![None, Some(0), None, None]), Scene::new("Hook", vec![Some(0), Some(1), None, None]), - Scene::new("Verse", vec![Some(1), Some(0), Some(0), None]), - Scene::new("Chorus", vec![Some(0), Some(1), None, None]), - Scene::new("Bridge", vec![Some(2), Some(0), Some(0), None]), + Scene::new("Verse", vec![Some(2), Some(0), Some(0), None]), + Scene::new("Chorus", vec![Some(1), Some(1), None, None]), + Scene::new("Bridge", vec![Some(3), Some(0), Some(0), None]), Scene::new("Outro", vec![None, Some(1), None, None]), ]; diff --git a/src/model.rs b/src/model.rs index 4920aa82..164fa5bf 100644 --- a/src/model.rs +++ b/src/model.rs @@ -32,7 +32,7 @@ pub struct App { /// Current position according to transport pub playhead: usize, /// Position of T0 for this playback within global timeline - pub play_started: Option, + pub play_started: Option<(usize, usize)>, /// Current sample rate and tempo. pub timebase: Arc, /// Display mode of grid section @@ -86,7 +86,10 @@ process!(App |self, _client, scope| { if self.playing != Some(transport.state) { match transport.state { TransportState::Rolling => { - self.play_started = Some(current_usecs as usize); + self.play_started = Some(( + current_frames as usize, + current_usecs as usize, + )); }, TransportState::Stopped => { self.play_started = None; diff --git a/src/model/phrase.rs b/src/model/phrase.rs index d8357a63..20142e21 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -44,12 +44,12 @@ impl Phrase { output: &mut MIDIChunk, notes_on: &mut Vec, timebase: &Arc, - (usec0, usecs, _): (usize, usize, f64), + (frame0, frames, _): (usize, usize, f64), ) { - let start = timebase.usecs_frames(usec0 as f64); - let end = timebase.usecs_frames((usec0 + usecs) as f64); + let start = frame0; + let end = frame0 + frames; 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 as f64, end as f64, repeat); //panic!("{start} {end} {repeat} {ticks:?}"); for (time, tick) in ticks.iter() { if let Some(events) = self.notes.get(&(*tick as usize)) { @@ -84,11 +84,13 @@ impl Timebase { //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)); + 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 + ticks.push(( + frame as usize % (end as usize-start as usize), + (frame / fpt) as usize) + ); }; }; if start_frame < end_frame { diff --git a/src/model/track.rs b/src/model/track.rs index 3a845c55..7d71c66a 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -119,7 +119,7 @@ impl Track { input: MidiIter, timebase: &Arc, playing: Option, - started: Option, + started: Option<(usize, usize)>, quant: usize, reset: bool, scope: &ProcessScope, @@ -145,13 +145,13 @@ impl Track { // Play from phrase into output buffer let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten(); if playing == Some(TransportState::Rolling) { - if let Some(started) = started { + if let Some((start_frame, start_usec)) = started { if let Some(phrase) = phrase { phrase.process_out( &mut self.midi_out_buf, &mut self.notes_on, timebase, - (usec0 - started, usecs, period) + (frame0.saturating_sub(start_frame), frames, period) ); } }