mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-22 11:57:43 +00:00
Reasonable convergence function
This commit is contained in:
parent
081188e80a
commit
e843a98876
1 changed files with 44 additions and 145 deletions
189
src/midi/time.rs
189
src/midi/time.rs
|
@ -1,5 +1,5 @@
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
use crate::dsl::dsl::{BasicLength, KnownLength, Group, GroupOrNote, Note, FOURTH, Times, EIGHTH};
|
use crate::dsl::dsl::{BasicLength, Group, GroupOrNote, KnownLength, Note, Times, EIGHTH, FOURTH};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
|
@ -56,172 +56,71 @@ impl KnownLength for TimeSignature {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeSignature {
|
impl TimeSignature {
|
||||||
fn converges_with(&self, other: TimeSignature) -> Result<(u32, TimeSignature), String> {
|
pub fn converges<T: KnownLength>(&self, multiple: Vec<T>) -> Result<u32, 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 bar_len = self.to_128th();
|
||||||
let mut bars = 1;
|
let result = multiple
|
||||||
let group_len = over.to_128th();
|
.iter()
|
||||||
let mut out = Err("Do not converge".to_string());
|
.fold(bar_len, |acc, t| lowest_common_divisor(t.to_128th(), acc));
|
||||||
|
|
||||||
let limit = 1000;
|
let limit = 1000;
|
||||||
|
|
||||||
while bars <= limit {
|
let out = result / bar_len;
|
||||||
let bars_len = bar_len * bars;
|
|
||||||
if bars_len % group_len == 0 {
|
|
||||||
// return
|
|
||||||
out = Ok(bars);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if bars == limit {
|
if limit > out {
|
||||||
break;
|
Ok(out)
|
||||||
} else {
|
} else {
|
||||||
bars += 1
|
Err("Does not converge".to_string())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn lowest_common_divisor(a: u32, b: u32) -> u32 {
|
||||||
fn test_converges_over() {
|
let mut lcm = u32::max(a, b);
|
||||||
let four_fourth = TimeSignature {
|
|
||||||
numerator: 4,
|
while lcm % a != 0 || lcm % b != 0 {
|
||||||
denominator: BasicLength::Fourth,
|
lcm += 1;
|
||||||
};
|
}
|
||||||
let three_fourth_group = Group {
|
|
||||||
notes: vec![GroupOrNote::SingleNote(Note::Hit)],
|
lcm
|
||||||
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]
|
#[test]
|
||||||
fn test_converges_with() {
|
fn test_lcm() {
|
||||||
let three_sixteenth = TimeSignature {
|
assert_eq!(lowest_common_divisor(128, 96), 384);
|
||||||
numerator: 3,
|
assert_eq!(lowest_common_divisor(96, 128), 384);
|
||||||
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]
|
#[test]
|
||||||
fn test_converges() {
|
fn test_converges() {
|
||||||
let three_sixteenth = TimeSignature {
|
|
||||||
numerator: 3,
|
|
||||||
denominator: BasicLength::Sixteenth,
|
|
||||||
};
|
|
||||||
let four_fourth = TimeSignature {
|
let four_fourth = TimeSignature {
|
||||||
numerator: 4,
|
numerator: 4,
|
||||||
denominator: BasicLength::Fourth,
|
denominator: BasicLength::Fourth,
|
||||||
};
|
};
|
||||||
|
let six_fourth = TimeSignature {
|
||||||
|
numerator: 6,
|
||||||
|
denominator: BasicLength::Fourth,
|
||||||
|
};
|
||||||
let three_fourth = TimeSignature {
|
let three_fourth = TimeSignature {
|
||||||
numerator: 3,
|
numerator: 3,
|
||||||
denominator: BasicLength::Fourth,
|
denominator: BasicLength::Fourth,
|
||||||
};
|
};
|
||||||
let thirteen_eights = TimeSignature {
|
let thirteen_eights = Group {
|
||||||
numerator: 13,
|
notes: vec![GroupOrNote::SingleNote(Note::Hit)],
|
||||||
denominator: BasicLength::Eighth,
|
length: FOURTH.clone(),
|
||||||
|
times: Times(12),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
let in_shards_poly = Group {
|
||||||
three_sixteenth.converges(vec![four_fourth, three_fourth, four_fourth]),
|
notes: vec![
|
||||||
Ok((3, four_fourth))
|
GroupOrNote::SingleNote(Note::Hit),
|
||||||
);
|
GroupOrNote::SingleNote(Note::Rest),
|
||||||
assert_eq!(
|
GroupOrNote::SingleGroup(thirteen_eights),
|
||||||
four_fourth.converges(vec![thirteen_eights]),
|
],
|
||||||
Ok((15, four_fourth))
|
length: EIGHTH.clone(),
|
||||||
);
|
times: Times(1),
|
||||||
|
};
|
||||||
|
assert_eq!(three_fourth.converges(vec![four_fourth]), Ok(4));
|
||||||
|
assert_eq!(four_fourth.converges(vec![three_fourth]), Ok(3));
|
||||||
|
assert_eq!(four_fourth.converges(vec![three_fourth, four_fourth]), Ok(3));
|
||||||
|
assert_eq!(four_fourth.converges(vec![three_fourth, six_fourth, four_fourth]), Ok(3));
|
||||||
|
assert_eq!(four_fourth.converges(vec![in_shards_poly]), Ok(13));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue