Tone.js/Tone/signal/TimelineSignal.js
2015-10-21 12:12:17 -04:00

321 lines
No EOL
9.9 KiB
JavaScript

define(["Tone/core/Tone", "Tone/signal/Signal", "Tone/core/Timeline"], function (Tone) {
"use strict";
/**
* @class A signal which adds the method getValueAtTime.
* Code and inspiration from https://github.com/jsantell/web-audio-automation-timeline
*/
Tone.TimelineSignal = function(){
var options = this.optionsObject(arguments, ["value", "units"], Tone.Signal.defaults);
//constructors
Tone.Signal.apply(this, options);
options.param = this._param;
Tone.Param.call(this, options);
/**
* The scheduled events
* @type {Tone.Timeline}
* @private
*/
this._events = new Tone.Timeline();
/**
* The initial scheduled value
* @type {Number}
* @private
*/
this._initial = this._fromUnits(this._param.value);
};
Tone.extend(Tone.TimelineSignal, Tone.Param);
/**
* The event types of a schedulable signal.
* @enum {String}
*/
Tone.TimelineSignal.Type = {
Linear : "linear",
Exponential : "exponential",
Target : "target",
Set : "set"
};
/**
* The current value of the signal.
* @memberOf Tone.TimelineSignal#
* @type {Number}
* @name value
*/
Object.defineProperty(Tone.TimelineSignal.prototype, "value", {
get : function(){
return this._toUnits(this._param.value);
},
set : function(value){
var convertedVal = this._fromUnits(value);
this._initial = convertedVal;
this._param.value = convertedVal;
}
});
///////////////////////////////////////////////////////////////////////////
// SCHEDULING
///////////////////////////////////////////////////////////////////////////
/**
* Schedules a parameter value change at the given time.
* @param {*} value The value to set the signal.
* @param {Time} time The time when the change should occur.
* @returns {Tone.TimelineSignal} this
* @example
* //set the frequency to "G4" in exactly 1 second from now.
* freq.setValueAtTime("G4", "+1");
*/
Tone.TimelineSignal.prototype.setValueAtTime = function (value, startTime) {
value = this._fromUnits(value);
startTime = this.toSeconds(startTime);
this._events.addEvent({
"type" : Tone.TimelineSignal.Type.Set,
"value" : value,
"time" : startTime
});
//invoke the original event
this._param.setValueAtTime(value, startTime);
return this;
};
/**
* Schedules a linear continuous change in parameter value from the
* previous scheduled parameter value to the given value.
*
* @param {number} value
* @param {Time} endTime
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.linearRampToValueAtTime = function (value, endTime) {
value = this._fromUnits(value);
endTime = this.toSeconds(endTime);
this._events.addEvent({
"type" : Tone.TimelineSignal.Type.Linear,
"value" : value,
"time" : endTime
});
this._param.linearRampToValueAtTime(value, endTime);
return this;
};
/**
* Schedules an exponential continuous change in parameter value from
* the previous scheduled parameter value to the given value.
*
* @param {number} value
* @param {Time} endTime
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.exponentialRampToValueAtTime = function (value, endTime) {
value = this._fromUnits(value);
value = Math.max(this._minOutput, value);
endTime = this.toSeconds(endTime);
this._events.addEvent({
"type" : Tone.TimelineSignal.Type.Exponential,
"value" : value,
"time" : endTime
});
this._param.exponentialRampToValueAtTime(value, endTime);
return this;
};
/**
* 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.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
value = this._fromUnits(value);
value = Math.max(this._minOutput, value);
startTime = this.toSeconds(startTime);
this._events.addEvent({
"type" : Tone.TimelineSignal.Type.Target,
"value" : value,
"time" : startTime,
"constant" : timeConstant
});
this._param.setTargetAtTime(value, startTime, timeConstant);
return this;
};
/**
* Cancels all scheduled parameter changes with times greater than or
* equal to startTime.
*
* @param {Time} startTime
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.cancelScheduledValues = function (after) {
this._events.clear(after);
this._param.cancelScheduledValues(this.toSeconds(after));
return this;
};
/**
* Sets the computed value at the given time. This provides
* a point from which a linear or exponential curve
* can be scheduled after.
* @param {Time} time When to set the ramp point
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.setRampPoint = function (time) {
time = this.toSeconds(time);
//get the value at the given time
var val = this.getValueAtTime(time);
this.setValueAtTime(val, time);
return this;
};
/**
* Do a linear ramp to the given value between the start and finish times.
* @param {Number} value The value to ramp to.
* @param {Time} start The beginning anchor point to do the linear ramp
* @param {Time} finish The ending anchor point by which the value of
* the signal will equal the given value.
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.linearRampToValueBetween = function (value, start, finish) {
this.setRampPoint(start);
this.linearRampToValueAtTime(value, finish);
return this;
};
/**
* Do a exponential ramp to the given value between the start and finish times.
* @param {Number} value The value to ramp to.
* @param {Time} start The beginning anchor point to do the exponential ramp
* @param {Time} finish The ending anchor point by which the value of
* the signal will equal the given value.
* @returns {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.exponentialRampToValueBetween = function (value, start, finish) {
this.setRampPoint(start);
this.exponentialRampToValueAtTime(value, finish);
return this;
};
///////////////////////////////////////////////////////////////////////////
// GETTING SCHEDULED VALUES
///////////////////////////////////////////////////////////////////////////
/**
* Returns the value before or equal to the given time
* @param {Number} time The time to query
* @return {Object} The event at or before the given time.
* @private
*/
Tone.TimelineSignal.prototype._searchBefore = function(time){
return this._events.getEvent(time);
};
/**
* The event after the given time
* @param {Number} time The time to query.
* @return {Object} The next event after the given time
* @private
*/
Tone.TimelineSignal.prototype._searchAfter = function(time){
return this._events.getEventAfter(time);
};
/**
* Get the scheduled value at the given time.
* @param {Number} time The time in seconds.
* @return {Number} The scheduled value at the given time.
*/
Tone.TimelineSignal.prototype.getValueAtTime = function(time){
var after = this._searchAfter(time);
var before = this._searchBefore(time);
//if it was set by
if (before === null){
return this._initial;
} else if (before.type === Tone.TimelineSignal.Type.Target){
var previous = this._searchBefore(before.time - 0.0001);
var previouVal;
if (previous === null){
previouVal = this._initial;
} else {
previouVal = previous.value;
}
return this._exponentialApproach(before.time, previouVal, before.value, before.constant, time);
} else if (after === null){
return before.value;
} else if (after.type === Tone.TimelineSignal.Type.Linear){
return this._linearInterpolate(before.time, before.value, after.time, after.value, time);
} else if (after.type === Tone.TimelineSignal.Type.Exponential){
return this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
} else {
return before.value;
}
return this._param.getValueAtTime(time);
};
/**
* When signals connect to other signals or AudioParams,
* they take over the output value of that signal or AudioParam.
* For all other nodes, the behavior is the same as a default <code>connect</code>.
*
* @override
* @param {AudioParam|AudioNode|Tone.Signal|Tone} node
* @param {number} [outputNumber=0] The output number to connect from.
* @param {number} [inputNumber=0] The input number to connect to.
* @returns {Tone.TimelineSignal} this
* @method
*/
Tone.TimelineSignal.prototype.connect = Tone.SignalBase.prototype.connect;
///////////////////////////////////////////////////////////////////////////
// AUTOMATION CURVE CALCULATIONS
// MIT License, copyright (c) 2014 Jordan Santell
///////////////////////////////////////////////////////////////////////////
/**
* Calculates the the value along the curve produced by setTargetAtTime
* @private
*/
Tone.TimelineSignal.prototype._exponentialApproach = function (t0, v0, v1, timeConstant, t) {
return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
};
/**
* Calculates the the value along the curve produced by linearRampToValueAtTime
* @private
*/
Tone.TimelineSignal.prototype._linearInterpolate = function (t0, v0, t1, v1, t) {
return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
};
/**
* Calculates the the value along the curve produced by exponentialRampToValueAtTime
* @private
*/
Tone.TimelineSignal.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
v0 = Math.max(this._minOutput, v0);
return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
};
/**
* Clean up.
* @return {Tone.TimelineSignal} this
*/
Tone.TimelineSignal.prototype.dispose = function(){
Tone.Signal.prototype.dispose.call(this);
Tone.Param.prototype.dispose.call(this);
this._events.dispose();
this._events = null;
};
return Tone.TimelineSignal;
});