mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
sync midi by frames rather than useconds
This commit is contained in:
parent
63b5eb3740
commit
b1e4ec3a88
4 changed files with 29 additions and 17 deletions
13
src/main.rs
13
src/main.rs
|
|
@ -107,6 +107,13 @@ pub fn main () -> Usually<()> {
|
||||||
08 * 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() },
|
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! {
|
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: 44.into(), vel: 100.into() },
|
||||||
00 * ppq/4 => MidiMessage::NoteOn { key: 36.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![
|
state.scenes = vec![
|
||||||
Scene::new("Intro", vec![None, Some(0), None, None]),
|
Scene::new("Intro", vec![None, Some(0), None, None]),
|
||||||
Scene::new("Hook", vec![Some(0), Some(1), 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("Verse", vec![Some(2), Some(0), Some(0), None]),
|
||||||
Scene::new("Chorus", vec![Some(0), Some(1), None, None]),
|
Scene::new("Chorus", vec![Some(1), Some(1), None, None]),
|
||||||
Scene::new("Bridge", vec![Some(2), Some(0), Some(0), None]),
|
Scene::new("Bridge", vec![Some(3), Some(0), Some(0), None]),
|
||||||
Scene::new("Outro", vec![None, Some(1), None, None]),
|
Scene::new("Outro", vec![None, Some(1), None, None]),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ pub struct App {
|
||||||
/// Current position according to transport
|
/// Current position according to transport
|
||||||
pub playhead: usize,
|
pub playhead: usize,
|
||||||
/// Position of T0 for this playback within global timeline
|
/// Position of T0 for this playback within global timeline
|
||||||
pub play_started: Option<usize>,
|
pub play_started: Option<(usize, usize)>,
|
||||||
/// Current sample rate and tempo.
|
/// Current sample rate and tempo.
|
||||||
pub timebase: Arc<Timebase>,
|
pub timebase: Arc<Timebase>,
|
||||||
/// Display mode of grid section
|
/// Display mode of grid section
|
||||||
|
|
@ -86,7 +86,10 @@ process!(App |self, _client, scope| {
|
||||||
if self.playing != Some(transport.state) {
|
if self.playing != Some(transport.state) {
|
||||||
match transport.state {
|
match transport.state {
|
||||||
TransportState::Rolling => {
|
TransportState::Rolling => {
|
||||||
self.play_started = Some(current_usecs as usize);
|
self.play_started = Some((
|
||||||
|
current_frames as usize,
|
||||||
|
current_usecs as usize,
|
||||||
|
));
|
||||||
},
|
},
|
||||||
TransportState::Stopped => {
|
TransportState::Stopped => {
|
||||||
self.play_started = None;
|
self.play_started = None;
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ impl Phrase {
|
||||||
output: &mut MIDIChunk,
|
output: &mut MIDIChunk,
|
||||||
notes_on: &mut Vec<bool>,
|
notes_on: &mut Vec<bool>,
|
||||||
timebase: &Arc<Timebase>,
|
timebase: &Arc<Timebase>,
|
||||||
(usec0, usecs, _): (usize, usize, f64),
|
(frame0, frames, _): (usize, usize, f64),
|
||||||
) {
|
) {
|
||||||
let start = timebase.usecs_frames(usec0 as f64);
|
let start = frame0;
|
||||||
let end = timebase.usecs_frames((usec0 + usecs) as f64);
|
let end = frame0 + frames;
|
||||||
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 as f64, end as f64, repeat);
|
||||||
//panic!("{start} {end} {repeat} {ticks:?}");
|
//panic!("{start} {end} {repeat} {ticks:?}");
|
||||||
for (time, tick) in ticks.iter() {
|
for (time, tick) in ticks.iter() {
|
||||||
if let Some(events) = self.notes.get(&(*tick as usize)) {
|
if let Some(events) = self.notes.get(&(*tick as usize)) {
|
||||||
|
|
@ -84,11 +84,13 @@ impl Timebase {
|
||||||
//panic!("{start_frame} {end_frame} {fpt}");
|
//panic!("{start_frame} {end_frame} {fpt}");
|
||||||
let mut ticks = vec![];
|
let mut ticks = vec![];
|
||||||
let mut add_frame = |frame: f64|{
|
let mut add_frame = |frame: f64|{
|
||||||
let jitter = frame.rem_euclid(fpt);
|
let jitter = frame.rem_euclid(fpt); // ramps
|
||||||
let last_jitter = (frame - 1.0).max(0.0) % fpt;
|
let next_jitter = (frame + 1.0).rem_euclid(fpt);
|
||||||
let next_jitter = frame + 1.0 % fpt;
|
if jitter > next_jitter { // at head of ramp crossing
|
||||||
if jitter <= last_jitter && jitter <= next_jitter {
|
ticks.push((
|
||||||
ticks.push((frame as usize % (end as usize-start as usize), (frame / fpt) as usize));
|
frame as usize % (end as usize-start as usize),
|
||||||
|
(frame / fpt) as usize)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if start_frame < end_frame {
|
if start_frame < end_frame {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ impl Track {
|
||||||
input: MidiIter,
|
input: MidiIter,
|
||||||
timebase: &Arc<Timebase>,
|
timebase: &Arc<Timebase>,
|
||||||
playing: Option<TransportState>,
|
playing: Option<TransportState>,
|
||||||
started: Option<usize>,
|
started: Option<(usize, usize)>,
|
||||||
quant: usize,
|
quant: usize,
|
||||||
reset: bool,
|
reset: bool,
|
||||||
scope: &ProcessScope,
|
scope: &ProcessScope,
|
||||||
|
|
@ -145,13 +145,13 @@ impl Track {
|
||||||
// Play from phrase into output buffer
|
// Play from phrase into output buffer
|
||||||
let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten();
|
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(started) = started {
|
if let Some((start_frame, start_usec)) = started {
|
||||||
if let Some(phrase) = phrase {
|
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,
|
||||||
(usec0 - started, usecs, period)
|
(frame0.saturating_sub(start_frame), frames, period)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue