AudioBufferSourceNode can only be used once, the current Audio implementation already works around this by creating a new source whenever play() is called.
Since setBuffer() assigns the buffer directly to the source, calls to setBuffer() will fail after having played the audio once, making it impossible to use another buffer with the Audio instance (at least on Chrome, resulting in "Cannot set buffer after it has been already been set", Safari and Firefox don't mind).
This pull requests stores the buffer as an instance variable, which makes it possible to reuse it with a different buffer.
I removed creating a source in the constructor, avoiding to create a source that is never used because a new one is created later in in play(). On the other hand, bind() is now called on each invocation of play(). Not sure if you would prefer saving the bound callback in an instance variable, I wasn't able to find a precedent in three's source.
Since loop also directly forwards to the source, it now also gets saved as an instance variable to avoid errors when calling setLoop() before play(). This makes the implementations of setLoop() and setPlaybackRate() consistent, also fixing setLoop() not returning this.
Don't rely on an existing buffer source node in Audio,
fixes calling setBuffer after Audio has been played
and makes get/setLoop consistent with get/setPlaybackRate.
@hoch how come Chrome errors here but not Firefox nor Safari?
No related to this change directly, but I believe it's slightly better to call .start() method at the end. It's also better to use playbackRate.setValueAtTime(value, this.startTime).
@sttz Do you mind doing these changes too?
Because Chrome does what the spec says:
Setting ASBN.buffer multiple times MUST throw an exception.
Call source.start last and use setValueAtTime instead of value setter
Thanks for the feedback, I've added the proposed changes.
I also had to use setValueAtTime( this.playbackRate, this.context.currentTime ) in the setPlaybackRate() method, so that changing the pitch while the audio is playing still works.
setValueAtTime( this.playbackRate, this.context.currentTime )