2018-02-05 03:55:19 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source", "Tone/core/Gain",
|
|
|
|
"Tone/core/AudioNode"], function(Tone){
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Wrapper around the native fire-and-forget OscillatorNode. Adds the
|
2018-05-17 22:38:53 +00:00
|
|
|
* ability to reschedule the stop method. ***[Tone.Oscillator](Oscillator) is better
|
|
|
|
* for most use-cases***
|
2018-02-05 03:55:19 +00:00
|
|
|
* @extends {Tone.AudioNode}
|
|
|
|
* @param {AudioBuffer|Tone.Buffer} buffer The buffer to play
|
|
|
|
* @param {Function} onload The callback to invoke when the
|
|
|
|
* buffer is done playing.
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode = function(){
|
|
|
|
|
|
|
|
var options = Tone.defaults(arguments, ["frequency", "type"], Tone.OscillatorNode);
|
|
|
|
Tone.AudioNode.call(this, options);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The callback to invoke after the
|
|
|
|
* buffer source is done playing.
|
|
|
|
* @type {Function}
|
|
|
|
*/
|
|
|
|
this.onended = options.onended;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The oscillator start time
|
|
|
|
* @type {Number}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._startTime = -1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The oscillator stop time
|
|
|
|
* @type {Number}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._stopTime = -1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The gain node which envelopes the OscillatorNode
|
|
|
|
* @type {Tone.Gain}
|
|
|
|
* @private
|
|
|
|
*/
|
2018-02-08 16:05:18 +00:00
|
|
|
this._gainNode = this.output = new Tone.Gain();
|
|
|
|
this._gainNode.gain.setValueAtTime(0, this.context.currentTime);
|
2018-02-05 03:55:19 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The oscillator
|
|
|
|
* @type {OscillatorNode}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._oscillator = this.context.createOscillator();
|
|
|
|
this._oscillator.connect(this._gainNode);
|
|
|
|
this.type = options.type;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The frequency of the oscillator
|
|
|
|
* @type {Frequency}
|
|
|
|
* @signal
|
|
|
|
*/
|
|
|
|
this.frequency = new Tone.Param(this._oscillator.frequency, Tone.Type.Frequency);
|
|
|
|
this.frequency.value = options.frequency;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The detune of the oscillator
|
|
|
|
* @type {Frequency}
|
|
|
|
* @signal
|
|
|
|
*/
|
|
|
|
this.detune = new Tone.Param(this._oscillator.detune, Tone.Type.Cents);
|
|
|
|
this.detune.value = options.detune;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The value that the buffer ramps to
|
|
|
|
* @type {Gain}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._gain = 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
Tone.extend(Tone.OscillatorNode, Tone.AudioNode);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The defaults
|
|
|
|
* @const
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode.defaults = {
|
|
|
|
"frequency" : 440,
|
|
|
|
"detune" : 0,
|
|
|
|
"type" : "sine",
|
|
|
|
"onended" : Tone.noOp
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the playback state of the oscillator, either "started" or "stopped".
|
|
|
|
* @type {Tone.State}
|
|
|
|
* @readOnly
|
|
|
|
* @memberOf Tone.OscillatorNode#
|
|
|
|
* @name state
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.OscillatorNode.prototype, "state", {
|
|
|
|
get : function(){
|
2018-02-08 16:05:18 +00:00
|
|
|
return this.getStateAtTime(this.now());
|
2018-02-05 03:55:19 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-03-05 18:00:45 +00:00
|
|
|
/**
|
|
|
|
* Get the playback state at the given time
|
|
|
|
* @param {Time} time The time to test the state at
|
|
|
|
* @return {Tone.State} The playback state.
|
|
|
|
*/
|
2018-02-08 16:05:18 +00:00
|
|
|
Tone.OscillatorNode.prototype.getStateAtTime = function(time){
|
2018-03-05 18:00:45 +00:00
|
|
|
time = this.toSeconds(time);
|
2018-02-08 16:05:18 +00:00
|
|
|
if (this._startTime !== -1 && time >= this._startTime && (this._stopTime === -1 || time <= this._stopTime)){
|
|
|
|
return Tone.State.Started;
|
|
|
|
} else {
|
|
|
|
return Tone.State.Stopped;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-02-05 03:55:19 +00:00
|
|
|
/**
|
|
|
|
* Start the oscillator node at the given time
|
|
|
|
* @param {Time=} time When to start the oscillator
|
|
|
|
* @return {OscillatorNode} this
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode.prototype.start = function(time){
|
|
|
|
if (this._startTime === -1){
|
|
|
|
this._startTime = this.toSeconds(time);
|
|
|
|
this._oscillator.start(this._startTime);
|
2018-02-08 16:05:18 +00:00
|
|
|
var now = this.context.currentTime;
|
|
|
|
this._gainNode.gain.cancelScheduledValues(now);
|
|
|
|
this._gainNode.gain.setValueAtTime(0, now);
|
2018-02-05 03:55:19 +00:00
|
|
|
this._gainNode.gain.setValueAtTime(1, this._startTime);
|
|
|
|
} else {
|
|
|
|
throw new Error("cannot call OscillatorNode.start more than once");
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets an arbitrary custom periodic waveform given a PeriodicWave.
|
|
|
|
* @param {PeriodicWave} periodicWave PeriodicWave should be created with context.createPeriodicWave
|
|
|
|
* @return {OscillatorNode} this
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode.prototype.setPeriodicWave = function(periodicWave){
|
|
|
|
this._oscillator.setPeriodicWave(periodicWave);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop the oscillator node at the given time
|
|
|
|
* @param {Time=} time When to stop the oscillator
|
|
|
|
* @return {OscillatorNode} this
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode.prototype.stop = function(time){
|
2018-02-08 16:05:18 +00:00
|
|
|
//cancel the previous stop
|
|
|
|
this.cancelStop();
|
|
|
|
//reschedule it
|
2018-02-05 03:55:19 +00:00
|
|
|
this._stopTime = this.toSeconds(time);
|
|
|
|
this._gainNode.gain.setValueAtTime(0, this._stopTime);
|
|
|
|
this.context.clearTimeout(this._timeout);
|
|
|
|
this._timeout = this.context.setTimeout(function(){
|
2018-02-08 16:05:18 +00:00
|
|
|
this._oscillator.stop(this.now());
|
2018-02-05 03:55:19 +00:00
|
|
|
this.onended();
|
|
|
|
}.bind(this), this._stopTime - this.now());
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2018-03-01 19:08:35 +00:00
|
|
|
/**
|
|
|
|
* Cancel a scheduled stop event
|
|
|
|
* @return {Tone.OscillatorNode} this
|
|
|
|
*/
|
2018-02-08 16:05:18 +00:00
|
|
|
Tone.OscillatorNode.prototype.cancelStop = function(){
|
|
|
|
if (this._startTime !== -1){
|
|
|
|
//cancel the stop envelope
|
|
|
|
this._gainNode.gain.cancelScheduledValues(this._startTime + this.sampleTime);
|
|
|
|
this._gainNode.gain.setValueAtTime(1, Math.max(this.now(), this._startTime));
|
|
|
|
this.context.clearTimeout(this._timeout);
|
|
|
|
this._stopTime = -1;
|
|
|
|
}
|
2018-03-01 19:08:35 +00:00
|
|
|
return this;
|
2018-02-08 16:05:18 +00:00
|
|
|
};
|
|
|
|
|
2018-02-05 03:55:19 +00:00
|
|
|
/**
|
|
|
|
* The oscillator type. Either 'sine', 'sawtooth', 'square', or 'triangle'
|
|
|
|
* @memberOf Tone.OscillatorNode#
|
|
|
|
* @type {Time}
|
|
|
|
* @name type
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.OscillatorNode.prototype, "type", {
|
|
|
|
get : function(){
|
|
|
|
return this._oscillator.type;
|
|
|
|
},
|
|
|
|
set : function(type){
|
|
|
|
this._oscillator.type = type;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clean up.
|
|
|
|
* @return {Tone.OscillatorNode} this
|
|
|
|
*/
|
|
|
|
Tone.OscillatorNode.prototype.dispose = function(){
|
2018-02-05 05:40:42 +00:00
|
|
|
this.context.clearTimeout(this._timeout);
|
2018-02-05 03:55:19 +00:00
|
|
|
Tone.AudioNode.prototype.dispose.call(this);
|
|
|
|
this.onended = null;
|
|
|
|
this._oscillator.disconnect();
|
|
|
|
this._oscillator = null;
|
|
|
|
this._gainNode.dispose();
|
|
|
|
this._gainNode = null;
|
|
|
|
this.frequency.dispose();
|
|
|
|
this.frequency = null;
|
|
|
|
this.detune.dispose();
|
|
|
|
this.detune = null;
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
return Tone.OscillatorNode;
|
|
|
|
});
|