webdaw-modules
Version:
a set of modules for building a web-based DAW
305 lines • 14.1 kB
JavaScript
"use strict";
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spread = (this && this.__spread) || function () {
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMusicXML = void 0;
var midi_1 = require("../util/midi");
var getVolume_1 = require("./part/getVolume");
var getPartName_1 = require("./part/getPartName");
var getChannel_1 = require("./part/getChannel");
var getInstrument_1 = require("./part/getInstrument");
var getMeasureNumber_1 = require("./measure/getMeasureNumber");
var getDivisions_1 = require("./measure/getDivisions");
var getSignature_1 = require("./measure/getSignature");
var getTempo_1 = require("./measure/getTempo");
var getRepeat_1 = require("./measure/getRepeat");
exports.parseMusicXML = function (xmlDoc, ppq) {
if (ppq === void 0) { ppq = 960; }
if (xmlDoc === null) {
return null;
}
var type;
if (xmlDoc.firstChild !== null && xmlDoc.firstChild.nextSibling !== null) {
type = xmlDoc.firstChild.nextSibling.nodeName;
// console.log('type', type, nsResolver);
if (type === "score-partwise") {
return parsePartWise(xmlDoc, ppq);
}
if (type === "score-timewise") {
return parseTimeWise(xmlDoc);
}
}
// console.log('unknown type', type);
return null;
};
var parsePartWise = function (xmlDoc, ppq) {
if (ppq === void 0) { ppq = 960; }
if (xmlDoc === null) {
return null;
}
// const nsResolver = xmlDoc.createNSResolver(
// xmlDoc.ownerDocument === null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement
// );
var nsResolver = xmlDoc.createNSResolver(xmlDoc.documentElement);
var partIterator = xmlDoc.evaluate("//score-part", xmlDoc, nsResolver, XPathResult.ANY_TYPE, null);
var parts = [];
var tiedNotes = {};
// const repeats: Repeat = [{ bar: 1, type: "forward" }];
var repeats = [];
var initialTempo = -1;
var initialNumerator = -1;
var initialDenominator = -1;
var index = -1;
var partNode;
while ((partNode = partIterator.iterateNext())) {
index += 1;
var _a = __read(getPartName_1.getPartName(xmlDoc, partNode, nsResolver), 2), partId = _a[0], partName = _a[1];
var volume = getVolume_1.getVolume(xmlDoc, partNode, nsResolver);
var velocity = (volume / 100) * 127;
var channel = getChannel_1.getChannel(xmlDoc, partNode, nsResolver);
var instrument = getInstrument_1.getInstrument(xmlDoc, partNode, nsResolver);
parts.push({ id: partId, name: partName, volume: volume, instrument: instrument, events: [] });
var measureIterator = xmlDoc.evaluate('//part[@id="' + partId + '"]/measure', partNode, nsResolver, XPathResult.ANY_TYPE, null);
var ticks = 0;
var tmp = void 0;
var measureNode = void 0;
var divisions = 24;
while ((measureNode = measureIterator.iterateNext())) {
var measureNumber = getMeasureNumber_1.getMeasureNumber(xmlDoc, measureNode, nsResolver);
divisions = getDivisions_1.getDivisions(xmlDoc, measureNode, nsResolver, divisions);
var signatureEvent = getSignature_1.getSignature(xmlDoc, measureNode, nsResolver);
if (signatureEvent !== null) {
signatureEvent.ticks = ticks;
signatureEvent.bar = measureNumber;
parts[index].events.push(signatureEvent);
if (initialNumerator === -1) {
(initialNumerator = signatureEvent.numerator, initialDenominator = signatureEvent.denominator);
}
}
var timeEvent = getTempo_1.getTempo(xmlDoc, measureNode, nsResolver);
if (timeEvent !== null) {
timeEvent.ticks = ticks;
timeEvent.bar = measureNumber;
parts[index].events.push(timeEvent);
if (initialTempo === -1) {
(initialTempo = timeEvent.bpm);
}
}
var repeat = getRepeat_1.getRepeat(xmlDoc, measureNode, nsResolver);
if (repeat !== null && measureNumber !== 1) {
// console.log(repeat, measureNumber);
repeats.push({ type: repeat, bar: measureNumber });
}
// get all notes and backups
var noteIterator = xmlDoc.evaluate("*[self::note or self::backup or self::forward]", measureNode, nsResolver, XPathResult.ANY_TYPE, null);
var noteNode = void 0;
while ((noteNode = noteIterator.iterateNext())) {
// console.log(noteNode);
var noteDuration = 0;
var noteDurationTicks = 0;
var voice = -1;
var staff = -1;
var tieStart = false;
var tieStop = false;
var tieIterator = xmlDoc.evaluate("tie", noteNode, nsResolver, XPathResult.ANY_TYPE, null);
var tieNode = void 0;
while ((tieNode = tieIterator.iterateNext())) {
var tieType = xmlDoc.evaluate("@type", tieNode, nsResolver, XPathResult.STRING_TYPE, null).stringValue;
if (tieType === "start") {
tieStart = true;
}
else if (tieType === "stop") {
tieStop = true;
}
}
var rest = xmlDoc.evaluate("rest", noteNode, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
var chord = xmlDoc.evaluate("chord", noteNode, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
var grace = xmlDoc.evaluate("grace", noteNode, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (rest !== null) {
noteDuration = xmlDoc.evaluate("duration", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
ticks += (noteDuration / divisions) * ppq;
// console.log("rest", ticks);
}
else if (noteNode.nodeName === "note" && grace === null) {
var step = xmlDoc.evaluate("pitch/step", noteNode, nsResolver, XPathResult.STRING_TYPE, null).stringValue;
var alter = xmlDoc.evaluate("pitch/alter", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
var octave = xmlDoc.evaluate("pitch/octave", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
tmp = xmlDoc.evaluate("voice", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
if (!isNaN(tmp)) {
voice = tmp;
}
tmp = xmlDoc.evaluate("staff", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
if (!isNaN(tmp)) {
staff = tmp;
}
noteDuration = xmlDoc.evaluate("duration", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
noteDurationTicks = (noteDuration / divisions) * ppq;
// const noteType = xmlDoc.evaluate('type', noteNode, nsResolver, XPathResult.STRING_TYPE, null).stringValue;
var noteName = step;
if (!isNaN(alter)) {
switch (alter) {
case -2:
noteName += "bb";
break;
case -1:
noteName += "b";
break;
case 1:
noteName += "#";
break;
case 2:
noteName += "##";
break;
}
}
// in case of a chord, move the cursor back
if (chord !== null) {
ticks -= noteDurationTicks;
}
// console.log(ticks, measureNumber, chord);
var noteNumber = midi_1.getNoteNumber(noteName, octave);
// console.log("\t", ticks, "ON", n++);
var note = {
ticks: ticks,
descr: "note on",
type: 0x90,
channel: channel,
noteNumber: noteNumber,
velocity: velocity,
bar: measureNumber,
};
ticks += noteDurationTicks;
parts[index].events.push(note);
//console.log('tie', tieStart, tieStop);
if (tieStart === false && tieStop === false) {
// no ties
//console.log('no ties', measureNumber, voice, noteNumber, tiedNotes);
// console.log(ticks, "OFF", index);
parts[index].events.push({
ticks: ticks,
descr: "note off",
type: 0x80,
channel: channel,
noteNumber: noteNumber,
velocity: 0,
bar: measureNumber,
});
}
else if (tieStart === true && tieStop === false) {
// start of tie
tiedNotes["N_" + staff + "-" + voice + "-" + noteNumber] = noteDurationTicks;
//console.log('start', measureNumber, voice, noteNumber, tiedNotes);
}
else if (tieStart === true && tieStop === true) {
// tied to yet another note
tiedNotes["N_" + staff + "-" + voice + "-" + noteNumber] += noteDurationTicks;
//console.log('thru', measureNumber, voice, noteNumber, tiedNotes);
}
else if (tieStart === false && tieStop === true) {
// end of tie
tiedNotes["N_" + staff + "-" + voice + "-" + noteNumber] += noteDurationTicks;
// console.log(ticks, "OFF", index);
parts[index].events.push({
// command: NOTE_OFF,
ticks: tiedNotes["N_" + staff + "-" + voice + "-" + noteNumber],
descr: "note off",
type: 0x80,
channel: channel,
noteNumber: noteNumber,
velocity: 0,
bar: measureNumber,
});
delete tiedNotes["N_" + staff + "-" + voice + "-" + noteNumber];
//console.log('end', measureNumber, voice, noteNumber, tiedNotes);
}
}
else if (noteNode.nodeName === "backup") {
noteDuration = xmlDoc.evaluate("duration", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
ticks -= (noteDuration / divisions) * ppq;
// console.log("backup", ticks);
//console.log(noteDuration, divisions);
}
else if (noteNode.nodeName === "forward") {
noteDuration = xmlDoc.evaluate("duration", noteNode, nsResolver, XPathResult.NUMBER_TYPE, null).numberValue;
ticks += (noteDuration / divisions) * ppq;
// console.log('forward', ticks);
//console.log(noteDuration, divisions);
}
}
}
}
var repeats2 = [];
var j = -1;
if (repeats.length && repeats[0].type !== "forward") {
repeats = __spread([{ type: "forward", bar: 1 }], repeats);
}
// console.log(repeats);
var filtered = [];
for (var k = 0; k < repeats.length; k++) {
var r = repeats[k];
var double = false;
for (var k1 = 0; k1 < filtered.length; k1++) {
var r1 = filtered[k1];
if (r1.bar === r.bar && r1.type === r.type) {
double = true;
break;
}
}
if (!double) {
filtered.push(r);
}
}
filtered.forEach(function (t, i) {
if (t.type === "forward") {
j++;
repeats2[j] = [t.bar, -1];
}
else if (t.type === "backward") {
repeats2[j][1] = t.bar;
}
});
// console.log(repeats, repeats2);
var i = 0;
var repeats3 = [];
repeats2.forEach(function (_a) {
var _b = __read(_a, 2), start = _b[0], end = _b[1];
repeats3.push({
start: start,
end: end,
active: true,
id: "score-" + i++,
});
});
// console.log(repeats3);
return {
ppq: ppq,
parts: parts,
repeats: repeats3,
initialTempo: initialTempo,
initialNumerator: initialNumerator,
initialDenominator: initialDenominator,
};
};
var parseTimeWise = function (doc) {
// to be implemented
return null;
};
//# sourceMappingURL=parser.js.map