UNPKG

abcjs

Version:

Renderer for abc music notation

312 lines (293 loc) 8.09 kB
const {noteToMidi} = require('../../synth/note-to-midi'); var TabNote = require('./tab-note'); var TabNotes = require('./tab-notes'); function buildCapo(self) { var capoTuning = null; var tuning = self.tuning; if (self.capo > 0) { capoTuning = []; for (var iii = 0; iii < tuning.length; iii++) { var curNote = new TabNote.TabNote(tuning[iii]); for (var jjj = 0; jjj < self.capo; jjj++) { curNote = curNote.nextNote(); } capoTuning[iii] = curNote.emit(); } } return capoTuning; } function buildPatterns(self) { var strings = []; var tuning = self.tuning; if (self.capo > 0) { tuning = self.capoTuning; } var pos = tuning.length - 1; for (var iii = 0; iii < tuning.length; iii++) { var nextNote = self.highestNote; // highest handled note if (iii != tuning.length - 1) { nextNote = tuning[iii + 1]; } var tabNotes = new TabNotes(tuning[iii],nextNote); var stringNotes = tabNotes.build(); if (stringNotes.error) { return stringNotes; } strings[pos--] = stringNotes; } return strings; } function buildSecond(first) { var seconds = []; seconds[0] = []; var strings = first.strings; for (var iii = 1; iii < strings.length; iii++) { seconds[iii] = strings[iii - 1]; } return seconds; } function sameString(self, chord) { for (var jjjj = 0; jjjj < chord.length - 1; jjjj++) { var curPos = chord[jjjj]; var nextPos = chord[jjjj + 1]; if (curPos.str == nextPos.str) { // same String // => change lower pos if (curPos.str == self.strings.length - 1) { // Invalid tab Chord position for instrument curPos.num = "?"; nextPos.num = "?"; return; } // change lower pitch on lowest string if (nextPos.num < curPos.num) { nextPos.str++; nextPos = noteToNumber(self, nextPos.note, nextPos.str, self.secondPos, self.strings[nextPos.str].length ); } else { curPos.str++; curPos = noteToNumber(self, curPos.note, curPos.str, self.secondPos, self.strings[curPos.str].length ); } // update table chord[jjjj] = curPos; chord[jjjj + 1] = nextPos; } } return null; } function handleChordNotes(self, notes) { var retNotes = []; for (var iiii = 0; iiii < notes.length; iiii++) { if (notes[iiii].endTie) continue; var note = new TabNote.TabNote(notes[iiii].name, self.clefTranspose); note.checkKeyAccidentals(self.accidentals, self.measureAccidentals) var curPos = toNumber(self, note); retNotes.push(curPos); } sameString(self, retNotes); return retNotes; } function noteToNumber(self, note, stringNumber, secondPosition , firstSize) { var strings = self.strings; note.checkKeyAccidentals(self.accidentals, self.measureAccidentals) ; if (secondPosition) { strings = secondPosition; } var noteName = note.emitNoAccidentals(); var num = strings[stringNumber].indexOf(noteName); var acc = note.acc; if (num != -1) { if (secondPosition) { num += firstSize; } if ( (note.isFlat || note.acc == -1) && (num == 0)) { // flat on 0 pos => previous string 7th position var noteEquiv = note.getAccidentalEquiv(); stringNumber++; num = strings[stringNumber].indexOf(noteEquiv.emit()); acc = 0; } return { num: (num + acc), str: stringNumber, note: note }; } return null; } function toNumber(self, note) { if (note.isAltered || note.natural) { var acc; if (note.isFlat) { if (note.isDouble) acc = "__" else acc = "_" } else if (note.isSharp) { if (note.isDouble) acc = "^^" else acc = "^" } else if (note.natural) acc = "=" self.measureAccidentals[note.name.toUpperCase()] = acc } for (var i = self.stringPitches.length-1; i >= 0; i--) { if (note.pitch + note.pitchAltered >= self.stringPitches[i]) { var num = note.pitch + note.pitchAltered - self.stringPitches[i] if (note.quarter === '^') num -= 0.5 else if (note.quarter === "v") num += 0.5 return { num: Math.round(num), str: self.stringPitches.length-1-i, // reverse the strings because string 0 is on the bottom note: note } } } return { num: "?", str: self.stringPitches.length-1, note: note, }; } StringPatterns.prototype.stringToPitch = function (stringNumber) { var startingPitch = 5.3; var bottom = this.strings.length - 1; return startingPitch + ((bottom - stringNumber) * this.linePitch); }; function invalidNumber( retNotes , note ) { var number = { num: "?", str: 0, note: note }; retNotes.push(number); retNotes.error = note.emit() + ': unexpected note for instrument' ; } StringPatterns.prototype.notesToNumber = function (notes, graces) { var note; var number; var error = null; var retNotes = null; if (notes) { retNotes = []; if (notes.length > 1) { retNotes = handleChordNotes(this, notes); if (retNotes.error) { error = retNotes.error; } } else { if (!notes[0].endTie) { note = new TabNote.TabNote(notes[0].name, this.clefTranspose); note.checkKeyAccidentals(this.accidentals, this.measureAccidentals) number = toNumber(this, note); if (number) { retNotes.push(number); } else { invalidNumber(retNotes, note); error = retNotes.error; } } } } if (error) return retNotes; var retGraces = null; if (graces) { retGraces = []; for (var iiii = 0; iiii < graces.length; iiii++) { note = new TabNote.TabNote(graces[iiii].name, this.clefTranspose); note.checkKeyAccidentals(this.accidentals, this.measureAccidentals) number = toNumber(this, note); if (number) { retGraces.push(number); } else { invalidNumber(retGraces, note); error = retNotes.error; } } } return { notes: retNotes, graces: retGraces, error: error }; }; StringPatterns.prototype.toString = function () { var arr = [] for (var i = 0; i < this.tuning.length; i++) { var str = this.tuning[i].replaceAll(',', '').replaceAll("'", '').toUpperCase(); if (str[0] === '_') str = str[1] + 'b ' else if (str[0] === '^') str = str[1] + "# " arr.push(str) } return arr.join(''); }; StringPatterns.prototype.tabInfos = function (plugin) { var _super = plugin._super; var name = _super.params.label; if (name) { var tunePos = name.indexOf('%T'); var tuning = ""; if (tunePos != -1) { tuning = this.toString(); if (plugin.capo > 0) { tuning += ' capo:' + plugin.capo; } name = name.replace('%T', tuning); } return name; } return ''; }; /** * Common patterns for all string instruments * @param {} plugin * @param {} tuning * @param {*} capo * @param {*} highestNote */ function StringPatterns(plugin) { var tuning = plugin.tuning; var capo = plugin.capo; var highestNote = plugin._super.params.highestNote; this.linePitch = plugin.linePitch; this.highestNote = "a'"; if (highestNote) { // override default this.highestNote = highestNote; } this.measureAccidentals = {} this.capo = 0; if (capo) { this.capo = capo; } this.transpose = plugin.transpose ? plugin.transpose : 0 this.tuning = tuning; this.stringPitches = [] for (var i = 0; i < this.tuning.length; i++) { var pitch = noteToMidi(this.tuning[i]) + this.capo this.stringPitches.push(pitch) } if (this.capo > 0) { this.capoTuning = buildCapo(this); } this.strings = buildPatterns(this); if (this.strings.error) { plugin._super.setError(this.strings.error); plugin.inError = true; return; } // second position pattern per string this.secondPos = buildSecond(this); } module.exports = StringPatterns;