diff --git a/src/midi/core.rs b/src/midi/core.rs index e9f9110..6645b7c 100644 --- a/src/midi/core.rs +++ b/src/midi/core.rs @@ -1,11 +1,12 @@ extern crate derive_more; +use std::cmp::Ordering; use std::collections::HashMap; use std::ops::Add; use midly::{live::LiveEvent, num::u15, Header, MidiMessage, Smf, Track}; use crate::dsl::dsl::{ - group_or_delimited_group, groups, BasicLength, Length, Group, ModdedLength, Note, Times, + group_or_delimited_group, groups, BasicLength, Group, Length, ModdedLength, Note, Times, }; // Typically used as number of ticks since the beginning of the track. @@ -51,6 +52,116 @@ pub struct TimeSignature { pub denominator: BasicLength, } +#[test] +fn test_cmp_time_signature() { + let three_sixteenth = TimeSignature { + numerator: 3, + denominator: BasicLength::Sixteenth, + }; + let four_fourth = TimeSignature { + numerator: 4, + denominator: BasicLength::Fourth, + }; + let two_secondth = TimeSignature { + numerator: 2, + denominator: BasicLength::Half, + }; + assert_eq!(three_sixteenth.cmp(&four_fourth), Ordering::Less); + // weird, but not worth changing + // May implement a new type Ord if it needs to be Equal. + assert_eq!(four_fourth.cmp(&two_secondth), Ordering::Greater); +} + +impl TimeSignature { + /// Checks if these two signatures converges for the next 200 bars. + fn converges_with(&self, other: TimeSignature) -> Result<(u32, TimeSignature), String> { + let d: u32 = std::cmp::max(self.denominator, other.denominator) + .to_u8() + .into(); + let d1: u32 = self.denominator.to_u8().into(); + let d2: u32 = other.denominator.to_u8().into(); + let coef1 = d / d1; + let coef2 = d / d2; + let num1: u32 = coef1 * (self.numerator as u32); + let num2: u32 = coef2 * (other.numerator as u32); + let greater_time_signature = self.max(&other); + let f = |max, min| { + let mut res = Err(format!("Not converges over 1000 bars of {:?}", other)); + for i in 1..1000 { + if (max * i) % min == 0 { + res = Ok((i, *greater_time_signature)); + break; + } + } + res + }; + match num1.cmp(&num2) { + std::cmp::Ordering::Less => f(num2, num1), + std::cmp::Ordering::Equal => Ok((1, *greater_time_signature)), + std::cmp::Ordering::Greater => f(num1, num2), + } + } + + fn converges( + &self, + time_signatures: Vec, + ) -> Result<(u32, TimeSignature), String> { + time_signatures + .iter() + .try_fold((1, *self), |(bars, ts), x| match ts.converges_with(*x) { + Ok((new_bars, greater_signature)) => { + if new_bars > bars { + if new_bars % bars == 0 { + Ok((new_bars, greater_signature)) + } else { + Err(format!("{:?} don't converge with {:?}", self, x)) + } + } else { + if bars % new_bars == 0 { + Ok((bars, greater_signature)) + } else { + Err(format!("{:?} don't converge with {:?}", self, x)) + } + } + } + Err(e) => Err(e), + }) + } +} + +#[test] +fn test_converges_with() { + let three_sixteenth = TimeSignature { + numerator: 3, + denominator: BasicLength::Sixteenth, + }; + let four_fourth = TimeSignature { + numerator: 4, + denominator: BasicLength::Fourth, + }; + assert_eq!( + three_sixteenth.converges_with(four_fourth), + Ok((3, four_fourth)) + ); +} + +#[test] +fn test_converges() { + let three_sixteenth = TimeSignature { + numerator: 3, + denominator: BasicLength::Sixteenth, + }; + let four_fourth = TimeSignature { + numerator: 4, + denominator: BasicLength::Fourth, + }; + let three_fourth = TimeSignature { + numerator: 3, + denominator: BasicLength::Fourth + }; + assert_eq!(three_sixteenth.converges(vec![four_fourth, three_fourth]), Ok((3, four_fourth))); +} + #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)] pub enum Part { KickDrum, @@ -59,6 +170,18 @@ pub enum Part { CrashCymbal, } +impl Part { + // https://computermusicresource.com/GM.Percussion.KeyMap.html + fn to_midi_key(&self) -> u8 { + match self { + Part::KickDrum => 36, + Part::SnareDrum => 38, + Part::HiHat => 46, + Part::CrashCymbal => 49, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Event { tick: T, @@ -151,7 +274,10 @@ impl EventGrid { for e in &self.events { let delta = e.tick - time; time = time + delta; - delta_grid.events.push(Event { tick: Delta(delta.0), event_type: e.event_type }) + delta_grid.events.push(Event { + tick: Delta(delta.0), + event_type: e.event_type, + }) } delta_grid } @@ -173,6 +299,18 @@ impl BasicLength { BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128), } } + + fn to_u8(&self) -> u8 { + match self { + BasicLength::Whole => 1, + BasicLength::Half => 2, + BasicLength::Fourth => 4, + BasicLength::Eighth => 8, + BasicLength::Sixteenth => 16, + BasicLength::ThirtySecond => 32, + BasicLength::SixtyFourth => 64, + } + } } impl ModdedLength { @@ -193,24 +331,22 @@ impl Length { /// Note length to MIDI ticks /// The function converts a musical note length to ticks, accounting for simple notes, tied notes, and /// triplets. - /// + /// /// Arguments: - /// + /// /// * `length`: `length` is a variable of type `Length`, which is an enum that represents different /// types of musical note lengths. The function `length_to_ticks` takes a `Length` as input and returns /// a `Tick`, which is a struct representing the number of ticks (a unit of time in music - /// + /// /// Returns: - /// + /// /// The function `length_to_ticks` takes a `Length` enum as input and returns a `Tick` value. The `Tick` /// value represents the duration of the note in ticks, which is a unit of time used in music notation /// software. fn to_ticks(&self) -> Tick { match self { Length::Simple(mlen) => mlen.to_ticks(), - Length::Tied(first, second) => { - first.to_ticks() + second.to_ticks() - } + Length::Tied(first, second) => first.to_ticks() + second.to_ticks(), Length::Triplet(mlen) => { let Tick(straight) = mlen.to_ticks(); let triplet = straight * 2 / 3; @@ -360,13 +496,13 @@ fn flatten_groups(part: Part, groups: Vec) -> EventGrid { } // Combines multiple sorted EventGrid -fn combine_event_grids<'a, T>(a: EventGrid, b : EventGrid) -> EventGrid +fn combine_event_grids<'a, T>(a: EventGrid, b: EventGrid) -> EventGrid where T: Ord, - EventGrid: Add, Output = EventGrid> + EventGrid: Add, Output = EventGrid>, { let mut all_events = a + b; - all_events.events.sort_by(|e1, e2| { e1.tick.cmp(&e2.tick)} ); + all_events.events.sort_by(|e1, e2| e1.tick.cmp(&e2.tick)); all_events } @@ -374,7 +510,7 @@ where fn merge_event_grids(mut eg: Vec>) -> EventGrid where T: Ord, - EventGrid: Add, Output = EventGrid> + Clone + EventGrid: Add, Output = EventGrid> + Clone, { let first = eg.pop().unwrap(); eg.iter().fold(first, |mut acc, next| { @@ -409,6 +545,10 @@ pub fn create_smf<'a>(groups: HashMap>) -> Smf<'a> { } /// Translates drum parts to a single MIDI track. -fn create_tracks<'a>(parts_and_groups: HashMap>) -> Vec>> { +fn create_tracks<'a>( + parts_and_groups: HashMap>, +) -> Vec>> { let event_grid = flatten_and_merge(parts_and_groups).to_delta(); + let drums = Vec::new(); + vec![drums] }