Skip to main content

rust_synth/math/
harmony.rs

1//! Golden-ratio harmony helpers — f32, TUI-facing.
2
3pub const PHI: f32 = 1.618_034;
4
5pub fn golden_freq(base: f32, step: i32) -> f32 {
6    let raw = base * PHI.powi(step);
7    fold_octave(raw, base)
8}
9
10pub fn fold_octave(mut f: f32, base: f32) -> f32 {
11    let lo = base * 0.5;
12    let hi = base * 2.0;
13    while f < lo {
14        f *= 2.0;
15    }
16    while f > hi {
17        f *= 0.5;
18    }
19    f
20}
21
22pub fn golden_pentatonic(base: f32) -> [f32; 5] {
23    [
24        base,
25        fold_octave(base / PHI, base),
26        fold_octave(base * PHI, base),
27        fold_octave(base / (PHI * PHI), base),
28        fold_octave(base * PHI * PHI, base),
29    ]
30}
31
32pub fn rand_f32(seed: &mut u64) -> f32 {
33    *seed ^= *seed << 13;
34    *seed ^= *seed >> 7;
35    *seed ^= *seed << 17;
36    let h = seed.wrapping_mul(0x2545_F491_4F6C_DD1D);
37    ((h >> 40) as i32 as f32) / ((1i32 << 23) as f32)
38}
39
40pub fn rand_u32(seed: &mut u64, n: u32) -> u32 {
41    *seed ^= *seed << 13;
42    *seed ^= *seed >> 7;
43    *seed ^= *seed << 17;
44    let h = seed.wrapping_mul(0x2545_F491_4F6C_DD1D);
45    (h >> 32) as u32 % n.max(1)
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use approx::assert_relative_eq;
52
53    #[test]
54    fn fold_keeps_octave() {
55        let f = fold_octave(55.0 * 16.0, 55.0);
56        assert!((27.5..=110.0).contains(&f));
57    }
58
59    #[test]
60    fn golden_step_zero_is_base() {
61        assert_relative_eq!(golden_freq(55.0, 0), 55.0, epsilon = 1e-4);
62    }
63
64    #[test]
65    fn rand_is_deterministic() {
66        let mut a = 42;
67        let mut b = 42;
68        assert_eq!(rand_f32(&mut a), rand_f32(&mut b));
69    }
70}