diff --git a/midi-file/src/track.rs b/midi-file/src/track.rs index 0b59309e..54762823 100644 --- a/midi-file/src/track.rs +++ b/midi-file/src/track.rs @@ -1,4 +1,7 @@ -use midly::{MidiMessage, TrackEvent, TrackEventKind}; +use midly::{ + num::{u4, u7}, + MidiMessage, TrackEvent, TrackEventKind, +}; use std::{collections::HashMap, sync::Arc, time::Duration}; use crate::tempo_track::TempoTrack; @@ -53,104 +56,151 @@ impl MidiTrack { tempo_track: &TempoTrack, track_events: &[TrackEvent], ) -> Self { - std::thread::scope(|tb| { - let notes = - tb.spawn(|| build_notes(track_id, track_color_id, tempo_track, track_events)); + let ( + events, + EventsBuilder { + programs, + notes, + has_drums, + has_other_than_drums, + .. + }, + ) = build(track_id, track_color_id, tempo_track, track_events); + + Self { + track_id, + track_color_id, + notes: notes.into(), + events: events.into(), + programs: programs.into(), + has_drums, + has_other_than_drums, + } + } +} - let events = - tb.spawn(|| build_events(track_id, track_color_id, tempo_track, track_events)); +struct NoteInfo { + velocity: u8, + channel: u8, + timestamp: Duration, +} + +#[derive(Default)] +struct EventsBuilder { + programs: Vec, + has_drums: bool, + has_other_than_drums: bool, - let notes = notes.join().unwrap(); - let (events, programs, has_drums, has_other_than_drums) = events.join().unwrap(); + active_notes: HashMap, + notes: Vec, +} - Self { +impl EventsBuilder { + fn notes_on_event( + &mut self, + channel: u4, + message: &MidiMessage, + timestamp: Duration, + track_id: usize, + track_color_id: usize, + ) { + let (key, velocity) = match message { + MidiMessage::NoteOn { vel, key } => (key.as_int(), vel.as_int()), + MidiMessage::NoteOff { vel, key } => (key.as_int(), vel.as_int()), + _ => { + return; + } + }; + + if let Some(active) = self.active_notes.remove(&key) { + let start = active.timestamp; + let end = timestamp; + let duration = end - start; + + let note = MidiNote { + start, + end, + duration, + note: key, + velocity: active.velocity, + channel: active.channel, track_id, track_color_id, - notes: notes.into(), - events: events.into(), - programs: programs.into(), - has_drums, - has_other_than_drums, - } - }) + }; + + self.notes.push(note); + } + + if let MidiMessage::NoteOn { .. } = message { + let note = NoteInfo { + channel: channel.as_int(), + velocity, + timestamp, + }; + self.active_notes.insert(key, note); + } } -} -fn build_notes( - track_id: usize, - track_color_id: usize, - tempo_track: &TempoTrack, - track_events: &[TrackEvent], -) -> Vec { - struct NoteInfo { - velocity: u8, - channel: u8, - pulses: u64, + fn check_for_drums(&mut self, channel: u4) { + if channel == 9 || channel == 15 { + self.has_drums = true; + } else { + self.has_other_than_drums = true; + } } - let mut active_notes: HashMap = HashMap::new(); - let mut notes = Vec::new(); + fn on_program_change(&mut self, channel: u4, timestamp: Duration, program: u7) { + self.programs.push(ProgramEvent { + timestamp, + channel: channel.as_int(), + program: program.as_int(), + }); + } - let mut pulses: u64 = 0; - for event in track_events.iter() { - pulses += event.delta.as_int() as u64; - - if let TrackEventKind::Midi { channel, message } = event.kind { - let (key, velocity) = match message { - MidiMessage::NoteOn { vel, key } => (key.as_int(), vel.as_int()), - MidiMessage::NoteOff { vel, key } => (key.as_int(), vel.as_int()), - _ => { - continue; + fn on_event( + &mut self, + channel: u4, + message: MidiMessage, + timestamp: Duration, + track_id: usize, + track_color_id: usize, + ) -> MidiEvent { + let message = match message { + midly::MidiMessage::NoteOn { key, vel } => { + self.check_for_drums(channel); + + if vel.as_int() > 0 { + message + } else { + midly::MidiMessage::NoteOff { key, vel } } - }; - - if let Some(active) = active_notes.remove(&key) { - let start = active.pulses; - let end = pulses; - - let start = tempo_track.pulses_to_duration(start); - let end = tempo_track.pulses_to_duration(end); - let duration = end - start; - - let note = MidiNote { - start, - end, - duration, - note: key, - velocity: active.velocity, - channel: active.channel, - track_id, - track_color_id, - }; - - notes.push(note); } + midly::MidiMessage::ProgramChange { program } => { + self.on_program_change(channel, timestamp, program); + message + } + message => message, + }; - let on = matches!(&message, MidiMessage::NoteOn { .. }) && velocity > 0; + self.notes_on_event(channel, &message, timestamp, track_id, track_color_id); - if on { - let note = NoteInfo { - channel: channel.as_int(), - velocity, - pulses, - }; - active_notes.insert(key, note); - } + MidiEvent { + channel: channel.as_int(), + timestamp, + message, + track_id, + track_color_id, } } - - notes } -fn build_events( +fn build( track_id: usize, track_color_id: usize, tempo_track: &TempoTrack, track_events: &[TrackEvent], -) -> (Vec, Vec, bool, bool) { - let mut programs = Vec::new(); - let mut has_drums = false; - let mut has_other_than_drums = false; +) -> (Vec, EventsBuilder) { + let mut builder = EventsBuilder::default(); let mut pulses: u64 = 0; let events = track_events @@ -160,44 +210,12 @@ fn build_events( match event.kind { TrackEventKind::Midi { channel, message } => { let timestamp = tempo_track.pulses_to_duration(pulses); - - let message = match message { - midly::MidiMessage::NoteOn { key, vel } => { - if channel == 9 || channel == 15 { - has_drums = true; - } else { - has_other_than_drums = true; - } - - if vel.as_int() > 0 { - message - } else { - midly::MidiMessage::NoteOff { key, vel } - } - } - midly::MidiMessage::ProgramChange { program } => { - programs.push(ProgramEvent { - timestamp, - channel: channel.as_int(), - program: program.as_int(), - }); - message - } - message => message, - }; - - Some(MidiEvent { - channel: channel.as_int(), - timestamp, - message, - track_id, - track_color_id, - }) + Some(builder.on_event(channel, message, timestamp, track_id, track_color_id)) } _ => None, } }) .collect(); - (events, programs, has_drums, has_other_than_drums) + (events, builder) }