Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the nearest value with a non-linear progression

This is a little complicated, but bear with me. I am trying to create a map that links the names of musical notes with their corresponding frequencies. Then I want to write a function that when provided a random frequency will return the note that is closest to that frequency.

The problem with this is that the frequencies of the notes are not generated with a linear formula, and therefore the frequency that is the exact middle between each note is not linear as well. (This basically means that the midpoint between notes is not perfectly in the middle so finding the midpoint by normal methods does not work.)

Some sample code used to generate the map of notes:

// Ordered starting with "B" so notes line up with frequencies
vector<string> names = {
    "B", "C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab", "A", "A#/Bb"
};

double f0 = 440;
map<string, map<int, double> > notes;

// Map notes to their corresponding frequencies
for (int octave = 0; octave < 10; ++octave) {
    for (int index = 0; index < names.size(); ++index) {

        // Get the exact frequency of the musical note
        double frequency               = f0*pow(pow(2, 1.0/12), index       - 10)*pow(pow(2, 1.0/12), 12*(octave + 1 - 5));

        // Get the exact frequency between this note and the next (I'm not doing anything with this yet, but I feel like this is on the right track.)
        double frequency_between_notes = f0*pow(pow(2, 1.0/12), index + 0.5 - 10)*pow(pow(2, 1.0/12), 12*(octave + 1 - 5));

        notes[names[index]][octave] = frequency;
    }
}

I want to write a function that given a random frequency it will return the note that is closest to that frequency.

Note& find_note(double frequency) {
    // Provided a random frequency find the ACTUAL nearest note using the non-linear formula that notes are calculated from.
    // Create the note object and return it.
}

The Note class looks something like this:

class Note {
public:
    Note(string name, int octave, double frequency) {
        name_      = name;
        octave_    = octave;
        frequency_ = frequency;
    }

    const string& get_name() const {
        return name_;
    }

    const int& get_octave() const {
        return octave_;
    }

    const double& get_frequency() const {
        return frequency_;
    }
private:
    string name_;
    int    octave_;
    double frequency_;
};

The equation used to calculate the frequencies of the notes came from https://pages.mtu.edu/~suits/NoteFreqCalcs.html.

How can I find the nearest note given a random frequency?

like image 289
tjwrona1992 Avatar asked Jan 03 '18 03:01

tjwrona1992


1 Answers

The logarithm of the frequencies of the semitones are evenly spaced. To find the closest note to a given frequency, just take the log of the frequency and find closest log of a note frequency.

Here is a simple function that takes a frequency in Hz and returns the closest semitone, as a number of semitones above (positive) or below (negative) A4 (440Hz)

const double LOG_SEMITONE = log(2.0)/12.0;

int getNote(double f)
{
    double note = log(f/440.0) / LOG_SEMITONE;
    return (int)round(note);
}
like image 131
Matt Timmermans Avatar answered Oct 19 '22 19:10

Matt Timmermans