Programming the Create Rhythms page

There are several programming techniques used in creating the Create Rhythms page that are similar to the Create Melody page. Since the process is primarily concerned with rhythm the process is a little more simple.

The basic premise of the process is for each duration of a user supplied rhythmic outline phrase swap that duration out with a short series of notes whose total duration is equal to the original. The main function of this process is named createRhythmFromOutline(rhythm_outline, mod_factors). The incoming lilypond code is first translated into the Tone.js format using the lilyPondAdapter.js module. The resulting code contains two arrays, one for the notes and one for the durations. The user can edit a mod factors list (one for each note) which is used to determine the array of replacement rhythms applied to the rhythmic outline phrase. The user can specify 'no mod' with a code of '1' or enter a specific number code. If the '*' code is used the code will select a random number between 1 and 5', these code are shown below.

The mod factor codes are used as parameters to a function that is used in the translation process. The following function is used to select a translation object.


function mapModFactorToDurations(type) {
    switch(type) {
        case 1: return oneToOneDurations; break;
        case 2: return oneToTwoDurations; break;
        case 3: return oneToThreeDurations; break;
        case 4: return oneToFourDurations; break;
        case 5: return oneToFiveDurations; break;
        default: return oneToOneDurations;
    }
}

These translation objects are the same as discussed on the Create Melody page. The duration from the rhythmic outline page is replaced with the rhythmic fragment from the translation object

The design is to translate a duration like a half note (2n in tonejs-speak) into two parts like dotted quarter and an eighth (['4n + 8n','8n'] in tonejs-speak) and other variations. The oneToTwoDurations var contains a few variations of note substitutions like below.


var oneToTwoDurations = {
//... translations of '1n'
   
   "2n": [["4n","4n"],["4n + 8n","8n"],["8n","4n + 8n"],["4n + 8n + 16n","16n"]],

... translations of '4n' and other dotted durations

}

// to be compatible with other duration translation arrays.
// need to wrap the values inside an array of one element
var oneToOneDurations = {
   "1n": ["1n"], "2n": ["2n"], "4n": ["4n"], 
...
}

// i.e. "1n": "1n", "2n": "2n", ...DOES NOT WORK

//      "1n": ["1n"], "2n": ["2n"], ...DOES WORK 

Other similar objects named oneToThreeDurations oneToFourDurations and oneToFiveDurations are used. The array.length of the value for a key of '2n' is used to randomly select one of the variations.

Before using the results the nested array needs to be flattened for tonejs to be able to play it properly. This required step was elusive as the logging during debugging didn't always show the nesting of the duration arrays. In other words, I couldn't see the problem using console.log() but until I flatten the nested arrays it didn't work. This is where using the browser dev tools for debugging was helpful as you could see nested arrays more clearly.

Lesson Learned: for debugging USE THE DEV TOOLS available in the javascript console of the the browser.


// this returns both notes and durations
var results = createRhythmFromOutline(lilynotes, mod_factors); 
var myNotes = flatten(results[0].slice());
var myDurs = flatten(results[1].slice());

Now the data is usable for Tone.js to play.

If a oneToSixDurations object is added to the code a change need to made in one place:

  1. update the upperLimit value in createModFactorArray()
    var upperLimit = 6; // (previous value was 5)

When the user supplied mod factor is '*', the upperLimit value is used to randomly select one of the oneToXXXDurations objects to use for translating that duration of the rhythmic outline note.

If only new rhythmic variations are added to the existing oneToXXXDurations family of objects then no additional changes are needed beyond those changes. The current code will recognize the new size changes.