diff --git a/JS/.eslintrc.js b/JS/.eslintrc.js
index 9bbda1f..19136ed 100644
--- a/JS/.eslintrc.js
+++ b/JS/.eslintrc.js
@@ -11,10 +11,6 @@ module.exports = {
},
"rules": {
"no-console":0,
- "quotes": [
- "error",
- "double"
- ],
"semi": [
"error",
"always"
diff --git a/JS/API/ScoreIterator.js b/JS/API/ScoreIterator.js
new file mode 100644
index 0000000..6d3554d
--- /dev/null
+++ b/JS/API/ScoreIterator.js
@@ -0,0 +1,271 @@
+"use strict";
+
+//"private static" utility definitions=========================================
+const xml2js = require("xml2js");
+const parser = new xml2js.Parser({explicitArray: false, mergeAttrs: true});
+const pitchToMidiNum = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A":9, "B": 11};
+
+function traverse(obj,func)
+{
+ for (let i in obj)
+ {
+ func.apply(this,[i, obj[i]]);
+ if (obj[i] !== null && typeof(obj[i])==="object") traverse(obj[i],func);
+ }
+}
+
+//create objects for each part, this will reduce searching whole score.
+//part being the full name of parts, ex: Solo Violin, Violin I, Violin II
+function makeInstrumentObjects(musicObj)
+{
+ let partNames = [];
+ let instrumentObjects = {}; //will be like {"Flute" [..array of measures]}
+ let measureArraysSeen = 0;
+
+ function process(key, value) //builds array of instrument objects
+ {
+ //first find the part names as they"re always towards the top of file
+ //This will be pushed in the correct order as we see them:
+ if (key === "part-name") partNames.push(value);
+ if (key === "measure")
+ {
+ const instrumentName = partNames[measureArraysSeen];
+ instrumentObjects[instrumentName] = [];
+
+ for (const measure of value)
+ {
+ instrumentObjects[instrumentName].push(measure);
+ }
+ measureArraysSeen++;
+ }
+ }
+
+ traverse(musicObj, process);
+ return instrumentObjects;
+}
+
+function ScoreIterable(instrumentObjects)
+{
+ let scoreIterable = {};
+ let part = [];
+
+ for (let instrumentName in instrumentObjects)
+ {
+ for (let measure of instrumentObjects[instrumentName])
+ {
+ let midiNum = 0;
+ let timeNotesMap = new Map(); //contains {"default-x", [pitches]}
+ //^ MUST USE MAP NOT OBJECT to ensure notes are always in correct order
+ //strategy: loop through measure to see symbols happening
+ //at same points in time (default-x) measureArraysSeen++;
+
+ let voiceTimes = [];
+ // ^^^ [5, 2] means voice 1 currently at 5, voice 2 currently at 2
+ //voiceTimes[voice] => gives the time in beats the voice is from
+ //beginning of measure
+ let bassNoteDuration = -123; //bass note of potential chord!
+ let notes = measure["note"];
+
+ if (notes !== undefined) //NOTE: returns an array of note tags for a measure
+ {
+ for (let singleNote of notes)
+ {
+ let voice = parseInt(singleNote["voice"]);
+
+ //check if first time seeing this voice
+ if (voice === undefined)
+ {
+ throw new Error("No voice tag??");
+ }
+ else
+ {
+ while (voiceTimes.length < voice)
+ {
+ voiceTimes.push(0);
+ }
+ }
+
+ if (singleNote["pitch"] !== undefined)
+ {
+ //1) Calculate midinum
+ //TODO: make helper
+ midiNum += pitchToMidiNum[singleNote["pitch"]["step"]];
+ if (singleNote["pitch"]["alter"] !== undefined)
+ midiNum += parseInt(singleNote["pitch"]["alter"]);
+ midiNum += parseInt(singleNote["pitch"]["octave"]) * 12;
+
+ let note = {};
+ note.midiNum = midiNum;
+ note.duration = parseInt(singleNote["duration"]);
+
+ let currentTime = voiceTimes[voice - 1];
+
+ //NOTE:two notes of same duration at same time can be same voice
+ //two notes of different duration at same start time are two voices
+ //^ this is mentioned in the musicxml standard!
+ //only single voice playing multiple notes has chord tag
+ if (singleNote["chord"] !== undefined)
+ {
+ currentTime = currentTime - bassNoteDuration;
+ }
+ // console.log("currentTime", currentTime);
+ // console.log("note", note);
+ let existingVal = timeNotesMap.get(currentTime);
+ // console.log("existing", existingVal);
+
+ if (existingVal)
+ {
+ // console.log("existingVal", existingVal);
+ existingVal.push(note);
+ timeNotesMap.set(currentTime, existingVal);
+ }
+ else
+ {
+ let arr = [];
+ arr.push(note);
+ timeNotesMap.set(currentTime, arr);
+ }
+
+ if (singleNote["chord"] === undefined)
+ {
+ voiceTimes[voice - 1] += note.duration;
+ bassNoteDuration = note.duration;
+ }
+ midiNum = 0;
+ }
+ else if (singleNote["rest"] !== undefined)
+ {
+ let currentTime = voiceTimes[voice - 1];
+ let existingVal = timeNotesMap.get(currentTime);
+
+ if (existingVal)
+ {
+ timeNotesMap.set(currentTime, existingVal);
+ }
+ else
+ {
+ let arr = [];
+ arr.push(parseInt(singleNote["duration"]));
+ timeNotesMap.set(currentTime, arr);
+ }
+
+ part.push(singleNote["duration"]); //TODO
+ }
+ } //loop through measure
+
+ let sortedKeys = [];
+
+ for (let key of timeNotesMap.keys())
+ {
+ sortedKeys.push(key);
+ }
+ sortedKeys.sort();
+
+ for (let key of sortedKeys)
+ {
+ let timeStampedMap = new Map();
+ timeStampedMap.set(key, timeNotesMap.get(key));
+ part.push(timeStampedMap);
+ }
+
+ console.log("timeNotesMap", timeNotesMap);
+ } //if note
+ } //instrumentName
+
+ scoreIterable[instrumentName] = part; //TODO
+ } //loop through instruments
+
+ return scoreIterable;
+}
+
+const errors =
+{
+ "noValidInstrumentSelected": 'No valid instrument selected, ex: ("Flute")!',
+ "noNext": "no next exists",
+ "noPrev": "no prev exists!",
+ "invalidPosition": "setPosition to invalid index"
+};
+
+//=============================================================================
+const factoryScoreIterator = (MusicXML) =>
+{
+ let musicObj;
+ parser.parseString(MusicXML, function (err, jsObj)
+ {
+ if (err) throw err;
+ musicObj = jsObj;
+ });
+
+ const instrumentObjects = makeInstrumentObjects(musicObj);
+ const scoreIterator = {};
+ let scoreIterable = ScoreIterable(instrumentObjects);
+ // console.dir(scoreIterable);
+
+ scoreIterable["Classical Guitar"].forEach((map) => console.log(map));
+ let selectedInstrument = "NONE";
+ let currentIndex = -1;
+
+ scoreIterator.selectInstrument = (instrumentName) =>
+ {
+ selectedInstrument = instrumentName;
+ };
+
+ scoreIterator.next = () =>
+ {
+ if (currentIndex === scoreIterable[selectedInstrument].length - 1)
+ {
+ throw new Error(errors.noNext);
+ }
+ else
+ {
+ currentIndex++;
+ }
+ if (scoreIterable[selectedInstrument] === undefined)
+ throw new Error(errors.noValidInstrumentSelected);
+ return scoreIterable[selectedInstrument][currentIndex];
+ };
+
+ scoreIterator.prev = () =>
+ {
+ if (currentIndex === 0)
+ {
+ throw new Error("No prev exists!");
+ }
+ else
+ {
+ currentIndex--;
+ }
+
+ if (scoreIterable[selectedInstrument] === undefined)
+ throw new Error(errors.noValidInstrumentSelected);
+ return scoreIterable[selectedInstrument][currentIndex];
+ };
+
+ scoreIterator.hasNext = () =>
+ {
+ if (scoreIterable[selectedInstrument] === undefined)
+ throw new Error(errors.noValidInstrumentSelected);
+
+ return (currentIndex < scoreIterable[selectedInstrument].length - 1);
+ };
+
+ scoreIterator.hasPrev = () =>
+ {
+ if (scoreIterable[selectedInstrument] === undefined)
+ throw new Error(errors.noValidInstrumentSelected);
+
+ return (currentIndex > 0);
+ };
+
+ scoreIterator.getPosition = () => currentIndex;
+
+ scoreIterator.setPosition = (position) =>
+ {
+ if (position > scoreIterable.length -1)
+ throw new Error(errors.invalidPosition);
+ currentIndex = position;
+ };
+ return scoreIterator;
+}; //end of factory
+
+module.exports = factoryScoreIterator;
diff --git a/JS/API/ScoreIterator.spec.js b/JS/API/ScoreIterator.spec.js
new file mode 100644
index 0000000..f245455
--- /dev/null
+++ b/JS/API/ScoreIterator.spec.js
@@ -0,0 +1,20 @@
+"use strict";
+const fs = require("fs");
+const test = require("tape").test;
+const ScoreIterator = require("./ScoreIterator");
+
+//TODO: test selectInstrument
+
+let musicXML = fs.readFileSync("../scores/guitar_two_voices.xml");
+// {"Classical Guitar":[[{"pitch":45}],[{"pitch":50}],[{"pitch":47},{"pitch":50}],[{"pitch":47}],"2",[{"pitch":41},{"pitch":50}],[{"pitch":41},{"pitch":47}]]}
+
+test("next", function(t)
+{
+ const scoreIterator = ScoreIterator(musicXML);
+
+ scoreIterator.selectInstrument("Classical Guitar");
+ // t.deepEqual(scoreIterator.next(), [], "next 1");
+
+
+ t.end();
+});
diff --git a/JS/API/strategy.txt b/JS/API/strategy.txt
new file mode 100644
index 0000000..65ba4fa
--- /dev/null
+++ b/JS/API/strategy.txt
@@ -0,0 +1,49 @@
+GOAL:
+next() => {"pitch": "A", "duration": 2, ... } // use pitch and octave instead of midi num?
+// internally this sets an index
+
+
+instrumentObjects =
+{
+ "flute": {"notes": [.......], "durations": [......], ...}
+ ...
+}
+
+return Object.assign({}, instrumentObjects.flute.notes[iterator]...)
+
+OR
+
+instrumentObjects =
+{
+ "flute": [[{"note": 23, "duration": 2}, {"note": 27, "duration": 2}], []]
+
+ ...
+}
+
+OR
+
+instrumentObjects =
+{
+ "flute": [{23: 2, 27:2}, []]
+ ^ NO. Cant use map because what if the chord has two of the same note?
+ ...
+}
+
+can we assume if there are two notes play at once in one part there are two voices?
+^ in reality no.. guitars can play two notes at once
+
+=====aside====
+should makeInstrumentObjects really be inside the factoryScoreSearcher and contained within a closure?
+From a scoping perspective, this is less static parent lookup and provents makeInstrumentObjects from being called twice...
+
+then again, should all private functions be static since their definition is consistent..? less memory?
+
+Why aren't you using map instead of object in makeInstrumentObjects
+
+
+=========================
+strategy to create chord iterable:
+loop through all notes of a measure and if they occur at same default-x (position),
+they are part of a chord. Clear upon new measure. What about ties???
+
+can this be used to create getNextchord
diff --git a/JS/scores/guitar.xml b/JS/scores/guitar.xml
new file mode 100644
index 0000000..fe1a0b3
--- /dev/null
+++ b/JS/scores/guitar.xml
@@ -0,0 +1,201 @@
+
+
+
+
+ guitar
+
+
+
+ MuseScore 2.0.3
+ 2017-03-28
+
+
+
+
+
+
+
+
+
+ 7.05556
+ 40
+
+
+ 1683.36
+ 1190.88
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+
+
+
+
+ guitar
+
+
+
+ Classical Guitar
+ Guit.
+
+ Classical Guitar
+
+
+
+ 1
+ 25
+ 78.7402
+ 0
+
+
+
+
+
+
+
+
+ -0.00
+ 608.23
+
+ 170.00
+
+
+
+ 1
+
+ 0
+
+
+
+ G
+ 2
+ -1
+
+
+
+
+ A
+ 3
+
+ 1
+ 1
+ quarter
+ up
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ B
+ 3
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ E
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+
+ F
+ 3
+
+ 2
+ 1
+ half
+ down
+
+
+
+ E
+ 3
+
+ 2
+ 1
+ half
+
+
+ 4
+
+
+
+ D
+ 4
+
+ 1
+ 2
+ quarter
+ down
+
+
+
+ F
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+
+
+ B
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+ 2
+
+
+ light-heavy
+
+
+
+
diff --git a/JS/scores/guitar_two_voices.xml b/JS/scores/guitar_two_voices.xml
new file mode 100644
index 0000000..111348d
--- /dev/null
+++ b/JS/scores/guitar_two_voices.xml
@@ -0,0 +1,191 @@
+
+
+
+
+ guitar
+
+
+
+ MuseScore 2.0.3
+ 2017-03-29
+
+
+
+
+
+
+
+
+
+ 7.05556
+ 40
+
+
+ 1683.36
+ 1190.88
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+
+
+
+
+ guitar
+
+
+
+ Classical Guitar
+ Guit.
+
+ Classical Guitar
+
+
+
+ 1
+ 25
+ 78.7402
+ 0
+
+
+
+
+
+
+
+
+ -0.00
+ 608.23
+
+ 170.00
+
+
+
+ 1
+
+ 0
+
+
+
+ G
+ 2
+ -1
+
+
+
+
+ A
+ 3
+
+ 1
+ 1
+ quarter
+ up
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ B
+ 3
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ E
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+
+ F
+ 3
+
+ 2
+ 1
+ half
+ down
+
+
+
+ E
+ 3
+
+ 2
+ 1
+ half
+
+
+ 3
+
+
+
+ F
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+
+
+ B
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+ 2
+
+
+ light-heavy
+
+
+
+
diff --git a/JS/scores/guitar_two_voices2.xml b/JS/scores/guitar_two_voices2.xml
new file mode 100644
index 0000000..e268ec3
--- /dev/null
+++ b/JS/scores/guitar_two_voices2.xml
@@ -0,0 +1,211 @@
+
+
+
+
+ guitar
+
+
+
+ MuseScore 2.0.3
+ 2017-03-29
+
+
+
+
+
+
+
+
+
+ 7.05556
+ 40
+
+
+ 1683.36
+ 1190.88
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+ 56.6929
+ 56.6929
+ 56.6929
+ 113.386
+
+
+
+
+
+
+ guitar
+
+
+
+ Classical Guitar
+ Guit.
+
+ Classical Guitar
+
+
+
+ 1
+ 25
+ 78.7402
+ 0
+
+
+
+
+
+
+
+
+ -0.00
+ 561.84
+
+ 170.00
+
+
+
+ 1
+
+ 0
+
+
+
+ G
+ 2
+ -1
+
+
+
+
+ A
+ 3
+
+ 1
+ 1
+ quarter
+ up
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ B
+ 3
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+ D
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+ E
+ 4
+
+ 1
+ 1
+ quarter
+ down
+
+
+
+
+
+ F
+ 3
+
+ 2
+ 1
+ half
+ down
+
+
+
+ B
+ 2
+
+ 1
+ 1
+ quarter
+ up
+
+
+
+ E
+ 3
+
+ 1
+ 1
+ quarter
+
+
+ 4
+
+
+
+ E
+ 4
+
+ 1
+ 2
+ quarter
+ up
+
+
+
+ A
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+
+
+ B
+ 3
+
+ 1
+ 2
+ quarter
+ down
+
+
+ 2
+
+
+ light-heavy
+
+
+
+