Tone.js/Tone/core/Param.js

443 lines
13 KiB
JavaScript
Raw Normal View History

define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/AudioParam"], function(Tone){
2015-10-21 16:11:19 +00:00
"use strict";
2014-06-14 23:11:37 +00:00
/**
* @class Tone.Param wraps the native Web Audio's AudioParam to provide
2015-10-21 14:27:44 +00:00
* additional unit conversion functionality. It also
* serves as a base-class for classes which have a single,
* automatable parameter.
2017-08-28 15:38:45 +00:00
* @extends {Tone.AudioNode}
* @param {AudioParam} param The parameter to wrap.
* @param {Tone.Type} units The units of the audio param.
* @param {Boolean} convert If the param should be converted.
2014-06-14 23:11:37 +00:00
*/
Tone.Param = function(){
2017-04-26 02:23:22 +00:00
var options = Tone.defaults(arguments, ["param", "units", "convert"], Tone.Param);
Tone.AudioNode.call(this);
/**
* The native parameter to control
* @type {AudioParam}
2015-06-19 04:52:04 +00:00
* @private
*/
this._param = this.input = options.param;
2015-02-13 15:52:38 +00:00
/**
* The units of the parameter
* @type {Tone.Type}
2015-02-13 15:52:38 +00:00
*/
this.units = options.units;
2015-08-16 19:16:07 +00:00
/**
* If the value should be converted or not
* @type {Boolean}
*/
this.convert = options.convert;
2015-10-21 14:27:44 +00:00
/**
* True if the signal value is being overridden by
2015-10-21 14:27:44 +00:00
* a connected signal.
* @readOnly
* @type {boolean}
* @private
*/
this.overridden = false;
if (!Tone.isUndef(options.value)){
this.value = options.value;
}
2014-06-14 23:11:37 +00:00
};
Tone.extend(Tone.Param, Tone.AudioNode);
2015-02-23 05:31:05 +00:00
/**
* Defaults
2015-02-23 05:31:05 +00:00
* @type {Object}
* @const
*/
Tone.Param.defaults = {
2015-05-24 13:45:15 +00:00
"units" : Tone.Type.Default,
"convert" : true,
"param" : undefined
2015-02-23 05:31:05 +00:00
};
/**
* The current value of the parameter.
* @memberOf Tone.Param#
2015-06-19 04:52:04 +00:00
* @type {Number}
* @name value
*/
Object.defineProperty(Tone.Param.prototype, "value", {
get : function(){
return this._toUnits(this._param.value);
},
set : function(value){
var convertedVal = this._fromUnits(value);
this._param.cancelScheduledValues(0);
this._param.value = convertedVal;
}
});
2017-12-16 18:09:52 +00:00
/**
* The minimum output value of the parameter
* @memberOf Tone.Param#
* @type {Number}
* @name value
*/
Object.defineProperty(Tone.Param.prototype, "minValue", {
get : function(){
if (this.units === Tone.Type.Time || this.units === Tone.Type.Frequency ||
this.units === Tone.Type.NormalRange || this.units === Tone.Type.Positive ||
this.units === Tone.Type.BPM){
return 0;
} else if (this.units === Tone.Type.AudioRange){
return -1;
} else if (this.units === Tone.Type.Decibels){
return -Infinity;
} else {
return this._param.minValue;
}
}
});
/**
* The maximum output value of the parameter
* @memberOf Tone.Param#
* @type {Number}
* @name value
*/
Object.defineProperty(Tone.Param.prototype, "maxValue", {
get : function(){
if (this.units === Tone.Type.NormalRange ||
this.units === Tone.Type.AudioRange){
return 1;
} else {
return this._param.maxValue;
}
}
});
/**
* Convert the given value from the type specified by Tone.Param.units
* into the destination value (such as Gain or Frequency).
2015-08-18 20:29:15 +00:00
* @private
* @param {*} val the value to convert
* @return {number} the number which the value should be set to
*/
Tone.Param.prototype._fromUnits = function(val){
if (this.convert || Tone.isUndef(this.convert)){
switch(this.units){
case Tone.Type.Time:
return this.toSeconds(val);
case Tone.Type.Frequency:
return this.toFrequency(val);
case Tone.Type.Decibels:
return Tone.dbToGain(val);
case Tone.Type.NormalRange:
return Math.min(Math.max(val, 0), 1);
case Tone.Type.AudioRange:
return Math.min(Math.max(val, -1), 1);
case Tone.Type.Positive:
return Math.max(val, 0);
default:
return val;
}
} else {
return val;
}
};
/**
2015-10-21 14:27:44 +00:00
* Convert the parameters value into the units specified by Tone.Param.units.
* @private
* @param {number} val the value to convert
* @return {number}
*/
Tone.Param.prototype._toUnits = function(val){
if (this.convert || Tone.isUndef(this.convert)){
switch(this.units){
case Tone.Type.Decibels:
return Tone.gainToDb(val);
default:
return val;
}
} else {
return val;
}
};
/**
* the minimum output value
* @type {Number}
* @private
*/
Tone.Param.prototype._minOutput = 0.00001;
2014-06-14 23:11:37 +00:00
/**
* Schedules a parameter value change at the given time.
2015-06-19 04:52:04 +00:00
* @param {*} value The value to set the signal.
* @param {Time} time The time when the change should occur.
* @returns {Tone.Param} this
2015-06-19 04:52:04 +00:00
* @example
* //set the frequency to "G4" in exactly 1 second from now.
2015-06-19 04:52:04 +00:00
* freq.setValueAtTime("G4", "+1");
2014-06-14 23:11:37 +00:00
*/
Tone.Param.prototype.setValueAtTime = function(value, time){
time = this.toSeconds(time);
Tone.isPast(time);
this._param.setValueAtTime(this._fromUnits(value), time);
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
2017-10-26 04:51:02 +00:00
/**
* Get the signals value at the given time. Subsequent scheduling
* may invalidate the returned value.
* @param {Time} time When to get the value
* @returns {Number} The value at the given time
*/
Tone.Param.prototype.getValueAtTime = function(time){
time = this.toSeconds(time);
return this._fromUnits(this._param.getValueAtTime(time));
};
/**
2015-02-27 18:40:35 +00:00
* Creates a schedule point with the current value at the current time.
* This is useful for creating an automation anchor point in order to
* schedule changes from the current value.
*
* @param {number=} now (Optionally) pass the now value in.
* @returns {Tone.Param} this
*/
2017-10-26 04:51:02 +00:00
Tone.Param.prototype.setRampPoint = function(time){
time = this.toSeconds(time);
var currentVal = this.getValueAtTime(time);
this.cancelAndHoldAtTime(time);
if (currentVal === 0){
currentVal = this._minOutput;
}
2017-10-26 04:51:02 +00:00
this._param.setValueAtTime(currentVal, time);
2015-02-02 03:56:33 +00:00
return this;
};
2014-06-14 23:11:37 +00:00
/**
* Schedules a linear continuous change in parameter value from the
2014-06-14 23:11:37 +00:00
* previous scheduled parameter value to the given value.
*
* @param {number} value
* @param {Time} endTime
* @returns {Tone.Param} this
2014-06-14 23:11:37 +00:00
*/
Tone.Param.prototype.linearRampToValueAtTime = function(value, endTime){
value = this._fromUnits(value);
2017-09-15 21:54:32 +00:00
endTime = this.toSeconds(endTime);
Tone.isPast(endTime);
this._param.linearRampToValueAtTime(value, endTime);
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
/**
* Schedules an exponential continuous change in parameter value from
2014-06-14 23:11:37 +00:00
* the previous scheduled parameter value to the given value.
*
* @param {number} value
* @param {Time} endTime
* @returns {Tone.Param} this
2014-06-14 23:11:37 +00:00
*/
Tone.Param.prototype.exponentialRampToValueAtTime = function(value, endTime){
value = this._fromUnits(value);
2015-08-16 19:16:07 +00:00
value = Math.max(this._minOutput, value);
2017-09-15 21:54:32 +00:00
endTime = this.toSeconds(endTime);
Tone.isPast(endTime);
this._param.exponentialRampToValueAtTime(value, endTime);
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
/**
* Schedules an exponential continuous change in parameter value from
* the current time and current value to the given value over the
* duration of the rampTime.
*
* @param {number} value The value to ramp to.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
* @param {Time} [startTime=now] When the ramp should start.
* @returns {Tone.Param} this
2015-02-27 18:40:35 +00:00
* @example
* //exponentially ramp to the value 2 over 4 seconds.
* signal.exponentialRampTo(2, 4);
*/
Tone.Param.prototype.exponentialRampTo = function(value, rampTime, startTime){
startTime = this.toSeconds(startTime);
this.setRampPoint(startTime);
this.exponentialRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
2015-02-02 03:56:33 +00:00
return this;
};
/**
* Schedules an linear continuous change in parameter value from
* the current time and current value to the given value over the
* duration of the rampTime.
*
* @param {number} value The value to ramp to.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
* @param {Time} [startTime=now] When the ramp should start.
* @returns {Tone.Param} this
2015-02-27 18:40:35 +00:00
* @example
* //linearly ramp to the value 4 over 3 seconds.
* signal.linearRampTo(4, 3);
*/
Tone.Param.prototype.linearRampTo = function(value, rampTime, startTime){
startTime = this.toSeconds(startTime);
this.setRampPoint(startTime);
this.linearRampToValueAtTime(value, startTime + this.toSeconds(rampTime));
2015-02-02 03:56:33 +00:00
return this;
};
2017-09-13 15:00:32 +00:00
/**
* Start exponentially approaching the target value at the given time. Since it
* is an exponential approach it will continue approaching after the ramp duration. The
2017-09-13 15:00:32 +00:00
* rampTime is the time that it takes to reach over 99% of the way towards the value.
* @param {number} value The value to ramp to.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
* @param {Time} [startTime=now] When the ramp should start.
* @returns {Tone.Param} this
* @example
* //exponentially ramp to the value 2 over 4 seconds.
* signal.exponentialRampTo(2, 4);
*/
Tone.Param.prototype.targetRampTo = function(value, rampTime, startTime){
startTime = this.toSeconds(startTime);
this.setRampPoint(startTime);
this.exponentialAppraochValueAtTime(value, startTime, rampTime);
2017-09-13 15:00:32 +00:00
return this;
};
/**
* Start exponentially approaching the target value at the given time. Since it
* is an exponential approach it will continue approaching after the ramp duration. The
* rampTime is the time that it takes to reach over 99% of the way towards the value. This methods
* is similar to setTargetAtTime except the third argument is a time instead of a 'timeConstant'
* @param {number} value The value to ramp to.
* @param {Time} time When the ramp should start.
* @param {Time} rampTime the time that it takes the
* value to ramp from it's current value
* @returns {Tone.Param} this
* @example
* //exponentially ramp to the value 2 over 4 seconds.
* signal.exponentialRampTo(2, 4);
*/
Tone.Param.prototype.exponentialAppraochValueAtTime = function(value, time, rampTime){
var timeConstant = Math.log(this.toSeconds(rampTime)+1)/Math.log(200);
time = this.toSeconds(time);
Tone.isPast(time);
return this.setTargetAtTime(value, time, timeConstant);
};
2014-06-14 23:11:37 +00:00
/**
* Start exponentially approaching the target value at the given time with
* a rate having the given time constant.
* @param {number} value
* @param {Time} startTime
* @param {number} timeConstant
* @returns {Tone.Param} this
2014-06-14 23:11:37 +00:00
*/
Tone.Param.prototype.setTargetAtTime = function(value, startTime, timeConstant){
value = this._fromUnits(value);
2015-03-21 22:10:03 +00:00
// The value will never be able to approach without timeConstant > 0.
if (timeConstant <= 0){
throw new Error("timeConstant must be greater than 0");
}
this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
/**
* Sets an array of arbitrary parameter values starting at the given time
* for the given duration.
*
* @param {Array} values
* @param {Time} startTime
* @param {Time} duration
2017-10-26 04:51:02 +00:00
* @param {NormalRange} [scaling=1] If the values in the curve should be scaled by some value
* @returns {Tone.Param} this
2014-06-14 23:11:37 +00:00
*/
2017-10-26 04:51:02 +00:00
Tone.Param.prototype.setValueCurveAtTime = function (values, startTime, duration, scaling) {
scaling = Tone.defaultArg(scaling, 1);
duration = this.toSeconds(duration);
startTime = this.toSeconds(startTime);
2017-10-26 04:51:02 +00:00
this.setValueAtTime(values[0] * scaling, startTime);
var segTime = duration / (values.length - 1);
for (var i = 1; i < values.length; i++){
2017-10-26 04:51:02 +00:00
this._param.linearRampToValueAtTime(this._fromUnits(values[i] * scaling), startTime + i * segTime);
2014-06-14 23:11:37 +00:00
}
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
/**
* Cancels all scheduled parameter changes with times greater than or
2014-06-14 23:11:37 +00:00
* equal to startTime.
*
2015-06-14 00:20:36 +00:00
* @param {Time} startTime
* @returns {Tone.Param} this
2014-06-14 23:11:37 +00:00
*/
Tone.Param.prototype.cancelScheduledValues = function(startTime){
this._param.cancelScheduledValues(this.toSeconds(startTime));
2015-02-02 03:56:33 +00:00
return this;
2014-06-14 23:11:37 +00:00
};
/**
* This is similar to [cancelScheduledValues](#cancelScheduledValues) except
* it holds the automated value at cancelTime until the next automated event.
* @param {Time} cancelTime
* @returns {Tone.Param} this
*/
Tone.Param.prototype.cancelAndHoldAtTime = function(cancelTime){
2017-10-26 04:51:02 +00:00
this._param.cancelAndHoldAtTime(this.toSeconds(cancelTime));
return this;
};
/**
* Ramps to the given value over the duration of the rampTime.
* Automatically selects the best ramp type (exponential or linear)
* depending on the `units` of the signal
*
* @param {number} value
* @param {Time} rampTime The time that it takes the
* value to ramp from it's current value
* @param {Time} [startTime=now] When the ramp should start.
* @returns {Tone.Param} this
2015-02-27 18:40:35 +00:00
* @example
* //ramp to the value either linearly or exponentially
2015-06-14 01:54:20 +00:00
* //depending on the "units" value of the signal
* signal.rampTo(0, 10);
* @example
* //schedule it to ramp starting at a specific time
* signal.rampTo(0, 10, 5)
*/
Tone.Param.prototype.rampTo = function(value, rampTime, startTime){
rampTime = Tone.defaultArg(rampTime, 0.1);
2017-03-01 20:46:21 +00:00
if (this.units === Tone.Type.Frequency || this.units === Tone.Type.BPM || this.units === Tone.Type.Decibels){
this.exponentialRampTo(value, rampTime, startTime);
} else {
this.linearRampTo(value, rampTime, startTime);
}
return this;
};
/**
* Clean up
* @returns {Tone.Param} this
*/
Tone.Param.prototype.dispose = function(){
Tone.AudioNode.prototype.dispose.call(this);
this._param = null;
2015-02-02 03:56:33 +00:00
return this;
};
return Tone.Param;
});