# Musical Theory and helper for Fretboard and TabStave
#
# copyright Laurent Schwartz 2015/2016
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
class Vex.Note
@DEBUG = false
L = (args...) -> console?.log("(Vex.Note)", args...) if Vex.Note.DEBUG
constructor: (@note, @duration = "4") ->
L "constructor: note=", @note
@reset()
error = (msg) -> new Vex.RERR("NoteError", msg)
allnotes = [
{name:"c",semitons:0, index:1},
{name:"d",semitons:2, index:2},
{name:"e",semitons:4, index:3},
{name:"f",semitons:5, index:4},
{name:"g",semitons:7, index:5},
{name:"a",semitons:9, index:6},
{name:"b",semitons:11, index:7}
]
@getIndex: (txtnotewithoutaccidental) ->
for n in allnotes
if n.name is txtnotewithoutaccidental then return n.index
throw error("Invalid note without accidental ! [" + txtnotewithoutaccidental + "] ");
@getNoteStructWithIndex: (index) ->
for n in allnotes
if n.index == index then return n
throw error("Invalid index ! " + index)
@getNoteStructWithoutAccidental: (txtnote) ->
for n in allnotes
if n.name == txtnote then return n
throw error("Invalid note ! " + txtnote)
@getTextLowerCaseNotes: (vexnotes) ->
notes = []
for n in vexnotes
notes.push(n.getTextLowerCase())
return notes
@getNote: (semitons) ->
refnote = allnotes[0]
for note in allnotes
if note.semitons <= semitons then refnote = note else break
accidental = semitons - refnote.semitons
switch accidental
when 0 then txtaccidental = ""
when 1 then txtaccidental = "#"
when 2 then txtaccidental = "##"
when -1 then txtaccidental = "b"
when -2 then txtaccidental = "bb"
else throw error("Invalid accidental ! " + accidental)
return "#{refnote.name}#{txtaccidental}"
@getTextNotes: (vexnotes) ->
notes = []
for n in vexnotes
notes.push(n.getText())
return notes
@getFretTextNotes: (vexnotes) ->
notes = []
for n in vexnotes
notes.push(n.getFretText())
return notes
reset: ->
L "reset()"
match = @note.match(/^([a-gA-G])([#]{0,2}|[b]{0,2})(\/([0-9]))?$/)
throw error("Invalid note: " + match[1]) unless match[1]
@name = match[1].toLowerCase()
switch match[2]
when "b" then @accidental = -1
when "bb" then @accidental = -2
when "#" then @accidental = 1
when "##" then @accidental = 2
when "" then @accidental = 0
@index = Vex.Note.getIndex(@name)
if (match[3]?)
@octave = parseInt(match[4], 10);
else
@octave = NaN
if !@duration?
@duration="q"
L "reset() name=", @name, " index=", @index, " semitons=", allnotes[@index - 1].semitons, " accidental=", @accidental, " duration=", @duration, " octave=", @octave
getSemitons: ->
if @index <= allnotes.length and @index >= 1
semitons = allnotes[@index - 1].semitons + @accidental
if (semitons < 0) then return semitons + 12;
if (semitons >= 12) then return semitons - 12;
return semitons;
throw error("index is out of range !" + @index + " " + allnotes)
getTextAccidental: ->
switch @accidental
when 0 then return ""
when 1 then return "#"
when 2 then return "##"
when -1 then return "b"
when -2 then return "bb"
throw error("Invalid accidental ! " + @accidental)
getFretText: ->
txtnote = @name.toUpperCase()
txtaccidental = ""
switch @accidental
when -1 then txtaccidental = "b"
when -2 then txtaccidental = "-"
when 1 then txtaccidental = "#"
when 2 then txtaccidental = "x"
when 0 then txtaccidental = ""
return "#{txtnote}#{txtaccidental}";
getLyText: ->
txtnote = @name.toLowerCase()
txtaccidental = ""
switch @accidental
when -1 then txtaccidental = "es"
when -2 then txtaccidental = "eses"
when 1 then txtaccidental = "is"
when 2 then txtaccidental = "isis"
when 0 then txtaccidental = ""
return "#{txtnote}#{txtaccidental}";
getText: ->
txtnote = @name.toUpperCase()
return "#{txtnote}#{@getTextAccidental()}";
getTextLowerCase: ->
return "#{@name}#{@getTextAccidental()}";
getStaveNote: (octave, duration)->
return new Vex.Flow.StaveNote({ keys: ["#{@name}#{@accidental}/#{octave}"], duration: duration })
equal: (note) ->
finalnote = note
if typeof(note) is 'string' then finalnote = new Vex.Note(note) else throw error("note is wrong type !" + note) unless note instanceof Vex.Note
return (@getTextLowerCase() == finalnote.getTextLowerCase() and @duration == finalnote.duration)
class Vex.Interval
@DEBUG = false
L = (args...) -> console?.log("(Vex.Interval)", args...) if Vex.Interval.DEBUG
constructor: (@name) ->
L "constructor: name=", @name
@reset()
error = (msg) -> new Vex.RERR("IntervalError", msg)
intervals = [
{name: "1", othername: "1", degree: "I", degreelc: "i", semitons: 0, index:1, accidental:0},
{name: "b2", othername: "2m", degree: "bII", degreelc: "bii",semitons:1, index:2, accidental:-1},
{name: "2", othername: "2M", degree: "II", degreelc: "ii",semitons:2, index:2, accidental:0},
{name: "#2", othername: "2a", degree: "#II", degreelc: "#ii",semitons:3, index:2, accidental:1},
{name: "bb3", othername: "3d", degree: "bbIII", degreelc: "bbiii",semitons:2, index:3, accidental:-2},
{name: "b3", othername: "3m", degree: "bIII", degreelc: "biii",semitons:3, index:3, accidental:-1},
{name: "3", othername: "3M", degree: "III", degreelc: "iii",semitons:4, index:3, accidental:0},
{name: "#3", othername: "3a", degree: "#III", degreelc: "#iii",semitons:5, index:3, accidental:1},
{name: "4", othername: "4J", degree: "IV", degreelc: "iv",semitons:5, index:4, accidental:0},
{name: "#4", othername: "4a",degree: "#IV", degreelc: "#iv",semitons:6, index:4, accidental:1},
{name: "b5", othername: "5d", degree: "bV", degreelc: "bv",semitons:6, index:5, accidental:-1},
{name: "5", othername: "5J", degree: "V", degreelc: "v",semitons:7, index:5, accidental:0},
{name: "#5", othername: "5a", degree: "#V", degreelc: "#v",semitons:8, index:5, accidental:1},
{name: "b6", othername: "6m", degree: "bVI", degreelc: "bvi",semitons:8, index:6, accidental:-1},
{name: "6", othername: "6M", degree: "VI", degreelc: "vi",semitons:9, index:6, accidental:0},
{name: "bb7", othername: "7d", degree: "bbVII", degreelc: "bbvii",semitons:9, index:7, accidental:-2},
{name: "b7", othername: "7m", degree: "bVII", degreelc: "bvii",semitons:10, index:7, accidental:-1},
{name: "7", othername: "7M", degree: "VII", degreelc: "vii",semitons:11, index:7, accidental:0}
]
getIntervalStructWithName: ->
for i in intervals
if i.name is @name or i.othername is @name or i.degree is @name or i.degreelc is @name then return i
throw error("Invalid Interval name !" + @name)
reset: ->
L "reset()"
@intervalstruct = @getIntervalStructWithName()
@name = @intervalstruct.name
@othername = @intervalstruct.othername
@index = @intervalstruct.index
@degree = @intervalstruct.degree
@degreelc = @intervalstruct.degreelc
@accidental = @intervalstruct.accidental
L "reset() name=", @name, " othername=", @othername, " index=", @index, " accidental=", @accidental, " semitons=", @getSemitons(),
getDegree: ->
return @intervalstruct.degree
getDegreeLowerCase: ->
return @intervalstruct.degreelc
getSemitons: ->
return @intervalstruct.semitons
getFretText: ->
return "#{@othername}"
getTextAccidental: ->
switch @accidental
when 0 then txtaccidental = ""
when 1 then txtaccidental = "#"
when 2 then txtaccidental = "##"
when -1 then txtaccidental = "b"
when -2 then txtaccidental = "bb"
return txtaccidental
getNote: (root) ->
finalroot = root
if typeof(root) is 'string' then finalroot = new Vex.Note(root) else throw error("root is wrong type !" + root) unless root instanceof Vex.Note
index = finalroot.index + @index - 1
if index > 7 then index -= 7
if index < 1 then index += 7
notestruct = Vex.Note.getNoteStructWithIndex index
accidental = finalroot.getSemitons() + @getSemitons() - notestruct.semitons
if accidental > 2 then accidental -= 12
if accidental < -2 then accidental += 12
L "getNote root=", root, " note=", notestruct.name, " index=", index, " accidental=", accidental
txtnote = notestruct.name
txtaccidental = ""
switch accidental
when 0 then txtaccidental = ""
when 1 then txtaccidental = "#"
when 2 then txtaccidental = "##"
when -1 then txtaccidental = "b"
when -2 then txtaccidental = "bb"
result = "#{txtnote}#{txtaccidental}"
L "getNote=", result
return new Vex.Note(result);
class Vex.Scale
@DEBUG = false
L = (args...) -> console?.log("(Vex.Scale)", args...) if Vex.Scale.DEBUG
constructor: (@intervals, @root) ->
L "constructor: intervals=", @intervals, " root=", @root
@reset()
error = (msg) -> new Vex.RERR("ScaleError", msg)
reset: ->
L "reset()"
vexintervals = []
for interval in @intervals
vexinterval = interval
if typeof(interval) is 'string' then vexinterval = new Vex.Interval(interval) else throw error("interval is wrong type !" + interval) unless interval instanceof Vex.Interval
vexintervals.push vexinterval
@intervals = vexintervals
vexroot = @root
if typeof(@root) is 'string' then vexroot = new Vex.Note(@root) else throw error("root is wrong type !" + @root) unless @root instanceof Vex.Note
@root = vexroot
L "new @intervals=", @intervals, "new @root=", @root
getDegree: (num) ->
semitons = @intervals[(num+2)%@intervals.length].getSemitons() - @intervals[num].getSemitons()
semitons += 12 if semitons < 0
L " semitons=", semitons
if new Vex.Interval("b3").getSemitons() == semitons
return @intervals[num].getDegreeLowerCase()
else
if new Vex.Interval("3").getSemitons() == semitons
return @intervals[num].getDegree()
error("Could not determine if degree is minor or major")
getNotes: ->
notes = []
for interval in @intervals
L " interval=", interval
notes.push interval.getNote(@root)
L "getNotes=", notes
return notes
getInterval: (vexnote) ->
for interval in @intervals
if interval.getNote(@root).getSemitons() == vexnote.getSemitons() then return interval
error("Could not find note in scale ! " + vexnote)
getNote: (num) ->
return @intervals[num].getNote(@root)
getTextLowerCaseNotes: ->
txtnotes = []
txtnotes.push interval.getNote(@root).getTextLowerCase() for interval in @intervals
L "getTextNotes=" + txtnotes
return txtnotes
getNoteCount: -> return @intervals.length
getTextNotes: ->
txtnotes = []
txtnotes.push interval.getNote(@root).getText() for interval in @intervals
L "getTextNotes=" + txtnotes
return txtnotes
getTextIntervals: ->
txtintervals = []
txtintervals.push interval.name for interval in @intervals
L "getTextIntervals=" + txtintervals
return txtintervals
getAccidentalCount: ->
notes = @getNotes()
accidentalCount = 0
for note in notes
accidentalCount += Math.abs(note.accidental)
L "getAccidentalCount=" + accidentalCount
return accidentalCount
allroot = [ "c", "c#", "db", "d", "d#", "eb", "e", "fb","e#", "f", "f#", "gb", "g", "g#", "ab", "a", "a#", "bb", "b", "b#", "cb" ]
@getScaleWithMinimumAccidental: (intervals) ->
scales = []
for note in allroot
root = new Vex.Note(note)
scale = new Vex.Scale(intervals, root)
rootvalue = root.getSemitons()
if scales[rootvalue]?
if scales[rootvalue].getAccidentalCount() > scale.getAccidentalCount()
scales[rootvalue] = scale
else
scales[rootvalue] = scale
return scales
# @getAllScalesWithNotes: (div_id, scalename, intervals, notes) ->
# div = document.getElementById(div_id)
# scales = []
# scaletxt = ""
# for note in allroot
# root = new Vex.Note(note)
# scale = new Vex.Scale(intervals, root)
# scalenotes = scale.getNotes()
# for i in notes
# found = false
# for j in scalenotes
# if i.toLowerCase() == j.toLowerCase()
# found = true
# break
# if found == false
# break
#
# if found
# scales.push scale
# div.innerHTML = div.innerHTML + scale.getNote(0) + " " + scalename +
#
#
# if scales.length > 0 and div?
# for scale in scales
# div.innerHTML = div.innerHTML + scale.getNote(0) + " " + scalename + "
"
#
#
# return scales
class Vex.FretboardHelper
@DEBUG = false
L = (args...) -> console?.log("(Vex.FretboardHelper)", args...) if Vex.FretboardHelper.DEBUG
error = (msg) -> new Vex.RERR("FretboardHelperError", msg)
@DEFAULT_OCTAVE_COLORS = ["#ccffff","#ffccff","#ffffcc","#88cccc", "#cc88cc", "#cccc88","#88aaaa","#aa88aa"]
@STANDARD_TUNING = {
bass_4_strings: [ 31, 26, 21, 16]
bass_5_strings: [ 31, 26, 21, 16, 11]
bass_6_strings: [ 36, 31, 26, 21, 16, 11]
guitar_6_strings: [ 40 ,35, 31, 26, 21, 16]
guitar_7_strings: [ 40 ,35, 31, 26, 21, 16, 11]
}
@DIAGRAMS = [
{fret_diagrams: ["x-xx-","x-x--","xxx--"], finger:3}
{fret_diagrams: ["x-x-x", "-xx-x", "-xx--","-xxx-", "--xx-", "--xxx","--x-x", "x-xxx"], finger:2}
]
MIN_FRET=0
MAX_FRET=22
ANNULAR_FINGER = 1
AURICULAR_FINGER = 2
INDEX_FINGER = 4
MIDDLE_FINGER = 8
@DEFAULT_POSITION_NAMES = ["position I", "position II", "position III", "position IV", "position V", "position VI", "position VII", "position VIII"]
@getAllLightsFromScale: (vexscale, options, customtuning) ->
L "getAllLightsFromScale() scale=", vexscale, " options=", options, " customtuning=", customtuning
opts =
strings: 4
rootfillcolor: "tomato",
otherfillcolor: "lightgray",
rootcolor: "white",
othercolor: "#000",
type: 0
_.extend(opts, options)
L "real opts=", opts
tuning= []
lights = []
notes = vexscale.getNotes()
if customtuning?
tuning = customtuning
else
if !opts.strings? then error("Invalid strings parameter ! " + opts.strings)
switch opts.strings
when 4 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_4_strings"]
when 5 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_5_strings"]
when 6 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_6_strings"]
else error("Invalid strings option ! [" + strings + "]")
if opts.start_fret? then start_fret = opts.start_fret else error("Invalid start_fret option ! " + opts.start_fret)
if opts.end_fret? then end_fret = opts.end_fret else error("Invalid end_fret option ! " + opts.end_fret)
for str in [1..opts.strings]
stringvalue = tuning[str - 1]
for fret in [start_fret..end_fret]
for noteindex in [0..(notes.length-1)]
note = notes[noteindex]
fretvalue = stringvalue + fret
fretsemitons = fretvalue % 12
if fretsemitons == note.getSemitons()
if opts.type != 3
if note.getSemitons() == vexscale.root.getSemitons()
color = opts.rootcolor
fillcolor = opts.rootfillcolor
else
color = opts.othercolor
fillcolor = opts.otherfillcolor
switch opts.type
when 0 then lights.push({fret:fret, string:str,color:color, fillColor:fillcolor})
when 1 then lights.push({fret:fret, string:str,color:color, fillColor:fillcolor, text:note.getFretText()})
when 2 then lights.push({fret:fret, string:str,color:color, fillColor:fillcolor, text:vexscale.intervals[noteindex].getFretText()})
when 3
colorindex = Math.floor(fretvalue/12)
color = Vex.FretboardHelper.DEFAULT_OCTAVE_COLORS[colorindex]
lights.push({fret:fret, string:str,color:"white", fillColor:color, text:note.getFretText()})
when 4
colorindex = Math.floor(fretvalue/12)
color = Vex.FretboardHelper.DEFAULT_OCTAVE_COLORS[colorindex]
lights.push({fret:fret, string:str,color:"white", fillColor:color})
else error("Invalid type parameter !")
break
return lights
@getAllLightsFromNotes: (notes, options, notes_to_display, customtuning) ->
L "getAllLightsFromNotes() notes=", notes, " options=", options, " notes_to_display=", notes_to_display, " customtuning=", customtuning
opts =
strings: 4
rootfillcolor: "tomato",
otherfillcolor: "lightgray",
type: 0
_.extend(opts, options)
L "real opts=", opts
tuning= []
lights = []
if customtuning?
tuning = customtuning
else
if !opts.strings? then error("Invalid strings parameter ! " + opts.strings)
switch opts.strings
when 4 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_4_strings"]
when 5 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_5_strings"]
when 6 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_6_strings"]
else error("Invalid strings option ! [" + strings + "]")
strings = opts.strings
if opts.start_fret? then start_fret = opts.start_fret else error("Invalid start_fret option ! " + opts.start_fret)
if opts.end_fret? then end_fret = opts.end_fret else error("Invalid end_fret option ! " + opts.end_fret)
for str in [1..strings]
stringvalue = tuning[str - 1]
for fret in [start_fret..end_fret]
for noteindex in [0..(notes.length-1)]
note = notes[noteindex]
if noteindex == 0
color = opts.rootcolor
fillcolor = opts.rootfillcolor
else
color = opts.othercolor;
fillcolor = opts.otherfillcolor
fretvalue = stringvalue + fret
fretsemitons = fretvalue % 12
if fretsemitons == note.getSemitons()
switch opts.type
when 0 then lights.push({fret:fret, string:str,color:color, fillColor:fillcolor})
when 1 then lights.push({fret:fret, string:str,color:color, fillColor:fillcolor, text:note.getFretText()})
when 3
colorindex = Math.floor(fretvalue/12)
color = Vex.FretboardHelper.DEFAULT_OCTAVE_COLORS[colorindex]
if notes_to_display? then text = notes_to_display[note.getSemitons()] else text = note.getFretText()
lights.push({fret:fret, string:str,color:"#000", fillColor:color, text:text})
when 4
colorindex = Math.floor(fretvalue/12)
color = Vex.FretboardHelper.DEFAULT_OCTAVE_COLORS[colorindex]
lights.push({fret:fret, string:str,color:"#000", fillColor:color})
else error("Invalid type parameter !")
break
return lights
@getBracket: (vexscale, options, customtuning) ->
L "getBracket() scale=", vexscale, " options=", options, " customtuning=" , customtuning
opts =
strings: 4
positionindex: 0
color: "black"
finger: 1
_.extend(opts, options)
L "real opts=", opts
tuning = []
if customtuning?
tuning = customtuning
else
switch opts.strings
when 4 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_4_strings"]
when 5 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_5_strings"]
when 6 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_6_strings"]
else
error("Invalid strings parameter ! [" + strings + "]")
root = {}
notes = vexscale.getNotes()
root = notes[opts.positionindex]
strings = tuning.length
switch opts.finger
when 1
for fret in [0..11]
if root.getSemitons() == (tuning[strings - 1] + fret) % 12
start_fret = fret
end_fret = fret + 4
finger_fret = fret
break
when 2
for fret in [2..13]
if root.getSemitons() == (tuning[strings - 1] + fret) % 12
start_fret = fret - 2
end_fret = fret + 2
finger_fret = fret
break
when 4
for fret in [4..15]
if root.getSemitons() == (tuning[strings - 1] + fret) % 12
start_fret = fret - 4
end_fret = fret
finger_fret = fret
break
fromFret = end_fret
toFret = start_fret
diagram = []
for str in [strings..1] by -1
stringvalue = tuning[str-1]
diagram[str-1] = ""
for fret in [start_fret..end_fret]
notefound = false
for note in notes
fretvalue = stringvalue + fret
fretsemitons = fretvalue % 12
if fretsemitons == note.getSemitons()
notefound = true
diagram[str-1] += "x"
if fret < fromFret then fromFret = fret
if fret > toFret then toFret = fret
if !notefound
diagram[str-1] += "-"
if fromFret == 12
fromFret = fromFret - 12
toFret = toFret - 12
finger_fret = finger_fret - 12
if opts.finger == 2
diagram_found = false
for ref in Vex.FretboardHelper.DIAGRAMS
if diagram_found then break
for fret_diagram in ref.fret_diagrams
if diagram[strings-1] == fret_diagram
finger = ref.finger
diagram_found = true
break
if !finger? then finger = opts.finger
else
finger = opts.finger
if !finger? then error("finger could not be found !")
bracket = {start_fret: fromFret, end_fret: toFret, position:"up" , level:1, color: opts.color, finger_fret: finger_fret, finger: finger}
L "bracket=", bracket
return bracket
@getAllBrackets: (vexscale, options, positionnames, customtuning) ->
L "getAllBrackets() scale=", vexscale, " options=", options, " customtuning=", customtuning
if !positionnames? then positionnames = Vex.FretboardHelper.DEFAULT_POSITION_NAMES
brackets = []
upbrackets = []
upbrackets[0] = {}
upbrackets[1] = {}
upbrackets[2] = {}
downbrackets = []
downbrackets[0] = {}
downbrackets[1] = {}
downbrackets[2] = {}
upordown = 0
for positionindex in [0..(vexscale.getNoteCount()-1)]
options.positionindex = positionindex
bracket = @getBracket(vexscale, options, customtuning)
bracket.position = (if upordown % 2 == 0 then "up" else "down")
if upordown % 2 == 0
if upbrackets[0][bracket.start_fret]? or upbrackets[0][bracket.end_fret]?
if upbrackets[1][bracket.start_fret]? or upbrackets[1][bracket.end_fret]?
bracket.level = 3
else
bracket.level=2
else
bracket.level=1
bracket.position="up"
for i in [bracket.start_fret..bracket.end_fret]
upbrackets[bracket.level-1][i] = bracket
else
if downbrackets[0][bracket.start_fret]? or downbrackets[0][bracket.end_fret]?
if downbrackets[1][bracket.start_fret]? or downbrackets[1][bracket.end_fret]?
bracket.level = 3
else
bracket.level=2
else
bracket.level=1
for i in [bracket.start_fret..bracket.end_fret]
downbrackets[bracket.level-1][i] = bracket
bracket.position="down"
bracket.text = positionnames[positionindex].replace(/_/g," ")
upordown += 1
brackets.push(bracket)
return brackets
@getAllBoxesFromBrackets: (brackets, strings, color) ->
boxes = []
for bracket in brackets
layername = "__box_of-" + bracket.text.replace(/ /g, "_") + "__"
boxes.push({start_fret: bracket.start_fret, end_fret: bracket.end_fret,start_string:1, end_string:strings, layer: layername, color: color})
return boxes
@getAllChords: (scale, options, chordtemplates, customtuning) ->
L "getAllChords() scale=", scale, " options=", options, " chordtemplates=", chordtemplates, " customtuning=", customtuning
tuning = []
if customtuning?
tuning = customtuning
else
switch options.strings
when 4 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_4_strings"]
when 5 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_5_strings"]
when 6 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_6_strings"]
else
error("Invalid strings parameter ! [" + strings + "]")
chords = []
for chordtemplate in chordtemplates
chord = @getChord(scale, options, chordtemplate, customtuning)
if chord != null then chords.push(chord)
L "chords=", chords
return chords
@getChord: (scale, options, chordtemplate, customtuning) ->
L "getChord() scale=", scale, " options=", options, " chordtemplate=", chordtemplate, " customtuning=", customtuning
opts =
strings: 4
rootfillcolor: "tomato",
otherfillcolor: "lightgray",
rootcolor: "white",
othercolor: "black",
barcolor: "black"
color: "black"
type: 0
_.extend(opts, options)
L "real opts=", opts
tuning = []
if customtuning?
tuning = customtuning
else
switch opts.strings
when 4 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_4_strings"]
when 5 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_5_strings"]
when 6 then tuning = Vex.FretboardHelper.STANDARD_TUNING["bass_6_strings"]
else
error("Invalid strings parameter ! [" + strings + "]")
chord = {}
chord.lights = []
chord.bars = []
chordindex = opts.chordindex
intervals = scale.intervals
sequence = chordtemplate.sequence
if sequence.length > opts.strings then error("Sequence must have 'strings' elements ! " + sequence.length);
sequence = chordtemplate.sequence
for i in [1..opts.strings]
if sequence[i-1] == 0
rootvalue = tuning[opts.strings-i]
break
bar = {index: chordtemplate.maxfret, strStart: tuning.length, strEnd: 1}
notecount = scale.getNoteCount()
intervalfound = false
sequence = chordtemplate.sequence
minfret = chordtemplate.minfret
maxfret = chordtemplate.maxfret
realfretoffset = chordtemplate.realfretoffset
sequenceindex = 0
for str in [tuning.length..1] by -1
if sequenceindex >= sequence.length then break
intervalindex = sequence[sequenceindex]
if !intervalindex?
sequenceindex++
continue
for fret in [minfret..maxfret]
if intervalfound then break
fretvalue = tuning[str-1] + fret
if fretvalue < rootvalue
intervalvalue = 12 - ((rootvalue - fretvalue) % 12)
else
intervalvalue = ((fretvalue - rootvalue) % 12)
L "intervalue=", intervalvalue, " fretvalue=", fretvalue, " rootvalue=" , rootvalue
interval = intervals[intervalindex]
if intervalvalue == interval.getSemitons()
if intervalindex == 0
fillcolor = opts.rootfillcolor
color = opts.rootcolor
else
fillcolor = opts.otherfillcolor
color = opts.othercolor
switch opts.type
when 0
thefret = fret + realfretoffset
light = {fret: thefret, string: str, fillColor: fillcolor, color: color}
chord.lights.push(light)
L " light=", light
when 1
text = interval.getFretText()
thefret = fret + realfretoffset
light = {fret: thefret, string: str, fillColor: fillcolor, color: color, text: text}
chord.lights.push(light)
L " light=", light
else error("Invalid type ! " + type)
if bar.index == fret
if str > bar.strEnd then bar.strEnd = str
if str < bar.strStart then bar.strStart = str
else
if bar.index > fret
bar = {index: fret, strStart: str, strEnd: str}
if str == tuning.length then chord.modeindex = intervalindex
intervalfound = true
sequenceindex++
break
intervalfound=false
sequencecount = 0
for i in sequence
if i != null then sequencecount++
if chord.lights.length < sequencecount then return null
bar.index += realfretoffset
if bar.strEnd - bar.strStart > 1
chord.bars = []
chord.bars.push({from_string:bar.strEnd, to_string:bar.strStart,fret: bar.index, fillColor:opts.barcolor})
return chord
@drawFingers: (fretboard, brackets, color) ->
L " markFingers() freboard=", fretboard, " barckets=", brackets, " color=", color
txtfinger = (afinger) ->
switch afinger
when 1
text = 'X : index'
text_opt = 'index : X'
when 2
text = 'X : majeur'
text_opt = 'majeur : X'
when 3
text = 'X : annulaire'
text_opt = 'annulaire : X'
when 4
text = 'X : auriculaire'
text_opt = 'auriculaire : X'
else error("Invalid finger ! " + afinger)
if fretboard.options.draw_vertical then return text_opt else return text
for bracket in brackets
fret = bracket.finger_fret
fretboard.activateLayer("__box_of-" + bracket.text.replace(/ /g, "_") + "__")
options = {}
options.color = color
options.text = txtfinger(bracket.finger)
options.fret = fret
if fretboard.options.draw_vertical
options.justification = "right"
fretboard.drawFinger(options)
fretboard.activateLayer(Vex.Flow.Fretboard.DEFAULT_LAYER_NAME)
class Vex.TabStaveHelper
@DEBUG = false
L = (args...) -> console?.log("(Vex.TabStaveHelper)", args...) if Vex.TabStaveHelper.DEBUG
error = (msg) -> new Vex.RERR("TabStaveHelperError", msg)
@shuffle: (array) ->
i=array.length
while i > 0
j = Math.floor(Math.random() * i)
x = array[--i]
array[i] = array[j]
array[j] = x
return array;
@chunk: (arr,len) ->
chunks = []
i = 0
n = arr.length
while i < n
chunks.push(arr.slice(i, i += len))
return chunks
@printPositions: (tabnotes) ->
L "tabnotes=", tabnotes
tabs=""
size=0
for note in tabnotes
if note.getPositions().length == 0
error("does not own position !")
else
for pos in note.getPositions()
tabs = tabs + " " + pos.str + "/" + pos.fret
size++
L "tabs["+size+"]=" , tabs
@createCanvas: (div_id, options) ->
for i in [1..options.staves]
$("#" + div_id).append("