mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-25 21:37:43 +00:00
TimeSignature convergence
This commit is contained in:
parent
b48546af61
commit
69f389008c
1 changed files with 154 additions and 14 deletions
160
src/midi/core.rs
160
src/midi/core.rs
|
@ -1,11 +1,12 @@
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
use midly::{live::LiveEvent, num::u15, Header, MidiMessage, Smf, Track};
|
use midly::{live::LiveEvent, num::u15, Header, MidiMessage, Smf, Track};
|
||||||
|
|
||||||
use crate::dsl::dsl::{
|
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.
|
// Typically used as number of ticks since the beginning of the track.
|
||||||
|
@ -51,6 +52,116 @@ pub struct TimeSignature {
|
||||||
pub denominator: BasicLength,
|
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<TimeSignature>,
|
||||||
|
) -> 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)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||||
pub enum Part {
|
pub enum Part {
|
||||||
KickDrum,
|
KickDrum,
|
||||||
|
@ -59,6 +170,18 @@ pub enum Part {
|
||||||
CrashCymbal,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Event<T> {
|
pub struct Event<T> {
|
||||||
tick: T,
|
tick: T,
|
||||||
|
@ -151,7 +274,10 @@ impl EventGrid<Tick> {
|
||||||
for e in &self.events {
|
for e in &self.events {
|
||||||
let delta = e.tick - time;
|
let delta = e.tick - time;
|
||||||
time = time + delta;
|
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
|
delta_grid
|
||||||
}
|
}
|
||||||
|
@ -173,6 +299,18 @@ impl BasicLength {
|
||||||
BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128),
|
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 {
|
impl ModdedLength {
|
||||||
|
@ -208,9 +346,7 @@ impl Length {
|
||||||
fn to_ticks(&self) -> Tick {
|
fn to_ticks(&self) -> Tick {
|
||||||
match self {
|
match self {
|
||||||
Length::Simple(mlen) => mlen.to_ticks(),
|
Length::Simple(mlen) => mlen.to_ticks(),
|
||||||
Length::Tied(first, second) => {
|
Length::Tied(first, second) => first.to_ticks() + second.to_ticks(),
|
||||||
first.to_ticks() + second.to_ticks()
|
|
||||||
}
|
|
||||||
Length::Triplet(mlen) => {
|
Length::Triplet(mlen) => {
|
||||||
let Tick(straight) = mlen.to_ticks();
|
let Tick(straight) = mlen.to_ticks();
|
||||||
let triplet = straight * 2 / 3;
|
let triplet = straight * 2 / 3;
|
||||||
|
@ -360,13 +496,13 @@ fn flatten_groups(part: Part, groups: Vec<Group>) -> EventGrid<Tick> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combines multiple sorted EventGrid<Tick>
|
// Combines multiple sorted EventGrid<Tick>
|
||||||
fn combine_event_grids<'a, T>(a: EventGrid<T>, b : EventGrid<T>) -> EventGrid<T>
|
fn combine_event_grids<'a, T>(a: EventGrid<T>, b: EventGrid<T>) -> EventGrid<T>
|
||||||
where
|
where
|
||||||
T: Ord,
|
T: Ord,
|
||||||
EventGrid<T>: Add<EventGrid<T>, Output = EventGrid<T>>
|
EventGrid<T>: Add<EventGrid<T>, Output = EventGrid<T>>,
|
||||||
{
|
{
|
||||||
let mut all_events = a + b;
|
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
|
all_events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +510,7 @@ where
|
||||||
fn merge_event_grids<T>(mut eg: Vec<EventGrid<T>>) -> EventGrid<T>
|
fn merge_event_grids<T>(mut eg: Vec<EventGrid<T>>) -> EventGrid<T>
|
||||||
where
|
where
|
||||||
T: Ord,
|
T: Ord,
|
||||||
EventGrid<T>: Add<EventGrid<T>, Output = EventGrid<T>> + Clone
|
EventGrid<T>: Add<EventGrid<T>, Output = EventGrid<T>> + Clone,
|
||||||
{
|
{
|
||||||
let first = eg.pop().unwrap();
|
let first = eg.pop().unwrap();
|
||||||
eg.iter().fold(first, |mut acc, next| {
|
eg.iter().fold(first, |mut acc, next| {
|
||||||
|
@ -409,6 +545,10 @@ pub fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>) -> Smf<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates drum parts to a single MIDI track.
|
/// Translates drum parts to a single MIDI track.
|
||||||
fn create_tracks<'a>(parts_and_groups: HashMap<Part, Vec<Group>>) -> Vec<Vec<midly::TrackEvent<'a>>> {
|
fn create_tracks<'a>(
|
||||||
|
parts_and_groups: HashMap<Part, Vec<Group>>,
|
||||||
|
) -> Vec<Vec<midly::TrackEvent<'a>>> {
|
||||||
let event_grid = flatten_and_merge(parts_and_groups).to_delta();
|
let event_grid = flatten_and_merge(parts_and_groups).to_delta();
|
||||||
|
let drums = Vec::new();
|
||||||
|
vec![drums]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue