#pragma once

#include <cmath>
#include <algorithm>

// Applicable for standard (even temperament) MIDI tuning only.
// The formula connecting the MIDI note number and the base frequency assume equal tuning based
// on A4 = 440 Hz.
constexpr int NUM_MIDI_KEYS = 128;  // Also inclusive of 0 & 128, apparently.
constexpr double A4_FREQUENCY = 440.0; // Hz
constexpr int A4_MIDI_KEY = 69; // Hehe
constexpr int KEYS_PER_OCTAVE = 12;

// Template parameter allows for (non-standard) fractional keys.
template <typename T>
constexpr double MidiKeyToFrequency(T key) {
    return A4_FREQUENCY * std::pow(2, (key - A4_MIDI_KEY) / KEYS_PER_OCTAVE);
}

constexpr double frequencyToMidiKey(double f) {
    return ((KEYS_PER_OCTAVE * std::log2(f / A4_FREQUENCY)) + A4_MIDI_KEY);
}

constexpr int frequencyToNearestMidiKey(double f) {
    return std::clamp(static_cast<int>(std::round(frequencyToMidiKey(f))), 0, NUM_MIDI_KEYS);
}
