Saturday 14 May 2011

Flash Dynamic Sound Generation 2: Musical Notes




Today I learnt how to produce musical notes in the equal temperament tuning using Flash dynamically generated sound. If you would like to see how I dynamically generated the sounds, you can check my previous post here.


Essentially, music notes are just certain frequencies, so I could reuse the different wave-forms I did previously to produce them. Now all I had to do was to find out how to determine the frequencies of the notes. The formula to produce the notes was:


Frequency = 2 ^ (n/12) * 440;
where n is the number of semitones relative to the note A4 which is 440 Hz.


For e.g. for the note A#4, n is 1, therefore the frequency is 2^(1/12) * 440 = 466.16 Hz. If you're not familiar with the note names, it is basically the note (A#) followed by the octave number (4).


Actually, the formula is quite intuitive if we think about it. There are 12 semitones in an octave and each octave is double the frequency of the previous octave. Using this information, we can tell that if we want to double in frequency we need to multiply by a factor of 2. However, we only double every 12 semitones and that is why we need to divide the semitone number by 12. Finally, there is a reference frequency which we need to use and the standard frequency to reference is the note A4 at 440 Hz.


As shown, calculating musical note frequencies is pretty easy and now I'll show how to convert the semitone number into a note name. I.e. Given the n = 2, let's find out how to get the note name "B4". Here's my code with the explanation below:


function NoteNumberToName(noteNum:int):String
{
// number from -69 to 58
// assumes noteNum 0 is A4

// octave number is counted from C, so we have to add an offset
// noteNum 0 is octave 4
var octaveNum:int = Math.floor((noteNum + 9) / 12) + 4;
var octaveNumStr:String = octaveNum.toString();

var octaveNote:int = (noteNum+69) % 12;

var returnString:String;
switch(octaveNote)
{
case 0: returnString = "C"+octaveNumStr; break;
case 1: returnString = "C#"+octaveNumStr; break;
case 2: returnString = "D"+octaveNumStr; break;
case 3: returnString = "D#"+octaveNumStr; break;
case 4: returnString = "E"+octaveNumStr; break;
case 5: returnString = "F"+octaveNumStr; break;
case 6: returnString = "F#"+octaveNumStr; break;
case 7: returnString = "G"+octaveNumStr; break;
case 8: returnString = "G#"+octaveNumStr; break;
case 9: returnString = "A"+octaveNumStr; break;
case 10: returnString = "A#"+octaveNumStr; break;
case 11: returnString = "B"+octaveNumStr; break;
default: returnString = "invalid"; break;
}
return returnString;
}


First of all, the range of notes I'm using is the same as the range of notes available in the MIDI system i.e. 128 notes from C-1 to G9, see here. I'm also taking in the input as a number relative A4 so note A4 is 0, C-1 is -69 and G9 is 58. However, you may wish to change this to a zero-based index if you like.


The quirky thing about note names is that it starts with C while the reference frequency starts from A4, so we have to add an offset of 9 since C is 9 semitones below A. So to get the octave number, i.e. the "4" in "A4", we just divide the note number by 12 semitones and add 4 since a note number 0 translate to A4.

Finally all that is left is to get the note itself, and to get this, we do a modulus operation and a switch case for the 12 different semitones. I figured there was no formula to determine the notes due to the uneven nature of having no semitone between E and F, and between B and C, so a switch case is ideal. If there is a formula, please do post it in the comments!

1 comment:

Anonymous said...

It is faster to create an array like this :

const NoteName:Array = ["C","C#","D","D#","E", and so on...];

returnString = NoteName[octaveNote] +octaveNumStr;

Get Adsense

Want to earn money like me? Get Google Adsense now by clicking this