The music theory content can be found at Music Fundamentals on the Web.
The playScale scale function was designed to play the MAJOR scale with the correct sounds AND the correct names. Tone.js doesn't care about the name really. It can understand either of the enharmonic names (or a frequency and other methods) and although it can't play a MIDI number directly it has functions to translate it into a frequency. So the extra layer of logic to enforce the naming rules of standard notation is really for the use of the Music Fundamentals class. As you may recall, rule 2 to enforce is that all of the alphabetical letters of the language (A-G) are used for each major scale. The same holds true for the three forms of MINOR (Natural Minor, Harmonic Minor and Melodic Minor). And the naming of notes can get weird due to this rule 2 enforcement. Also similar to Major scales some letter names are not used a starting notes for minor scale (in those cases, the other enharmonic name is used instead).
We solved the enharmonic naming problem by creating two arrays of note names that contained the two enhamonic names for each indexed location. This could have been created differently, for example a single array with each element a two element array of the enharmonic names. But we have two parallel arrays of names that are accessed via their corresponding MIDI number (as an index into those arrays). You might not have noticed the names choosen for each of the two arrays. We'll examine them closely now.
var MIDI_SHARP_NAMES = ['B#_0', 'C#_1', 'Cx_1', 'D#_1', 'E_1', 'E#_1', 'F#_1', 'Fx_1', 'G#_1', 'Gx_1', 'A#_1', 'B_1',
'B#_1', 'C#0', 'Cx0', 'D#0', 'E0', 'E#0', 'F#0', 'Fx0', 'G#0', 'Gx0', 'A#0', 'B0',
'B#0', 'C#1', 'Cx1', 'D#1', 'E1', 'E#1', 'F#1', 'Fx1', 'G#1', 'Gx1', 'A#1', 'B1',
'B#1', 'C#2', 'Cx2', 'D#2', 'E2', 'E#2', 'F#2', 'Fx2', 'G#2', 'Gx2', 'A#2', 'B2',
'B#2', 'C#3', 'Cx3', 'D#3', 'E3', 'E#3', 'F#3', 'Fx3', 'G#3', 'Gx3', 'A#3', 'B3',
'B#3', 'C#4', 'Cx4', 'D#4', 'E4', 'E#4', 'F#4', 'Fx4', 'G#4', 'Gx4', 'A#4', 'B4',
'B#4', 'C#5', 'Cx5', 'D#5', 'E5', 'E#5', 'F#5', 'Fx5', 'G#5', 'Gx5', 'A#5', 'B5',
'B#5', 'C#6', 'Cx6', 'D#6', 'E6', 'E#6', 'F#6', 'Fx6', 'G#6', 'Gx6', 'A#6', 'B6',
'B#6', 'C#7', 'Cx7', 'D#7', 'E7', 'E#7', 'F#7', 'Fx7', 'G#7', 'Gx7', 'A#7', 'B7',
'B#7', 'C#8', 'Cx8', 'D#8', 'E8', 'E#8', 'F#8', 'Fx8', 'G#8', 'Gx8', 'A#8', 'B8',
'B#8', 'C#9', 'Cx9', 'D#9', 'E9', 'E#9', 'F#9', 'Fx9'];
var MIDI_FLAT_NAMES = ['C_1', 'Db_1', 'D_1', 'Eb_1', 'Fb_1', 'F_1', 'Gb_1', 'G_1', 'Ab_1', 'A_1', 'Bb_1', 'Cb0',
'C0', 'Db0', 'D0', 'Eb0', 'Fb0', 'F0', 'Gb0', 'G0', 'Ab0', 'A0', 'Bb0', 'Cb1',
'C1', 'Db1', 'D1', 'Eb1', 'Fb1', 'F1', 'Gb1', 'G1', 'Ab1', 'A1', 'Bb1', 'Cb2',
'C2', 'Db2', 'D2', 'Eb2', 'Fb2', 'F2', 'Gb2', 'G2', 'Ab2', 'A2', 'Bb2', 'Cb3',
'C3', 'Db3', 'D3', 'Eb3', 'Fb3', 'F3', 'Gb3', 'G3', 'Ab3', 'A3', 'Bb3', 'Cb4',
'C4', 'Db4', 'D4', 'Eb4', 'Fb4', 'F4', 'Gb4', 'G4', 'Ab4', 'A4', 'Bb4', 'Cb5',
'C5', 'Db5', 'D5', 'Eb5', 'Fb5', 'F5', 'Gb5', 'G5', 'Ab5', 'A5', 'Bb5', 'Cb6',
'C6', 'Db6', 'D6', 'Eb6', 'Fb6', 'F6', 'Gb6', 'G6', 'Ab6', 'A6', 'Bb6', 'Cb7',
'C7', 'Db7', 'D7', 'Eb7', 'Fb7', 'F7', 'Gb7', 'G7', 'Ab7', 'A7', 'Bb7', 'Cb8',
'C8', 'Db8', 'D8', 'Eb8', 'Fb8', 'F8', 'Gb8', 'G8', 'Ab8', 'A8', 'Bb8', 'Cb9',
'C9', 'Db9', 'D9', 'Eb9', 'Fb9', 'F9', 'Gb9', 'G9'];
Each line has the names for one octave, each line changes that octave number. Also notice that in the MIDI_SHARP_NAMES array, the letter name between E and F# is E# (not F). That is because we sometimes need E#, not too often but is must be a choice if we have to spell C# major correctly. Similarly in MIDI_FLAT_NAMES the name Fb is used instead of E because of the rare time when it's the correct name. Similarly the rare names Cb and B# are found in the arrays. Every one of the 7 letters (A-G) has a natural form (i.e. A) and a flat form (i.e. Ab) and a sharp form (i.e. A#) giving us 21 names for our 12 tones. But odd as it may seem, that's still not enough note names. It's all because of rule 2. To follow the expanding note naming adventure consider the following:
Every major scale has a relative minor. The natural minor is the true relative in that it contains exactly the same notes as its relative major. As an example B major has exactly the same notes as its relative minor G# natural minor. The note names are just like B major (starting at a different location, of course).
B major: B,C#,D#,E,F#,G#,F#,A#,B
G# minor: G#,F#,A#,B,C#,D#,E,F#,G#
But G# minor comes in three forms, the other two, harmonic and melodic both raise the seventh scale degree. The name of the seventh scale degree is F# and we need to raise it a half step. Visualize it on a keyboard and you see the 'F#' move up to 'G'. But we already used letter G with the G# so rule 2 alarms go off!! and people runs for their lives, danger!!! we can't live like this!!!!. So to save civilization, what 'they' did was name the 'G' note, F 'double sharp'.
Really?
Yes, Really.
They were so serious that they gave it a symbol of its own: 'x'. The symbol 'Fx' means F double sharp. Same as a G, except it's following a naming rule that makes for silly correct names like Fx. Take a close look at MIDI_SHARP_NAMES, you'll see Cx, Fx and Gx names. The can of worms has been opened. But fortunately if we limit our scale types to these three forms of minor (and add the silly names to our enharmonic choices) we can play and correctly spell the three forms of minor starting from the allowable letter names.
We can reuse the code from Play Major scale with the addition of the formulas needed to play the three forms of minor. Those arrays are shown below along with a function that gets our choice from the scale type menu. That scale formula used as a paramenter to the playScale function which is activated by the click of the button. Again just to emphasize the point. These interval formula arrays are all that is needed to play the correct sounds. The function makeScale() does the work of assembling the array of correct names. The naming logic is for those of us who care about the standard music notation names. Maybe we could use them to draw the notes? If so, we need the correct names. (The makeScale() function was discussed on the Play Major Scale page.)
Frequently the melodic minor scale is presented as having an 'ascending' form and a 'descending' form. The ascending form is as defined here and the descending form is the same as natural minor. While there are many examples of that change in a use of minor when the melodic contour ascends and descends the are just as many examples where a descending phrase will use the 'ascending' form. My thought on the subject is: Why confuse the issue? there is no 'descending' melodic minor, we already have a name for that scale: natural minor. In minor keys, any of the three forms can be used as needed from one moment to the next to create the desired musical expression. If we wanted to create a formula for 'classical melodic minor' (up and down) it would use the numbers [0,2,3,5,7,9,11,12,10,8,7,5,3,2,0], using melodic minor on the way up and natural minor (in reverse) on the way down. But our makeScale() function isn't designed to handle that yet. So we'll stick with the three distinct forms.
var NATURAL_MINOR_SCALE = [0,2,3,5,7,8,10,12];
var HARMONIC_MINOR_SCALE = [0,2,3,5,7,8,11,12];
var MELODIC_MINOR_SCALE = [0,2,3,5,7,9,11,12];
function getScaleFormula() {
var minorTypeMenu = document.getElementById("minorType");
var minorType = minorTypeMenu.options[minorTypeMenu.selectedIndex].value;
if(minorType === "NaturalMinor") {
return NATURAL_MINOR_SCALE;
} else if(minorType === "HarmonicMinor") {
return HARMONIC_MINOR_SCALE;
} else if(minorType === "MelodicMinor") {
return MELODIC_MINOR_SCALE;
} else {
return NATURAL_MINOR_SCALE;
}
}
function playScale(){
var synth = new Tone.Synth().toDestination();
var keyNameMenu = document.getElementById('key');
var keyName = keyNameMenu.options[keyNameMenu.selectedIndex].value;
var scaleFormula = getScaleFormula();
var myScale = makeScale(scaleFormula, keyName);
document.getElementById("scaleDisplay").innerHTML = '<h2>'+myScale+'</h2>';
var patternMenu = document.getElementById('melodicPattern');
var patternName = patternMenu.options[patternMenu.selectedIndex].value;
var pattern = new Tone.Pattern(function(time, note){
//the order of the notes passed in depends on the pattern
synth.triggerAttackRelease(note, '4n', time);
}, myScale, patternName).start(0);
var tempo = document.myForm.tempo.value;
Tone.Transport.bpm.value = tempo
synth.volume.value = document.myForm.volume.value;
Tone.Transport.start('+0.1');
}
Choose a Minor scale type, a key and a pattern then click Play Minor Scale
After you click the Play Minor Scale button you'll see a display of the note names created. These are the names returned from the makeScale function and are used with the Tone.js function shown below. Notice the different spelling for the keys of A#/Bb and D#/Eb. Each pair sounds the same but have completely different spelling AND the sharp versions have double sharps in their harmonic and melodic forms. (The G#/Ab pair is one octave apart.) Yes, this music theory stuff can be tricky. And maybe not exactly sane. I wonder if we'll need another array of crazy names sometime soon. Fortunately not right now.
One final comment. Best practice is to have all of the javascript functions in a script separate from this web page. But so that those learning about Tone.js can easily see this sample code, the javascript is embeded in this page. View source for details.