From 56cabe4ab6e141f94c8c1892a6b0f77ddcfe75d6 Mon Sep 17 00:00:00 2001 From: PolyMeilex Date: Sun, 17 Sep 2023 22:32:35 +0200 Subject: [PATCH] Move tempo track out of utils --- midi-file/src/lib.rs | 4 +- midi-file/src/midi.rs | 4 +- midi-file/src/{utils.rs => tempo_track.rs} | 104 ++++++++++----------- midi-file/src/track.rs | 14 +-- 4 files changed, 61 insertions(+), 65 deletions(-) rename midi-file/src/{utils.rs => tempo_track.rs} (61%) diff --git a/midi-file/src/lib.rs b/midi-file/src/lib.rs index eabe553b..ce3c34c7 100644 --- a/midi-file/src/lib.rs +++ b/midi-file/src/lib.rs @@ -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", diff --git a/midi-file/src/midi.rs b/midi-file/src/midi.rs index 49a4bd27..fa278e45 100644 --- a/midi-file/src/midi.rs +++ b/midi-file/src/midi.rs @@ -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}; @@ -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 = smf diff --git a/midi-file/src/utils.rs b/midi-file/src/tempo_track.rs similarity index 61% rename from midi-file/src/utils.rs rename to midi-file/src/tempo_track.rs index 5f0d4e5f..ff0d44bf 100644 --- a/midi-file/src/utils.rs +++ b/midi-file/src/tempo_track.rs @@ -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); impl std::ops::Deref for TempoTrack { @@ -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) } diff --git a/midi-file/src/track.rs b/midi-file/src/track.rs index dc00ce9c..1f0a5db0 100644 --- a/midi-file/src/track.rs +++ b/midi-file/src/track.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use crate::{pulses_to_duration, TempoTrack}; +use crate::tempo_track::TempoTrack; use { midly::{MidiMessage, TrackEvent, TrackEventKind}, @@ -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, ); @@ -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 } => { @@ -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 { @@ -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 {