|
|
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(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; |
|
|
if (iii != tuning.length - 1) { |
|
|
nextNote = tuning[iii + 1]; |
|
|
} |
|
|
var stringNotes = tabNotes(tuning[iii], nextNote); |
|
|
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) { |
|
|
|
|
|
|
|
|
if (curPos.str == self.strings.length - 1) { |
|
|
|
|
|
curPos.num = "?"; |
|
|
nextPos.num = "?"; |
|
|
return; |
|
|
} |
|
|
|
|
|
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 |
|
|
); |
|
|
} |
|
|
|
|
|
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(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)) { |
|
|
|
|
|
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, |
|
|
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(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(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 name = plugin.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 ''; |
|
|
}; |
|
|
|
|
|
|
|
|
StringPatterns.prototype.suppress = function (plugin) { |
|
|
var suppress = plugin.params.suppress; |
|
|
if (suppress) { |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function StringPatterns(plugin) { |
|
|
|
|
|
var tuning = plugin.tuning; |
|
|
var capo = plugin.capo; |
|
|
var highestNote = plugin.params.highestNote; |
|
|
this.linePitch = plugin.linePitch; |
|
|
this.highestNote = "a'"; |
|
|
if (highestNote) { |
|
|
|
|
|
this.highestNote = highestNote; |
|
|
} |
|
|
this.measureAccidentals = {} |
|
|
this.capo = 0; |
|
|
if (capo) { |
|
|
this.capo = parseInt(capo, 10); |
|
|
} |
|
|
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.setError(this.strings.error); |
|
|
plugin.inError = true; |
|
|
return; |
|
|
} |
|
|
|
|
|
this.secondPos = buildSecond(this); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = StringPatterns; |