pub fn pythagorean_tuning(n: usize) -> Vec<f32> {
let mut ratios = Vec::with_capacity(n);
let fifth: f32 = 3.0 / 2.0;
for i in 0..n {
let mut ratio = fifth.powi(i as i32);
while ratio >= 2.0 {
ratio /= 2.0;
}
while ratio < 1.0 {
ratio *= 2.0;
}
ratios.push(ratio);
}
ratios.sort_by(|a, b| a.partial_cmp(b).unwrap());
ratios
}
pub fn just_intonation_major() -> Vec<f32> {
vec![
1.0, 9.0/8.0, 5.0/4.0, 4.0/3.0, 3.0/2.0, 5.0/3.0, 15.0/8.0, 2.0, ]
}
pub fn just_intonation_minor() -> Vec<f32> {
vec![
1.0, 9.0/8.0, 6.0/5.0, 4.0/3.0, 3.0/2.0, 8.0/5.0, 9.0/5.0, 2.0, ]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pythagorean_tuning_12_notes() {
let tuning = pythagorean_tuning(12);
assert_eq!(tuning.len(), 12);
assert!((tuning[0] - 1.0).abs() < 0.0001);
for &ratio in &tuning {
assert!(ratio >= 1.0 && ratio < 2.0);
}
for i in 0..tuning.len() - 1 {
assert!(tuning[i] <= tuning[i + 1]);
}
}
#[test]
fn test_pythagorean_tuning_has_pure_fifth() {
let tuning = pythagorean_tuning(12);
let has_pure_fifth = tuning.iter().any(|&r| (r - 1.5).abs() < 0.0001);
assert!(has_pure_fifth, "Pythagorean tuning should contain a pure fifth (3/2)");
}
#[test]
fn test_pythagorean_tuning_single_note() {
let tuning = pythagorean_tuning(1);
assert_eq!(tuning.len(), 1);
assert!((tuning[0] - 1.0).abs() < 0.0001);
}
#[test]
fn test_pythagorean_tuning_different_sizes() {
let tuning_5 = pythagorean_tuning(5);
assert_eq!(tuning_5.len(), 5);
let tuning_7 = pythagorean_tuning(7);
assert_eq!(tuning_7.len(), 7);
for i in 0..tuning_7.len() - 1 {
assert!(tuning_7[i] < tuning_7[i + 1], "Notes should be unique");
}
}
#[test]
fn test_just_intonation_major() {
let scale = just_intonation_major();
assert_eq!(scale.len(), 8);
assert_eq!(scale[0], 1.0); assert_eq!(scale[1], 9.0/8.0); assert_eq!(scale[2], 5.0/4.0); assert_eq!(scale[3], 4.0/3.0); assert_eq!(scale[4], 3.0/2.0); assert_eq!(scale[5], 5.0/3.0); assert_eq!(scale[6], 15.0/8.0); assert_eq!(scale[7], 2.0); }
#[test]
fn test_just_intonation_minor() {
let scale = just_intonation_minor();
assert_eq!(scale.len(), 8);
assert_eq!(scale[0], 1.0); assert_eq!(scale[2], 6.0/5.0); assert_eq!(scale[5], 8.0/5.0); assert_eq!(scale[6], 9.0/5.0); assert_eq!(scale[7], 2.0); }
#[test]
fn test_just_intonation_scales_ascending() {
let major = just_intonation_major();
let minor = just_intonation_minor();
for i in 0..major.len() - 1 {
assert!(major[i] < major[i + 1]);
}
for i in 0..minor.len() - 1 {
assert!(minor[i] < minor[i + 1]);
}
}
#[test]
fn test_just_intonation_pure_intervals() {
let major = just_intonation_major();
assert_eq!(major[4], 3.0/2.0);
assert_eq!(major[3], 4.0/3.0);
assert_eq!(major[2], 5.0/4.0);
}
}