The music theory content can be found at Music Fundamentals on the Web.
We can use similar code as our play scale code for creating an interval. Instead of thinking about (1) a key note an then (2) a scale formula (an array of numbers), we'll think of (1) a first note and (2) an interval formula (just one number). We can apply the interval formula with addition or subtraction to the first note (MIDI number) which will create ascending or descending intervals. After we have the MIDI numbers we translate them into note names and use code similar to playScale(). Just so we have something specific to intervals, will write a function called playInterval().
function playInterval(notes) {
var synth = new Tone.Synth().toDestination();
var interval = new Tone.Sequence(function(time, note){
synth.triggerAttackRelease(note, 1);
}, notes, "2n");
//begin at the beginning
interval.loop = false;
interval.start(0);
Tone.Transport.start("+0.1");
}
The code for playing of the notes of the interval is simple, the code for proper and correct naming of the notes will require more thought (and code). To play the interval we play the first note (or if it's a MIDI number, translate it into a name), add (or subtract) the MIDI numbers to get the second note. Translate that to a name and play it. We don't care if it's the correct name, regardless, it's the correct sound. Since Tone.js is all about the sounds, focusing on the PLAYING of interval is pretty straight forward.
Now dealing with the crazy naming system, that's another story.
If we wanted to make an app for interval eartraining we would want to allow a random feature (using Math.random) where a random name is selected for the first note and a random interval size is selected to create the interval. If we were to use sharp names only, many of the naming problems would go away but that isn't the way it works in the real music world. We need to be able to play intervals from any legal name in music, both C# and Db.
Here is a problem scenario: we start on a note named Db (MIDI number 61) and we have a interval number of 4 (major 3rd). If we use an ascending interval the midi number becomes 65 (letter F). However if we name the first note C# (still MIDI number 61) it's most likely that the upper note should be called E# (major 3rd above C#). It may be surprising but C#-E# will show up more often than C#-F. If we want to use reasonable interval names we'll have to keep track of the name assigned to the first note in order to reasonably name the second note. The interval of C# to F is technically a diminished 4th. It does occur on ocassion, it's the inversion of an augmented 5th and part of an augmented chord. But a major 3rd interval is as common as a sunrise. We should probably call the F an E# in this case. This is just one of conundrums in naming intervals. What about a major third below Db? Well the note should have a B letter name right?, hmm I guess Bbb (B double flat). Yikes maybe this is when letter A (making the interval a diminished 4th) makes more sense. Or maybe force that first note to be called C#. We have the control to do what we want in our code. But....get the excederin.
We'll get a start with setting up some rules for naming intervals. To keep things fairly simple we'll limit our interval names to either one of the two arrays but not use names from both. This will limit use from some real world intervals such as augmented 2nds like Eb - F# but we'll live with it. For random intervals we'll use three random numbers (using getRandomIntInclusive(min,max) from mozilla developer network). The first random number picks which array to use. The second random number picks the first note. The third random number picks the size of the interval. Use the first note as the index into the selected array to get the name of the first note. Add (or subtract for descending intervals) the interval number to the starting note, use that value as the index into the array to get the name of the second note. Use those names to create the notes array for the playInterval() function.
But remember the opened can of worms? Yes, this random method means the sometimes our first note name will have a 'x' (double sharp). We could prevent that with some more code (check the name for a 'x', is it's present, use the other MIDI names array) but we'll just leave it as is. Just adding more proof that the music language has some serious quirks that must be dealt with. Yes, this needs much more work if we want the double sharps to come out only when necessary and not randomly.[Sigh.]
// FROM: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var intervalNotesSaved = ['C4','C4']; // default unison to start
function setupAndPlayInterval() {
var whichArray = getRandomIntInclusive(0,1);
var firstNote = getRandomIntInclusive(60,71);
var numberOfHalfSteps = getRandomIntInclusive(0,12);
var myNoteArray = [];
var myNotes = [];
var descendingIntervalCheckbox = document.getElementById("descendingInterval");
if (descendingIntervalCheckbox.checked) {
numberOfHalfSteps = numberOfHalfSteps * -1;
}
if(whichArray === 0) {
myNoteArray = MIDI_SHARP_NAMES;
} else {
myNoteArray = MIDI_FLAT_NAMES;
}
myNotes.push(myNoteArray[firstNote]);
myNotes.push(myNoteArray[firstNote+numberOfHalfSteps]);
intervalNotesSaved = myNotes; // save for possible replay
playInterval(myNotes);
}
// replay button .onclick
function replayInterval() {
if(intervalNotesSaved) {
playInterval(intervalNotesSaved);
}
}
// show Note Names .onclick
function showNoteNames() {
var intervalNum = (Tone.Frequency(intervalNotesSaved[1]).toMidi()) - (Tone.Frequency(intervalNotesSaved[0]).toMidi());
document.getElementById('intervalDisplay').innerHTML = '<h2>'+intervalNotesSaved[0]+", second note: "+intervalNotesSaved[1]+", half steps: "+intervalNum+'</h2>';
}
Click the Create New Interval button and listen. Given the name of the first note, what to you think the name of the second note is? How many half steps are in the interval? Click the Show Note Names button to reveal the name of the second note. As mentioned above, there will be some unexpected names shown, due to the two array of names we needed to correctly spell major and minor scales. We could go back to a single MIDI name array that has only the 'normal' names. But that doesn't address the issue either. If we want more logical note names we'll need a different (non-random) approach to selecting names for creating intervals.
We'll do more work on this note naming issue in Play Intervals part two.
(click the stop button between plays)