message: 'stop' is invoked on the last scheduled stop value
diff: @@ -173,7 +173,10 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
  throw new Error("Tone.BufferSource can only be started once.");
  }
  - if (this.buffer.loaded){
  + if (!this.buffer.loaded){
  + throw new Error("Tone.BufferSource: buffer is either not set or not loaded.");
  + }
  +
  time = this.toSeconds(time);
  //if it's a loop the default offset is the loopstart point
  if (this.loop){
  @@ -232,9 +235,6 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
  this._sourceStarted = true;
  this._source.start(time, offset);
  }
  - } else {
  - throw new Error("Tone.BufferSource: buffer is either not set or not loaded.");
  - }
  return this;
  };
  @@ -247,13 +247,20 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
  * @return {Tone.BufferSource} this
  */
  Tone.BufferSource.prototype.stop = function(time, fadeOutTime){
  - if (this.buffer.loaded){
  + if (!this.buffer.loaded){
  + throw new Error("Tone.BufferSource: buffer is either not set or not loaded.");
  + }
  - time = this.toSeconds(time);
  + if (this._sourceStopped){
  + throw new Error("Tone.BufferSource cannot be stopped, it has already finished playing");
  + }
  - //if this is before the previous stop
  - if (this._stopTime === -1 || this._stopTime > time){
  + time = this.toSeconds(time);
  + //if the event has already been scheduled, clear it
  + if (this._stopTime !== -1){
  + this.cancelStop();
  + }
  //stop if it's schedule before the start time
  if (time <= this._startTime){
  this._gainNode.gain.cancelScheduledValues(time);
  @@ -289,14 +296,26 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
  Tone.context.clearTimeout(this._onendedTimeout);
  this._onendedTimeout = Tone.context.setTimeout(this._onended.bind(this), this._stopTime - this.now());
  - }
  - } else {
  - throw new Error("Tone.BufferSource: buffer is either not set or not loaded.");
  - }
  return this;
  };
  + /**
  + * Cancel a scheduled stop event
  + * @return {Tone.BufferSource} this
  + */
  + Tone.BufferSource.prototype.cancelStop = function(){
  + if (this._startTime !== -1 && !this._sourceStopped){
  + //cancel the stop envelope
  + var fadeInTime = this.toSeconds(this.fadeIn);
  + this._gainNode.gain.cancelScheduledValues(this._startTime + fadeInTime + this.sampleTime);
  + this._gainNode.gain.setValueAtTime(1, Math.max(this.now(), this._startTime + fadeInTime+ this.sampleTime));
  + this.context.clearTimeout(this._onendedTimeout);
  + this._stopTime = -1;
  + }
  + return this;
  + };
  +
  /**
  * Internal callback when the buffer is ended.
  * Invokes `onended` and disposes the node.
  @@ -371,6 +390,7 @@ define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/G
  },
  set : function(loop){
  this._source.loop = loop;
  + this.cancelStop();
  }
  });
  