@thi.ng/dsp
Version:
Composable signal generators, oscillators, filters, FFT, spectrum, windowing & related DSP utils
158 lines (157 loc) • 3.5 kB
JavaScript
import { clamp01 } from "@thi.ng/math/interval";
import { add } from "./add.js";
import { AGen } from "./agen.js";
import { curve } from "./curve.js";
var EnvPhase = /* @__PURE__ */ ((EnvPhase2) => {
EnvPhase2[EnvPhase2["ATTACK"] = 0] = "ATTACK";
EnvPhase2[EnvPhase2["DECAY"] = 1] = "DECAY";
EnvPhase2[EnvPhase2["SUSTAIN"] = 2] = "SUSTAIN";
EnvPhase2[EnvPhase2["RELEASE"] = 3] = "RELEASE";
EnvPhase2[EnvPhase2["IDLE"] = 4] = "IDLE";
return EnvPhase2;
})(EnvPhase || {});
const adsr = (opts) => new ADSR(opts);
class ADSR extends AGen {
_phase;
_curve;
_atime;
_dtime;
_rtime;
_acurve;
_dcurve;
_sustain;
_speriod;
_gain;
constructor(opts) {
super(0);
opts = {
a: 0,
d: 0,
s: 1,
r: 0,
acurve: 0.1,
dcurve: 1e-3,
slen: Infinity,
gain: 1,
...opts
};
this.setAttack(opts.a);
this.setDecay(opts.d);
this.setRelease(opts.r);
this.setSustain(opts.s, opts.slen);
this.setCurveA(opts.acurve);
this.setCurveD(opts.dcurve);
this.setGain(opts.gain);
this.reset();
}
copy() {
return new ADSR({
a: this._atime,
d: this._dtime,
s: this._sustain,
r: this._rtime,
acurve: this._acurve,
dcurve: this._dcurve,
gain: this._gain,
slen: this._speriod
});
}
reset() {
this._phase = 0 /* ATTACK */;
this._curve = curve(0, 1, this._atime + 1, this._acurve, true);
this._val = 0;
return this;
}
release() {
if (this._phase < 3 /* RELEASE */) {
this._phase = 3 /* RELEASE */;
this._curve = curve(
this._sustain,
0,
this._rtime + 1,
this._dcurve,
true
);
}
}
isSustained() {
return this._phase === 2 /* SUSTAIN */;
}
isDone() {
return this._phase === 4 /* IDLE */;
}
next() {
let v;
switch (this._phase) {
case 4 /* IDLE */:
return 0;
case 0 /* ATTACK */:
v = this._curve.next();
if (v >= 1) {
v = 1;
this._phase = 1 /* DECAY */;
this._curve = curve(
1,
this._sustain,
this._dtime + 1,
this._dcurve,
true
);
}
break;
case 1 /* DECAY */:
v = this._curve.next();
if (v <= this._sustain) {
v = this._sustain;
this._phase = 2 /* SUSTAIN */;
this._curve = add(1, 1);
}
break;
case 2 /* SUSTAIN */:
if (this._curve.next() >= this._speriod) {
this.release();
}
return this._val;
case 3 /* RELEASE */:
v = this._curve.next();
if (v < 0) {
v = 0;
this._phase = 4 /* IDLE */;
}
}
return this._val = v * this._gain;
}
setAttack(steps) {
this._atime = Math.max(steps, 0);
}
setDecay(steps) {
this._dtime = Math.max(steps, 0);
}
setRelease(steps) {
this._rtime = Math.max(steps, 0);
}
/**
* Sets sustain level & duration. If the latter is omitted, the
* current value will be retained.
*
* @param level -
* @param duration -
*/
setSustain(level, duration) {
this._sustain = clamp01(level);
duration !== void 0 && (this._speriod = duration);
}
setCurveA(ratio) {
this._acurve = Math.max(ratio, 1e-9);
}
setCurveD(ratio) {
this._dcurve = Math.max(ratio, 1e-9);
}
setGain(gain) {
this._gain = gain;
}
}
export {
ADSR,
adsr
};