xxxxxxxxxx
151
//Based on https://www.earlevel.com/main/2013/06/03/envelope-generators-adsr-code/
const [screen_width, screen_height] = [1024, 512];
const [env_idle, env_attack, env_decay, env_sustain, env_release] = [0,1,2,3,4];
let drawing = false;
let timestep = 0;
let lastY = 0;
SCALE_FACTOR = Math.pow(2, 31);//12;
MAX = SCALE_FACTOR - 1;
function setup() {
createCanvas(screen_width, screen_height);
background(220); //grey
sampRate = 4096;
setAttackRate(sampRate); // .1 second
setDecayRate(02 * sampRate);
setReleaseRate(5 * sampRate);
setSustainLevel(0.6);
//attackCoef = Math.round(attackCoef * SCALE_FACTOR);
//attackBase = Math.round(attackBase * SCALE_FACTOR);
state = 0;
console.log({ attackRate, decayRate, releaseRate, attackCoef, decayCoef, releaseCoef, targetRatioA, targetRatioDR, attackBase, decayBase, releaseBase });
console.log('attackCoef', attackCoef);
console.log('attackBase', attackBase);
}
function mousePressed() { //gate start
if (!drawing) background(220);
drawing = true;
state = env_attack;
}
function mouseReleased() { //gate end
state = env_release;
}
function keyTyped() {
if (key == 't' && mouseIsPressed) {
state = env_attack;
}
}
function draw() {
if (!drawing) return;
for (i = timestep; i < (timestep + 4); i++) {
for (j = 0; j < 32; j++) {
process();
}
expo = output / SCALE_FACTOR;
stroke(0); //black line
strokeWeight(1);
line(i - 1, (1 - lastY) * screen_height, i, (1 - expo) * screen_height);
lastY = expo;
}
timestep = i;
if (timestep >= screen_width) { //stop drawing when we hit the right edge
timestep = 0;
drawing = false;
expo = 0;
}
}
let state;
let output = 0;
let attackRate, decayRate, releaseRate
let attackCoef, decayCoef, releaseCoef;
let sustainLevel;
let targetRatioA = .05, targetRatioDR = .001;
let attackBase, decayBase, releaseBase;
let count = 0;
function process() {
count++;
switch (state) {
case env_idle:
break;
case env_attack:
output = (output * attackCoef) / SCALE_FACTOR + attackBase;
output = Math.round(output);
//output = (output * attackCoef) + attackBase;
if (output >= MAX) {
console.log({ count });
count = 0;
output = MAX;
state = env_decay;
}
break;
case env_decay:
//output = (output * decayCoef) + decayBase;
output = (output * Math.round(decayCoef * SCALE_FACTOR)) / SCALE_FACTOR + Math.round(decayBase * SCALE_FACTOR);
output = Math.round(output);
if (output <= sustainLevel * SCALE_FACTOR) {
output = sustainLevel * SCALE_FACTOR;
state = env_sustain;
}
break;
case env_sustain:
break;
case env_release:
output = releaseBase + output * releaseCoef;
if (output <= 0.0) {
output = 0.0;
state = env_idle;
}
}
return output;
}
function setAttackRate(rate) {
attackRate = rate;
attackCoef = calcCoef(rate, targetRatioA);
attackCoef = Math.round(attackCoef * SCALE_FACTOR);
attackBase = ((MAX + Math.round(targetRatioA * SCALE_FACTOR)) * (MAX - attackCoef)) / SCALE_FACTOR;
attackBase = Math.round(attackBase);
}
function setDecayRate(rate) {
decayRate = rate;
decayCoef = calcCoef(rate, targetRatioDR);
decayBase = (sustainLevel - targetRatioDR) * (1.0 - decayCoef);
}
function setReleaseRate(rate) {
releaseRate = rate;
releaseCoef = calcCoef(rate, targetRatioDR);
releaseBase = -targetRatioDR * (1.0 - releaseCoef);
}
function calcCoef(rate, targetRatio) {
return (rate <= 0) ? 0.0 : Math.exp(-Math.log((1.0 + targetRatio) / targetRatio) / rate);
//return (rate <= 0) ? 0.0 : Math.pow((1.0 + targetRatio) / targetRatio, -1 / rate);
}
function setSustainLevel(level) {
sustainLevel = level;
decayBase = (sustainLevel - targetRatioDR) * (1.0 - decayCoef);
}