reson/
tuning.rs

1use std::cell::OnceCell;
2use std::sync::{Arc, OnceLock};
3use lazy_static::lazy_static;
4use crate::Note;
5
6/// Denotes the pitch in Hz for each MIDI note.
7#[derive(Copy, Clone)]
8pub struct Tuning {
9    notes: [f32; 128]
10}
11
12impl Tuning {
13    /// Creates a new equal temperament tuning, based on the provided pitch for the note A4.
14    pub fn equal_temperament(a4: f32) -> Self {
15        let mut notes = [0.0; 128];
16        for note in 0..128 {
17            notes[note] = 440.0 * 2.0f32.powf((note as f32 - 69.0) / 12.0);
18        }
19        Self { notes }
20    }
21
22    /// Gets an reference to the standard tuning system in which A4 is 440Hz.
23    pub fn concert_pitch() -> Arc<Self> {
24        static TUNING: OnceLock<Arc<Tuning>> = OnceLock::new();
25        TUNING.get_or_init(|| Arc::new(Self::equal_temperament(440.0))).clone()
26    }
27
28    /// Gets the pitch of the provided MIDI note, which must be between 0 and 127.
29    pub fn pitch(&self, note: Note) -> f32 {
30        *self.notes.get(note as usize)
31            .expect("MIDI note must be between 0 and 127.")
32    }
33}