mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-24 21:07:42 +00:00
Fix length measurements
This commit is contained in:
parent
a94a2d331a
commit
081188e80a
6 changed files with 415 additions and 237 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -9,6 +9,7 @@
|
||||||
"./Cargo.toml",
|
"./Cargo.toml",
|
||||||
"./Cargo.toml",
|
"./Cargo.toml",
|
||||||
"./Cargo.toml",
|
"./Cargo.toml",
|
||||||
|
"./Cargo.toml",
|
||||||
"./Cargo.toml"
|
"./Cargo.toml"
|
||||||
],
|
],
|
||||||
"docwriter.style": "RustDoc"
|
"docwriter.style": "RustDoc"
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::collections::HashMap;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use poly::dsl::dsl;
|
use poly::dsl::dsl;
|
||||||
use poly::midi;
|
use poly::midi::core::{Part, create_smf};
|
||||||
use poly::midi::core::{Part, create_smf, TimeSignature};
|
use poly::midi::time::TimeSignature;
|
||||||
|
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
|
||||||
|
|
123
src/dsl/dsl.rs
123
src/dsl/dsl.rs
|
@ -10,6 +10,12 @@ use nom::branch::alt;
|
||||||
|
|
||||||
use nom::combinator::{map, map_res};
|
use nom::combinator::{map, map_res};
|
||||||
|
|
||||||
|
|
||||||
|
/// Allows measurement in 128th notes.
|
||||||
|
pub trait KnownLength {
|
||||||
|
fn to_128th(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum BasicLength {
|
pub enum BasicLength {
|
||||||
Whole,
|
Whole,
|
||||||
|
@ -21,6 +27,20 @@ pub enum BasicLength {
|
||||||
SixtyFourth
|
SixtyFourth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KnownLength for BasicLength {
|
||||||
|
fn to_128th(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
BasicLength::Whole => 128,
|
||||||
|
BasicLength::Half => 64,
|
||||||
|
BasicLength::Fourth => 32,
|
||||||
|
BasicLength::Eighth => 16,
|
||||||
|
BasicLength::Sixteenth => 8,
|
||||||
|
BasicLength::ThirtySecond => 4,
|
||||||
|
BasicLength::SixtyFourth => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BasicLength {
|
impl BasicLength {
|
||||||
/// Unsafe method, so it should only be used from `add`.
|
/// Unsafe method, so it should only be used from `add`.
|
||||||
fn from_num(n: u16) -> Self {
|
fn from_num(n: u16) -> Self {
|
||||||
|
@ -87,6 +107,18 @@ pub enum ModdedLength {
|
||||||
Dotted(BasicLength)
|
Dotted(BasicLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KnownLength for ModdedLength {
|
||||||
|
fn to_128th(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
ModdedLength::Plain(bl) => bl.to_128th(),
|
||||||
|
ModdedLength::Dotted(bl) => {
|
||||||
|
let l = bl.to_128th();
|
||||||
|
l + l /2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Length {
|
pub enum Length {
|
||||||
Simple(ModdedLength),
|
Simple(ModdedLength),
|
||||||
|
@ -94,21 +126,15 @@ pub enum Length {
|
||||||
Triplet(ModdedLength)
|
Triplet(ModdedLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Length {
|
impl KnownLength for Length {
|
||||||
// fn from_group(group: Group) -> Self {
|
fn to_128th(&self) -> u32 {
|
||||||
// let mut numerator = 0;
|
match self {
|
||||||
// for x in group.notes {
|
Length::Simple(ml) => ml.to_128th(),
|
||||||
// match x {
|
Length::Tied(ml1, ml2) => ml1.to_128th() + ml2.to_128th(),
|
||||||
// crate::dsl::dsl::GroupOrNote::SingleGroup(g) => {
|
Length::Triplet(ml) => ml.to_128th() * 2 / 3
|
||||||
// todo!()
|
}
|
||||||
// },
|
}
|
||||||
// crate::dsl::dsl::GroupOrNote::SingleNote(_) => {
|
}
|
||||||
// numerator = numerator + 1;
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Note {
|
pub enum Note {
|
||||||
|
@ -117,6 +143,7 @@ pub enum Note {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct Times(pub u16);
|
pub struct Times(pub u16);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -132,6 +159,20 @@ pub struct Group {
|
||||||
pub times: Times
|
pub times: Times
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KnownLength for Group {
|
||||||
|
fn to_128th(&self) -> u32 {
|
||||||
|
let mut acc = 0;
|
||||||
|
let note_length = self.length.to_128th();
|
||||||
|
for group in self.notes.iter() {
|
||||||
|
match group {
|
||||||
|
GroupOrNote::SingleGroup(subgroup) => { acc += subgroup.to_128th(); },
|
||||||
|
GroupOrNote::SingleNote(_) => { acc += note_length; },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
acc * self.times.0 as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for Group {
|
impl std::ops::Deref for Group {
|
||||||
type Target = Vec<GroupOrNote>;
|
type Target = Vec<GroupOrNote>;
|
||||||
|
|
||||||
|
@ -141,61 +182,61 @@ impl std::ops::Deref for Group {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static WHOLE : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Whole));
|
pub static WHOLE : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Whole));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static HALF : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Half));
|
pub static HALF : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Half));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Fourth));
|
pub static FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Fourth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static EIGHTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Eighth));
|
pub static EIGHTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Eighth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTEENTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Sixteenth));
|
pub static SIXTEENTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static THIRTY_SECOND : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::ThirtySecond));
|
pub static THIRTY_SECOND : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTY_FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::SixtyFourth));
|
pub static SIXTY_FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static WHOLE_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Whole));
|
pub static WHOLE_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Whole));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static HALF_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Half));
|
pub static HALF_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Half));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Fourth));
|
pub static FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Fourth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static EIGHTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Eighth));
|
pub static EIGHTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Eighth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTEENTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Sixteenth));
|
pub static SIXTEENTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Sixteenth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static THIRTY_SECOND_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::ThirtySecond));
|
pub static THIRTY_SECOND_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::ThirtySecond));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTY_FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::SixtyFourth));
|
pub static SIXTY_FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::SixtyFourth));
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static WHOLE_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Whole));
|
pub static WHOLE_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Whole));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static HALF_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Half));
|
pub static HALF_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Half));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Fourth));
|
pub static FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Fourth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static EIGHTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Eighth));
|
pub static EIGHTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Eighth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTEENTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Sixteenth));
|
pub static SIXTEENTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static THIRTY_SECOND_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::ThirtySecond));
|
pub static THIRTY_SECOND_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static SIXTY_FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::SixtyFourth));
|
pub static SIXTY_FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static HIT : GroupOrNote = GroupOrNote::SingleNote(Note::Hit);
|
pub static HIT : GroupOrNote = GroupOrNote::SingleNote(Note::Hit);
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static REST : GroupOrNote = GroupOrNote::SingleNote(Note::Rest);
|
pub static REST : GroupOrNote = GroupOrNote::SingleNote(Note::Rest);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static ONCE : &Times = &Times(1);
|
pub static ONCE : &Times = &Times(1);
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static TWICE: &Times = &Times(2);
|
pub static TWICE: &Times = &Times(2);
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static THRICE : &Times = &Times(3);
|
pub static THRICE : &Times = &Times(3);
|
||||||
|
|
||||||
|
|
||||||
fn hit(input: &str) -> IResult<&str, Note> {
|
fn hit(input: &str) -> IResult<&str, Note> {
|
||||||
|
|
294
src/midi/core.rs
294
src/midi/core.rs
|
@ -15,22 +15,10 @@ use crate::dsl::dsl::{
|
||||||
group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Length, ModdedLength, Note,
|
group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Length, ModdedLength, Note,
|
||||||
Times,
|
Times,
|
||||||
};
|
};
|
||||||
|
use crate::midi::time::{TimeSignature};
|
||||||
|
|
||||||
// Typically used as number of ticks since the beginning of the track.
|
// Typically used as number of ticks since the beginning of the track.
|
||||||
#[derive(
|
#[derive(Debug, Clone, Copy, PartialEq,Eq,PartialOrd,Ord,derive_more::Add, derive_more::Sub, derive_more::Mul, derive_more::Rem,derive_more::Display)]
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
PartialOrd,
|
|
||||||
Ord,
|
|
||||||
derive_more::Add,
|
|
||||||
derive_more::Sub,
|
|
||||||
derive_more::Mul,
|
|
||||||
derive_more::Rem,
|
|
||||||
derive_more::Display,
|
|
||||||
)]
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Tick(pub u128);
|
pub struct Tick(pub u128);
|
||||||
|
|
||||||
|
@ -46,12 +34,23 @@ fn test_add_tick() {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Delta(pub u128);
|
pub struct Delta(pub u128);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
NoteOn(Part),
|
NoteOn(Part),
|
||||||
NoteOff(Part),
|
NoteOff(Part),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ord for EventType {
|
||||||
|
fn cmp(&self, other: &EventType) -> Ordering {
|
||||||
|
match (self, other) {
|
||||||
|
(EventType::NoteOn(a), EventType::NoteOn(b)) => a.cmp(b),
|
||||||
|
(EventType::NoteOn(_), EventType::NoteOff(_)) => Ordering::Greater,
|
||||||
|
(EventType::NoteOff(_), EventType::NoteOn(_)) => Ordering::Less,
|
||||||
|
(EventType::NoteOff(a), EventType::NoteOff(b)) => a.cmp(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EventType {
|
impl EventType {
|
||||||
fn is_note_on(&self) -> bool {
|
fn is_note_on(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -61,144 +60,6 @@ impl EventType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct TimeSignature {
|
|
||||||
pub numerator: u8,
|
|
||||||
pub denominator: BasicLength,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimeSignature {
|
|
||||||
pub fn new(numerator: u8, denominator: BasicLength) -> Self {
|
|
||||||
Self {
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Mul<u8> for TimeSignature {
|
|
||||||
type Output = TimeSignature;
|
|
||||||
fn mul(self, rhs: u8) -> TimeSignature {
|
|
||||||
TimeSignature {
|
|
||||||
numerator: self.numerator * rhs as u8,
|
|
||||||
denominator: self.denominator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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, four_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,
|
||||||
|
@ -219,14 +80,40 @@ impl Part {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||||
pub struct Event<T> {
|
pub struct Event<T> {
|
||||||
tick: T,
|
tick: T,
|
||||||
event_type: EventType,
|
event_type: EventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl <T> Ord for Event<T>
|
||||||
|
where
|
||||||
|
T: Ord
|
||||||
|
{
|
||||||
|
fn cmp(&self, other: &Event<T>) -> Ordering {
|
||||||
|
if self.tick == other.tick {
|
||||||
|
self.event_type.cmp(&other.event_type)
|
||||||
|
} else {
|
||||||
|
self.tick.cmp(&other.tick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ord_event_t() {
|
||||||
|
let first_on = Event{ tick: Tick(0), event_type: EventType::NoteOn(Part::KickDrum)};
|
||||||
|
let first_off = Event{ tick: Tick(24), event_type: EventType::NoteOff(Part::KickDrum)};
|
||||||
|
let second_on = Event{ tick: Tick(24), event_type: EventType::NoteOn(Part::KickDrum)};
|
||||||
|
assert_eq!(first_on.cmp(&first_off), Ordering::Less);
|
||||||
|
assert_eq!(first_off.cmp(&second_on), Ordering::Less);
|
||||||
|
|
||||||
|
let mut vec1 = vec![second_on, first_off, first_on];
|
||||||
|
vec1.sort_by(|x,y| {x.cmp(y)});
|
||||||
|
assert_eq!(vec1, vec![first_on, first_off, second_on]);
|
||||||
|
}
|
||||||
|
|
||||||
// Events are supposed to be sorted by T at all times.
|
// Events are supposed to be sorted by T at all times.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct EventGrid<T> {
|
pub struct EventGrid<T> {
|
||||||
events: Vec<Event<T>>,
|
events: Vec<Event<T>>,
|
||||||
length: Tick,
|
length: Tick,
|
||||||
|
@ -247,16 +134,7 @@ impl<T> EventGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl <T> Iterator for EventGrid<T> {
|
impl<T: Add<Tick, Output = T> + Clone + Ord + std::fmt::Debug> Add for EventGrid<T> {
|
||||||
// type Item = Event<T>;
|
|
||||||
|
|
||||||
// fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
// let e = self.events.iter().next();
|
|
||||||
// e.map(|x| *x )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl<T: Add<Tick, Output = T> + Clone + Ord> Add for EventGrid<T> {
|
|
||||||
type Output = EventGrid<T>;
|
type Output = EventGrid<T>;
|
||||||
|
|
||||||
fn add(mut self, other: EventGrid<T>) -> EventGrid<T> {
|
fn add(mut self, other: EventGrid<T>) -> EventGrid<T> {
|
||||||
|
@ -269,7 +147,9 @@ impl<T: Add<Tick, Output = T> + Clone + Ord> Add for EventGrid<T> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.events.extend(other_events);
|
self.events.extend(other_events);
|
||||||
self.events.sort();
|
// I don't know why sort() doesn't work in the same way.
|
||||||
|
self.events.sort_by(|x,y| { x.cmp(y) });
|
||||||
|
println!("self.events: {:?}", self.events);
|
||||||
self.length = self.length + other.length;
|
self.length = self.length + other.length;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -395,7 +275,7 @@ impl<T> EventGrid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventGrid<Tick> {
|
impl EventGrid<Tick> {
|
||||||
/// Converts a sorted `EventGrid<Tick>`
|
/// Converts a single-track(!!!!) sorted `EventGrid<Tick>`
|
||||||
fn to_delta(&self) -> EventGrid<Delta> {
|
fn to_delta(&self) -> EventGrid<Delta> {
|
||||||
let mut time = Tick(0);
|
let mut time = Tick(0);
|
||||||
let mut delta_grid = EventGrid::new();
|
let mut delta_grid = EventGrid::new();
|
||||||
|
@ -416,7 +296,7 @@ static TICKS_PER_QUARTER_NOTE: u16 = 48;
|
||||||
|
|
||||||
impl BasicLength {
|
impl BasicLength {
|
||||||
/// `BasicLength` to MIDI Ticks
|
/// `BasicLength` to MIDI Ticks
|
||||||
fn to_ticks(&self) -> Tick {
|
pub fn to_ticks(&self) -> Tick {
|
||||||
match self {
|
match self {
|
||||||
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
|
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
|
||||||
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
|
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
|
||||||
|
@ -428,7 +308,8 @@ impl BasicLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_u8(&self) -> u8 {
|
|
||||||
|
pub fn to_note_length(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
BasicLength::Whole => 1,
|
BasicLength::Whole => 1,
|
||||||
BasicLength::Half => 2,
|
BasicLength::Half => 2,
|
||||||
|
@ -556,6 +437,8 @@ fn flatten_group(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
// grid.events.sort() is not the same for some reason
|
||||||
|
grid.events.sort_by(|x,y| { x.cmp(y) });
|
||||||
cycle_grid(grid, *times)
|
cycle_grid(grid, *times)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,6 +578,7 @@ impl Iterator for EventIterator {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
println!("============");
|
println!("============");
|
||||||
println!("self.time: {}", self.time);
|
println!("self.time: {}", self.time);
|
||||||
|
println!("self.kick: {:?}", self.kick.peek());
|
||||||
let mut min_part = Part::KickDrum;
|
let mut min_part = Part::KickDrum;
|
||||||
let mut min_tick = self.limit;
|
let mut min_tick = self.limit;
|
||||||
let mut min_event: Event<Tick> = Event {
|
let mut min_event: Event<Tick> = Event {
|
||||||
|
@ -716,19 +600,6 @@ impl Iterator for EventIterator {
|
||||||
Some(e) => {
|
Some(e) => {
|
||||||
println!("{:?}", 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 {
|
if e.tick == self.time {
|
||||||
println!("e.tick = self.time");
|
println!("e.tick = self.time");
|
||||||
min_part = p;
|
min_part = p;
|
||||||
|
@ -748,6 +619,7 @@ impl Iterator for EventIterator {
|
||||||
None => continue,
|
None => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println!("<<< min_event: {:?}", min_event);
|
||||||
|
|
||||||
let mut group_length: Tick;
|
let mut group_length: Tick;
|
||||||
|
|
||||||
|
@ -781,7 +653,7 @@ impl Iterator for EventIterator {
|
||||||
"min_event.tick ({}) = self.time ({})",
|
"min_event.tick ({}) = self.time ({})",
|
||||||
min_event.tick, self.time
|
min_event.tick, self.time
|
||||||
);
|
);
|
||||||
Tick(self.time.0 + min_event.tick.0)
|
self.time + min_event.tick
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"min_event.tick ({}) <> self.time ({})",
|
"min_event.tick ({}) <> self.time ({})",
|
||||||
|
@ -833,21 +705,56 @@ impl Iterator for EventIterator {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_event_iterator_impl() {
|
fn test_event_iterator_impl() {
|
||||||
let empty = EventGrid::new();
|
let empty = EventGrid::new();
|
||||||
let flat_kick = flatten_group(
|
let kick1 = flatten_group(
|
||||||
&group_or_delimited_group("(4x-)").unwrap().1,
|
&group_or_delimited_group("(4x-)").unwrap().1,
|
||||||
Part::KickDrum,
|
Part::KickDrum,
|
||||||
&mut Tick(0),
|
&mut Tick(0),
|
||||||
);
|
);
|
||||||
let snare = flatten_group(
|
let snare1 = flatten_group(
|
||||||
&group_or_delimited_group("4-x").unwrap().1,
|
&group_or_delimited_group("4-x").unwrap().1,
|
||||||
Part::SnareDrum,
|
Part::SnareDrum,
|
||||||
&mut Tick(0),
|
&mut Tick(0),
|
||||||
);
|
);
|
||||||
|
let kick2 = flatten_group(
|
||||||
|
&group_or_delimited_group("8xxxxxxxx").unwrap().1,
|
||||||
|
Part::KickDrum,
|
||||||
|
&mut Tick(0),
|
||||||
|
);
|
||||||
|
let snare2 = flatten_group(
|
||||||
|
&group_or_delimited_group("4-x-x").unwrap().1,
|
||||||
|
Part::SnareDrum,
|
||||||
|
&mut Tick(0),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EventIterator::new(
|
EventIterator::new(
|
||||||
flat_kick.clone(),
|
kick2.clone(),
|
||||||
snare.clone(),
|
snare2.clone(),
|
||||||
|
empty.clone(),
|
||||||
|
empty.clone(),
|
||||||
|
Tick(96)
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| { x.0 })
|
||||||
|
.collect::<Vec<Event<Tick>>>(),
|
||||||
|
vec![
|
||||||
|
Event { event_type: EventType::NoteOn(Part::KickDrum), tick: Tick(0) },
|
||||||
|
Event { event_type: EventType::NoteOff(Part::KickDrum), tick: Tick(24) },
|
||||||
|
Event { event_type: EventType::NoteOn(Part::KickDrum), tick: Tick(24) },
|
||||||
|
Event { event_type: EventType::NoteOff(Part::KickDrum), tick: Tick(48) },
|
||||||
|
Event { event_type: EventType::NoteOn(Part::SnareDrum), tick: Tick(48) },
|
||||||
|
Event { event_type: EventType::NoteOn(Part::KickDrum), tick: Tick(48) },
|
||||||
|
Event { event_type: EventType::NoteOff(Part::KickDrum), tick: Tick(72) },
|
||||||
|
Event { event_type: EventType::NoteOn(Part::KickDrum), tick: Tick(72) },
|
||||||
|
Event { event_type: EventType::NoteOff(Part::KickDrum), tick: Tick(96) },
|
||||||
|
Event { event_type: EventType::NoteOn(Part::SnareDrum), tick: Tick(96) }
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
EventIterator::new(
|
||||||
|
kick1.clone(),
|
||||||
|
snare1.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
Tick(96)
|
Tick(96)
|
||||||
|
@ -877,7 +784,7 @@ fn test_event_iterator_impl() {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EventIterator::new(
|
EventIterator::new(
|
||||||
flat_kick.clone(),
|
kick1.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
|
@ -899,7 +806,7 @@ fn test_event_iterator_impl() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EventIterator::new(
|
EventIterator::new(
|
||||||
flat_kick.clone(),
|
kick1.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
empty.clone(),
|
empty.clone(),
|
||||||
|
@ -949,7 +856,7 @@ fn flatten_and_merge(
|
||||||
|
|
||||||
// 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.
|
// 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<Part, Vec<Group>>, time_signature: TimeSignature) -> Smf<'a> {
|
pub fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>, time_signature: TimeSignature) -> Smf<'a> {
|
||||||
let tracks = vec![]; // create_tracks(groups, time_signature); // FIXME
|
let tracks = create_tracks(groups, time_signature); // FIXME
|
||||||
// https://majicdesigns.github.io/MD_MIDIFile/page_timing.html
|
// https://majicdesigns.github.io/MD_MIDIFile/page_timing.html
|
||||||
// says " If it is not specified the MIDI default is 48 ticks per quarter note."
|
// 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.
|
// As it's required in `Header`, let's use the same value.
|
||||||
|
@ -984,11 +891,12 @@ fn create_tracks<'a>(
|
||||||
panic!("Result has no midi notes")
|
panic!("Result has no midi notes")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let event_grid = EventGrid {
|
let event_grid_tick = EventGrid {
|
||||||
events,
|
events,
|
||||||
length: *time,
|
length: *time,
|
||||||
}
|
};
|
||||||
.to_delta();
|
println!("event grid in ticks: {:?}", event_grid_tick);
|
||||||
|
let event_grid = event_grid_tick.to_delta();
|
||||||
let mut drums = Vec::new();
|
let mut drums = Vec::new();
|
||||||
// let midi_tempo = MidiTempo::from_tempo(Tempo(130)).0;
|
// 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::Tempo(midi_tempo)) });
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod core;
|
pub mod core;
|
||||||
|
pub mod time;
|
227
src/midi/time.rs
Normal file
227
src/midi/time.rs
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
extern crate derive_more;
|
||||||
|
use crate::dsl::dsl::{BasicLength, KnownLength, Group, GroupOrNote, Note, FOURTH, Times, EIGHTH};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use std;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct TimeSignature {
|
||||||
|
pub numerator: u8,
|
||||||
|
pub denominator: BasicLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeSignature {
|
||||||
|
pub fn new(numerator: u8, denominator: BasicLength) -> Self {
|
||||||
|
Self {
|
||||||
|
numerator,
|
||||||
|
denominator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<u8> for TimeSignature {
|
||||||
|
type Output = TimeSignature;
|
||||||
|
fn mul(self, rhs: u8) -> TimeSignature {
|
||||||
|
TimeSignature {
|
||||||
|
numerator: self.numerator * rhs as u8,
|
||||||
|
denominator: self.denominator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 KnownLength for TimeSignature {
|
||||||
|
fn to_128th(&self) -> u32 {
|
||||||
|
self.denominator.to_128th() * self.numerator as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeSignature {
|
||||||
|
fn converges_with(&self, other: TimeSignature) -> Result<(u32, TimeSignature), String> {
|
||||||
|
let d: u32 = std::cmp::max(self.denominator, other.denominator)
|
||||||
|
.to_note_length()
|
||||||
|
.into();
|
||||||
|
let d1: u32 = self.denominator.to_note_length().into();
|
||||||
|
let d2: u32 = other.denominator.to_note_length().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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The function takes a vector of time signatures and tries to find out if they all converge over a finite number of bars.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
///
|
||||||
|
/// * `time_signatures`: `time_signatures` is a vector of `TimeSignature` structs. The function
|
||||||
|
/// `converges` takes this vector as input and iterates over it using the `iter()` method. It then
|
||||||
|
/// uses the `try_fold()` method to fold over the vector and accumulate a result.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
///
|
||||||
|
/// Returns the number of bars to converge + suggested time signature.
|
||||||
|
/// Returns Err if signatures won't converge.
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of bars in the specified time signature that it takes to converge with the group.
|
||||||
|
/// Otherwise returns Err.
|
||||||
|
fn converges_over<T: KnownLength>(&self, over: T) -> Result<u32, String> {
|
||||||
|
let bar_len = self.to_128th();
|
||||||
|
let mut bars = 1;
|
||||||
|
let group_len = over.to_128th();
|
||||||
|
let mut out = Err("Do not converge".to_string());
|
||||||
|
let limit = 1000;
|
||||||
|
|
||||||
|
while bars <= limit {
|
||||||
|
let bars_len = bar_len * bars;
|
||||||
|
if bars_len % group_len == 0 {
|
||||||
|
// return
|
||||||
|
out = Ok(bars);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if bars == limit {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
bars += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_converges_over() {
|
||||||
|
let four_fourth = TimeSignature {
|
||||||
|
numerator: 4,
|
||||||
|
denominator: BasicLength::Fourth,
|
||||||
|
};
|
||||||
|
let three_fourth_group = Group {
|
||||||
|
notes: vec![GroupOrNote::SingleNote(Note::Hit)],
|
||||||
|
length: FOURTH.clone(),
|
||||||
|
times: Times(3)
|
||||||
|
};
|
||||||
|
let thirteen_eights = Group {
|
||||||
|
notes: vec![GroupOrNote::SingleNote(Note::Hit)],
|
||||||
|
length: FOURTH.clone(),
|
||||||
|
times: Times(12)
|
||||||
|
};
|
||||||
|
let in_shards_poly = Group {
|
||||||
|
notes: vec![GroupOrNote::SingleNote(Note::Hit), GroupOrNote::SingleNote(Note::Rest), GroupOrNote::SingleGroup(thirteen_eights)],
|
||||||
|
length: EIGHTH.clone(),
|
||||||
|
times: Times(1)
|
||||||
|
};
|
||||||
|
assert_eq!(four_fourth.converges_over(three_fourth_group), Ok(3));
|
||||||
|
assert_eq!(four_fourth.converges_over(in_shards_poly), Ok(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_converges_with() {
|
||||||
|
let three_sixteenth = TimeSignature {
|
||||||
|
numerator: 3,
|
||||||
|
denominator: BasicLength::Sixteenth,
|
||||||
|
};
|
||||||
|
let four_fourth = TimeSignature {
|
||||||
|
numerator: 4,
|
||||||
|
denominator: BasicLength::Fourth,
|
||||||
|
};
|
||||||
|
let thirteen_eights = TimeSignature {
|
||||||
|
numerator: 13,
|
||||||
|
denominator: BasicLength::Eighth,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
three_sixteenth.converges_with(four_fourth),
|
||||||
|
Ok((3, four_fourth))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
thirteen_eights.converges_with(four_fourth),
|
||||||
|
Ok((15, 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,
|
||||||
|
};
|
||||||
|
let thirteen_eights = TimeSignature {
|
||||||
|
numerator: 13,
|
||||||
|
denominator: BasicLength::Eighth,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
three_sixteenth.converges(vec![four_fourth, three_fourth, four_fourth]),
|
||||||
|
Ok((3, four_fourth))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
four_fourth.converges(vec![thirteen_eights]),
|
||||||
|
Ok((15, four_fourth))
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue