Tone.js/Tone/signal/Signal.js
2014-07-04 13:47:56 -04:00

296 lines
No EOL
8.3 KiB
JavaScript

define(["Tone/core/Tone", "Tone/core/Master"], function(Tone){
/**
* all signals share a common constant signal generator
*
* @static
* @private
* @type {OscillatorNode}
*/
var generator = Tone.context.createOscillator();
/**
* @static
* @private
* @type {WaveShaperNode}
*/
var constant = Tone.context.createWaveShaper();
//generate the waveshaper table which outputs 1 for any input value
(function(){
var len = 8;
var curve = new Float32Array(len);
for (var i = 0; i < len; i++){
//all inputs produce the output value
curve[i] = 1;
}
constant.curve = curve;
})();
generator.connect(constant);
generator.start(0);
generator.noGC();
/**
* @class Constant audio-rate signal.
* Tone.Signal is a core component which allows for sample-accurate
* synchronization of many components. Tone.Signal can be scheduled
* with all of the functions available to AudioParams
*
* @constructor
* @extends {Tone}
* @param {number=} value (optional) initial value
*/
Tone.Signal = function(value){
Tone.call(this);
/**
* scales the constant output to the desired output
* @type {GainNode}
*/
this.scalar = this.context.createGain();
/**
* the ratio of the this value to the control signal value
*
* @private
* @type {number}
*/
this._syncRatio = 1;
//connect the constant 1 output to the node output
this.chain(constant, this.scalar, this.output);
//signal passes through
this.input.connect(this.output);
//set the default value
this.setValue(this.defaultArg(value, 0));
};
Tone.extend(Tone.Signal);
/**
* @return {number} the current value of the signal
*/
Tone.Signal.prototype.getValue = function(){
return this.scalar.gain.value;
};
/**
* set the value of the signal right away
* will be overwritten if there are previously scheduled automation curves
*
* @param {number} value
*/
Tone.Signal.prototype.setValue = function(value){
if (this._syncRatio === 0){
value = 0;
} else {
value *= this._syncRatio;
}
this.scalar.gain.value = value;
};
/**
* Schedules a parameter value change at the given time.
*
* @param {number} value
* @param {Tone.Time} time
*/
Tone.Signal.prototype.setValueAtTime = function(value, time){
value *= this._syncRatio;
this.scalar.gain.setValueAtTime(value, this.toSeconds(time));
};
/**
* creates a schedule point with the current value at the current time
*
* @param {number=} now (optionally) pass the now value in
* @returns {number} the current value
*/
Tone.Signal.prototype.setCurrentValueNow = function(now){
now = this.defaultArg(now, this.now());
var currentVal = this.getValue();
this.cancelScheduledValues(now);
this.scalar.gain.setValueAtTime(currentVal, now);
return currentVal;
};
/**
* Schedules a linear continuous change in parameter value from the
* previous scheduled parameter value to the given value.
*
* @param {number} value
* @param {Tone.Time} endTime
*/
Tone.Signal.prototype.linearRampToValueAtTime = function(value, endTime){
value *= this._syncRatio;
this.scalar.gain.linearRampToValueAtTime(value, this.toSeconds(endTime));
};
/**
* Schedules an exponential continuous change in parameter value from
* the previous scheduled parameter value to the given value.
*
* NOTE: Chrome will throw an error if you try to exponentially ramp to a
* value 0 or less.
*
* @param {number} value
* @param {Tone.Time} endTime
*/
Tone.Signal.prototype.exponentialRampToValueAtTime = function(value, endTime){
value *= this._syncRatio;
this.scalar.gain.exponentialRampToValueAtTime(value, this.toSeconds(endTime));
};
/**
* Schedules an exponential continuous change in parameter value from
* the current time and current value to the given value.
*
* @param {number} value
* @param {Tone.Time} endTime
*/
Tone.Signal.prototype.exponentialRampToValueNow = function(value, endTime){
var now = this.now();
this.setCurrentValueNow(now);
value *= this._syncRatio;
//make sure that the endTime doesn't start with +
if (endTime.toString().charAt(0) === "+"){
endTime = endTime.substr(1);
}
this.scalar.gain.exponentialRampToValueAtTime(value, now + this.toSeconds(endTime));
};
/**
* Schedules an linear continuous change in parameter value from
* the current time and current value to the given value at the given time.
*
* @param {number} value
* @param {Tone.Time} endTime
*/
Tone.Signal.prototype.linearRampToValueNow = function(value, endTime){
var now = this.now();
this.setCurrentValueNow(now);
value *= this._syncRatio;
//make sure that the endTime doesn't start with +
if (endTime.toString().charAt(0) === "+"){
endTime = endTime.substr(1);
}
this.scalar.gain.linearRampToValueAtTime(value, now + this.toSeconds(endTime));
};
/**
* Start exponentially approaching the target value at the given time with
* a rate having the given time constant.
*
* @param {number} value
* @param {Tone.Time} startTime
* @param {number} timeConstant
*/
Tone.Signal.prototype.setTargetAtTime = function(value, startTime, timeConstant){
value *= this._syncRatio;
this.output.gain.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
};
/**
* Sets an array of arbitrary parameter values starting at the given time
* for the given duration.
*
* @param {Array<number>} values
* @param {Tone.Time} startTime
* @param {Tone.Time} duration
*/
Tone.Signal.prototype.setValueCurveAtTime = function(values, startTime, duration){
for (var i = 0; i < values.length; i++){
values[i] *= this._syncRatio;
}
this.scalar.gain.setValueCurveAtTime(values, this.toSeconds(startTime), this.toSeconds(duration));
};
/**
* Cancels all scheduled parameter changes with times greater than or
* equal to startTime.
*
* @param {Tone.Time} startTime
*/
Tone.Signal.prototype.cancelScheduledValues = function(startTime){
this.scalar.gain.cancelScheduledValues(this.toSeconds(startTime));
};
/**
* Sync this to another signal and it will always maintain the
* ratio between the two signals until it is unsynced
*
* Signals can only be synced to one other signal. while syncing,
* if a signal's value is changed, the new ratio between the signals
* is maintained as the syncing signal is changed.
*
* @param {Tone.Signal} signal to sync to
* @param {number=} ratio optionally pass in the ratio between
* the two signals, otherwise it will be computed
*/
Tone.Signal.prototype.sync = function(signal, ratio){
if (ratio){
this._syncRatio = ratio;
} else {
//get the sync ratio
if (signal.getValue() !== 0){
this._syncRatio = this.getValue() / signal.getValue();
} else {
this._syncRatio = 0;
}
}
//make a new scalar which is not connected to the constant signal
this.scalar.disconnect();
this.scalar = this.context.createGain();
this.chain(signal, this.scalar, this.output);
//set it ot the sync ratio
this.scalar.gain.value = this._syncRatio;
};
/**
* unbind the signal control
*
* will leave the signal value as it was without the influence of the control signal
*/
Tone.Signal.prototype.unsync = function(){
//make a new scalar so that it's disconnected from the control signal
//get the current gain
var currentGain = this.getValue();
this.scalar.disconnect();
this.scalar = this.context.createGain();
this.scalar.gain.value = currentGain / this._syncRatio;
this._syncRatio = 1;
//reconnect things up
this.chain(constant, this.scalar, this.output);
};
/**
* internal dispose method to tear down the node
*/
Tone.Signal.prototype.dispose = function(){
//disconnect everything
this.output.disconnect();
this.scalar.disconnect();
this.output = null;
this.scalar = null;
};
/**
* Signals can connect to other Signals
*
* @override
* @param {AudioParam|AudioNode|Tone.Signal|Tone} node
*/
Tone.Signal.prototype.connect = function(node){
//zero it out so that the signal can have full control
if (node instanceof Tone.Signal){
node.setValue(0);
} else if (node instanceof AudioParam){
node.value = 0;
}
this.output.connect(node);
};
return Tone.Signal;
});