From a94a2d331a8a2232a5f032f6427a7bb390f54c82 Mon Sep 17 00:00:00 2001 From: Denis Redozubov Date: Tue, 16 May 2023 16:31:57 +0400 Subject: [PATCH] EventIterator in progress --- src/midi/core.rs | 398 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 296 insertions(+), 102 deletions(-) diff --git a/src/midi/core.rs b/src/midi/core.rs index fa07b32..0a65356 100644 --- a/src/midi/core.rs +++ b/src/midi/core.rs @@ -52,6 +52,15 @@ pub enum EventType { NoteOff(Part), } +impl EventType { + fn is_note_on(&self) -> bool { + match self { + EventType::NoteOn(_) => true, + EventType::NoteOff(_) => false, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct TimeSignature { pub numerator: u8, @@ -643,42 +652,27 @@ fn flatten_groups(part: Part, groups: &Vec) -> EventGrid { grid } -// Combines a vector of sorted EventGrid into a single `EventGrid` -fn merge_event_grids(mut eg: Vec>) -> EventGrid { - let first = eg.pop().unwrap(); - eg.iter().fold(first, |mut acc, next| { - acc = acc * (*next).clone(); - acc - }) -} - -pub struct EventIterator -where - T:Clone -{ - kick: Peekable>>>, - snare: Peekable>>>, - hihat: Peekable>>>, - crash: Peekable>>>, +pub struct EventIterator { + kick: Peekable>>>, + snare: Peekable>>>, + hihat: Peekable>>>, + crash: Peekable>>>, kick_length: Tick, snare_length: Tick, hihat_length: Tick, crash_length: Tick, limit: Tick, - time: Tick + time: Tick, } -impl EventIterator -where - T: Clone - { +impl EventIterator { fn new( - kick_grid: EventGrid, - snare_grid: EventGrid, - hihat_grid: EventGrid, - crash_grid: EventGrid, + kick_grid: EventGrid, + snare_grid: EventGrid, + hihat_grid: EventGrid, + crash_grid: EventGrid, limit_value: Tick, - ) -> EventIterator { + ) -> EventIterator { let event_iterator = EventIterator { kick_length: kick_grid.length.clone(), snare_length: snare_grid.length.clone(), @@ -689,81 +683,262 @@ where hihat: hihat_grid.into_iter().cycle().peekable(), crash: crash_grid.into_iter().cycle().peekable(), limit: limit_value, - time: Tick(0) + time: Tick(0), }; event_iterator } } -impl Iterator for EventIterator { - type Item = Event; +impl Iterator for EventIterator { + type Item = (Event, Tick); fn next(&mut self) -> Option { + println!("============"); + println!("self.time: {}", self.time); let mut min_part = Part::KickDrum; let mut min_tick = self.limit; - let mut min_event: Event = Event { tick: Tick(0), event_type: EventType::NoteOn(Part::KickDrum) }; - let mut min_group_length: Tick; + let mut min_event: Event = Event { + tick: Tick(0), + event_type: EventType::NoteOn(Part::KickDrum), + }; + let candidates = vec![ - (self.kick.peek().unwrap(), Part::KickDrum), - (self.snare.peek().unwrap(), Part::SnareDrum), - (self.hihat.peek().unwrap(), Part::HiHat), - (self.crash.peek().unwrap(), Part::CrashCymbal), + (self.kick.peek(), Part::KickDrum), + (self.snare.peek(), Part::SnareDrum), + (self.hihat.peek(), Part::HiHat), + (self.crash.peek(), Part::CrashCymbal), ]; - for (&e, p) in candidates { - if e.tick <= min_tick { - min_part = p; - min_tick = e.tick; - min_event = e; - } else { - continue; + println!("candidates: {:?}", candidates); + + for (o, p) in candidates { + match o { + Some(e) => { + println!("{:?}", e); + + // FIXME + // fails with: + // + // self.time: 96 + // Event { tick: Tick(0), event_type: NoteOn(KickDrum) } + // e.tick <= min_tick + // Event { tick: Tick(96), event_type: NoteOff(SnareDrum) } + // continue + // Kick + // group_length: 96 + // self.time = group_length + // updated self.time: 96 + // self.time = self.limit + if e.tick == self.time { + println!("e.tick = self.time"); + min_part = p; + min_tick = e.tick; + min_event = *e; + continue; + } else if e.tick <= min_tick { + println!("e.tick <= min_tick"); + min_part = p; + min_tick = e.tick; + min_event = *e; + } else { + println!("continue"); + continue; + } + } + None => continue, } } + let mut group_length: Tick; + match min_part { Part::KickDrum => { + println!("Kick"); self.kick.next(); - min_group_length = self.kick_length; - }, + group_length = self.kick_length; + } Part::SnareDrum => { + println!("Snare"); self.snare.next(); - min_group_length = self.snare_length; - }, + group_length = self.snare_length; + } Part::HiHat => { self.hihat.next(); - min_group_length = self.hihat_length; - }, + group_length = self.hihat_length; + } Part::CrashCymbal => { self.crash.next(); - min_group_length = self.crash_length; - }, + group_length = self.crash_length; + } }; - if min_event.tick < self.limit { - self.time = self.time + min_event.tick; - if self.time > min_group_length { - let remainder = Tick(self.time.0 % min_group_length.0); - min_event.tick = self.time + remainder; - Some(min_event) - } else { - Some(min_event) + println!("group_length: {}", group_length); + self.time = match self.time.cmp(&group_length) { + Ordering::Less => { + println!("self.time < group_length"); + if min_event.tick == self.time { + println!( + "min_event.tick ({}) = self.time ({})", + min_event.tick, self.time + ); + Tick(self.time.0 + min_event.tick.0) + } else { + println!( + "min_event.tick ({}) <> self.time ({})", + min_event.tick, self.time + ); + Tick(self.time.0 + (self.time.0 % group_length.0) + min_event.tick.0) + } + } + Ordering::Equal => { + println!("self.time = group_length ({})", self.time); + if self.time == min_event.tick { + self.time + } else { + self.time + min_event.tick + } + + } + Ordering::Greater => { + println!("self.time ({}) > group_length ({})", self.time, group_length); + Tick(self.time.0 + (group_length.0 % self.time.0) + min_event.tick.0) + } + }; + + println!("updated self.time: {}", self.time); + + match self.time.cmp(&self.limit) { + Ordering::Less => { + println!("self.time < self.limit"); + min_event.tick = self.time; + Some((min_event, self.time)) + } + Ordering::Equal => { + println!("self.time = self.limit"); + if min_event.event_type.is_note_on() { + None + } else { + min_event.tick = self.time; + Some((min_event, self.time)) + } + } + Ordering::Greater => { + println!("self.time > self.limit"); + None } - } else { - None } } } +#[test] +fn test_event_iterator_impl() { + let empty = EventGrid::new(); + let flat_kick = flatten_group( + &group_or_delimited_group("(4x-)").unwrap().1, + Part::KickDrum, + &mut Tick(0), + ); + let snare = flatten_group( + &group_or_delimited_group("4-x").unwrap().1, + Part::SnareDrum, + &mut Tick(0), + ); + + assert_eq!( + EventIterator::new( + flat_kick.clone(), + snare.clone(), + empty.clone(), + empty.clone(), + Tick(96) + ) + .into_iter() + .map(|x| { x.0 }) + .collect::>>(), + vec![ + Event { + tick: Tick(0), + event_type: EventType::NoteOn(Part::KickDrum) + }, + Event { + tick: Tick(48), + event_type: EventType::NoteOn(Part::SnareDrum) + }, + Event { + tick: Tick(96), + event_type: EventType::NoteOff(Part::KickDrum) + }, + Event { + tick: Tick(96), + event_type: EventType::NoteOff(Part::SnareDrum) + } + ] + ); + + assert_eq!( + EventIterator::new( + flat_kick.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + Tick(96) + ) + .into_iter() + .map(|x| { x.0 }) + .collect::>>(), + [ + Event { + tick: Tick(0), + event_type: EventType::NoteOn(Part::KickDrum) + }, + Event { + tick: Tick(48), + event_type: EventType::NoteOff(Part::KickDrum) + } + ] + ); + assert_eq!( + EventIterator::new( + flat_kick.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + Tick(144) + ) + .into_iter() + .map(|x| { x.0 }) + .collect::>>(), + [ + Event { + tick: Tick(0), + event_type: EventType::NoteOn(Part::KickDrum) + }, + Event { + tick: Tick(48), + event_type: EventType::NoteOff(Part::KickDrum) + }, + Event { + tick: Tick(96), + event_type: EventType::NoteOn(Part::KickDrum) + }, + Event { + tick: Tick(144), + event_type: EventType::NoteOff(Part::KickDrum) + } + ] + ); +} + // Returns time as a number of ticks from beginning, has to be turned into the midi delta-time. -fn flatten_and_merge<'a>( +fn flatten_and_merge( mut groups: HashMap>, time_signature: TimeSignature, -) -> EventIterator { +) -> EventIterator { let f = |p| { groups - .get(&p) - .map(|g| flatten_groups(p, g)) - .unwrap_or(EventGrid::new()) + .get(&p) + .map(|g| flatten_groups(p, g)) + .unwrap_or(EventGrid::new()) }; let kick = f(Part::KickDrum); let snare = f(Part::SnareDrum); @@ -774,10 +949,10 @@ fn flatten_and_merge<'a>( // The length of a beat is not standard, so in order to fully describe the length of a MIDI tick the MetaMessage::Tempo event should be present. pub fn create_smf<'a>(groups: HashMap>, time_signature: TimeSignature) -> Smf<'a> { - let tracks = vec![] ; // create_tracks(groups, time_signature); // FIXME - // https://majicdesigns.github.io/MD_MIDIFile/page_timing.html - // says " If it is not specified the MIDI default is 48 ticks per quarter note." - // As it's required in `Header`, let's use the same value. + let tracks = vec![]; // create_tracks(groups, time_signature); // FIXME + // https://majicdesigns.github.io/MD_MIDIFile/page_timing.html + // says " If it is not specified the MIDI default is 48 ticks per quarter note." + // As it's required in `Header`, let's use the same value. let metrical = midly::Timing::Metrical(u15::new(TICKS_PER_QUARTER_NOTE)); Smf { header: Header { @@ -788,36 +963,55 @@ pub fn create_smf<'a>(groups: HashMap>, time_signature: TimeSig } } -// /// Translates drum parts to a single MIDI track. -// fn create_tracks<'a>( -// parts_and_groups: HashMap>, -// time_signature: TimeSignature, // tempo: u32 -// ) -> Vec>> { -// //FIXME: unhardcode time signature -// let event_grid = flatten_and_merge(parts_and_groups, TimeSignature { numerator: 4, denominator: BasicLength::Fourth ); -// let mut drums = Vec::new(); -// // let midi_tempo = MidiTempo::from_tempo(Tempo(130)).0; -// // drums.push(TrackEvent { delta: u28::from(0), kind: TrackEventKind::Meta(MetaMessage::Tempo(midi_tempo)) }); -// // drums.push(TrackEvent { delta: u28::from(0), kind: TrackEventKind::Meta(MetaMessage::TimeSignature(4, 4, MIDI_CLOCKS_PER_CLICK.clone(), 8))}); -// for event in event_grid.events { -// let midi_message = match event.event_type { -// EventType::NoteOn(part) => MidiMessage::NoteOn { -// key: part.to_midi_key(), -// vel: u7::from(120), -// }, -// EventType::NoteOff(part) => MidiMessage::NoteOff { -// key: part.to_midi_key(), -// vel: u7::from(0), -// }, -// }; -// drums.push(TrackEvent { -// delta: u28::from(event.tick.0 as u32), -// kind: TrackEventKind::Midi { -// channel: u4::from(10), -// message: midi_message, -// }, -// }) -// } +/// Translates drum parts to a single MIDI track. +fn create_tracks<'a>( + parts_and_groups: HashMap>, + time_signature: TimeSignature, // tempo: u32 +) -> Vec>> { + //FIXME: unhardcode time signature + let events_iter = flatten_and_merge( + parts_and_groups, + TimeSignature { + numerator: 4, + denominator: BasicLength::Fourth, + }, + ); + let event_pairs: Vec<(Event, Tick)> = events_iter.collect(); + let events = event_pairs.iter().map(|x| x.0).collect(); + let time = match event_pairs.last() { + Some((_, time)) => time, + None => { + panic!("Result has no midi notes") + } + }; + let event_grid = EventGrid { + events, + length: *time, + } + .to_delta(); + let mut drums = Vec::new(); + // let midi_tempo = MidiTempo::from_tempo(Tempo(130)).0; + // drums.push(TrackEvent { delta: u28::from(0), kind: TrackEventKind::Meta(MetaMessage::Tempo(midi_tempo)) }); + // drums.push(TrackEvent { delta: u28::from(0), kind: TrackEventKind::Meta(MetaMessage::TimeSignature(4, 4, MIDI_CLOCKS_PER_CLICK.clone(), 8))}); + for event in event_grid.events { + let midi_message = match event.event_type { + EventType::NoteOn(part) => MidiMessage::NoteOn { + key: part.to_midi_key(), + vel: u7::from(120), + }, + EventType::NoteOff(part) => MidiMessage::NoteOff { + key: part.to_midi_key(), + vel: u7::from(0), + }, + }; + drums.push(TrackEvent { + delta: u28::from(event.tick.0 as u32), + kind: TrackEventKind::Midi { + channel: u4::from(10), + message: midi_message, + }, + }) + } -// vec![drums] -// } + vec![drums] +}