UNPKG

abcjs

Version:

Renderer for abc music notation

292 lines (278 loc) 9.53 kB
/** * Tablature Absolute elements factory */ var AbsoluteElement = require('../write/creation/elements/absolute-element'); var RelativeElement = require('../write/creation/elements/relative-element'); function isObject(a) { return a != null && a.constructor === Object; } function cloneObject(dest, src) { for (var prop in src) { if (src.hasOwnProperty(prop)) { if (!(Array.isArray(src[prop]) || isObject(src[prop]))) { dest[prop] = src[prop]; } } } } function cloneAbsolute(absSrc) { var returned = new AbsoluteElement('', 0, 0, '', 0); cloneObject(returned, absSrc); returned.top = 0; returned.bottom = -1; if (absSrc.abcelem) { returned.abcelem = {}; cloneObject(returned.abcelem, absSrc.abcelem); if (returned.abcelem.el_type === "note") returned.abcelem.el_type = 'tabNumber'; } // TODO-PER: This fixes the classes because the element isn't created at the right time. absSrc.cloned = returned return returned; } function cloneAbsoluteAndRelatives(absSrc, plugin) { var returned = cloneAbsolute(absSrc); if (plugin) { var children = absSrc.children; // proceed with relative as well var first = true; for (var ii = 0; ii < children.length; ii++) { var child = children[ii]; var relative = new RelativeElement('', 0, 0, 0, ''); cloneObject(relative, child); first = plugin.tablature.setRelative(child, relative, first); returned.children.push(relative); } } return returned; } function buildTabAbsolute(plugin, absX, relX) { var tabIcon = 'tab.tiny'; var tabYPos = 7.5; if (plugin.isTabBig) { tabIcon = 'tab.big'; tabYPos = 10; } var element = { el_type: "tab", icon: tabIcon, Ypos: tabYPos }; var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0); tabAbsolute.x = absX; var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab"); tabRelative.x = relX; tabAbsolute.children.push(tabRelative); if (tabAbsolute.abcelem.el_type == 'tab') { tabRelative.pitch = tabYPos; } return tabAbsolute; } function lyricsDim(abs) { if (abs.extra) { for (var ii = 0; ii < abs.extra.length; ii++) { var extra = abs.extra[ii]; if (extra.type == 'lyric') { return { bottom: extra.bottom, height: extra.height }; } } } return null; } function TabAbsoluteElements() { this.accidentals = null; } function getInitialStaffSize(staffGroup) { var returned = 0; for (var ii = 0; ii < staffGroup.length; ii++) { if (!staffGroup[ii].tabNameInfos) returned++; } return returned; } function buildRelativeTabNote(plugin, relX, def, curNote, isGrace) { var strNote = curNote.num; if (curNote.note.quarter != null) { // add tab quarter => needs to string conversion then strNote = strNote.toString(); strNote += curNote.note.quarter; } var pitch = plugin.semantics.stringToPitch(curNote.str); def.notes.push({ num: strNote, str: curNote.str, pitch: curNote.note.emit() }); var opt = { type: 'tabNumber' }; var tabNoteRelative = new RelativeElement( strNote, 0, 0, pitch+0.3, opt); tabNoteRelative.x = relX; tabNoteRelative.isGrace = isGrace; tabNoteRelative.isAltered = curNote.note.isAltered; return tabNoteRelative; } function getXGrace(abs, index) { var found = 0; if (abs.extra) { for (var ii = 0; ii < abs.extra.length; ii++) { if (abs.extra[ii].c.indexOf('noteheads') >= 0) { if (found === index) { return abs.extra[ii].x + abs.extra[ii].w/2; } else { found++; } } } } return -1; } function graceInRest( absElem ) { if (absElem.abcelem) { var elem = absElem.abcelem; if (elem.rest) { return elem.gracenotes; } } return null; } function convertToNumber(plugin, pitches, graceNotes) { var tabPos = plugin.semantics.notesToNumber(pitches, graceNotes); if (tabPos.error) { plugin._super.setError(tabPos.error); return tabPos; // give up on error here } if (tabPos.graces && tabPos.notes) { // add graces to last note in notes var posNote = tabPos.notes.length - 1; tabPos.notes[posNote].graces = tabPos.graces; } return tabPos; } function buildGraceRelativesForRest(plugin,abs,absChild,graceNotes,tabVoice) { for (var mm = 0; mm < graceNotes.length; mm++) { var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true }; var graceX = getXGrace(absChild, mm); var curGrace = graceNotes[mm]; var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true); abs.children.push(tabGraceRelative); tabVoice.push(defGrace); } } /** * Build tab absolutes by scanning current staff line absolute array * @param {*} staffAbsolute */ TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig ) { var staffSize = getInitialStaffSize(staffAbsolute); var source = staffAbsolute[staffIndex+voiceIndex]; var dest = staffAbsolute[staffSize+staffIndex+voiceIndex]; var tabPos = null; var defNote = null; if (source.children[0].abcelem.el_type != 'clef') { // keysig missing => provide one for tabs if (keySig != 'none') { source.children.splice(0, 0, keySig); } } for (var ii = 0; ii < source.children.length; ii++) { var absChild = source.children[ii]; var absX = absChild.x; var relX = absX; // if (absChild.children.length > 0) { // relX = absChild.children[0].x; // } if ( (absChild.isClef) ) { dest.children.push(buildTabAbsolute(plugin, absX, relX)); if (absChild.abcelem.type.indexOf('-8') >= 0) plugin.semantics.strings.clefTranspose = -12 if (absChild.abcelem.type.indexOf('+8') >= 0) plugin.semantics.strings.clefTranspose = 12 } switch (absChild.type) { case 'staff-extra key-signature': // refresh key accidentals this.accidentals = absChild.abcelem.accidentals; plugin.semantics.strings.accidentals = this.accidentals; break; case 'bar': plugin.semantics.strings.measureAccidentals = {} var lastBar = false; if (ii === source.children.length-1) { // used for final line bar drawing // for multi tabs / multi staves lastBar = true; } var cloned = cloneAbsoluteAndRelatives(absChild, plugin); if (cloned.abcelem.barNumber) { delete cloned.abcelem.barNumber; for (var bn = 0; bn < cloned.children.length; bn++) { if (cloned.children[bn].type === "barNumber" ) { cloned.children.splice(bn, 1); break; } } } cloned.abcelem.lastBar = lastBar; dest.children.push(cloned); tabVoice.push({ el_type: absChild.abcelem.el_type, type: absChild.abcelem.type, endChar: absChild.abcelem.endChar, startChar: absChild.abcelem.startChar, abselem: cloned }); break; case 'rest': var restGraces = graceInRest(absChild); if (restGraces) { // to number conversion tabPos = convertToNumber(plugin, null, restGraces); if (tabPos.error) return; // build relative for grace defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true }; buildGraceRelativesForRest(plugin, abs, absChild, tabPos.graces, tabVoice); } break; case 'note': var abs = cloneAbsolute(absChild); abs.x = absChild.heads[0].x + absChild.heads[0].w / 2; // center the number abs.lyricDim = lyricsDim(absChild); var pitches = absChild.abcelem.pitches; var graceNotes = absChild.abcelem.gracenotes; abs.type = 'tabNumber'; // to number conversion tabPos = convertToNumber(plugin, pitches, graceNotes); if (tabPos.error) return; if (tabPos.graces) { // add graces to last note in notes var posNote = tabPos.notes.length - 1; tabPos.notes[posNote].graces = tabPos.graces; } // build relative defNote = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [] }; for (var ll = 0; ll < tabPos.notes.length; ll++) { var curNote = tabPos.notes[ll]; if (curNote.graces) { for (var mm = 0; mm < curNote.graces.length; mm++) { var defGrace = { el_type: "note", startChar: absChild.abcelem.startChar, endChar: absChild.abcelem.endChar, notes: [], grace: true }; var graceX = getXGrace(absChild , mm); var curGrace = curNote.graces[mm]; var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true); abs.children.push(tabGraceRelative); tabVoice.push(defGrace); } } var tabNoteRelative = buildRelativeTabNote(plugin, abs.x+absChild.heads[ll].dx, defNote, curNote, false); abs.children.push(tabNoteRelative); } if (defNote.notes.length > 0) { defNote.abselem = abs; tabVoice.push(defNote); dest.children.push(abs); } break; } } }; module.exports = TabAbsoluteElements;