From notes to frequencies and back again

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 community , follow the blog, twitter, G+ .You want to learn Google Apps Script?

Learning Apps Script, (and transitioning from VBA) are covered comprehensively in my my book, Going Gas - from VBA to Apps script, available All formats are available now from O'Reilly,Amazon and all good bookshops. You can also read a preview on O'Reilly

If you prefer Video style learning I also have two courses available. also published by O'Reilly.
Google Apps Script for Developers and Google Apps Script for Beginners.



Comments