mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 16:48:00 +00:00
259 lines
8.1 KiB
JavaScript
259 lines
8.1 KiB
JavaScript
define(["Tone/core/Tone", "Tone/core/Timeline"], function (Tone) {
|
|
|
|
//initialize the events of the audio param
|
|
function initializeAudioParam(param){
|
|
if (!param._events){
|
|
param._events = new Tone.Timeline(1000);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// AUTOMATION CURVE CALCULATIONS
|
|
// MIT License, copyright (c) 2014 Jordan Santell
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Calculates the the value along the curve produced by setTargetAtTime
|
|
var 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
|
|
var 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
|
|
var exponentialInterpolate = function (t0, v0, t1, v1, t) {
|
|
return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
|
|
};
|
|
|
|
/**
|
|
* The event types
|
|
* @enum {String}
|
|
* @private
|
|
*/
|
|
var AutomationType = {
|
|
Linear : "linearRampToValueAtTime",
|
|
Exponential : "exponentialRampToValueAtTime",
|
|
Target : "setTargetAtTime",
|
|
SetValue : "setValueAtTime"
|
|
};
|
|
|
|
//only shim if needed
|
|
if (Tone.supported){
|
|
|
|
// overwrite getting the default value
|
|
Object.defineProperty(AudioParam.prototype, "value", {
|
|
get : function(){
|
|
var now = Tone.context.currentTime;
|
|
return this.getValueAtTime(now);
|
|
},
|
|
set : function(val){
|
|
this._initialValue = val;
|
|
//set the value
|
|
var now = Tone.context.currentTime;
|
|
this.setValueAtTime(val, now);
|
|
}
|
|
});
|
|
|
|
// defaultValue
|
|
if (!AudioParam.prototype.hasOwnProperty("defaultValue")){
|
|
Object.defineProperty(AudioParam.prototype, "defaultValue", {
|
|
get : function(){
|
|
return 1;
|
|
}
|
|
});
|
|
}
|
|
|
|
// minValue
|
|
if (!AudioParam.prototype.hasOwnProperty("minValue")){
|
|
Object.defineProperty(AudioParam.prototype, "minValue", {
|
|
get : function(){
|
|
return -3.4028235e38;
|
|
}
|
|
});
|
|
}
|
|
|
|
// maxValue
|
|
if (!AudioParam.prototype.hasOwnProperty("maxValue")){
|
|
Object.defineProperty(AudioParam.prototype, "maxValue", {
|
|
get : function(){
|
|
return 3.4028235e38;
|
|
}
|
|
});
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// SCHEDULING
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// wrap the basic methods
|
|
["setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime"].forEach(function(method){
|
|
var nativeMethodName = "_native_"+method;
|
|
if (!AudioParam.prototype[nativeMethodName]){
|
|
//make a copy of the original method prefixed _native_
|
|
AudioParam.prototype[nativeMethodName] = AudioParam.prototype[method];
|
|
AudioParam.prototype[method] = function(value, time){
|
|
//initialize the events array if it hasn't already
|
|
initializeAudioParam(this);
|
|
//check if the exponential ramp is starting from above 0
|
|
if (method === AutomationType.Exponential){
|
|
var before = this._events.get(time);
|
|
if (before && this.getValueAtTime(before.time) <= 0){
|
|
throw new Error("exponentialRampToValueAtTime must ramp from a value > 0");
|
|
}
|
|
}
|
|
//remember the events
|
|
this._events.add({
|
|
type : method,
|
|
time : time,
|
|
value : value
|
|
});
|
|
//invoke the native method
|
|
return AudioParam.prototype[nativeMethodName].call(this, value, time);
|
|
};
|
|
}
|
|
});
|
|
|
|
// setTargetAtTime
|
|
if (!AudioParam.prototype._native_setTargetAtTime){
|
|
AudioParam.prototype._native_setTargetAtTime = AudioParam.prototype.setTargetAtTime;
|
|
AudioParam.prototype.setTargetAtTime = function(value, time, timeConstant){
|
|
//initialize the events array if it hasn't already
|
|
initializeAudioParam(this);
|
|
this._events.add({
|
|
type : AutomationType.Target,
|
|
value : value,
|
|
time : time,
|
|
constant : timeConstant
|
|
});
|
|
return this._native_setTargetAtTime(value, time, timeConstant);
|
|
};
|
|
}
|
|
|
|
// setValueCurveAtTime
|
|
if (!AudioParam.prototype._native_setValueCurveAtTime){
|
|
AudioParam.prototype._native_setValueCurveAtTime = AudioParam.prototype.setValueCurveAtTime;
|
|
AudioParam.prototype.setValueCurveAtTime = function(values, time, duration){
|
|
//initialize the events array if it hasn't already
|
|
initializeAudioParam(this);
|
|
//set the initial value
|
|
this._events.add({
|
|
type : AutomationType.SetValue,
|
|
value : values[0],
|
|
time : time,
|
|
});
|
|
var segTime = duration / (values.length - 1);
|
|
//set the rest as linear ramps
|
|
for (var i = 1; i < values.length; i++){
|
|
this._events.add({
|
|
type : AutomationType.Linear,
|
|
value : values[i],
|
|
time : time + i * segTime,
|
|
});
|
|
}
|
|
//call the native method
|
|
return this._native_setValueCurveAtTime(values, time, duration);
|
|
};
|
|
}
|
|
|
|
// cancelScheduledValues
|
|
if (!AudioParam.prototype._native_cancelScheduledValues){
|
|
AudioParam.prototype._native_cancelScheduledValues = AudioParam.prototype.cancelScheduledValues;
|
|
AudioParam.prototype.cancelScheduledValues = function(time){
|
|
initializeAudioParam(this);
|
|
this._events.cancel(time);
|
|
return this._native_cancelScheduledValues(time);
|
|
};
|
|
}
|
|
|
|
// cancelAndHoldAtTime
|
|
if (!AudioParam.prototype._native_cancelAndHoldAtTime){
|
|
AudioParam.prototype._native_cancelAndHoldAtTime = AudioParam.prototype.cancelAndHoldAtTime;
|
|
AudioParam.prototype.cancelAndHoldAtTime = function(time){
|
|
initializeAudioParam(this);
|
|
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._native_cancelAndHoldAtTime){
|
|
this._native_cancelScheduledValues(time);
|
|
}
|
|
if (after.type === AutomationType.Linear){
|
|
if (!this._native_cancelAndHoldAtTime){
|
|
this.linearRampToValueAtTime(valueAtTime, time);
|
|
} else {
|
|
this._events.add({
|
|
type : AutomationType.Linear,
|
|
value : valueAtTime,
|
|
time : time
|
|
});
|
|
}
|
|
} else if (after.type === AutomationType.Exponential){
|
|
if (!this._native_cancelAndHoldAtTime){
|
|
this.exponentialRampToValueAtTime(valueAtTime, time);
|
|
} else {
|
|
this._events.add({
|
|
type : AutomationType.Exponential,
|
|
value : valueAtTime,
|
|
time : time
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//set the value at the given time
|
|
this._events.add({
|
|
type : AutomationType.SetValue,
|
|
value : valueAtTime,
|
|
time : time
|
|
});
|
|
if (this._native_cancelAndHoldAtTime){
|
|
return this._native_cancelAndHoldAtTime(time);
|
|
} else {
|
|
return this._native_setValueAtTime(valueAtTime, time);
|
|
}
|
|
};
|
|
}
|
|
|
|
// getValueAtTime
|
|
AudioParam.prototype.getValueAtTime = function(time){
|
|
initializeAudioParam(this);
|
|
var after = this._events.getAfter(time);
|
|
var before = this._events.get(time);
|
|
var initialValue = Tone.defaultArg(this._initialValue, this.defaultValue);
|
|
//if it was set by
|
|
if (before === null){
|
|
return initialValue;
|
|
} else if (before.type === AutomationType.Target){
|
|
var previous = this._events.getBefore(before.time);
|
|
var previousVal;
|
|
if (previous === null){
|
|
previousVal = initialValue;
|
|
} else {
|
|
previousVal = previous.value;
|
|
}
|
|
return exponentialApproach(before.time, previousVal, before.value, before.constant, time);
|
|
} else if (after === null){
|
|
return before.value;
|
|
} else if (after.type === AutomationType.Linear){
|
|
return linearInterpolate(before.time, before.value, after.time, after.value, time);
|
|
} else if (after.type === AutomationType.Exponential){
|
|
return exponentialInterpolate(before.time, before.value, after.time, after.value, time);
|
|
} else {
|
|
return before.value;
|
|
}
|
|
};
|
|
}
|
|
});
|