moving automation logic to Param instead of AudioParam shim

This commit is contained in:
Yotam Mann 2017-12-29 20:05:57 -05:00
parent b4c81f7115
commit 1104f3cfc0
2 changed files with 161 additions and 19 deletions

View file

@ -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;
};

View file

@ -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();
});