   init: ->
     context = @audioContext = new window.AudioContext
     bufferManager = @sampleBufferManager = new SampleBufferManager(context)
-    bufferManager.onload =>
-      @playheadTime = context.currentTime
+    bufferManager.loadSamples().then => @playheadTime = context.currentTime
 
   note: (note, options={}) ->
     options = _.extend {gain: 1, duration: 3}, options

SampleManager =
  sampleNotes: ['B0'].concat ("C#{n}" for n in [1..8])
  noteBuffers: {}
  loadingCallbacks: {}
  sampleNotesLoaded: false
  playOnLoad: []

  withNoteBuffer: (note, cb) ->
    return cb(buffer) if buffer = @noteBuffers[note]
    @loadingCallbacks[note] ||= []
    @loadingCallbacks[note].push cb
    return if @loadingCallbacks[note].length > 1
    url = "#{PianoSampleURLBase}#{note.toLowerCase()}.mp3"
    xhrPromise({url, responseType: 'arraybuffer'})
      .then (buffer) =>
        @noteBuffers[note] = buffer
        cb(buffer) for cb in @loadingCallbacks[note]
        delete @loadingCallbacks[note]
      .done()

  load: ->
    countdown = @sampleNotes.length
    for note in @sampleNotes
      @withNoteBuffer note, =>
        return if @sampleNotesLoaded
        countdown -= 1
        @sampleNotesLoaded ||= countdown == 0
        return unless @sampleNotesLoaded
        fn() for fn in @playOnLoad
        playOnLoad = null

  onload: (cb) ->
    return window.setTimeout(cb, 1) if @sampleNotesLoaded
    @playOnLoad.push cb

class SampleBufferManager
  loadingCallbacks: {}
  noteBuffers: {}

  constructor: (@audioContext) ->
    @sampleNotes = SampleManager.sampleNotes
    @sampleNotes.map (note) =>
      @withNoteBuffer note, ->

  onload: (cb) ->
    SampleManager.onload(cb)

  withNoteBuffer: (note, cb) ->
    return cb(buffer) if buffer = @noteBuffers[note]
    @loadingCallbacks[note] ||= []
    @loadingCallbacks[note].push cb
    return if @loadingCallbacks[note].length > 1
    SampleManager.withNoteBuffer note, (arrayBuffer) =>
      @audioContext.decodeAudioData arrayBuffer, (buffer) =>
        @noteBuffers[note] = buffer
        cb(buffer) for cb in @loadingCallbacks[note]
        delete @loadingCallbacks[note]
      , (e) -> console.error 'error decoding', note

Player =
  audioContext: null

      bend += name2midi(note) - name2midi(base)
      note = base

    @sampleBufferManager.getNoteBuffer(note).then (buffer) =>
      sourceNode = @audioContext.createBufferSource()
      sourceNode.buffer = buffer
      # TODO different tuning
