From f5be68d0d5b1300cd751f277e0506a4d5fea52d1 Mon Sep 17 00:00:00 2001 From: Denis Redozubov Date: Mon, 15 May 2023 19:45:36 +0400 Subject: [PATCH] Compiles, untested EventIterator --- Cargo.lock | 7 ++ Cargo.toml | 3 +- src/dsl/dsl.rs | 16 +--- src/midi/core.rs | 225 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 192 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c696a5..aea78a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + [[package]] name = "either" version = "1.8.1" @@ -330,6 +336,7 @@ version = "0.1.0" dependencies = [ "clap", "derive_more", + "dyn-clone", "midly", "nom", ] diff --git a/Cargo.toml b/Cargo.toml index 650264f..46f931b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ path = "src/bin/main.rs" nom = "*" midly = "0.5.3" derive_more = "*" -clap = { version = "4.2.7", features = ["derive"] } \ No newline at end of file +clap = { version = "4.2.7", features = ["derive"] } +dyn-clone = "1.0.11" \ No newline at end of file diff --git a/src/dsl/dsl.rs b/src/dsl/dsl.rs index d537a62..7027340 100644 --- a/src/dsl/dsl.rs +++ b/src/dsl/dsl.rs @@ -1,6 +1,6 @@ use std::str; use std::vec::Vec; -use std::ops::{Add, Mul}; +use std::ops::{Add}; pub use nom::character::complete::{char, digit1}; use nom::multi::many1; @@ -110,20 +110,6 @@ pub enum Length { // } // } -impl Add for Length { - type Output = Self; - - fn add(self, rhs: Length) -> Length { - match self { - Length::Simple(mlen) => todo!(), - Length::Tied(_, _) => todo!(), - Length::Triplet(_) => todo!(), - } - } -} - - - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Note { Hit, diff --git a/src/midi/core.rs b/src/midi/core.rs index c01ca5c..fa07b32 100644 --- a/src/midi/core.rs +++ b/src/midi/core.rs @@ -1,12 +1,15 @@ extern crate derive_more; use std::cmp::Ordering; use std::collections::HashMap; +use std::iter::Cycle; +use std::iter::Peekable; use std::ops::{Add, Mul}; +use std::path::Iter; use midly::{ num::u15, num::u24, num::u28, num::u4, num::u7, Header, MidiMessage, Smf, Track, TrackEventKind, }; -use midly::{MetaMessage, TrackEvent}; +use midly::{EventIter, MetaMessage, TrackEvent}; use crate::dsl::dsl::{ group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Length, ModdedLength, Note, @@ -25,6 +28,7 @@ use crate::dsl::dsl::{ derive_more::Add, derive_more::Sub, derive_more::Mul, + derive_more::Rem, derive_more::Display, )] #[repr(transparent)] @@ -181,7 +185,7 @@ fn test_converges() { denominator: BasicLength::Fourth, }; assert_eq!( - three_sixteenth.converges(vec![four_fourth, three_fourth]), + three_sixteenth.converges(vec![four_fourth, three_fourth, four_fourth]), Ok((3, four_fourth)) ); } @@ -219,6 +223,30 @@ pub struct EventGrid { length: Tick, } +impl IntoIterator for EventGrid { + type Item = Event; + type IntoIter = std::vec::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.events.into_iter() + } +} + +impl EventGrid { + pub fn iter(&self) -> std::slice::Iter<'_, Event> { + self.events.iter() + } +} + +// impl Iterator for EventGrid { +// type Item = Event; + +// fn next(&mut self) -> Option { +// let e = self.events.iter().next(); +// e.map(|x| *x ) +// } +// } + impl + Clone + Ord> Add for EventGrid { type Output = EventGrid; @@ -606,7 +634,7 @@ fn test_cycle_grid() { ); } -fn flatten_groups(part: Part, groups: Vec) -> EventGrid { +fn flatten_groups(part: Part, groups: &Vec) -> EventGrid { let mut time: Tick = Tick(0); let mut grid: EventGrid = EventGrid::new(); groups.iter().for_each(|group| { @@ -616,8 +644,7 @@ fn flatten_groups(part: Part, groups: Vec) -> EventGrid { } // Combines a vector of sorted EventGrid into a single `EventGrid` -fn merge_event_grids(mut eg: Vec>) -> 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(); @@ -625,18 +652,129 @@ fn merge_event_grids(mut eg: Vec>) -> EventGrid }) } -// Returns time as a number of ticks from beginning, has to be turned into the midi delta-time. -fn flatten_and_merge(groups: HashMap>) -> EventGrid { - let mut eg = Vec::new(); - for (part, group) in groups { - eg.push(flatten_groups(part, group)) +pub struct EventIterator +where + T:Clone +{ + kick: Peekable>>>, + snare: Peekable>>>, + hihat: Peekable>>>, + crash: Peekable>>>, + kick_length: Tick, + snare_length: Tick, + hihat_length: Tick, + crash_length: Tick, + limit: Tick, + time: Tick +} + +impl EventIterator +where + T: Clone + { + fn new( + kick_grid: EventGrid, + snare_grid: EventGrid, + hihat_grid: EventGrid, + crash_grid: EventGrid, + limit_value: Tick, + ) -> EventIterator { + let event_iterator = EventIterator { + kick_length: kick_grid.length.clone(), + snare_length: snare_grid.length.clone(), + hihat_length: hihat_grid.length.clone(), + crash_length: crash_grid.length.clone(), + kick: kick_grid.into_iter().cycle().peekable(), + snare: snare_grid.into_iter().cycle().peekable(), + hihat: hihat_grid.into_iter().cycle().peekable(), + crash: crash_grid.into_iter().cycle().peekable(), + limit: limit_value, + time: Tick(0) + }; + event_iterator } - merge_event_grids(eg) +} + +impl Iterator for EventIterator { + type Item = Event; + + fn next(&mut self) -> Option { + 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 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), + ]; + + for (&e, p) in candidates { + if e.tick <= min_tick { + min_part = p; + min_tick = e.tick; + min_event = e; + } else { + continue; + } + } + + match min_part { + Part::KickDrum => { + self.kick.next(); + min_group_length = self.kick_length; + }, + Part::SnareDrum => { + self.snare.next(); + min_group_length = self.snare_length; + }, + Part::HiHat => { + self.hihat.next(); + min_group_length = self.hihat_length; + }, + Part::CrashCymbal => { + self.crash.next(); + min_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) + } + } else { + None + } + } +} + +// Returns time as a number of ticks from beginning, has to be turned into the midi delta-time. +fn flatten_and_merge<'a>( + mut groups: HashMap>, + time_signature: TimeSignature, +) -> EventIterator { + let f = |p| { + groups + .get(&p) + .map(|g| flatten_groups(p, g)) + .unwrap_or(EventGrid::new()) + }; + let kick = f(Part::KickDrum); + let snare = f(Part::SnareDrum); + let hihat = f(Part::HiHat); + let crash = f(Part::CrashCymbal); + EventIterator::new(kick, snare, hihat, crash, Tick(1000000)) } // 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 = create_tracks(groups, time_signature); // FIXME + 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. @@ -650,35 +788,36 @@ 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>> { - let event_grid = flatten_and_merge(parts_and_groups).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, - }, - }) - } +// /// 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, +// }, +// }) +// } - vec![drums] -} +// vec![drums] +// }