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
JavaScript
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]
* ],
*