compoze
Version:
music writer
255 lines (211 loc) • 6.97 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.
Vex = require 'vexflow'
Pencil = require 'penciljs'
_ = require 'lodash'
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: false
soundfont_url: "lib/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", $(canvas).position().left)
overlay.css("top", $(canvas).position().top)
overlay.addClass(overlay_class)
$(canvas).after(overlay)
ctx = Vex.Flow.Renderer.getCanvasContext(overlay.get(0), width, height)
ctx.scale(scale, scale)
pencil = new Pencil(overlay.get(0))
return {
pencil: pencil
canvas: overlay.get(0)
}
removeControls: ->
.remove() if ?
.remove() if ?
.draw() if ?
render: ->
data = .getPlayerData()
= data.scale
if not
overlay = getOverlay(data.context, data.scale, .overlay_class)
= overlay.pencil
= new Pencil.Rectangle(0,0,8,75).by().setContext({globalAlpha:0})
if .show_controls and not ?.rendered
= new Pencil.RegularPolygon(10,12, 8, 3).by
.setContext
fillStyle : '#0dad51'
strokeStyle : '#0dad51'
.on 'click', (event) =>
= new Pencil.Rectangle(35,5,14,14).by
.setContext
fillStyle : '#0dad51'
strokeStyle : '#0dad51'
.on 'click', (event) =>
.rendered = true
.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) ->
.setContext
fillStyle : '#0dad51'
strokeStyle : '#0dad51'
globalAlpha : 0.3
.setPosition( x, (y - 10))
.draw()
playNote: (notes) ->
L "(#{@current_ticks}) playNote: ", notes
for note in notes
x = note.getAbsoluteX() + 2
y = note.getStave().getYForLine(0)
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
if
do
else
= [].tick.value()
stop: ->
L "Stop"
window.clearInterval() if ?
= null
= 0
= 0
= 0
= false
start: ->
L "Start"
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
.draw()
MIDI.loadPlugin
soundfontUrl: .soundfont_url
instruments: [.instrument]
callback: () =>
Vex.Flow.Player.INSTRUMENTS_LOADED[.instrument] = true
= false
# .content = ""