Skip to content

Instantly share code, notes, and snippets.

@khrykin
Created January 19, 2020 13:44
Show Gist options
  • Save khrykin/7e543c3af4b90e788b1a942b88bd7c4b to your computer and use it in GitHub Desktop.
Save khrykin/7e543c3af4b90e788b1a942b88bd7c4b to your computer and use it in GitHub Desktop.
Logarithmic fader (envelope) in javascript
/**
* Fades values in and out logarithmically.
* @param {number} sampleRate - Number of samples per duration unit
* @param {number} duration - Duration of an envelope
* @param {number} unity - Unity approximation
*/
function Fader(sampleRate, duration, unity = 0.999) {
this._sampleDuration = duration / sampleRate;
let T = -duration / Math.log(1 - unity);
this._exp = Math.exp(this._sampleDuration / T);
this._numSamples = duration / this._sampleDuration;
this._isActive = false;
}
Fader.fadeIn = 0;
Fader.fadeOut = 1;
/**
* Starts an envelope with initial and target values.
* Note that initial value isn't included in the envelope.
*/
Fader.prototype.start = function(initialValue, targetValue) {
this._currentUnitySample = 0.0;
this._currentSampleIndex = 0;
this._fadeType = targetValue <= initialValue ? Fader.fadeOut : Fader.fadeIn;
this._isActive = true;
switch (this._fadeType) {
case Fader.fadeIn:
this._minValue = initialValue;
this._maxValue = targetValue;
this._getCurrentExponentSample = function() {
return this._currentUnitySample / this._exp + 1 - 1 / this._exp;
};
break;
case Fader.fadeOut:
this._minValue = targetValue;
this._maxValue = initialValue;
this._getCurrentExponentSample = function() {
return this._currentUnitySample / this._exp;
};
break;
}
this._currentUnitySample =
(initialValue - this._minValue) / (this._maxValue - this._minValue);
};
/**
* Returns the next unity sample (in the range of [0.0, 1.0] for this envelope.
*/
Fader.prototype.getNextUnitySample = function() {
let unitySample;
if (this._currentSampleIndex < this._numSamples - 1) {
unitySample = this._getCurrentExponentSample();
this._currentSampleIndex++;
} else {
switch (this._fadeType) {
case Fader.fadeIn:
unitySample = 1;
break;
case Fader.fadeOut:
unitySample = 0;
break;
}
this._isActive = false;
}
this._currentUnitySample = unitySample;
return unitySample;
};
/**
* Returns the next sample for this envelope.
*/
Fader.prototype.getNextSample = function() {
let unitySample = this.getNextUnitySample();
return (this._maxValue - this._minValue) * unitySample + this._minValue;
};
/**
* Returns the total number of samples for this envelope.
*/
Fader.prototype.getNumSamples = function() {
return this._numSamples;
};
/**
* Returns the duration of a single sample
*/
Fader.prototype.getSampleDuration = function() {
return this._sampleDuration;
};
/**
* Returns a boolean, indicating whether the target value is reached
*/
Fader.prototype.isActive = function() {
return this._isActive;
};
/* Usage */
let sampleRate = 10;
let duration = 0.5;
let fader = new Fader(sampleRate, duration);
/* Fade in */
fader.start(0.1, 0.5);
console.log("Fade in samples:");
for (let i = 0; i < fader.getNumSamples(); i++) {
console.log(fader.getNextSample());
}
/* Fade out */
fader.start(0.5, 0.3);
console.log("Fade out samples:");
for (let i = 0; i < fader.getNumSamples(); i++) {
console.log(fader.getNextSample());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment