UNPKG

maia-util

Version:

Utility math and music functions supporting various applications by Music Artificial Intelligence Algorithms, Inc.

1,355 lines (1,265 loc) 128 kB
var mu = (function () { 'use strict'; function array_equals(arr,arr2){ // Joe on Stack Overflow 27/12/2014. // In // array Array mandatory // Out Boolean // Returns true if two arrays are equal, and false otherwise. // http://stackoverflow.com/questions/7837456/comparing-two-arrays-in-javascript // If the other array is a falsy value, return. if (!arr2) return false; // Compare lengths. if (arr.length != arr2.length) return false; for (let i = 0, l=arr.length; i < l; i++){ // Check if we have nested arr2s. if (arr[i] instanceof Array && arr2[i] instanceof Array){ // Recurse into the nested arr2s. if (!array_equals(arr[i],arr2[i])) return false; } else if (arr[i] != arr2[i]){ // Warning - two different object instances will never be equal: // {x:20} != {x:20}. return false; } } return true; } function array_approx_equals(arr, arr2, tol = 0.00002){ // In // array Array mandatory, assumed to be (nested) numeric. // Out Boolean // Returns true if two arrays are approximately equal, up to a given // tolerance, and false otherwise. // If the other array is a falsy value, return. if (!arr2) return false; // Compare lengths. if (arr.length != arr2.length) return false; for (let i = 0, l=arr.length; i < l; i++){ // Check if we have nested arr2s. if (arr[i] instanceof Array && arr2[i] instanceof Array){ // Recurse into the nested arr2s. if (!array_approx_equals(arr[i],arr2[i])) return false; } else if (Math.abs(arr[i] - arr2[i]) >= tol){ return false; } } return true; } function index_item_1st_occurs(arr,a){ // Tom Collins 1/2/2015. // In // a Array, Boolean, Number, or String mandatory // Out Integer // Returns the index at which the given argument a firsts occur. It is more // robust than indexOf functionality because it will match arguments // consisting of arrays, strings, and booleans as well as numbers. It will // not match arbitrary objects, however (see second example in testing). var typeofa = typeof a; var instanceofarraya = a instanceof Array; var idx = -1; var i = 0; while (i < arr.length) { if (typeof arr[i] == typeofa){ if(instanceofarraya && arr[i] instanceof Array){ if (array_equals(arr[i],a)){ idx = i; i = arr.length - 1; } } else { if (arr[i] == a){ idx = i; i = arr.length - 1; } } } i=i+1; } return idx; } function index_item_1st_doesnt_occur(arr,a){ // Tom Collins 1/2/2015. // In // a Array, Boolean, Number, or String mandatory // Out Integer // Returns the index at which the given argument a first does not occur. It // is robust in the sense that it will match arguments consisting of arrays, // strings, and booleans as well as numbers. It will not match arbitrary // objects, however (see second example in testing). var typeofa = typeof a; var instanceofarraya = a instanceof Array; var idx = -1; var i = 0; while (i < arr.length) { if (!(typeof arr[i] == typeofa) || (instanceofarraya && !(arr[i] instanceof Array))){ idx = i; i = arr.length - 1; } else { if(instanceofarraya && arr[i] instanceof Array){ if (!array_equals(arr[i],a)){ idx = i; i = arr.length - 1; } } else { if (!(arr[i] == a)){ idx = i; i = arr.length - 1; } } } i=i+1; } return idx; } Array.prototype.equals = function(a){ return array_equals(this,a) }; Array.prototype.approx_equals = function(a, tol = 0.00002){ return array_approx_equals(this,a,tol) }; Array.prototype.index_item_1st_occurs = function(a){ return index_item_1st_occurs(this,a) }; Array.prototype.index_item_1st_doesnt_occur = function(a){ return index_item_1st_doesnt_occur(this,a) }; // Utility. function append_ontimes_to_time_signatures(time_sigs_array, crotchets_per_bar){ // Tom Collins 26/2/2015. // This function appends ontimes to rows of the time-signature table. Added // an optional argument crotchets_per_bar, so that in the event of an // anacrusis, the first bar is assigned the correct ontime. if (crotchets_per_bar == undefined){ var ontime = 0; } else { var ontime = -crotchets_per_bar; } time_sigs_array[0]["ontime"] = ontime; var i = 1; var n = time_sigs_array.length; while (i < n) { var c = (time_sigs_array[i]["barNo"] - time_sigs_array[i - 1]["barNo"])*time_sigs_array[i - 1]["topNo"]* 4/time_sigs_array[i - 1]["bottomNo"]; var d = time_sigs_array[i - 1]["ontime"] + c; time_sigs_array[i]["ontime"] = d; i=i+1; } return time_sigs_array; } function row_of_max_ontime_leq_ontime_arg(ontime, time_sigs_array){ // Tom Collins 17/10/2014. // In // ontime Number mandatory // time_sigs_array Array mandatory // Out Array // This function returns the row (in a list of time signatures) of the // maximal ontime less than or equal to the ontime argument. var ontime_out = time_sigs_array[0]; var i = 0; var n = time_sigs_array.length; while (i < n) { if (ontime < time_sigs_array[i]["ontime"]){ ontime_out = time_sigs_array[i - 1]; i = n - 1; } else if (ontime == time_sigs_array[0]["ontime"]){ ontime_out = time_sigs_array[i]; i = n - 1; } else if (i == n - 1){ ontime_out = time_sigs_array[i]; } i=i+1; } return ontime_out; } /** * Given an ontime and a time-signature array (with ontimes included), this * function returns the bar number and beat number of that ontime. Bar numbers * are one-indexed, meaning the bar number of an ontime in an anacrusis is zero. * * @author Tom Collins * @comment 17th October 2014 * @param {number} ontime - An ontime (time counting in crotchet beats starting * from 0 for bar 1 beat 1) for which we want to know the corresponding bar and * beat number. * @param {TimeSignature[]} time_sigs_array - An array of time signatures. * @return {number[]} An array whose first element is a bar number and whose * second element is a beat number. * * @example * var tsArr = [ * { * "barNo": 1, * "topNo": 4, * "bottomNo": 4, * "ontime": 0 * }, * { * "barNo": 3, * "topNo": 3, * "bottomNo": 4, * "ontime": 8 * } * ]; * bar_and_beat_number_of_ontime(11, tsArr); * → * [4, 1] */ function bar_and_beat_number_of_ontime(ontime, time_sigs_array){ var n = time_sigs_array.length; var relevant_row = row_of_max_ontime_leq_ontime_arg(ontime, time_sigs_array); if (ontime >= 0) { var excess = ontime - relevant_row["ontime"]; var local_beat_bar = relevant_row["topNo"]*4/relevant_row["bottomNo"]; var a = [ relevant_row["barNo"] + Math.floor(excess/local_beat_bar), (excess % local_beat_bar) + 1 ]; } else { var anacrusis_beat = time_sigs_array[0]["topNo"] + ontime + 1; var a = [0, anacrusis_beat]; } return a; } const lookup = [ { "sign": "G", "line": 2, "name": "treble clef"}, { "sign": "G", "line": 2, "clefOctaveChange": 1, "name": "treble clef 8va" }, { "sign": "G", "line": 2, "clefOctaveChange": 2, "name": "treble clef 15ma" }, { "sign": "G", "line": 2, "clefOctaveChange": -1, "name": "treble clef 8vb" }, { "sign": "G", "line": 1, "name": "French violin clef" }, { "sign": "C", "line": 1, "name": "soprano clef" }, { "sign": "C", "line": 2, "name": "mezzo-soprano clef" }, { "sign": "C", "line": 3, "name": "alto clef" }, { "sign": "C", "line": 4, "name": "tenor clef" }, { "sign": "C", "line": 4, "name": "baritone clef (C clef)" }, { "sign": "F", "line": 4, "name": "bass clef" }, { "sign": "F", "line": 4, "clefOctaveChange": 1, "name": "bass clef 8va" }, { "sign": "F", "line": 4, "clefOctaveChange": 2, "name": "bass clef 15ma" }, { "sign": "F", "line": 4, "clefOctaveChange": -1, "name": "bass clef 8vb" }, { "sign": "F", "line": 4, "clefOctaveChange": -2, "name": "bass clef 15mb" }, { "sign": "F", "line": 3, "name": "baritone clef (F clef)" }, { "sign": "F", "line": 5, "name": "subbass clef 15mb" }, // These last two do not seem to be supported. { "sign": "percussion", "line": 2, "name": "percussion clef" }, { "sign": "TAB", "line": 0, "name": "tablature" } ]; function clef_sign_and_line2clef_name(sign, line, clef_octave_change){ var i = 0; while (i < lookup.length){ if (lookup[i].sign == sign && lookup[i].line == line && (clef_octave_change == undefined || lookup[i].clefOctaveChange && lookup[i].clefOctaveChange == clef_octave_change)){ var clef_name = lookup[i].name; i = lookup.length - 1; } i++; } if (clef_name == undefined){ return "unknown"; } else { return clef_name; } } function convert_1st_bar2anacrusis_val(bar_1, divisions){ // Tom Collins 25/2/2015. // In // bar_1 Object mandatory // divisions Integer mandatory // Out Array // This function works out how long an anacrusis contained in bar_1 should // last. // Get top and bottom number from time signature, to work out how long a full // first bar should last. if (bar_1.attributes){ var attributes = bar_1.attributes; for (let j = 0; j < attributes.length; j++){ if (attributes[j].time){ // Assuming there is only one time per attribute... var time_sig_1 = {}; time_sig_1.topNo = parseInt(attributes[j].time[0].beats[0]); time_sig_1.bottomNo = parseInt(attributes[j].time[0]['beat-type'][0]); } } } if (time_sig_1 == undefined) { console.log('It was not possible to find a time signature in the first ' + 'bar of the top staff.'); console.log('Returning default values for the anacrusis and crotchets '+ 'bar, which may be wrong.'); return [0, 4]; } var anacrusis = 0; var crotchets_per_bar = 4*time_sig_1.topNo/time_sig_1.bottomNo; var dur_in_1st_bar_should_be = divisions*crotchets_per_bar; var ontime = 0; // Get backup value. if (bar_1.backup){ var backups = bar_1.backup; } else { backups = []; } // Increment over the notes. if (bar_1.note){ var notes = bar_1.note; for (let note_index = 0; note_index < notes.length; note_index++){ if (notes[note_index].grace == undefined){ // This is the integer duration expressed in MusicXML. var duration = parseInt(notes[note_index].duration[0]); var offtime = ontime + duration; // Correct rounding errors in the offtime values. // If the note is a second, third, etc. note of a chord, then do // not increment the ontime variable. if (note_index < notes.length - 1 && notes[note_index + 1].chord); else { // Do increment the ontime value. ontime = offtime; } } } } var compar = ontime/(backups.length + 1); if (compar != dur_in_1st_bar_should_be){ anacrusis = -compar/divisions; } return [anacrusis, crotchets_per_bar]; } function default_page_and_system_breaks(staff_and_clef_names, final_bar_no){ // Tom Collins 1/3/2015. // In // staff_and_clef_names Array mandatory // final_bar_no Integer mandatory // Out Array // If the page_breaks and system_breaks variables are empty, this function // will populate them with default values based on the number of staves and // bars. var page_breaks = []; var system_breaks = []; var nos_staves = staff_and_clef_names.length; switch (nos_staves){ case 1: var sbreak = 4; var pbreak = 10*sbreak; break; case 2: var sbreak = 4; var pbreak = 5*sbreak; break; case 3: var sbreak = 4; var pbreak = 3*sbreak; break; case 4: var sbreak = 4; var pbreak = 2*sbreak; break; case 5: var sbreak = 4; var pbreak = 2*sbreak; break; case 6: var sbreak = 4; var pbreak = 2*sbreak; break; default: var sbreak = 4; var pbreak = sbreak; break; } var curr_bar = sbreak; while (curr_bar < final_bar_no){ if (curr_bar%pbreak == 0){ page_breaks.push(curr_bar + 1); } else { system_breaks.push(curr_bar + 1); } curr_bar = curr_bar + sbreak; } return [page_breaks, system_breaks]; } function sort_points_asc_by_id(a, b){ // Tom Collins 18/2/2015. // In // a Object mandatory // b Object mandatory // Out Object // A helper function, to sort two notes (points) or rests ascending by the // values in the id field. var c = a.ID; var d = b.ID; if (typeof c == "string"){ c = parseFloat(c); } if (typeof d == "string"){ d = parseFloat(d); } return c - d; } function group_grace_by_contiguous_id(grace_array){ // Tom Collins 18/2/2015. // In // grace_array Array mandatory // An array of grace notes is the input to this function. The function groups // these grace notes into new arrays whose membership is determined by // contiguity of the id fields. This is to make sure that if several grace // notes precede an ordinary note, these are grouped together and (later) // attached to this ordinary note. var ga = grace_array.sort(sort_points_asc_by_id); if (ga.length > 0){ var gag = [[ga[0]]]; var gj = 0; for (let gi = 1; gi < ga.length; gi++){ if (parseFloat(ga[gi].ID) == parseFloat(gag[gj][gag[gj].length - 1].ID) + 1){ gag[gj].push(ga[gi]); } else { gag.push([ga[gi]]); gj++; } } } else { var gag = []; } return gag; } const midi_residue_lookup_array = [ [0, 0], [1, 0], [2, 1], [3, 2], [4, 2], [5, 3], [6, 3], [7, 4], [8, 4], [9, 5], [10, 6], [11, 6] ]; function guess_morphetic_in_c_major(mnn){ // Tom Collins 15/10/2014. // In // mnn Integer mandatory // Out Integer // This function takes a MIDI note number as its only argument. It // attempts to guess the corresponding morphetic pitch number, assuming // a key of or close to C major. var octave = Math.floor(mnn/12 - 1); var midi_residue = mnn - 12*(octave + 1); var midi_residue_idx = 0; var n = midi_residue_lookup_array.length; var i = 0; while (i < n){ if (midi_residue == midi_residue_lookup_array[i][0]){ midi_residue_idx = i; i = n - 1; } i=i+1; } var mpn_residue = midi_residue_lookup_array[midi_residue_idx][1]; var a = mpn_residue + 7*octave + 32; return a; } const fifth_steps_lookup_array = [ // Major keys. [[-6, 0], 6, 4], [[-5, 0], -1, -1], [[-4, 0], 4, 2], [[-3, 0], -3, -2], [[-2, 0], 2, 1], [[-1, 0], -5, -3], [[0, 0], 0, 0], [[1, 0], 5, 3], [[2, 0], -2, -1], [[3, 0], 3, 2], [[4, 0], -4, -2], [[5, 0], 1, 1], [[6, 0], -6, -3], // Minor keys. [[-3, 5], 6, 4], [[-2, 5], -1, -1], [[-1, 5], 4, 2], [[0, 5], -3, -2], [[1, 5], 2, 1], [[2, 5], -5, -3], [[3, 5], 0, 0], [[4, 5], 5, 3], [[5, 5], -2, -1], [[6, 5], 3, 2], [[-6, 5], 3, 2], [[7, 5], -4, -2], [[-5, 5], -4, -2], [[8, 5], 1, 1], [[-4, 5], 1, 1], [[9, 5], -6, -3] ]; function guess_morphetic(mnn, fifth_steps, mode){ // Tom Collins 15/10/2014. // In // mnn Integer mandatory // fifth_steps Integer mandatory // mode Integer mandatory // This function takes a MIDI note number and a key (represented by steps on // the circle of fiths, and mode). It attempts to guess the corresponding // morphetic pitch number, given the key. var fifth_steps_idx = 0; var n = fifth_steps_lookup_array.length; var i = 0; while (i < n){ if (fifth_steps == fifth_steps_lookup_array[i][0][0] && mode == fifth_steps_lookup_array[i][0][1]){ fifth_steps_idx = i; i = n - 1; } i=i+1; } var trans = fifth_steps_lookup_array[fifth_steps_idx].slice(1); var z = mnn + trans[0]; var w = guess_morphetic_in_c_major(z); var a = w - trans[1]; return a; } const pitch_class_lookup_array = [ [[12, 6], "B#"], [[0, 0], "C"], [[0, 1], "Dbb"], [[13, 6], "B##"], [[1, 0], "C#"], [[1, 1], "Db"], [[2, 0], "C##"], [[2, 1], "D"], [[2, 2], "Ebb" ], [[3, 1], "D#"], [[3, 2], "Eb"], [[3, 3], "Fbb"], [[4, 1], "D##"], [[4, 2], "E"], [[4, 3], "Fb"], [[5, 2], "E#"], [[5, 3], "F"], [[5, 4], "Gbb"], [[6, 2], "E##"], [[6, 3], "F#"], [[6, 4], "Gb"], [[7, 3], "F##"], [[7, 4], "G"], [[7, 5], "Abb"], [[8, 4], "G#"], [[8, 5], "Ab"], [[9, 4], "G##"], [[9, 5], "A"], [[9, 6], "Bbb"], [[-2, 0], "Cbb"], [[10, 5], "A#"], [[10, 6], "Bb"], [[11, 5], "A##"], [[11, 6], "B"], [[-1, 0], "Cb"] ]; function midi_note_morphetic_pair2pitch_and_octave(mnn, mpn){ // Tom Collins 15/10/2014. // In // mnn Integer mandatory // mpn Integer mandatory // Out String // This function converts a pair consisting of a MIDI note number and a // morphetic pitch number into a string consisting of a note's pitch and // octave. var octave = Math.floor((mpn - 32)/7); var midi_residue = mnn - 12*(octave + 1); var mpn_residue = mpn - (7*octave + 32); var pitch_class_idx = undefined; var n = pitch_class_lookup_array.length; var i = 0; while (i < n){ if (midi_residue == pitch_class_lookup_array[i][0][0] && mpn_residue == pitch_class_lookup_array[i][0][1]){ pitch_class_idx = i; i = n - 1; } i=i+1; } var a = pitch_class_lookup_array[pitch_class_idx][1] + octave; return a; } const lookup$1 = [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ]; function mnn2pitch_simple(MNN){ // Tom Collins 6/1/2016. // In // metadata Integer mandatory // Out String // This function converts a MIDI note number into a pitch class and octave. // It does so in a completely naive manner (no consideration of global or // local key), but this is handy for things like Tone.js playback, which tend // to prefer "C" to "B#", "C#" to "Db" (I think), and "G" to "F##". var octave = Math.floor(MNN/12 - 1); var MNN_mod_12 = MNN % 12; return lookup$1[MNN_mod_12] + octave //.toString(); } var keysLookup = [ { "nosSymbols": 0, "mode": "major", "keyName": "C major" }, { "nosSymbols": 1, "mode": "major", "keyName": "G major" }, { "nosSymbols": 2, "mode": "major", "keyName": "D major" }, { "nosSymbols": 3, "mode": "major", "keyName": "A major" }, { "nosSymbols": 4, "mode": "major", "keyName": "E major" }, { "nosSymbols": 5, "mode": "major", "keyName": "B major" }, { "nosSymbols": 6, "mode": "major", "keyName": "F# major" }, { "nosSymbols": 7, "mode": "major", "keyName": "C# major" }, { "nosSymbols": 8, "mode": "major", "keyName": "G# major" }, { "nosSymbols": 9, "mode": "major", "keyName": "D# major" }, { "nosSymbols": 10, "mode": "major", "keyName": "A# major" }, { "nosSymbols": 11, "mode": "major", "keyName": "E# major" }, { "nosSymbols": -1, "mode": "major", "keyName": "F major" }, { "nosSymbols": -2, "mode": "major", "keyName": "Bb major" }, { "nosSymbols": -3, "mode": "major", "keyName": "Eb major" }, { "nosSymbols": -4, "mode": "major", "keyName": "Ab major" }, { "nosSymbols": -5, "mode": "major", "keyName": "Db major" }, { "nosSymbols": -6, "mode": "major", "keyName": "Gb major" }, { "nosSymbols": -7, "mode": "major", "keyName": "Cb major" }, { "nosSymbols": -8, "mode": "major", "keyName": "Fb major" }, { "nosSymbols": -9, "mode": "major", "keyName": "Bbb major" }, { "nosSymbols": -10, "mode": "major", "keyName": "Ebb major" }, { "nosSymbols": -11, "mode": "major", "keyName": "Abb major" }, { "nosSymbols": 0, "mode": "minor", "keyName": "A minor" }, { "nosSymbols": 1, "mode": "minor", "keyName": "E minor" }, { "nosSymbols": 2, "mode": "minor", "keyName": "B minor" }, { "nosSymbols": 3, "mode": "minor", "keyName": "F# minor" }, { "nosSymbols": 4, "mode": "minor", "keyName": "C# minor" }, { "nosSymbols": 5, "mode": "minor", "keyName": "G# minor" }, { "nosSymbols": 6, "mode": "minor", "keyName": "D# minor" }, { "nosSymbols": 7, "mode": "minor", "keyName": "A# minor" }, { "nosSymbols": 8, "mode": "minor", "keyName": "E# minor" }, { "nosSymbols": 9, "mode": "minor", "keyName": "B# minor" }, { "nosSymbols": 10, "mode": "minor", "keyName": "F## minor" }, { "nosSymbols": 11, "mode": "minor", "keyName": "C## minor" }, { "nosSymbols": -1, "mode": "minor", "keyName": "D minor" }, { "nosSymbols": -2, "mode": "minor", "keyName": "G minor" }, { "nosSymbols": -3, "mode": "minor", "keyName": "C minor" }, { "nosSymbols": -4, "mode": "minor", "keyName": "F minor" }, { "nosSymbols": -5, "mode": "minor", "keyName": "Bb minor" }, { "nosSymbols": -6, "mode": "minor", "keyName": "Eb minor" }, { "nosSymbols": -7, "mode": "minor", "keyName": "Ab minor" }, { "nosSymbols": -8, "mode": "minor", "keyName": "Db minor" }, { "nosSymbols": -9, "mode": "minor", "keyName": "Gb minor" }, { "nosSymbols": -10, "mode": "minor", "keyName": "Cb minor" }, { "nosSymbols": -11, "mode": "minor", "keyName": "Fb minor" }, { "nosSymbols": 0, "mode": "ionian", "keyName": "C ionian" }, { "nosSymbols": 1, "mode": "ionian", "keyName": "G ionian" }, { "nosSymbols": 2, "mode": "ionian", "keyName": "D ionian" }, { "nosSymbols": 3, "mode": "ionian", "keyName": "A ionian" }, { "nosSymbols": 4, "mode": "ionian", "keyName": "E ionian" }, { "nosSymbols": 5, "mode": "ionian", "keyName": "B ionian" }, { "nosSymbols": 6, "mode": "ionian", "keyName": "F# ionian" }, { "nosSymbols": 7, "mode": "ionian", "keyName": "C# ionian" }, { "nosSymbols": 8, "mode": "ionian", "keyName": "G# ionian" }, { "nosSymbols": 9, "mode": "ionian", "keyName": "D# ionian" }, { "nosSymbols": 10, "mode": "ionian", "keyName": "A# ionian" }, { "nosSymbols": 11, "mode": "ionian", "keyName": "E# ionian" }, { "nosSymbols": -1, "mode": "ionian", "keyName": "F ionian" }, { "nosSymbols": -2, "mode": "ionian", "keyName": "Bb ionian" }, { "nosSymbols": -3, "mode": "ionian", "keyName": "Eb ionian" }, { "nosSymbols": -4, "mode": "ionian", "keyName": "Ab ionian" }, { "nosSymbols": -5, "mode": "ionian", "keyName": "Db ionian" }, { "nosSymbols": -6, "mode": "ionian", "keyName": "Gb ionian" }, { "nosSymbols": -7, "mode": "ionian", "keyName": "Cb ionian" }, { "nosSymbols": -8, "mode": "ionian", "keyName": "Fb ionian" }, { "nosSymbols": -9, "mode": "ionian", "keyName": "Bbb ionian" }, { "nosSymbols": -10, "mode": "ionian", "keyName": "Ebb ionian" }, { "nosSymbols": -11, "mode": "ionian", "keyName": "Abb ionian" }, { "nosSymbols": 0, "mode": "dorian", "keyName": "D dorian" }, { "nosSymbols": 1, "mode": "dorian", "keyName": "A dorian" }, { "nosSymbols": 2, "mode": "dorian", "keyName": "E dorian" }, { "nosSymbols": 3, "mode": "dorian", "keyName": "B dorian" }, { "nosSymbols": 4, "mode": "dorian", "keyName": "F# dorian" }, { "nosSymbols": 5, "mode": "dorian", "keyName": "C# dorian" }, { "nosSymbols": 6, "mode": "dorian", "keyName": "G# dorian" }, { "nosSymbols": 7, "mode": "dorian", "keyName": "D# dorian" }, { "nosSymbols": 8, "mode": "dorian", "keyName": "A# dorian" }, { "nosSymbols": 9, "mode": "dorian", "keyName": "E# dorian" }, { "nosSymbols": 10, "mode": "dorian", "keyName": "B# dorian" }, { "nosSymbols": 11, "mode": "dorian", "keyName": "F## dorian" }, { "nosSymbols": -1, "mode": "dorian", "keyName": "G dorian" }, { "nosSymbols": -2, "mode": "dorian", "keyName": "C dorian" }, { "nosSymbols": -3, "mode": "dorian", "keyName": "F dorian" }, { "nosSymbols": -4, "mode": "dorian", "keyName": "Bb dorian" }, { "nosSymbols": -5, "mode": "dorian", "keyName": "Eb dorian" }, { "nosSymbols": -6, "mode": "dorian", "keyName": "Ab dorian" }, { "nosSymbols": -7, "mode": "dorian", "keyName": "Db dorian" }, { "nosSymbols": -8, "mode": "dorian", "keyName": "Gb dorian" }, { "nosSymbols": -9, "mode": "dorian", "keyName": "Cb dorian" }, { "nosSymbols": -10, "mode": "dorian", "keyName": "Fb dorian" }, { "nosSymbols": -11, "mode": "dorian", "keyName": "Bbb dorian" }, { "nosSymbols": 0, "mode": "phrygian", "keyName": "E phrygian" }, { "nosSymbols": 1, "mode": "phrygian", "keyName": "B phrygian" }, { "nosSymbols": 2, "mode": "phrygian", "keyName": "F# phrygian" }, { "nosSymbols": 3, "mode": "phrygian", "keyName": "C# phrygian" }, { "nosSymbols": 4, "mode": "phrygian", "keyName": "G# phrygian" }, { "nosSymbols": 5, "mode": "phrygian", "keyName": "D# phrygian" }, { "nosSymbols": 6, "mode": "phrygian", "keyName": "A# phrygian" }, { "nosSymbols": 7, "mode": "phrygian", "keyName": "E# phrygian" }, { "nosSymbols": 8, "mode": "phrygian", "keyName": "B# phrygian" }, { "nosSymbols": 9, "mode": "phrygian", "keyName": "F## phrygian" }, { "nosSymbols": 10, "mode": "phrygian", "keyName": "C## phrygian" }, { "nosSymbols": 11, "mode": "phrygian", "keyName": "G## phrygian" }, { "nosSymbols": -1, "mode": "phrygian", "keyName": "A phrygian" }, { "nosSymbols": -2, "mode": "phrygian", "keyName": "D phrygian" }, { "nosSymbols": -3, "mode": "phrygian", "keyName": "G phrygian" }, { "nosSymbols": -4, "mode": "phrygian", "keyName": "C phrygian" }, { "nosSymbols": -5, "mode": "phrygian", "keyName": "F phrygian" }, { "nosSymbols": -6, "mode": "phrygian", "keyName": "Bb phrygian" }, { "nosSymbols": -7, "mode": "phrygian", "keyName": "Eb phrygian" }, { "nosSymbols": -8, "mode": "phrygian", "keyName": "Ab phrygian" }, { "nosSymbols": -9, "mode": "phrygian", "keyName": "Db phrygian" }, { "nosSymbols": -10, "mode": "phrygian", "keyName": "Gb phrygian" }, { "nosSymbols": -11, "mode": "phrygian", "keyName": "Cb phrygian" }, { "nosSymbols": 0, "mode": "lydian", "keyName": "F lydian" }, { "nosSymbols": 1, "mode": "lydian", "keyName": "C lydian" }, { "nosSymbols": 2, "mode": "lydian", "keyName": "G lydian" }, { "nosSymbols": 3, "mode": "lydian", "keyName": "D lydian" }, { "nosSymbols": 4, "mode": "lydian", "keyName": "A lydian" }, { "nosSymbols": 5, "mode": "lydian", "keyName": "E lydian" }, { "nosSymbols": 6, "mode": "lydian", "keyName": "B lydian" }, { "nosSymbols": 7, "mode": "lydian", "keyName": "F# lydian" }, { "nosSymbols": 8, "mode": "lydian", "keyName": "C# lydian" }, { "nosSymbols": 9, "mode": "lydian", "keyName": "G# lydian" }, { "nosSymbols": 10, "mode": "lydian", "keyName": "D# lydian" }, { "nosSymbols": 11, "mode": "lydian", "keyName": "A# lydian" }, { "nosSymbols": -1, "mode": "lydian", "keyName": "Bb lydian" }, { "nosSymbols": -2, "mode": "lydian", "keyName": "Eb lydian" }, { "nosSymbols": -3, "mode": "lydian", "keyName": "Ab lydian" }, { "nosSymbols": -4, "mode": "lydian", "keyName": "Db lydian" }, { "nosSymbols": -5, "mode": "lydian", "keyName": "Gb lydian" }, { "nosSymbols": -6, "mode": "lydian", "keyName": "Cb lydian" }, { "nosSymbols": -7, "mode": "lydian", "keyName": "Fb lydian" }, { "nosSymbols": -8, "mode": "lydian", "keyName": "Bbb lydian" }, { "nosSymbols": -9, "mode": "lydian", "keyName": "Ebb lydian" }, { "nosSymbols": -10, "mode": "lydian", "keyName": "Abb lydian" }, { "nosSymbols": -11, "mode": "lydian", "keyName": "Dbb lydian" }, { "nosSymbols": 0, "mode": "mixolydian", "keyName": "G mixolydian" }, { "nosSymbols": 1, "mode": "mixolydian", "keyName": "D mixolydian" }, { "nosSymbols": 2, "mode": "mixolydian", "keyName": "A mixolydian" }, { "nosSymbols": 3, "mode": "mixolydian", "keyName": "E mixolydian" }, { "nosSymbols": 4, "mode": "mixolydian", "keyName": "B mixolydian" }, { "nosSymbols": 5, "mode": "mixolydian", "keyName": "F# mixolydian" }, { "nosSymbols": 6, "mode": "mixolydian", "keyName": "C# mixolydian" }, { "nosSymbols": 7, "mode": "mixolydian", "keyName": "G# mixolydian" }, { "nosSymbols": 8, "mode": "mixolydian", "keyName": "D# mixolydian" }, { "nosSymbols": 9, "mode": "mixolydian", "keyName": "A# mixolydian" }, { "nosSymbols": 10, "mode": "mixolydian", "keyName": "E# mixolydian" }, { "nosSymbols": 11, "mode": "mixolydian", "keyName": "B# mixolydian" }, { "nosSymbols": -1, "mode": "mixolydian", "keyName": "C mixolydian" }, { "nosSymbols": -2, "mode": "mixolydian", "keyName": "F mixolydian" }, { "nosSymbols": -3, "mode": "mixolydian", "keyName": "Bb mixolydian" }, { "nosSymbols": -4, "mode": "mixolydian", "keyName": "Eb mixolydian" }, { "nosSymbols": -5, "mode": "mixolydian", "keyName": "Ab mixolydian" }, { "nosSymbols": -6, "mode": "mixolydian", "keyName": "Db mixolydian" }, { "nosSymbols": -7, "mode": "mixolydian", "keyName": "Gb mixolydian" }, { "nosSymbols": -8, "mode": "mixolydian", "keyName": "Cb mixolydian" }, { "nosSymbols": -9, "mode": "mixolydian", "keyName": "Fb mixolydian" }, { "nosSymbols": -10, "mode": "mixolydian", "keyName": "Bbb mixolydian" }, { "nosSymbols": -11, "mode": "mixolydian", "keyName": "Ebb mixolydian" }, { "nosSymbols": 0, "mode": "aeolian", "keyName": "A aeolian" }, { "nosSymbols": 1, "mode": "aeolian", "keyName": "E aeolian" }, { "nosSymbols": 2, "mode": "aeolian", "keyName": "B aeolian" }, { "nosSymbols": 3, "mode": "aeolian", "keyName": "F# aeolian" }, { "nosSymbols": 4, "mode": "aeolian", "keyName": "C# aeolian" }, { "nosSymbols": 5, "mode": "aeolian", "keyName": "G# aeolian" }, { "nosSymbols": 6, "mode": "aeolian", "keyName": "D# aeolian" }, { "nosSymbols": 7, "mode": "aeolian", "keyName": "A# aeolian" }, { "nosSymbols": 8, "mode": "aeolian", "keyName": "E# aeolian" }, { "nosSymbols": 9, "mode": "aeolian", "keyName": "B# aeolian" }, { "nosSymbols": 10, "mode": "aeolian", "keyName": "F## aeolian" }, { "nosSymbols": 11, "mode": "aeolian", "keyName": "C## aeolian" }, { "nosSymbols": -1, "mode": "aeolian", "keyName": "D aeolian" }, { "nosSymbols": -2, "mode": "aeolian", "keyName": "G aeolian" }, { "nosSymbols": -3, "mode": "aeolian", "keyName": "C aeolian" }, { "nosSymbols": -4, "mode": "aeolian", "keyName": "F aeolian" }, { "nosSymbols": -5, "mode": "aeolian", "keyName": "Bb aeolian" }, { "nosSymbols": -6, "mode": "aeolian", "keyName": "Eb aeolian" }, { "nosSymbols": -7, "mode": "aeolian", "keyName": "Ab aeolian" }, { "nosSymbols": -8, "mode": "aeolian", "keyName": "Db aeolian" }, { "nosSymbols": -9, "mode": "aeolian", "keyName": "Gb aeolian" }, { "nosSymbols": -10, "mode": "aeolian", "keyName": "Cb aeolian" }, { "nosSymbols": -11, "mode": "aeolian", "keyName": "Fb aeolian" }, { "nosSymbols": 0, "mode": "locrian", "keyName": "B locrian" }, { "nosSymbols": 1, "mode": "locrian", "keyName": "F# locrian" }, { "nosSymbols": 2, "mode": "locrian", "keyName": "C# locrian" }, { "nosSymbols": 3, "mode": "locrian", "keyName": "G# locrian" }, { "nosSymbols": 4, "mode": "locrian", "keyName": "D# locrian" }, { "nosSymbols": 5, "mode": "locrian", "keyName": "A# locrian" }, { "nosSymbols": 6, "mode": "locrian", "keyName": "E## locrian" }, { "nosSymbols": 7, "mode": "locrian", "keyName": "B## locrian" }, { "nosSymbols": 8, "mode": "locrian", "keyName": "F## locrian" }, { "nosSymbols": 9, "mode": "locrian", "keyName": "C## locrian" }, { "nosSymbols": 10, "mode": "locrian", "keyName": "G## locrian" }, { "nosSymbols": 11, "mode": "locrian", "keyName": "D## locrian" }, { "nosSymbols": -1, "mode": "locrian", "keyName": "E locrian" }, { "nosSymbols": -2, "mode": "locrian", "keyName": "A locrian" }, { "nosSymbols": -3, "mode": "locrian", "keyName": "D locrian" }, { "nosSymbols": -4, "mode": "locrian", "keyName": "G locrian" }, { "nosSymbols": -5, "mode": "locrian", "keyName": "C locrian" }, { "nosSymbols": -6, "mode": "locrian", "keyName": "F locrian" }, { "nosSymbols": -7, "mode": "locrian", "keyName": "Bb locrian" }, { "nosSymbols": -8, "mode": "locrian", "keyName": "Eb locrian" }, { "nosSymbols": -9, "mode": "locrian", "keyName": "Ab locrian" }, { "nosSymbols": -10, "mode": "locrian", "keyName": "Db locrian" }, { "nosSymbols": -11, "mode": "locrian", "keyName": "Gb locrian"} ]; function nos_symbols_and_mode2key_name(nos_symbols, mode){ // Tom Collins 19/2/2015. // In // nos_symbols Integer mandatory // mode String mandatory // Out String // This function takes the number of symbols in a key signature and a string // specifying the mode, and converts these pieces of information to a string // naming the key signature. For instance, -2 symbols means 2 flats, and // aeolian mode would give G aeolian. var i = 0; while (i < keysLookup.length){ if (keysLookup[i].nosSymbols == nos_symbols && keysLookup[i].mode == mode){ var key_name = keysLookup[i].keyName; i = keysLookup.length - 1; } i++; } if (key_name == undefined){ return "not specified"; } else { return key_name; } } function row_of_max_bar_leq_bar_arg(bar, time_sigs_array){ // Tom Collins 17/10/2014. // In // bar Integer mandatory // time_sigs_array Array mandatory // Out Array // This function returns the row (in a list of time signatures) of the // maximal bar number less than or equal to the bar number argument. var bar_out = time_sigs_array[0]; var i = 0; var n = time_sigs_array.length; while (i < n) { if (bar < time_sigs_array[i]["barNo"]){ bar_out = time_sigs_array[i - 1]; i = n - 1; } else if (bar == time_sigs_array[0]["barNo"]){ bar_out = time_sigs_array[i]; i = n - 1; } else if (i == n - 1){ bar_out = time_sigs_array[i]; } i=i+1; } return bar_out; } /** * Given a bar number and beat number, and a time-signature array (with ontimes * appended), this function returns the ontime of that bar and beat number. * * @author Tom Collins * @comment 17th October 2014 * @param {number} bar - A bar number for which we want to know the * corresponding ontime (time counting in crotchet beats starting from 0 for bar * 1 beat 1). * @param {number} beat - A beat number for which we want to know the * corresponding ontime (time counting in crotchet beats starting from 0 for bar * 1 beat 1). * @param {TimeSignature[]} time_sigs_array - An array of time signatures. * @return {number} An ontime * * @example * var tsArr = [ * { * "barNo": 1, * "topNo": 4, * "bottomNo": 4, * "ontime": 0 * }, * { * "barNo": 3, * "topNo": 3, * "bottomNo": 4, * "ontime": 8 * } * ]; * ontime_of_bar_and_beat_number(4, 1, tsArr); * → * 11 */ function ontime_of_bar_and_beat_number(bar, beat, time_sigs_array){ var n = time_sigs_array.length; var relevant_row = row_of_max_bar_leq_bar_arg(bar, time_sigs_array); var excess = bar - relevant_row["barNo"]; var local_beat_bar = relevant_row["topNo"]*4/relevant_row["bottomNo"]; var a = relevant_row["ontime"] + excess*local_beat_bar + beat - 1; return a; } const pitch_class_lookup_array$1 = [ [[12, 6], "B#"], [[0, 0], "C"], [[0, 1], "Dbb"], [[13, 6], "B##"], [[1, 0], "C#"], [[1, 1], "Db"], [[2, 0], "C##"], [[2, 1], "D"], [[2, 2], "Ebb" ], [[3, 1], "D#"], [[3, 2], "Eb"], [[3, 3], "Fbb"], [[4, 1], "D##"], [[4, 2], "E"], [[4, 3], "Fb"], [[5, 2], "E#"], [[5, 3], "F"], [[5, 4], "Gbb"], [[6, 2], "E##"], [[6, 3], "F#"], [[6, 4], "Gb"], [[7, 3], "F##"], [[7, 4], "G"], [[7, 5], "Abb"], [[8, 4], "G#"], [[8, 5], "Ab"], [[9, 4], "G##"], [[9, 5], "A"], [[9, 6], "Bbb"], [[-2, 0], "Cbb"], [[10, 5], "A#"], [[10, 6], "Bb"], [[11, 5], "A##"], [[11, 6], "B"], [[-1, 0], "Cb"] ]; function pitch_and_octave2midi_note_morphetic_pair(pitch_and_octave){ // Tom Collins 15/10/2014. // In // pitch_and_octave String mandatory // Out Array // This function converts a string consisting of a note's pitch and octave // into a pair consisting of a MIDI note number and a morphetic pitch // number. var length_arg = pitch_and_octave.length; var pitch_class = pitch_and_octave.slice(0, length_arg - 1); var octave = pitch_and_octave[length_arg - 1]; var pitch_class_idx = 1; var n = pitch_class_lookup_array$1.length; var i = 0; while (i < n){ if (pitch_class == pitch_class_lookup_array$1[i][1]){ pitch_class_idx = i; i = n - 1; } i=i+1; } var midi_mpn_residue = pitch_class_lookup_array$1[pitch_class_idx][0]; var a = []; a[0] = 12*octave + 12 + midi_mpn_residue[0]; a[1] = 7*octave + 32 + midi_mpn_residue[1]; return a; } function remove_duplicate_clef_changes(clef_changes){ // Tom Collins 23/2/2015. // In // clef_changes Array mandatory // Out Array // This function inspects pairs of clef changes. If there is a clef change // in bar n, and a clef change to the same clef in bar n + 1, the clef // change in bar n is removed because it is probably a cautionary. var arr_out = []; for (let clefi = 0; clefi < clef_changes.length - 1; clefi++){ if (clef_changes[clefi + 1].barNo != clef_changes[clefi].barNo + 1 || clef_changes[clefi + 1].clef != clef_changes[clefi].clef || clef_changes[clefi + 1].staffNo != clef_changes[clefi].staffNo){ arr_out.push(clef_changes[clefi]); } } if (clef_changes.length > 0){ arr_out.push(clef_changes[clef_changes.length - 1]); } return arr_out; } function resolve_expressions(expressions){ // Tom Collins 28/2/2015 // In // expressions Array mandatory // Out Array // When crescendos and diminuendos are expressed as lines (hairpins, wedges), // they have a stopping point as well as a starting point. This function // locates wedges stops corresponding to wedge starts, and unites the two // pieces of information in one array object. // Remove all stop wedges from the expressions array. var wedge_stops = []; var i = expressions.length - 1; while (i >= 0){ if (expressions[i].type.wedge && expressions[i].type.wedge == "stop"){ wedge_stops.push(expressions[i]); expressions.splice(i, 1); } i--; } // Loop over the expressions array and associate each wedge with a member of // wedge_stops. let target_idx; for (let j = 0; j < expressions.length; j++){ if (expressions[j].type.wedge){ // Find the target index in wedge_stops. target_idx = -1; var k = 0; while (k < wedge_stops.length){ if (wedge_stops[k].staffNo == expressions[j].staffNo && wedge_stops[k].placement == expressions[j].placement && wedge_stops[k].ontime >= expressions[j].ontime){ // We found it! target_idx = k; k = wedge_stops.length - 1; } k++; } if (target_idx >= 0){ // Add some properties to expressions[j]. expressions[j].barOff = wedge_stops[target_idx].barOn; expressions[j].beatOff = wedge_stops[target_idx].beatOn; expressions[j].offtime = wedge_stops[target_idx].ontime; } else { console.log('Could not find a stop for wedge: ', expressions[j]); } } } return expressions; } function sort_points_asc(a, b){ // Tom Collins 17/11/2014. // In // a Object mandatory // b Object mandatory // Out Object // A helper function to sort two notes (points) or rests by ascending ontime. // If the ontimes match and MNNs are defined, sort by these instead. If these // match, sort by staffNo. If these match, sort by voiceNo. if (a.ontime != b.ontime){ return a.ontime - b.ontime; } if (a.MNN != undefined){ if (a.MNN != b.MNN){ return a.MNN - b.MNN; } } if (a.staffNo != b.staffNo){ return a.staffNo - b.staffNo; } return a.voiceNo - b.voiceNo; } function staff_voice_xml2staff_voice_json(voice_no_from_xml, staff_nos_for_this_id, part_idx){ // Tom Collins 22/2/2015. // In // voice_no_from_xml Integer mandatory // staff_nos_for_this_id Array mandatory // part_idx Integer mandatory // Out Array // This function converts MusicXML 2.0 voice assignments, which can go beyond // 1-4 into 5-8 in order to encode multiple staves within the same part, to // json_score voice assignments, which use staff number to encode multiple // staves within the same part separately, and a voice number always in the // range 0-3. if (voice_no_from_xml !== undefined){ // There is a maximum of four voices per staff. In MusicXML 2.0, voices 5-8 // are used to encode a second staff in the same part. In a json_score // these will have separate staff numbers, and this is handled here. The // convention of using voices 5-8 to encode a second staff in the same part // is not adhered to by hum2xml. var staff_idx = Math.floor((voice_no_from_xml - 1)/4); var staffNo = staff_nos_for_this_id[staff_idx]; var voiceNo = voice_no_from_xml%4 - 1; } else { var staffNo = part_idx; var voiceNo = 0; } return [staffNo, voiceNo]; } // File conversion. function comp_obj2note_point_set(comp_obj){ // Tom Collins 2/2/2015. // In // comp_obj Object mandatory // Out Array // This function iterates over the notes property of a Composition object, // and converts the objects found there into a point-set format, with // so-called columns for ontime, MNN, MPN, duration, staff number, and // velocity in [0, 1]. var notes = comp_obj.notes; var out_array = []; for (let inote = 0; inote < notes.length; inote++){ var note = [ notes[inote].ontime, notes[inote].MNN, notes[inote].MPN, notes[inote].duration, notes[inote].staffNo ]; if (notes[inote].tonejs !== undefined && notes[inote].tonejs.volume !== undefined) { note.push(notes[inote].tonejs.volume); } else if (notes[inote].velocity !== undefined){ note.push(notes[inote].velocity); } else { note.push(.8); } out_array.push(note); } return out_array } function restrict_point_set_in_nth_to_xs(point_set, n, xs){ // Tom Collins 24/11/2014. // In // point_set Array mandatory // n Integer mandatory // xs Array mandatory // Out Array // The first argument to this function is an array consisting of numeric // arrays of uniform dimension (what I call a point set). We are interested // in the nth element of each array, where n is the second argument. A point // is retained in the output if its nth value is a member of the array // specified by the third argument. var point_set_out = []; for (let ip = 0; ip < point_set.length; ip++){ if (xs.indexOf(point_set[ip][n]) != -1){ point_set_out.push(point_set[ip]); } } return point_set_out; } function get_unique(arr){ // Tom Collins 24/11/2014. // In // arr Array mandatory // Out Array // This function returns unique elements from the input array. It will not // handle nested arrays properly (see unique_rows). var a = []; for (let i=0, l=arr.length; i<l; i++){ if (a.indexOf(arr[i]) === -1){ a.push(arr[i]); } } return a; } function split_point_set_by_staff(point_set, staff_idx){ // Tom Collins 2/2/2015. // In // point_set Array mandatory // staff_idx Integer mandatory // Out Array // This function splits a point set into multiple point sets, grouping by the // values in the (staff_idx)th element. // Get the unique staves. var staves = []; for (let ipt = 0; ipt < point_set.length; ipt++){ staves.push(point_set[ipt][staff_idx]); } var unq_staves = get_unique(staves).sort(function(a, b){return a - b}); var out_array = []; // Create a point set consisting of points in each staff. for (let iuq = 0; iuq < unq_staves.length; iuq++){ var curr_point_set = restrict_point_set_in_nth_to_xs( point_set, staff_idx, [unq_staves[iuq]]); out_array[iuq] = curr_point_set; } return out_array; } function copy_array_object(arr){ // Tom Collins 21/2/2015. // In // arr Array mandatory // Out Array // This function returns an independent copy of an array object. return JSON.parse(JSON.stringify(arr)); } // Point-set operations. function copy_point_set(point_set){ // Tom Collins 24/11/2014. // In // point_set Array mandatory // Out Array // This function returns an independent copy of a point set. var E = []; point_set.map(function(x){ E.push(x.slice()); }); return E; // Old version. // var n = point_set.length; // var E = new Array(n); // var i = 0; // Increment over D and E. // while (i < n){ // E[i] = point_set[i].slice(); // i++; // } // return E; } function index_point_set(point_set){ // Tom Collins 24/11/2014. // In // point_set Array mandatory // Out Array // This function pushes index values to the last element of each point. var k = point_set[0].length; var n = point_set.length; var i = 0; // Increment over point_set. while (i < n){ point_set[i][k] = i; i++; } return point_set; } function lex_more(u, v, k){ // Tom Collins 24/11/2014. // In // u Array mandatory // v Array mandatory // k Integer optional // This function returns 1 if u is more than v, where more than is the // lexicographic ordering. It returns -1 otherwise. // In general, for two vectors u and v, this function finds the first index // i such that u(i) is not equal to v(i). If u(i) is more than v(i), then u // is more than v. If v(i) is more than u(i), then v is more than u. In // the event that u equals v, u is not more than v. if (typeof k === 'undefined') { k = u.length; } // Logical outcome. var tf = -1; var i = 0; // Increment over u, v. while (i < k){ if (u[i] == v[i]){ i++; } else { if (u[i] > v[i]){ tf = 1; i = k + 1; } else { i = k + 1; } } } return tf; } function sort_rows(point_set){ // Tom Collins 24/11/2014. // In // point_set Array mandatory // Out Array // The only argument to this function is an array consisting of numeric // arrays of uniform dimension (what I call a point set). This function // returns the elements in lexicographic order as first argument. As second // argument are the indices of each element from the input array. // Create an independent copy of the dataset. var E = copy_point_set(point_set); // Index the copied dataset. E = index_point_set(E); // Sort the indexed and copied dataset. E.sort(lex_more); // Create a new variable that will contain just the dataset. var k = point_set[0].length; var n = point_set.length; var F = new Array(n); // Create a new variable that will contain just the index. var g = new Array(n); var i = 0; // Increment over E, F, and g. while (i < n){ F[i] = E[i].slice(0, k); g[i] = E[i][k]; i++; } return [F, g]; } /** * This function counts rows of the input `point_set`, weighted, if desired, by * values in `wght_idx`. * * @author Tom Collins and Christian Coulon * @comment 7th November 2015 * @param {PointSet} point_set - A point set * @param {number} [wght_idx] - The dimension of each point that should be used * to weight the count. If left undefined, each occurrence of a point will * increment the count of that point by 1. * @return {Object} [PointSet, number[]] An array whose first element is a * {@link PointSet} (unique and lexicographically ascending version of the * input), and whose second element is a (possibly weighted) count of those * points in the input. * * @example * var ps = [[64, 2], [65, 1], [67, 1], [67, 1]]; * var w = 1; * count_rows(ps, w) * → * [ * [ * [64, 2], [65, 1], [67, 1] * ], *