vextab
Version:
A VexTab Parser for VexFlow
248 lines (207 loc) • 6.91 kB
text/coffeescript
# VexTab Player
# Copyright 2012 Mohit Cheppudira <mohit@muthanna.com>
#
# This class is responsible for rendering the elements
# parsed by Vex.Flow.VexTab.
class Vex.Flow.Player
= false
= {}
L = (args...) -> console?.log("(Vex.Flow.Player)", args...) if Vex.Flow.Player.DEBUG
Fraction = Vex.Flow.Fraction
RESOLUTION = Vex.Flow.RESOLUTION
noteValues = Vex.Flow.Music.noteValues
drawDot = Vex.drawDot
INSTRUMENTS = {
"acoustic_grand_piano": 0,
"acoustic_guitar_nylon": 24,
"acoustic_guitar_steel": 25,
"electric_guitar_jazz": 26,
"distortion_guitar": 30,
"electric_bass_finger": 33,
"electric_bass_pick": 34,
"trumpet": 56,
"brass_section": 61,
"soprano_sax": 64,
"alto_sax": 65,
"tenor_sax": 66,
"baritone_sax": 67,
"flute": 73,
"synth_drum": 118
}
constructor: (, options) ->
L "Initializing player: ", options
=
instrument: "acoustic_grand_piano"
tempo: 120
show_controls: true
soundfont_url: "/soundfont/"
overlay_class: "vextab-player"
_.extend(, options) if options?
L "Using soundfonts in: #{@options.soundfont_url}"
= null
= null
setArtist: (artist) ->
= artist
setTempo: (tempo) ->
L "New tempo: ", tempo
.tempo = tempo
setInstrument: (instrument) ->
L "New instrument: ", instrument
if instrument not in _.keys(INSTRUMENTS)
throw new Vex.RERR("PlayerError", "Invalid instrument: " + instrument)
.instrument = instrument
reset: ->
.attachPlayer(this)
= {}
= []
= .tempo * (RESOLUTION / 4)
= 25 # ms: 50 = 20hz
= / (60 * (1000/))
= 0
if ?
.remove()
= null
getOverlay = (context, scale, overlay_class) ->
canvas = context.canvas
height = canvas.height
width = canvas.width
overlay = $('<canvas>')
overlay.css("position", "absolute")
overlay.css("left", 0)
overlay.css("top", 0)
overlay.addClass(overlay_class)
$(canvas).after(overlay)
ctx = Vex.Flow.Renderer.getCanvasContext(overlay.get(0), width, height)
ctx.scale(scale, scale)
ps = new paper.PaperScope()
ps.setup(overlay.get(0))
return {
paper: ps
canvas: overlay.get(0)
}
removeControls: ->
.remove() if ?
.remove() if ?
.view.draw() if ?
render: ->
data = .getPlayerData()
= data.scale
if not
overlay = getOverlay(data.context, data.scale, .overlay_class)
= overlay.paper
= new .Path.Rectangle(0,0,13,85)
= new .PointText(35, 12)
if .show_controls
= new .Path.RegularPolygon(new .Point(25,10), 3, 7, 7)
.fillColor = '#396'
.opacity = 0.8
.rotate(90)
.onMouseUp = (event) =>
= new .Path.Rectangle(3,3,10,10)
.fillColor = '#396'
.opacity = 0.8
.onMouseUp = (event) =>
.view.draw()
staves = data.voices
total_ticks = new Fraction(0, 1)
for voice_group in staves
max_voice_tick = new Fraction(0, 1)
for voice, i in voice_group
total_voice_ticks = new Fraction(0, 1)
for note in voice.getTickables()
unless note.shouldIgnoreTicks()
abs_tick = total_ticks.clone()
abs_tick.add(total_voice_ticks)
abs_tick.simplify()
key = abs_tick.toString()
if _.has(, key)
[key].notes.push(note)
else
[key] =
tick: abs_tick
value: abs_tick.value()
notes: [note]
total_voice_ticks.add(note.getTicks())
if total_voice_ticks.value() > max_voice_tick.value()
max_voice_tick.copy(total_voice_ticks)
total_ticks.add(max_voice_tick)
= _.sortBy(_.values(), (tick) -> tick.value)
= _.last()
L
updateMarker: (x, y) ->
.fillColor = '#369'
.opacity = 0.2
.setPosition(new .Point(x * , y * ))
.view.draw()
playNote: (notes) ->
L "(#{@current_ticks}) playNote: ", notes
for note in notes
x = note.getAbsoluteX() + 4
y = note.getStave().getYForLine(2)
if ?
continue if note.isRest()
keys = note.getPlayNote()
duration = note.getTicks().value() / (/60)
for key in keys
[note, octave] = key.split("/")
note = note.trim().toLowerCase()
note_value = noteValues[note]
continue unless note_value?
midi_note = (24 + (octave * 12)) + noteValues[note].int_val
MIDI.noteOn(0, midi_note, 127, 0)
MIDI.noteOff(0, midi_note, duration)
refresh: ->
if
return
+=
if >= and .length > 0
[].notes
++
if >= .length
= true
else
= [].tick.value()
stop: ->
L "Stop"
window.clearInterval() if ?
.fillColor = '#396' if ?
.view.draw() if ?
= null
= 0
= 0
= 0
= false
start: ->
L "Start"
.fillColor = '#a36' if ?
MIDI.programChange(0, INSTRUMENTS[.instrument])
# try to update, maybe notes were changed dynamically
= window.setInterval((() => ), )
play: ->
L "Play: ", ,
if Vex.Flow.Player.INSTRUMENTS_LOADED[.instrument] and not
else
L "Loading instruments..."
.content = "Loading instruments..."
.fillColor = "green"
= true
.view.draw()
MIDI.loadPlugin
soundfontUrl: .soundfont_url
instruments: [.instrument]
callback: () =>
Vex.Flow.Player.INSTRUMENTS_LOADED[.instrument] = true
= false
.content = ""