This is part of a bigger project that I’ll write up at some later date. These functions from it might be of some use to anyone who wants to identify a pitch from an audio input. The whole topic of pitch identification is a long story with lots of maths, but eventually you will want to convert between a frequency and a note.

Note Index

These functions are intended within the scope of all the notes on a standard keyboard – using the SPN notation, c0 through B8 (108 notes) where c0 has noteIndex 0, and b8 has noteIndex 107, and use a tempered scaled based on the standard A4 at 440hz.

You can supply either a frequency, or a note index, and get back this ‘note’ object. Both sharp and flat scale notations are given so you can use the appropriate one for the key. This one is for Ab (or G#) at SPN7.

{

 "accidental": true,

 "flatScale": {

 "base": "A",

 "name": "Ab",

 "spn": "Ab7"

 },

 "frequency": 3322.437580639563,

 "noteIndex": 92,

 "octave": 7,

 "octaveIndex": 8,

 "sharpScale": {

 "base": "G",

 "name": "G#",

 "spn": "G#7"

 },

 "waveLength": 0.10383942260055587

}

The code

Given a note index (0-107), make a note object

/**

   * get notation & frequency from a note number

   * @param {number} noteIndex between 0 - C0 and 107 - B8

   * @return {object} note

   */

  ns.getNote = function (noteIndex) {

    const sharpScale = "CcDdEFfGgAaB";

    const flatScale =  "CdDeEFgGaAbB";

    const octaveIndex = noteIndex % 12;

    const accidental = isAccident(sharpScale.slice (octaveIndex, octaveIndex+1));

    const octave = Math.floor(noteIndex/12);

    const frequency = ns.getNoteFrequency(noteIndex);

    const speedOfSound = 345; // m/s

    

    return {

      noteIndex:noteIndex,

      octaveIndex:noteIndex % 12,

      accidental:accidental,



      flatScale: names (flatScale, "b"),

      sharpScale:names (sharpScale, "#"),



      octave:octave,

      frequency:frequency,

      waveLength:speedOfSound/frequency

    };

    

    function names (scale, a) {

      return {

        base:scale.slice (octaveIndex, octaveIndex+1).toUpperCase(),

        spn:scale.slice (octaveIndex, octaveIndex+1).toUpperCase()+(accidental ? a : '') + octave,

        name:scale.slice (octaveIndex, octaveIndex+1).toUpperCase()+(accidental ? a : '')

      };

    }

    

    function isAccident(c) {

      return c.toUpperCase(c) !== c;

    }

};

Figure out the frequency of a given note index

/**

   * get the frequency of a given note using a tempered scale

   */

  ns.getNoteFrequency = function (noteIndex) {

    

    // a4 note Index

    const a4 = 9 + 4 * 12;

    // freq of a4

    const a4Freq = 440;

    // base

    const tr2 = Math.pow(2,1/12);

    // semitones from a4

    const s = noteIndex - a4;

    // result

    return a4Freq * Math.pow (tr2 , s);



};

Generate a note object for the nearest note that corresponds to this frequency

/**

   * get the note of a frequency

   * @param {number} frequency in hz

   * @return {object} note

   */

  ns.getNoteFromFrequency = function (frequency , sharp) {

    // a4 note Index

    const a4 = 9 + 4 * 12;

    // freq of a4

    const a4Freq = 440;

    // base

    const tr2 = Math.pow(2,1/12);

    // calc note index

    const noteIndex = Math.log(frequency/a4Freq) / Math.log (tr2) + a4;

    // round off to nearest note and get as a note

    return ns.getNote(Math.round(noteIndex));

};
For more like this see Google Apps Scripts Snippets
Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.