vexflow-musicxml
Version:
MusicXml Parser for vexflow
192 lines (154 loc) • 6.5 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: src/MusicXmlRenderer.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: src/MusicXmlRenderer.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* @file
* @description Parser and renderer for Music XML files to Vex Flow
* @author {@link mailto:neumann.benni@gmail.com|neumann.benni@gmail.com}
* @version 0.1
*/
import Vex from 'vexflow';
import { MusicXml } from './xml/MusicXml.js';
import { Measure } from './vex/Measure.js';
const { Flow } = Vex;
/**
* MusicXmlRenderer
* @param
*/
export class MusicXmlRenderer {
constructor(data, canvas) {
this.musicXml = new MusicXml(data);
console.profileEnd('parsing');
console.log(this.musicXml);
if (false) {
const part = 1;
const from = 1;
const to = 2;
this.musicXml.Parts = [this.musicXml.Parts[part]];
this.musicXml.Parts[0].Measures = this.musicXml.Parts[0].Measures.slice(from, to);
}
this.mStartMeasure = 0;
this.mStopMeasure = this.musicXml.Parts[0].Measures.length;
this.isSvg = !(canvas instanceof HTMLCanvasElement);
this.canvas = canvas;
// eslint-disable-next-line max-len
this.renderer = new Flow.Renderer(this.canvas, this.isSvg ? Flow.Renderer.Backends.SVG : Flow.Renderer.Backends.CANVAS);
// Properties for rendering
this.ctx = this.renderer.getContext();
this.Drawables = [];
// Some formatting constants
this.staveSpace = 100;
this.staveXOffset = 20;
this.staveYOffset = 20;
this.calculateLayout();
console.time('parse');
this.parse().render();
console.timeEnd('parse');
}
getScoreHeight() {
return this.systemSpace * this.format.linesPerPage;
}
// https://github.com/0xfe/vexflow/blob/1.2.83/tests/formatter_tests.js line 271
parse() {
const allParts = this.musicXml.Parts;
for (const [p] of allParts.entries()) {
const part = allParts[p];
for (let m = this.mStartMeasure; m < this.mStopMeasure; m++) {
const measure = part.Measures[m];
this.Drawables.push(new Measure(measure, this.format, this.ctx));
}
}
// Connect the first measures in a line
const MeasuresFirstInLine = this.Drawables.filter(m => m.firstInLine);
const MeasureNumsFirstInLine = new Set(MeasuresFirstInLine.map(m => m.xmlMeasure.Number));
MeasureNumsFirstInLine.forEach((n) => {
// Get all the measures from all parts with the same starting number.
// Get their stavelist(s) and concatenate them in one array. Now we have an
// array of staves that are in the first line.
const system = [].concat(...MeasuresFirstInLine.filter(m => m.xmlMeasure.Number === n).map(m => m.staveList));
for (let s = 0; s < system.length - 1; s++) {
// It actually doesn't matter which measure we use for the connectors
MeasuresFirstInLine[0].addConnector(system[s], system[s + 1], Flow.StaveConnector.type.SINGLE_LEFT);
}
});
return this;
}
clear() {
$(this.ctx.svg).empty();
}
set StartMeasure(value) {
// TODO: Recalculate layout and rerender
this.mStartMeasure = value;
this.calculateLayout();
this.Drawables = [];
this.clear();
this.parse().render();
}
set StopMeasure(value) {
// TODO: Recalculate layout and rerender
this.mStopMeasure = value;
this.calculateLayout();
this.clear();
this.Drawables = [];
this.parse().render();
}
calculateLayout() {
this.stavesPerSystem = this.musicXml.Parts
.map(p => p.getAllStaves()) // get all the staves in a part
.reduce((e, ne) => e + ne); // sum them up
this.width = this.isSvg ? parseInt(this.canvas.getAttribute('width'), 10) : this.canvas.width;
const startWidth = document.body.clientWidth < 250 ? document.body.clientWidth : 250;
this.systemSpace = this.staveSpace * this.stavesPerSystem + 50;
this.measuresPerStave = Math.floor(this.width / startWidth); // measures per stave
this.staveWidth = Math.round(this.width / this.measuresPerStave) - this.staveXOffset;
this.format = {
staveSpace: this.staveSpace,
staveXOffset: this.staveXOffset,
staveYOffset: this.staveYOffset,
systemSpace: this.systemSpace,
// FIXME: Refactor to stavesPerMeasure
measuresPerStave: this.measuresPerStave,
totalMeasures: (this.mStopMeasure - this.mStartMeasure),
staveWidth: this.staveWidth,
stavesPerSystem: this.musicXml.getStavesPerSystem(),
width: this.width,
linesPerPage: Math.ceil((this.mStopMeasure - this.mStartMeasure) + 1 / this.measuresPerStave),
};
// Set the SVG viewbox according to the calculated layout
const vb = [0, 0, this.width, this.getScoreHeight()];
this.ctx.setViewBox(vb);
}
render() {
this.Drawables.forEach(d => d.draw());
}
}
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Test.html">Test</a></li></ul><h3>Classes</h3><ul><li><a href="ClefVisitor.html">ClefVisitor</a></li><li><a href="Key_Key.html">Key</a></li><li><a href="KeyVisitor.html">KeyVisitor</a></li><li><a href="Measure.html">Measure</a></li><li><a href="MeasureVisitor.html">MeasureVisitor</a></li><li><a href="MusicXmlRenderer.html">MusicXmlRenderer</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteVisitor.html">NoteVisitor</a></li><li><a href="Part.html">Part</a></li><li><a href="TimeSignatureVisitor.html">TimeSignatureVisitor</a></li><li><a href="TimeVisitor.html">TimeVisitor</a></li><li><a href="XmlObject.html">XmlObject</a></li><li><a href="XmlSerializer_XmlSerializer.html">XmlSerializer</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Mon Nov 20 2017 21:30:34 GMT+0100 (CET)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>