UNPKG

vexflow-musicxml

Version:

MusicXml Parser for vexflow

192 lines (154 loc) 6.5 kB
<!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 &lt; 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 &lt; 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 &lt; 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>