Skip to content

Commit

Permalink
Move tempo track out of utils
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Sep 17, 2023
1 parent 866d54a commit 56cabe4
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 65 deletions.
4 changes: 2 additions & 2 deletions midi-file/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
mod midi;
pub mod playback;
pub mod program_map;
mod tempo_track;
mod track;
mod utils;

pub use midly;
pub use {midi::*, playback::*, track::*, utils::*};
pub use {midi::*, playback::*, track::*};

pub static INSTRUMENT_NAMES: [&str; 128] = [
"Acoustic Grand Piano",
Expand Down
4 changes: 2 additions & 2 deletions midi-file/src/midi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{program_map::ProgramMap, utils, MidiTrack};
use crate::{program_map::ProgramMap, tempo_track::TempoTrack, MidiTrack};
use midly::{Format, Smf, Timing};
use std::{fs, path::Path};

Expand Down Expand Up @@ -33,7 +33,7 @@ impl Midi {
return Err(String::from("Midi File Has No Tracks"));
}

let tempo_track = utils::TempoTrack::build(&smf.tracks);
let tempo_track = TempoTrack::build(&smf.tracks);

let mut track_color_id = 0;
let tracks: Vec<MidiTrack> = smf
Expand Down
104 changes: 50 additions & 54 deletions midi-file/src/utils.rs → midi-file/src/tempo_track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,6 @@ use crate::TempoEvent;
use midly::{MetaMessage, TrackEvent, TrackEventKind};
use std::{collections::HashMap, time::Duration};

fn pulse_to_duration(pulses: u64, tempo: u32, pulses_per_quarter_note: u16) -> Duration {
let u_time = pulses as f64 / pulses_per_quarter_note as f64;
// We floor only because Synthesia floors,
// so if we want to test for timing regresions we have to do the same
let time = (u_time * tempo as f64).floor() as u64;
Duration::from_micros(time)
}

pub fn pulses_to_duration(
tempo_events: &[TempoEvent],
event_pulses: u64,
pulses_per_quarter_note: u16,
) -> Duration {
let mut res = Duration::ZERO;

let mut hit = false;
let mut last_tempo_event_pulses = 0u64;
let mut running_tempo = 500_000;

let event_pulses = event_pulses;

for tempo_event in tempo_events.iter() {
let tempo_event_pulses = tempo_event.absolute_pulses;

// If the time we're asking to convert is still beyond
// this tempo event, just add the last time slice (at
// the previous tempo) to the running wall-clock time.
let delta_pulses = if event_pulses > tempo_event_pulses {
tempo_event_pulses - last_tempo_event_pulses
} else {
hit = true;
event_pulses - last_tempo_event_pulses
};

res += pulse_to_duration(delta_pulses, running_tempo, pulses_per_quarter_note);

// If the time we're calculating is before the tempo event we're
// looking at, we're done.
if hit {
break;
}

running_tempo = tempo_event.tempo;
last_tempo_event_pulses = tempo_event_pulses;
}

if !hit {
let remaining_pulses = event_pulses - last_tempo_event_pulses;
res += pulse_to_duration(remaining_pulses, running_tempo, pulses_per_quarter_note);
}

res
}

pub struct TempoTrack(Vec<TempoEvent>);

impl std::ops::Deref for TempoTrack {
Expand Down Expand Up @@ -110,4 +56,54 @@ impl TempoTrack {

TempoTrack(tempo_events)
}

pub fn pulses_to_duration(&self, event_pulses: u64, pulses_per_quarter_note: u16) -> Duration {
let mut res = Duration::ZERO;

let mut hit = false;
let mut last_tempo_event_pulses = 0u64;
let mut running_tempo = 500_000;

let event_pulses = event_pulses;

for tempo_event in self.0.iter() {
let tempo_event_pulses = tempo_event.absolute_pulses;

// If the time we're asking to convert is still beyond
// this tempo event, just add the last time slice (at
// the previous tempo) to the running wall-clock time.
let delta_pulses = if event_pulses > tempo_event_pulses {
tempo_event_pulses - last_tempo_event_pulses
} else {
hit = true;
event_pulses - last_tempo_event_pulses
};

res += pulse_to_duration(delta_pulses, running_tempo, pulses_per_quarter_note);

// If the time we're calculating is before the tempo event we're
// looking at, we're done.
if hit {
break;
}

running_tempo = tempo_event.tempo;
last_tempo_event_pulses = tempo_event_pulses;
}

if !hit {
let remaining_pulses = event_pulses - last_tempo_event_pulses;
res += pulse_to_duration(remaining_pulses, running_tempo, pulses_per_quarter_note);
}

res
}
}

fn pulse_to_duration(pulses: u64, tempo: u32, pulses_per_quarter_note: u16) -> Duration {
let u_time = pulses as f64 / pulses_per_quarter_note as f64;
// We floor only because Synthesia floors,
// so if we want to test for timing regresions we have to do the same
let time = (u_time * tempo as f64).floor() as u64;
Duration::from_micros(time)
}
14 changes: 7 additions & 7 deletions midi-file/src/track.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::time::Duration;

use crate::{pulses_to_duration, TempoTrack};
use crate::tempo_track::TempoTrack;

use {
midly::{MidiMessage, TrackEvent, TrackEventKind},
Expand Down Expand Up @@ -64,14 +64,14 @@ impl MidiTrack {
pub fn new(
track_id: usize,
track_color_id: usize,
tempo_events: &TempoTrack,
tempo_track: &TempoTrack,
track_events: &[TrackEvent],
pulses_per_quarter_note: u16,
) -> Self {
let notes = build_notes(
track_id,
track_color_id,
tempo_events,
tempo_track,
track_events,
pulses_per_quarter_note,
);
Expand All @@ -88,7 +88,7 @@ impl MidiTrack {
match event.kind {
TrackEventKind::Midi { channel, message } => {
let timestamp =
pulses_to_duration(tempo_events, pulses, pulses_per_quarter_note);
tempo_track.pulses_to_duration(pulses, pulses_per_quarter_note);

let message = match message {
midly::MidiMessage::NoteOn { key, vel } => {
Expand Down Expand Up @@ -145,7 +145,7 @@ impl MidiTrack {
fn build_notes(
track_id: usize,
track_color_id: usize,
tempo_events: &TempoTrack,
tempo_track: &TempoTrack,
track_events: &[TrackEvent],
pulses_per_quarter_note: u16,
) -> Vec<MidiNote> {
Expand Down Expand Up @@ -175,8 +175,8 @@ fn build_notes(
let start = active.pulses;
let end = pulses;

let start = pulses_to_duration(tempo_events, start, pulses_per_quarter_note);
let end = pulses_to_duration(tempo_events, end, pulses_per_quarter_note);
let start = tempo_track.pulses_to_duration(start, pulses_per_quarter_note);
let end = tempo_track.pulses_to_duration(end, pulses_per_quarter_note);
let duration = end - start;

let note = MidiNote {
Expand Down

0 comments on commit 56cabe4

Please sign in to comment.