#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TemplateSet {
KrumhanslKessler,
Temperley,
}
#[derive(Debug, Clone)]
pub struct KeyTemplates {
pub major: [Vec<f32>; 12],
pub minor: [Vec<f32>; 12],
}
impl KeyTemplates {
pub fn new() -> Self {
Self::new_with_template_set(TemplateSet::KrumhanslKessler)
}
pub fn new_with_template_set(template_set: TemplateSet) -> Self {
match template_set {
TemplateSet::KrumhanslKessler => Self::new_krumhansl_kessler(),
TemplateSet::Temperley => Self::new_temperley(),
}
}
fn new_krumhansl_kessler() -> Self {
let c_major = vec![
6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88,
];
let c_minor = vec![
6.33, 2.68, 3.52, 5.38, 2.60, 3.53, 2.54, 4.75, 3.98, 2.69, 3.34, 3.17,
];
let mut major: [Vec<f32>; 12] = [
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
];
let mut minor: [Vec<f32>; 12] = [
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
];
for key_idx in 0..12 {
for semitone_idx in 0..12 {
major[key_idx][semitone_idx] = c_major[(semitone_idx + 12 - key_idx) % 12];
}
for semitone_idx in 0..12 {
let source_idx = (semitone_idx + 12 - key_idx) % 12;
minor[key_idx][semitone_idx] = c_minor[source_idx];
}
}
fn l2_normalize(v: &mut [f32]) {
let norm = v.iter().map(|&x| x * x).sum::<f32>().sqrt();
if norm > 1e-12 {
for x in v.iter_mut() {
*x /= norm;
}
}
}
for k in 0..12 {
l2_normalize(&mut major[k]);
l2_normalize(&mut minor[k]);
}
Self { major, minor }
}
fn new_temperley() -> Self {
let c_major = vec![
5.0, 2.0, 3.5, 2.0, 4.5, 4.0, 2.0, 4.5, 2.0, 3.5, 1.5, 4.0,
];
let c_minor = vec![
5.0, 2.0, 3.5, 5.0, 2.0, 3.5, 2.0, 4.5, 3.5, 2.0, 4.0, 3.5,
];
let mut major: [Vec<f32>; 12] = [
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
];
let mut minor: [Vec<f32>; 12] = [
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
vec![0.0; 12],
];
for key_idx in 0..12 {
for semitone_idx in 0..12 {
major[key_idx][semitone_idx] = c_major[(semitone_idx + 12 - key_idx) % 12];
}
for semitone_idx in 0..12 {
let source_idx = (semitone_idx + 12 - key_idx) % 12;
minor[key_idx][semitone_idx] = c_minor[source_idx];
}
}
fn l2_normalize(v: &mut [f32]) {
let norm = v.iter().map(|&x| x * x).sum::<f32>().sqrt();
if norm > 1e-12 {
for x in v.iter_mut() {
*x /= norm;
}
}
}
for k in 0..12 {
l2_normalize(&mut major[k]);
l2_normalize(&mut minor[k]);
}
Self { major, minor }
}
pub fn get_template(&self, key_idx: u32) -> &[f32] {
if key_idx < 12 {
&self.major[key_idx as usize]
} else {
&self.minor[(key_idx - 12) as usize]
}
}
pub fn get_major_template(&self, key_idx: u32) -> &[f32] {
&self.major[key_idx as usize % 12]
}
pub fn get_minor_template(&self, key_idx: u32) -> &[f32] {
&self.minor[key_idx as usize % 12]
}
}
impl Default for KeyTemplates {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_templates_creation() {
let templates = KeyTemplates::new();
for i in 0..12 {
assert_eq!(templates.major[i].len(), 12);
assert_eq!(templates.minor[i].len(), 12);
}
}
#[test]
fn test_c_major_template() {
let templates = KeyTemplates::new();
let c_major = templates.get_major_template(0);
let tonic = c_major[0];
let third = c_major[4];
let fifth = c_major[7];
let max = c_major.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
assert!((tonic - max).abs() < 1e-6);
let mut sorted = c_major.to_vec();
sorted.sort_by(|a, b| b.partial_cmp(a).unwrap());
let top4_threshold = sorted[3];
assert!(third >= top4_threshold);
assert!(fifth >= top4_threshold);
let chromatic_avg = (c_major[1] + c_major[3] + c_major[6] + c_major[8] + c_major[10]) / 5.0;
assert!(tonic > chromatic_avg * 1.4);
assert!(third > chromatic_avg * 1.1);
assert!(fifth > chromatic_avg * 1.2);
}
#[test]
fn test_a_minor_template() {
let templates = KeyTemplates::new();
let a_minor = templates.get_minor_template(9);
assert!(a_minor[9] > 0.1); assert!(a_minor[0] > 0.1); assert!(a_minor[4] > 0.1); }
#[test]
fn test_get_template() {
let templates = KeyTemplates::new();
let c_major = templates.get_template(0);
assert_eq!(c_major.len(), 12);
let a_minor = templates.get_template(21); assert_eq!(a_minor.len(), 12);
}
#[test]
fn test_template_rotation() {
let templates = KeyTemplates::new();
let _c_major = templates.get_major_template(0);
let d_major = templates.get_major_template(2);
assert!(d_major[2] > 0.1);
}
}