mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 16:48:00 +00:00
follower uses a single smoothing value
This commit is contained in:
parent
b1ae21930f
commit
eb003d8b6c
3 changed files with 87 additions and 131 deletions
|
@ -7,6 +7,7 @@
|
|||
* Tone.Meter uses RMS instead of peak (thanks [@Idicious](https://github.com/Idicious))
|
||||
* Tone.Sampler supports polyphonic syntax (thanks [@zfan40](https://github.com/zfan40))
|
||||
* Building files with [webpack](https://webpack.js.org/)
|
||||
* Follower uses a single "smoothing" value instead of separate attacks and releases
|
||||
|
||||
|
||||
### r12
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract", "Tone/signal/Multiply",
|
||||
"Tone/signal/Signal", "Tone/signal/WaveShaper", "Tone/type/Type", "Tone/core/Delay", "Tone/core/AudioNode"], function(Tone){
|
||||
define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract",
|
||||
"Tone/signal/Signal", "Tone/type/Type", "Tone/core/Delay", "Tone/core/AudioNode"], function(Tone){
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @class Tone.Follower is a crude envelope follower which will follow
|
||||
* the amplitude of an incoming signal.
|
||||
* Take care with small (< 0.02) attack or decay values
|
||||
* as follower has some ripple which is exaggerated
|
||||
* at these values. Read more about envelope followers (also known
|
||||
* the amplitude of an incoming signal. Read more about envelope followers (also known
|
||||
* as envelope detectors) on [Wikipedia](https://en.wikipedia.org/wiki/Envelope_detector).
|
||||
*
|
||||
* @constructor
|
||||
* @extends {Tone.AudioNode}
|
||||
* @param {Time|Object} [attack] The rate at which the follower rises.
|
||||
* @param {Time=} release The rate at which the folower falls.
|
||||
* @param {Time} [smoothing=0.05] The rate of change of the follower.
|
||||
* @example
|
||||
* var follower = new Tone.Follower(0.2, 0.4);
|
||||
* var follower = new Tone.Follower(0.3);
|
||||
*/
|
||||
Tone.Follower = function(){
|
||||
|
||||
var options = Tone.defaults(arguments, ["attack", "release"], Tone.Follower);
|
||||
var options = Tone.defaults(arguments, ["smoothing"], Tone.Follower);
|
||||
Tone.AudioNode.call(this);
|
||||
this.createInsOuts(1, 1);
|
||||
|
||||
|
@ -38,13 +34,7 @@ define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract", "Tone/signa
|
|||
this._filter = this.context.createBiquadFilter();
|
||||
this._filter.type = "lowpass";
|
||||
this._filter.frequency.value = 0;
|
||||
this._filter.Q.value = -100;
|
||||
|
||||
/**
|
||||
* @type {WaveShaperNode}
|
||||
* @private
|
||||
*/
|
||||
this._frequencyValues = new Tone.WaveShaper();
|
||||
this._filter.Q.value = 0;
|
||||
|
||||
/**
|
||||
* @type {Tone.Subtract}
|
||||
|
@ -53,39 +43,25 @@ define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract", "Tone/signa
|
|||
this._sub = new Tone.Subtract();
|
||||
|
||||
/**
|
||||
* delay node to compare change over time
|
||||
* @type {Tone.Delay}
|
||||
* @private
|
||||
*/
|
||||
this._delay = new Tone.Delay(this.blockTime);
|
||||
|
||||
/**
|
||||
* this keeps it far from 0, even for very small differences
|
||||
* @type {Tone.Multiply}
|
||||
* the smoothing value
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
this._mult = new Tone.Multiply(10000);
|
||||
this._smoothing = options.smoothing;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._attack = options.attack;
|
||||
this.input.connect(this._delay, this._sub);
|
||||
this.input.connect(this._sub, 0, 1);
|
||||
this._sub.chain(this._filter, this._abs, this.output);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this._release = options.release;
|
||||
|
||||
//the smoothed signal to get the values
|
||||
this.input.chain(this._abs, this._filter, this.output);
|
||||
//the difference path
|
||||
this._abs.connect(this._sub, 0, 1);
|
||||
this._filter.chain(this._delay, this._sub);
|
||||
//threshold the difference and use the thresh to set the frequency
|
||||
this._sub.chain(this._mult, this._frequencyValues, this._filter.frequency);
|
||||
//set the attack and release values in the table
|
||||
this._setAttackRelease(this._attack, this._release);
|
||||
//set the smoothing initially
|
||||
this.smoothing = options.smoothing;
|
||||
};
|
||||
|
||||
Tone.extend(Tone.Follower, Tone.AudioNode);
|
||||
|
@ -95,60 +71,22 @@ define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract", "Tone/signa
|
|||
* @type {Object}
|
||||
*/
|
||||
Tone.Follower.defaults = {
|
||||
"attack" : 0.05,
|
||||
"release" : 0.5
|
||||
};
|
||||
|
||||
/**
|
||||
* sets the attack and release times in the wave shaper
|
||||
* @param {Time} attack
|
||||
* @param {Time} release
|
||||
* @private
|
||||
*/
|
||||
Tone.Follower.prototype._setAttackRelease = function(attack, release){
|
||||
var minTime = this.blockTime;
|
||||
attack = Tone.Time(attack).toFrequency();
|
||||
release = Tone.Time(release).toFrequency();
|
||||
attack = Math.max(attack, minTime);
|
||||
release = Math.max(release, minTime);
|
||||
this._frequencyValues.setMap(function(val){
|
||||
if (val <= 0){
|
||||
return attack;
|
||||
} else {
|
||||
return release;
|
||||
}
|
||||
});
|
||||
"smoothing" : 0.05,
|
||||
};
|
||||
|
||||
/**
|
||||
* The attack time.
|
||||
* @memberOf Tone.Follower#
|
||||
* @type {Time}
|
||||
* @name attack
|
||||
* @name smoothing
|
||||
*/
|
||||
Object.defineProperty(Tone.Follower.prototype, "attack", {
|
||||
Object.defineProperty(Tone.Follower.prototype, "smoothing", {
|
||||
get : function(){
|
||||
return this._attack;
|
||||
return this._smoothing;
|
||||
},
|
||||
set : function(attack){
|
||||
this._attack = attack;
|
||||
this._setAttackRelease(this._attack, this._release);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The release time.
|
||||
* @memberOf Tone.Follower#
|
||||
* @type {Time}
|
||||
* @name release
|
||||
*/
|
||||
Object.defineProperty(Tone.Follower.prototype, "release", {
|
||||
get : function(){
|
||||
return this._release;
|
||||
},
|
||||
set : function(release){
|
||||
this._release = release;
|
||||
this._setAttackRelease(this._attack, this._release);
|
||||
set : function(smoothing){
|
||||
this._smoothing = smoothing;
|
||||
this._filter.frequency.value = Tone.Time(smoothing).toFrequency() * 0.5;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -167,17 +105,12 @@ define(["Tone/core/Tone", "Tone/signal/Abs", "Tone/signal/Subtract", "Tone/signa
|
|||
Tone.AudioNode.prototype.dispose.call(this);
|
||||
this._filter.disconnect();
|
||||
this._filter = null;
|
||||
this._frequencyValues.disconnect();
|
||||
this._frequencyValues = null;
|
||||
this._delay.dispose();
|
||||
this._delay = null;
|
||||
this._sub.disconnect();
|
||||
this._sub = null;
|
||||
this._abs.dispose();
|
||||
this._abs = null;
|
||||
this._mult.dispose();
|
||||
this._mult = null;
|
||||
this._curve = null;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,69 +17,91 @@ function(Follower, Basic, Offline, Test, Signal, PassAudio, PassAudioStereo){
|
|||
it("handles getter/setter as Object", function(){
|
||||
var foll = new Follower();
|
||||
var values = {
|
||||
"attack" : 0.2,
|
||||
"release" : 0.4
|
||||
"smoothing" : 0.2,
|
||||
};
|
||||
foll.set(values);
|
||||
expect(foll.get()).to.have.keys(["attack", "release"]);
|
||||
expect(foll.get().attack).to.be.closeTo(0.2, 0.001);
|
||||
expect(foll.get().release).to.be.closeTo(0.4, 0.001);
|
||||
expect(foll.get()).to.have.keys(["smoothing"]);
|
||||
expect(foll.get().smoothing).to.be.closeTo(0.2, 0.001);
|
||||
foll.dispose();
|
||||
});
|
||||
|
||||
it("can be constructed with an object", function(){
|
||||
var follower = new Follower({
|
||||
"attack" : 0.5,
|
||||
"release" : 0.3
|
||||
"smoothing" : 0.5,
|
||||
});
|
||||
expect(follower.attack).to.be.closeTo(0.5, 0.001);
|
||||
expect(follower.release).to.be.closeTo(0.3, 0.001);
|
||||
expect(follower.smoothing).to.be.closeTo(0.5, 0.001);
|
||||
follower.dispose();
|
||||
});
|
||||
|
||||
it("smoothes the incoming signal", function(){
|
||||
it("smoothes the incoming signal at 0.1", function(){
|
||||
return Offline(function(){
|
||||
var foll = new Follower(0.1, 0.5).toMaster();
|
||||
var foll = new Follower(0.1).toMaster();
|
||||
var sig = new Signal(0);
|
||||
sig.connect(foll);
|
||||
sig.setValueAtTime(1, 0.1);
|
||||
}, 0.1).then(function(buffer){
|
||||
expect(buffer.max()).to.lessThan(1);
|
||||
sig.setValueAtTime(0, 0.3);
|
||||
}, 0.41).then(function(buffer){
|
||||
expect(buffer.getValueAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.1)).to.be.closeTo(0.0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.15)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.2)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.3)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.35)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.4)).to.be.closeTo(0, 0.2);
|
||||
});
|
||||
});
|
||||
|
||||
/*it("smoothing follows attack and release", function(done){
|
||||
var foll, sig;
|
||||
var offline = new Offline(1);
|
||||
offline.before(function(dest){
|
||||
foll = new Follower(0.1, 0.5);
|
||||
sig = new Signal(0);
|
||||
it("smoothes the incoming signal at 0.05", function(){
|
||||
return Offline(function(){
|
||||
var foll = new Follower(0.05).toMaster();
|
||||
var sig = new Signal(0);
|
||||
sig.connect(foll);
|
||||
foll.connect(dest);
|
||||
sig.setValueAtTime(1, 0);
|
||||
sig.setValueAtTime(0, 0.4);
|
||||
sig.setValueAtTime(1, 0.1);
|
||||
sig.setValueAtTime(0, 0.3);
|
||||
}, 0.41).then(function(buffer){
|
||||
expect(buffer.getValueAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.1)).to.be.closeTo(0.0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.125)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.15)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.3)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.325)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.35)).to.be.closeTo(0, 0.2);
|
||||
});
|
||||
var delta = 0.15;
|
||||
offline.test(function(sample, time){
|
||||
if (time < 0.1){
|
||||
expect(sample).to.be.within(0 - delta, 1 + delta);
|
||||
} else if (time < 0.4){
|
||||
expect(sample).to.be.closeTo(1, delta);
|
||||
} else if (time < 0.65){
|
||||
expect(sample).to.be.above(0);
|
||||
} else if (time < 0.9){
|
||||
expect(sample).to.be.within(0 - delta, 1 + delta);
|
||||
} else {
|
||||
expect(sample).to.be.closeTo(0, delta);
|
||||
}
|
||||
});
|
||||
offline.after(function(){
|
||||
foll.dispose();
|
||||
sig.dispose();
|
||||
done();
|
||||
|
||||
it("smoothes the incoming signal at 0.2", function(){
|
||||
return Offline(function(){
|
||||
var foll = new Follower(0.2).toMaster();
|
||||
var sig = new Signal(0);
|
||||
sig.connect(foll);
|
||||
sig.setValueAtTime(1, 0.1);
|
||||
sig.setValueAtTime(0, 0.3);
|
||||
}, 0.51).then(function(buffer){
|
||||
expect(buffer.getValueAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.1)).to.be.closeTo(0.0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.2)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.3)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.4)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.5)).to.be.closeTo(0, 0.2);
|
||||
});
|
||||
});
|
||||
|
||||
it("smoothes the incoming signal at 0.5", function(){
|
||||
return Offline(function(){
|
||||
var foll = new Follower(0.5).toMaster();
|
||||
var sig = new Signal(0);
|
||||
sig.connect(foll);
|
||||
sig.setValueAtTime(1, 0.1);
|
||||
sig.setValueAtTime(0, 0.6);
|
||||
}, 1.11).then(function(buffer){
|
||||
expect(buffer.getValueAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.1)).to.be.closeTo(0.0, 0.01);
|
||||
expect(buffer.getValueAtTime(0.35)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(0.6)).to.be.closeTo(1, 0.2);
|
||||
expect(buffer.getValueAtTime(0.85)).to.be.closeTo(0.5, 0.2);
|
||||
expect(buffer.getValueAtTime(1.1)).to.be.closeTo(0, 0.2);
|
||||
});
|
||||
});
|
||||
offline.run();
|
||||
});*/
|
||||
|
||||
it("passes the incoming signal through", function(){
|
||||
var follower;
|
||||
|
|
Loading…
Reference in a new issue