       bend += name2midi(note) - name2midi(base)
       note = base
 
-    @sampleBufferManager.withNoteBuffer note, (buffer) =>
+    @sampleBufferManager.getNoteBuffer(note).then (buffer) =>
       sourceNode = @audioContext.createBufferSource()
       sourceNode.buffer = buffer
       # TODO different tuning

  init: ->
    context = @audioContext = new window.AudioContext
    bufferManager = @sampleBufferManager = new SampleBufferManager(context)
    bufferManager.onload =>
      @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: {}
  sampleNotesLoaded: false

  getNoteBuffer: (note) ->
    @noteBuffers[note] or= do ->
      url = "#{PianoSampleURLBase}#{note.toLowerCase()}.mp3"
      xhrPromise({url, responseType: 'arraybuffer'})

  loadSamples: ->
    Q.all(@getNoteBuffer(note) for note in @sampleNotes)


class SampleBufferManager
  noteBuffers: {}

  constructor: (@audioContext) ->
    @sampleNotes = SampleManager.sampleNotes
    @loadSamples()

  loadSamples: (cb) ->
    Q.all(@getNoteBuffer(note) for note in @sampleNotes)

  getNoteBuffer: (note, cb) ->
    @noteBuffers[note] or=
      SampleManager.getNoteBuffer(note)
      .then (arrayBuffer) =>
        d = Q.defer()
        @audioContext.decodeAudioData arrayBuffer, (buffer) =>
          d.resolve buffer
        , (e) -> d.reject "error decoding #{note}"
        d.promise

Player =
  audioContext: null
