Skip to main content

mittens_engine/engine/ecs/system/
music_system.rs

1use crate::engine::ecs::component::{MusicNote, NotePitch};
2
3/// Music system.
4///
5/// Provides musical-pitch → frequency conversion. Note-trigger dispatch lives
6/// in the `AudioSchedulePlay` intent path (see docs/spec/audio-sources.md);
7/// this system is now stateless pitch math.
8#[derive(Debug, Default)]
9pub struct MusicSystem;
10
11impl MusicSystem {
12    pub fn new() -> Self {
13        Self
14    }
15
16    pub(crate) fn frequency_hz_for_pitch(pitch: NotePitch, octave: u16) -> f32 {
17        // Equal-tempered scale, A4 = 440Hz.
18        // MIDI: C-1 = 0, C4 = 60, A4 = 69.
19        let semitone_from_c = match pitch {
20            NotePitch::C => 0,
21            NotePitch::D => 2,
22            NotePitch::E => 4,
23            NotePitch::F => 5,
24            NotePitch::G => 7,
25            NotePitch::A => 9,
26            NotePitch::B => 11,
27        };
28
29        let octave_i32 = octave as i32;
30        let midi = (octave_i32 + 1) * 12 + semitone_from_c;
31        let n = (midi - 69) as f32 / 12.0;
32        440.0 * 2.0_f32.powf(n)
33    }
34
35    pub fn frequency_hz_for_note(note: MusicNote) -> f32 {
36        Self::frequency_hz_for_pitch(note.pitch(), note.octave())
37    }
38}