mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 08:38:00 +00:00
moving automation logic to Param instead of AudioParam shim
This commit is contained in:
parent
b4c81f7115
commit
1104f3cfc0
2 changed files with 161 additions and 19 deletions
|
@ -1,4 +1,4 @@
|
|||
define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/AudioParam"], function(Tone){
|
||||
define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/core/Timeline"], function(Tone){
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -45,6 +45,13 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
*/
|
||||
this.overridden = false;
|
||||
|
||||
/**
|
||||
* The timeline which tracks all of the automations.
|
||||
* @type {Tone.Timeline}
|
||||
* @private
|
||||
*/
|
||||
this._events = new Tone.Timeline(1000);
|
||||
|
||||
if (!Tone.isUndef(options.value)){
|
||||
this.value = options.value;
|
||||
}
|
||||
|
@ -71,12 +78,12 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
*/
|
||||
Object.defineProperty(Tone.Param.prototype, "value", {
|
||||
get : function(){
|
||||
return this._toUnits(this._param.value);
|
||||
var now = this.now();
|
||||
return this._toUnits(this.getValueAtTime(now));
|
||||
},
|
||||
set : function(value){
|
||||
var convertedVal = this._fromUnits(value);
|
||||
this._param.cancelScheduledValues(0);
|
||||
this._param.value = convertedVal;
|
||||
this._initialValue = this._fromUnits(value);
|
||||
this.setValueAtTime(value, this.now());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -175,6 +182,18 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
*/
|
||||
Tone.Param.prototype._minOutput = 0.00001;
|
||||
|
||||
/**
|
||||
* The event types
|
||||
* @enum {String}
|
||||
* @private
|
||||
*/
|
||||
Tone.Param.AutomationType = {
|
||||
Linear : "linearRampToValueAtTime",
|
||||
Exponential : "exponentialRampToValueAtTime",
|
||||
Target : "setTargetAtTime",
|
||||
SetValue : "setValueAtTime"
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedules a parameter value change at the given time.
|
||||
* @param {*} value The value to set the signal.
|
||||
|
@ -186,8 +205,14 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
*/
|
||||
Tone.Param.prototype.setValueAtTime = function(value, time){
|
||||
time = this.toSeconds(time);
|
||||
value = this._fromUnits(value);
|
||||
Tone.isPast(time);
|
||||
this._param.setValueAtTime(this._fromUnits(value), time);
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.SetValue,
|
||||
"value" : value,
|
||||
"time" : time,
|
||||
});
|
||||
this._param.setValueAtTime(value, time);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -199,7 +224,32 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
*/
|
||||
Tone.Param.prototype.getValueAtTime = function(time){
|
||||
time = this.toSeconds(time);
|
||||
return this._fromUnits(this._param.getValueAtTime(time));
|
||||
var after = this._events.getAfter(time);
|
||||
var before = this._events.get(time);
|
||||
var initialValue = Tone.defaultArg(this._initialValue, this._param.defaultValue);
|
||||
var value = initialValue;
|
||||
//if it was set by
|
||||
if (before === null){
|
||||
value = initialValue;
|
||||
} else if (before.type === Tone.Param.AutomationType.Target){
|
||||
var previous = this._events.getBefore(before.time);
|
||||
var previousVal;
|
||||
if (previous === null){
|
||||
previousVal = initialValue;
|
||||
} else {
|
||||
previousVal = previous.value;
|
||||
}
|
||||
value = this._exponentialApproach(before.time, previousVal, before.value, before.constant, time);
|
||||
} else if (after === null){
|
||||
value = before.value;
|
||||
} else if (after.type === Tone.Param.AutomationType.Linear){
|
||||
value = this._linearInterpolate(before.time, before.value, after.time, after.value, time);
|
||||
} else if (after.type === Tone.Param.AutomationType.Exponential){
|
||||
value = this._exponentialInterpolate(before.time, before.value, after.time, after.value, time);
|
||||
} else {
|
||||
value = before.value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -217,7 +267,7 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
if (currentVal === 0){
|
||||
currentVal = this._minOutput;
|
||||
}
|
||||
this._param.setValueAtTime(currentVal, time);
|
||||
this.setValueAtTime(this._toUnits(currentVal), time);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -233,6 +283,11 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
value = this._fromUnits(value);
|
||||
endTime = this.toSeconds(endTime);
|
||||
Tone.isPast(endTime);
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.Linear,
|
||||
"value" : value,
|
||||
"time" : endTime,
|
||||
});
|
||||
this._param.linearRampToValueAtTime(value, endTime);
|
||||
return this;
|
||||
};
|
||||
|
@ -250,6 +305,12 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
value = Math.max(this._minOutput, value);
|
||||
endTime = this.toSeconds(endTime);
|
||||
Tone.isPast(endTime);
|
||||
//store the event
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.Exponential,
|
||||
"time" : endTime,
|
||||
"value" : value
|
||||
});
|
||||
this._param.exponentialRampToValueAtTime(value, endTime);
|
||||
return this;
|
||||
};
|
||||
|
@ -351,7 +412,14 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
if (timeConstant <= 0){
|
||||
throw new Error("timeConstant must be greater than 0");
|
||||
}
|
||||
this._param.setTargetAtTime(value, this.toSeconds(startTime), timeConstant);
|
||||
startTime = this.toSeconds(startTime)
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.Target,
|
||||
"value" : value,
|
||||
"time" : startTime,
|
||||
"constant" : timeConstant
|
||||
});
|
||||
this._param.setTargetAtTime(value, startTime, timeConstant);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -372,7 +440,7 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
this.setValueAtTime(values[0] * scaling, startTime);
|
||||
var segTime = duration / (values.length - 1);
|
||||
for (var i = 1; i < values.length; i++){
|
||||
this._param.linearRampToValueAtTime(this._fromUnits(values[i] * scaling), startTime + i * segTime);
|
||||
this.linearRampToValueAtTime(values[i] * scaling, startTime + i * segTime);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
@ -381,22 +449,75 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
* Cancels all scheduled parameter changes with times greater than or
|
||||
* equal to startTime.
|
||||
*
|
||||
* @param {Time} startTime
|
||||
* @param {Time} time
|
||||
* @returns {Tone.Param} this
|
||||
*/
|
||||
Tone.Param.prototype.cancelScheduledValues = function(startTime){
|
||||
this._param.cancelScheduledValues(this.toSeconds(startTime));
|
||||
Tone.Param.prototype.cancelScheduledValues = function(time){
|
||||
time = this.toSeconds(time);
|
||||
this._events.cancel(time);
|
||||
this._param.cancelScheduledValues(time);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is similar to [cancelScheduledValues](#cancelScheduledValues) except
|
||||
* it holds the automated value at cancelTime until the next automated event.
|
||||
* @param {Time} cancelTime
|
||||
* it holds the automated value at time until the next automated event.
|
||||
* @param {Time} time
|
||||
* @returns {Tone.Param} this
|
||||
*/
|
||||
Tone.Param.prototype.cancelAndHoldAtTime = function(cancelTime){
|
||||
this._param.cancelAndHoldAtTime(this.toSeconds(cancelTime));
|
||||
Tone.Param.prototype.cancelAndHoldAtTime = function(time){
|
||||
var valueAtTime = this.getValueAtTime(time);
|
||||
//if there is an event at the given time
|
||||
//and that even is not a "set"
|
||||
var before = this._events.get(time);
|
||||
var after = this._events.getAfter(time);
|
||||
if (before && before.time === time){
|
||||
//remove everything after
|
||||
if (after){
|
||||
this._events.cancel(after.time);
|
||||
} else {
|
||||
this._events.cancel(time + 1e-6);
|
||||
}
|
||||
} else if (after){
|
||||
//cancel the next event(s)
|
||||
this._events.cancel(after.time);
|
||||
if (!this._param.cancelAndHoldAtTime){
|
||||
this._param.cancelScheduledValues(time);
|
||||
}
|
||||
if (after.type === Tone.Param.AutomationType.Linear){
|
||||
if (!this._param.cancelAndHoldAtTime){
|
||||
this.linearRampToValueAtTime(valueAtTime, time);
|
||||
} else {
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.Linear,
|
||||
"value" : valueAtTime,
|
||||
"time" : time
|
||||
});
|
||||
}
|
||||
} else if (after.type === Tone.Param.AutomationType.Exponential){
|
||||
if (!this._param.cancelAndHoldAtTime){
|
||||
this.exponentialRampToValueAtTime(valueAtTime, time);
|
||||
} else {
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.Exponential,
|
||||
"value" : valueAtTime,
|
||||
"time" : time
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//set the value at the given time
|
||||
this._events.add({
|
||||
"type" : Tone.Param.AutomationType.SetValue,
|
||||
"value" : valueAtTime,
|
||||
"time" : time
|
||||
});
|
||||
if (this._param.cancelAndHoldAtTime){
|
||||
return this._param.cancelAndHoldAtTime(time);
|
||||
} else {
|
||||
return this._param.setValueAtTime(valueAtTime, time);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -428,6 +549,26 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
return this;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// AUTOMATION CURVE CALCULATIONS
|
||||
// MIT License, copyright (c) 2014 Jordan Santell
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculates the the value along the curve produced by setTargetAtTime
|
||||
Tone.Param.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
|
||||
Tone.Param.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
|
||||
Tone.Param.prototype._exponentialInterpolate = function (t0, v0, t1, v1, t) {
|
||||
return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Clean up
|
||||
* @returns {Tone.Param} this
|
||||
|
@ -435,6 +576,7 @@ define(["Tone/core/Tone", "Tone/type/Type", "Tone/core/AudioNode", "Tone/shim/Au
|
|||
Tone.Param.prototype.dispose = function(){
|
||||
Tone.AudioNode.prototype.dispose.call(this);
|
||||
this._param = null;
|
||||
this._events = null;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ define(["helper/Basic", "Test", "Tone/core/Param", "Tone/type/Type", "Tone/signa
|
|||
"units" : Tone.Type.Decibels,
|
||||
});
|
||||
param.value = -10;
|
||||
expect(param._param.value).to.be.closeTo(0.315, 0.01);
|
||||
expect(param.value).to.be.closeTo(-10, 0.01);
|
||||
param.dispose();
|
||||
});
|
||||
|
||||
|
@ -71,7 +71,7 @@ define(["helper/Basic", "Test", "Tone/core/Param", "Tone/type/Type", "Tone/signa
|
|||
"convert" : false
|
||||
});
|
||||
param.value = -10;
|
||||
expect(param._param.value).to.be.closeTo(-10, 0.001);
|
||||
expect(param.value).to.be.closeTo(-10, 0.001);
|
||||
param.dispose();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue