firefly_rust/audio/
freq.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
pub const SAMPLE_RATE: u32 = 44_100;

#[derive(Copy, Clone)]
pub enum Pitch {
    C,
    Cs,
    D,
    Ds,
    E,
    F,
    Fs,
    G,
    Gs,
    A,
    As,
    B,
}

impl TryFrom<char> for Pitch {
    type Error = ();

    fn try_from(value: char) -> Result<Self, Self::Error> {
        match value {
            'A' => Ok(Self::A),
            'B' => Ok(Self::B),
            'C' => Ok(Self::C),
            'D' => Ok(Self::D),
            'E' => Ok(Self::E),
            'F' => Ok(Self::F),
            'G' => Ok(Self::G),
            _ => Err(()),
        }
    }
}

#[derive(Copy, Clone)]
pub struct Freq(pub(super) f32);

impl Freq {
    pub const ZERO: Self = Self(0.);

    // https://www.liutaiomottola.com/formulae/freqtab.htm

    /// C0, MIDI note #12
    pub const C0: Self = Self(16.351);
    pub const CS0: Self = Self(17.324);
    pub const D0: Self = Self(18.354);
    pub const DS0: Self = Self(19.445);
    pub const E0: Self = Self(20.601);
    pub const F0: Self = Self(21.827);
    pub const FS0: Self = Self(23.124);
    pub const G0: Self = Self(24.499);
    pub const GS0: Self = Self(25.956);
    /// A0, the lowest note of a piano
    pub const A0: Self = Self(27.5);
    pub const AS0: Self = Self(29.135);
    /// B0, the lowest note of a 5 string bass
    pub const B0: Self = Self(30.868);
    /// C1, the lowest note of double bass with C extension
    pub const C1: Self = Self(32.703);
    pub const CS1: Self = Self(34.648);
    pub const D1: Self = Self(36.708);
    pub const DS1: Self = Self(38.891);
    /// E1, the lowest note of a bass
    pub const E1: Self = Self(41.203);
    pub const F1: Self = Self(43.654);
    pub const FS1: Self = Self(46.249);
    pub const G1: Self = Self(48.999);
    pub const GS1: Self = Self(51.913);
    pub const A1: Self = Self(55.);
    pub const AS1: Self = Self(58.27);
    pub const B1: Self = Self(61.735);
    pub const C2: Self = Self(65.406);
    pub const CS2: Self = Self(69.296);
    pub const D2: Self = Self(73.416);
    pub const DS2: Self = Self(77.782);
    /// E2, the lowest note of a guitar.
    pub const E2: Self = Self(82.407);
    pub const F2: Self = Self(87.307);
    pub const FS2: Self = Self(92.499);
    pub const G2: Self = Self(97.999);
    pub const GS2: Self = Self(103.826);
    pub const A2: Self = Self(110.);
    pub const AS2: Self = Self(116.541);
    pub const B2: Self = Self(123.471);
    pub const C3: Self = Self(130.813);
    pub const CS3: Self = Self(138.591);
    pub const D3: Self = Self(146.832);
    pub const DS3: Self = Self(155.563);
    pub const E3: Self = Self(164.814);
    pub const F3: Self = Self(174.614);
    pub const FS3: Self = Self(184.997);
    /// G3, the lowest note of a violin.
    pub const G3: Self = Self(195.998);
    pub const GS3: Self = Self(207.652);
    pub const A3: Self = Self(220.);
    pub const AS3: Self = Self(233.082);
    pub const B3: Self = Self(246.942);
    /// C4, the "middle C".
    pub const C4: Self = Self(261.626);
    pub const CS4: Self = Self(277.183);
    pub const D4: Self = Self(293.665);
    pub const DS4: Self = Self(311.127);
    pub const E4: Self = Self(329.628);
    pub const F4: Self = Self(349.228);
    pub const FS4: Self = Self(369.994);
    pub const G4: Self = Self(391.995);
    pub const GS4: Self = Self(415.305);
    /// A4, the tuning reference note.
    pub const A4: Self = Self(440.);
    pub const AS4: Self = Self(466.164);
    pub const B4: Self = Self(493.883);
    pub const C5: Self = Self(523.251);
    pub const CS5: Self = Self(554.365);
    pub const D5: Self = Self(587.33);
    pub const DS5: Self = Self(622.254);
    pub const E5: Self = Self(659.255);
    pub const F5: Self = Self(698.456);
    pub const FS5: Self = Self(739.989);
    pub const G5: Self = Self(783.991);
    pub const GS5: Self = Self(830.609);
    pub const A5: Self = Self(880.);
    pub const AS5: Self = Self(932.328);
    pub const B5: Self = Self(987.767);
    pub const C6: Self = Self(1046.502);
    pub const CS6: Self = Self(1108.731);
    pub const D6: Self = Self(1174.659);
    pub const DS6: Self = Self(1244.508);
    pub const E6: Self = Self(1318.51);
    pub const F6: Self = Self(1396.913);
    pub const FS6: Self = Self(1479.978);
    pub const G6: Self = Self(1567.982);
    pub const GS6: Self = Self(1661.219);
    pub const A6: Self = Self(1760.);
    pub const AS6: Self = Self(1864.655);
    pub const B6: Self = Self(1975.533);
    pub const C7: Self = Self(2093.005);
    pub const CS7: Self = Self(2217.461);
    pub const D7: Self = Self(2349.318);
    pub const DS7: Self = Self(2489.016);
    pub const E7: Self = Self(2637.021);
    pub const F7: Self = Self(2793.826);
    pub const FS7: Self = Self(2959.955);
    pub const G7: Self = Self(3135.964);
    pub const GS7: Self = Self(3322.438);
    pub const A7: Self = Self(3520.);
    pub const AS7: Self = Self(3729.31);
    pub const B7: Self = Self(3951.066);
    /// C8, the highest note of a piano.
    pub const C8: Self = Self(4186.009);
    pub const CS8: Self = Self(4434.922);
    pub const D8: Self = Self(4698.636);
    pub const DS8: Self = Self(4978.032);
    pub const E8: Self = Self(5274.042);
    pub const F8: Self = Self(5587.652);
    pub const FS8: Self = Self(5919.91);
    pub const G8: Self = Self(6271.928);
    pub const GS8: Self = Self(6644.876);
    pub const A8: Self = Self(7040.);
    pub const AS8: Self = Self(7458.62);
    pub const B8: Self = Self(7902.132);
    pub const C9: Self = Self(8372.018);
    pub const CS9: Self = Self(8869.844);
    pub const D9: Self = Self(9397.272);
    pub const DS9: Self = Self(9956.064);
    pub const E9: Self = Self(10548.084);
    pub const F9: Self = Self(11175.304);
    pub const FS9: Self = Self(11839.82);
    pub const G9: Self = Self(12543.856);
    /// G#9, MIDI note #128, the top of the MIDI tuning range.
    pub const GS9: Self = Self(13289.752);
    pub const A9: Self = Self(14080.);
    pub const AS9: Self = Self(14917.24);
    /// B9. For most of adults, it is already beyond the hearing range.
    pub const B9: Self = Self(15804.264);

    #[must_use]
    pub fn hz(hz: f32) -> Self {
        Self(hz)
    }

    #[must_use]
    #[expect(clippy::cast_precision_loss)]
    pub fn midi(note: u8) -> Self {
        // https://inspiredacoustics.com/en/MIDI_note_numbers_and_center_frequencies
        // https://en.wikipedia.org/wiki/Musical_note#MIDI
        let mut f: f32 = match note % 12 {
            0 => 8.1758,
            1 => 8.66,
            2 => 9.18,
            3 => 9.72,
            4 => 10.30,
            5 => 10.91,
            6 => 11.56,
            7 => 12.25,
            8 => 12.98,
            9 => 13.75,
            10 => 14.57,
            _ => 15.43,
        };
        let oct = note / 12;
        f *= (1 << oct) as f32;
        Self(f)
    }

    #[must_use]
    #[expect(clippy::cast_precision_loss)]
    pub fn note(pitch: Pitch, octave: u8) -> Self {
        // https://github.com/crbulakites/hum/blob/master/src/hum_process/hum_math.rs
        // https://en.wikipedia.org/wiki/Musical_note#Pitch_frequency_in_hertz
        let mut f: f32 = match pitch {
            Pitch::C => 16.351,
            Pitch::Cs => 17.324,
            Pitch::D => 18.354,
            Pitch::Ds => 19.445,
            Pitch::E => 20.601,
            Pitch::F => 21.827,
            Pitch::Fs => 23.124,
            Pitch::G => 24.499,
            Pitch::Gs => 25.956,
            Pitch::A => 27.5,
            Pitch::As => 29.135,
            Pitch::B => 30.868,
        };
        f *= (1 << octave) as f32;
        Freq(f)
    }
}

impl From<f32> for Freq {
    fn from(value: f32) -> Self {
        Self(value)
    }
}