UNPKG

compoze

Version:

music writer

212 lines (176 loc) 6.54 kB
# Vex.Flow.VexTab # Copyright 2012 Mohit Cheppudira <mohit@muthanna.com> # # This class implements the semantic analysis of the Jison # output, and generates elements that can be used by # Vex.Flow.Artist to render the notation. # parsed by Vex.Flow.VexTab. Vex = require 'vexflow' _ = require 'lodash' parser = require './grammer.js' class VexTab @DEBUG = false L = (args...) -> console?.log("(Vex.Flow.VexTab)", args...) if VexTab.DEBUG # Private methods newError = (object, msg) -> new Vex.RERR("ParseError", "#{msg} in line #{object._l} column #{object._c}") # Public methods constructor: (@artist) -> @reset() reset: -> @valid = false @elements = false isValid: -> @valid getArtist: -> return @artist parseStaveOptions: (options) -> params = {} return params unless options? notation_option = null for option in options error = (msg) -> newError(option, msg) params[option.key] = option.value switch option.key when "notation", "tablature" notation_option = option throw error("'#{option.key}' must be 'true' or 'false'") if option.value not in ["true", "false"] when "key" throw error("Invalid key signature '#{option.value}'") unless _.has(Vex.Flow.keySignature.keySpecs, option.value) when "clef" clefs = ["treble", "bass", "tenor", "alto", "percussion", "none"] throw error("'clef' must be one of #{clefs.join(', ')}") if option.value not in clefs when "voice" voices = ["top", "bottom", "new"] throw error("'voice' must be one of #{voices.join(', ')}") if option.value not in voices when "time" try new Vex.Flow.TimeSignature(option.value) catch e throw error("Invalid time signature: '#{option.value}'") when "tuning" try new Vex.Flow.Tuning(option.value) catch e throw error("Invalid tuning: '#{option.value}'") when "strings" num_strings = parseInt(option.value) throw error("Invalid number of strings: #{num_strings}") if (num_strings < 4 or num_strings > 8) else throw error("Invalid option '#{option.key}'") if params.notation == "false" and params.tablature == "false" throw newError(notation_option, "Both 'notation' and 'tablature' can't be invisible") return params parseCommand: (element) -> if element.command is "bar" @artist.addBar(element.type) if element.command is "tuplet" @artist.makeTuplets(element.params.tuplet, element.params.notes) if element.command is "annotations" @artist.addAnnotations(element.params) if element.command is "rest" @artist.addRest(element.params) if element.command is "command" @artist.runCommand(element.params, element._l, element._c) parseChord: (element) -> L "parseChord:", element @artist.addChord( _.map(element.chord, (note)-> _.pick(note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator')), element.articulation, element.decorator) parseFret: (note) -> @artist.addNote(_.pick( note, 'time', 'dot', 'fret', 'string', 'articulation', 'decorator')) parseABC: (note) -> @artist.addNote(_.pick( note, 'time', 'dot', 'fret', 'abc', 'octave', 'string', 'articulation', 'decorator')) parseStaveElements: (notes) -> L "parseStaveElements:", notes for element in notes if element.time @artist.setDuration(element.time, element.dot) if element.command @parseCommand(element) if element.chord @parseChord(element) if element.abc @parseABC(element) else if element.fret @parseFret(element) parseStaveText: (text_line) -> @artist.addTextVoice() unless _.isEmpty(text_line) position = 0 justification = "center" smooth = true font = null bartext = => @artist.addTextNote("", 0, justification, false, true) createNote = (text) => ignore_ticks = false if text[0] == "|" ignore_ticks = true text = text[1..] try @artist.addTextNote(text, position, justification, smooth, ignore_ticks) catch e throw newError(str, "Bad text or duration. Did you forget a comma?" + e) for str in text_line text = str.text.trim() if text.match(/\.font=.*/) font = text[6..] @artist.setTextFont(font) else if text[0] == ":" @artist.setDuration(text) else if text[0] == "." command = text[1..] switch command when "center", "left", "right" justification = command when "strict" smooth = false when "smooth" smooth = true when "bar", "|" bartext() else position = parseInt(text[1..], 10) else if text == "|" bartext() else if text[0..1] == "++" @artist.addTextVoice() else createNote(text) generate: -> for stave in @elements switch stave.element when "stave", "tabstave" @artist.addStave(stave.element, @parseStaveOptions(stave.options)) @parseStaveElements(stave.notes) if stave.notes? @parseStaveText(stave.text) if stave.text? when "voice" @artist.addVoice(@parseStaveOptions(stave.options)) @parseStaveElements(stave.notes) if stave.notes? @parseStaveText(stave.text) if stave.text? when "options" options = {} for option in stave.params options[option.key] = option.value try @artist.setOptions(options) catch e throw newError(stave, e.message) else throw newError(stave, "Invalid keyword '#{stave.element}'") parse: (code) -> parser.parseError = (message, hash) -> L "VexTab parse error: ", message, hash message = "Unexpected text '#{hash.text}' at line #{hash.loc.first_line} column #{hash.loc.first_column}." throw new Vex.RERR("ParseError", message) throw new Vex.RERR("ParseError", "No code") unless code? L "Parsing:\n#{code}" # Strip lines stripped_code = (line.trim() for line in code.split(/\r\n|\r|\n/)) @elements = parser.parse(stripped_code.join("\n")) if @elements @generate() @valid = true return @elements module.exports = VexTab