| 156 | } |
| 157 | |
| 158 | u8 Note::frequencyToMidiNote(float hz) const { |
| 159 | if (hz <= 0.0f) { |
| 160 | return NO_NOTE; |
| 161 | } |
| 162 | |
| 163 | // MIDI note = 69 + 12 × log₂(f / 440) |
| 164 | float semitones = 12.0f * (fl::logf(hz / A4_FREQUENCY) / fl::logf(2.0f)); |
| 165 | int midiNote = static_cast<int>(A4_MIDI_NOTE + semitones + 0.5f); // Round to nearest |
| 166 | |
| 167 | // Clamp to valid MIDI range (0-127) |
| 168 | if (midiNote < 0) { |
| 169 | return 0; |
| 170 | } |
| 171 | if (midiNote > 127) { |
| 172 | return 127; |
| 173 | } |
| 174 | |
| 175 | return static_cast<u8>(midiNote); |
| 176 | } |
| 177 | |
| 178 | float Note::midiNoteToFrequency(u8 note) const { |
| 179 | if (note == NO_NOTE) { |