A classical ADSR triggered via MIDI Input:
//-----------------------------------------------------------
getDescription = function () {
return "ADSR";
}
//-----------------------------------------------------------
makeParameter = function (name) {
var value = 0;
var listeners = [];
return {
name: name,
units: "",
stepCount: 0,
toString: function (val) { return val; },
fromString: function (str) { return parseFloat (str); },
addListener: function (obj) { listeners.push (obj); },
getValue: function () { return value; },
getPlain: function () { return value; },
setValue: function (val) {
value = val;
for (i = 0; i < listeners.length; ++i)
listeners[i] (this);
},
};
}
//-----------------------------------------------------------
makeBoolParameter = function (name) {
param = makeParameter (name);
param.stepCount = 1;
param.toString = function (value) { return value >= 0.5 ? "On" : "Off"; };
param.fromString = function (str) { if (str == "On") return 1.; if (str == "Off") return 0.; };
return param;
};
//-----------------------------------------------------------
makeRangeParameter = function (name, min, max, units, precision) {
if (precision == undefined)
precision = 2;
var precision = Math.pow (10, precision);
param = makeParameter (name);
param.units = units;
param.toString = function (value) {
value = normalizedToPlain (min, max, value);
value = Math.round (value * precision) / precision;
return value + (this.units ? " " + this.units : "");
};
param.fromString = function (str) {
var v = parseFloat (str);
if (!isNaN (v))
return plainToNormalized (min, max, v);
};
param.getPlain = function () { return normalizedToPlain (min, max, this.getValue ()); };
return param;
};
//-----------------------------------------------------------
makeADSR = function () {
const Phase = { Attack: 1, Decay: 2, Hold: 3, Release: 4 };
var attack = 0;
var decay = 0;
var sustain = 0;
var release = 0;
var phase = Phase.Attack;
var value = 0;
var time = -1;
var doHold = 0;
var doRetrigger = 0;
obj = {
setAttack: function (newValue) { attack = newValue;},
setDecay: function (newValue) { decay = newValue; },
setSustain: function (newValue) { sustain = newValue; },
setRelease: function (newValue) { release = newValue; },
setRetrigger: function (newValue) { doRetrigger = newValue > 0.5; },
trigger: function () {
if (doRetrigger)
{
time = 0;
}
else
{
time = value * attack;
}
phase = Phase.Attack;
},
setHold: function (hold) { doHold = hold; },
release: function () {
doHold = 0;
if (phase == Phase.Hold)
{
phase = Phase.Release;
time = 0;
}
},
process: function (numSamples) {
if (time < 0)
return 0;
time += numSamples;
if (phase == Phase.Attack)
{
if (time > attack)
{
phase = Phase.Decay;
value = 1.;
time -= attack;
}
else
{
value = time / attack;
}
}
if (phase == Phase.Decay)
{
if (time > decay)
{
phase = Phase.Hold;
time -= decay;
value = sustain;
}
else
{
value = 1. - (1. - sustain) * time / decay;
}
}
if (phase == Phase.Hold)
{
if (doHold == 0)
{
phase = Phase.Release;
}
}
if (phase == Phase.Release)
{
if (time > release)
{
phase = Phase.Attack;
time = -1;
value = 0;
}
else
{
value = sustain * (1 - (time / release));
}
}
return value;
}
};
return obj;
};
const AttackIndex = 1;
const DecayIndex = 2;
const SustainIndex = 3;
const ReleaseIndex = 4;
const VelocityIndex = 5;
const RetriggerIndex = 6;
parameter = [];
parameter[AttackIndex] = makeRangeParameter ("Attack", 0, 3000, "ms", 0);
parameter[DecayIndex] = makeRangeParameter ("Decay", 0, 3000, "ms", 0);
parameter[SustainIndex] = makeRangeParameter ("Sustain", 0, 100, "%", 0);
parameter[ReleaseIndex] = makeRangeParameter ("Release", 0, 3000, "ms", 0);
parameter[VelocityIndex] = makeRangeParameter ("Velocity", 0, 100, "%", 0);
parameter[RetriggerIndex] = makeBoolParameter ("Retrigger");
adsr = makeADSR ();
sr = getSampleRate () / 1000;
parameter[AttackIndex].addListener (function (param) { adsr.setAttack (param.getPlain () * sr); });
parameter[DecayIndex].addListener (function (param) { adsr.setDecay (param.getPlain () * sr); });
parameter[SustainIndex].addListener (function (param) { adsr.setSustain (param.getValue ()); });
parameter[ReleaseIndex].addListener (function (param) { adsr.setRelease (param.getPlain () * sr); });
parameter[RetriggerIndex].addListener (function (param) { adsr.setRetrigger (param.getPlain ()); });
key = {noteID: -1, velocity: 0};
//-----------------------------------------------------------
processModulation = function (inputValue, numSamples) {
return adsr.process (numSamples) * key.velocity;
}
pressedNote = -1;
//-----------------------------------------------------------
onNoteOnEvent = function (channel, pitch, velocity, tuning, noteID) {
if (key.noteID == -1)
{
key.noteID = noteID;
velAmount = parameter[VelocityIndex].getValue ();
key.velocity = 1. + (velocity * velAmount) - 1. * velAmount;
adsr.trigger ();
adsr.setHold (1);
}
}
//-----------------------------------------------------------
onNoteOffEvent = function (channel, pitch, velocity, tuning, noteID) {
if (key.noteID == noteID)
{
key.noteID = -1;
adsr.release ();
}
}
//-----------------------------------------------------------
onParamChange = function (paramIndex, newValue) {
if (parameter[paramIndex])
parameter[paramIndex].setValue (newValue);
}
//-----------------------------------------------------------
paramValueToString = function (paramIndex, paramValue) {
if (parameter[paramIndex])
return parameter[paramIndex].toString (paramValue);
}
//-----------------------------------------------------------
stringToParamValue = function (paramIndex, string) {
if (parameter[paramIndex])
return parameter[paramIndex].fromString (string);
}
//-----------------------------------------------------------
getParamTitle = function (paramIndex) {
if (parameter[paramIndex])
return parameter[paramIndex].name;
}
//-----------------------------------------------------------
getParamStepCount = function (paramIndex) {
if (parameter[paramIndex])
return parameter[paramIndex].stepCount;
}