pub fn hz_to_octs(freq: f64) -> f64 {
(freq / (440.0 / 16.0)).log2()
}
pub fn hz_to_octs_tuned(freq: f64, tuning: f64, bins_per_octave: usize) -> f64 {
let a440 = 440.0 * 2.0_f64.powf(tuning / bins_per_octave as f64);
(freq / (a440 / 16.0)).log2()
}
pub fn cqt_frequencies(n_bins: usize, fmin: f64, bins_per_octave: usize, tuning: f64) -> Vec<f64> {
let correction = 2.0_f64.powf(tuning / bins_per_octave as f64);
(0..n_bins)
.map(|k| correction * fmin * 2.0_f64.powf(k as f64 / bins_per_octave as f64))
.collect()
}
pub fn octs_to_hz(octs: f64) -> f64 {
(440.0 / 16.0) * 2.0_f64.powf(octs)
}
pub fn hz_to_midi(freq: f64) -> f64 {
12.0 * (freq / 440.0).log2() + 69.0
}
pub fn midi_to_hz(note: f64) -> f64 {
440.0 * 2.0_f64.powf((note - 69.0) / 12.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hz_to_octs() {
assert!((hz_to_octs(440.0) - 4.0).abs() < 1e-12);
assert!((hz_to_octs(880.0) - 5.0).abs() < 1e-12);
assert!((hz_to_octs(220.0) - 3.0).abs() < 1e-12);
assert!((hz_to_octs(27.5) - 0.0).abs() < 1e-12);
}
#[test]
fn test_octs_to_hz() {
assert!((octs_to_hz(4.0) - 440.0).abs() < 1e-10);
assert!((octs_to_hz(5.0) - 880.0).abs() < 1e-10);
assert!((octs_to_hz(0.0) - 27.5).abs() < 1e-10);
}
#[test]
fn test_hz_to_octs_roundtrip() {
for &freq in &[27.5, 55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 4186.0] {
let roundtrip = octs_to_hz(hz_to_octs(freq));
assert!(
(roundtrip - freq).abs() < 1e-10,
"roundtrip failed for {freq}: got {roundtrip}"
);
}
}
#[test]
fn test_hz_to_midi() {
assert!((hz_to_midi(440.0) - 69.0).abs() < 1e-12);
assert!((hz_to_midi(880.0) - 81.0).abs() < 1e-12);
assert!((hz_to_midi(261.6255653005986) - 60.0).abs() < 1e-10);
}
#[test]
fn test_midi_to_hz() {
assert!((midi_to_hz(69.0) - 440.0).abs() < 1e-10);
assert!((midi_to_hz(81.0) - 880.0).abs() < 1e-10);
assert!((midi_to_hz(60.0) - 261.6255653005986).abs() < 1e-10);
}
#[test]
fn test_hz_midi_roundtrip() {
for ¬e in &[21.0, 33.0, 45.0, 57.0, 69.0, 81.0, 93.0, 108.0] {
let roundtrip = hz_to_midi(midi_to_hz(note));
assert!(
(roundtrip - note).abs() < 1e-10,
"roundtrip failed for MIDI {note}: got {roundtrip}"
);
}
}
}