Files
harmonizer_plugin/Source/adsr.cpp
2025-10-25 14:49:02 -04:00

148 lines
3.2 KiB
C++

/*
==============================================================================
adsr.cpp
Created: 25 Oct 2025 2:09:32pm
Author: mickl
==============================================================================
*/
#include "adsr.h"
#include "math.h"
#ifndef M_E
#define M_E 2.71828182845904523536
#endif
void Adsr::Init(float sample_rate, int blockSize)
{
sample_rate_ = sample_rate / blockSize;
attackShape_ = -1.f;
attackTarget_ = 0.0f;
attackTime_ = -1.f;
decayTime_ = -1.f;
releaseTime_ = -1.f;
sus_level_ = 0.7f;
x_ = 0.0f;
gate_ = false;
mode_ = ADSR_SEG_IDLE;
SetTime(ADSR_SEG_ATTACK, 0.1f);
SetTime(ADSR_SEG_DECAY, 0.1f);
SetTime(ADSR_SEG_RELEASE, 0.1f);
}
void Adsr::Retrigger(bool hard)
{
mode_ = ADSR_SEG_ATTACK;
if (hard)
x_ = 0.f;
}
void Adsr::SetTime(int seg, float time)
{
switch (seg)
{
case ADSR_SEG_ATTACK: SetAttackTime(time, 0.0f); break;
case ADSR_SEG_DECAY:
{
SetTimeConstant(time, decayTime_, decayD0_);
}
break;
case ADSR_SEG_RELEASE:
{
SetTimeConstant(time, releaseTime_, releaseD0_);
}
break;
default: return;
}
}
void Adsr::SetAttackTime(float timeInS, float shape)
{
if ((timeInS != attackTime_) || (shape != attackShape_))
{
attackTime_ = timeInS;
attackShape_ = shape;
if (timeInS > 0.f)
{
float x = shape;
float target = 9.f * powf(x, 10.f) + 0.3f * x + 1.01f;
attackTarget_ = target;
float logTarget = logf(1.f - (1.f / target)); // -1 for decay
attackD0_ = 1.f - expf(logTarget / (timeInS * sample_rate_));
}
else
attackD0_ = 1.f; // instant change
}
}
void Adsr::SetDecayTime(float timeInS)
{
SetTimeConstant(timeInS, decayTime_, decayD0_);
}
void Adsr::SetReleaseTime(float timeInS)
{
SetTimeConstant(timeInS, releaseTime_, releaseD0_);
}
void Adsr::SetTimeConstant(float timeInS, float& time, float& coeff)
{
if (timeInS != time)
{
time = timeInS;
if (time > 0.f)
{
const float target = logf(1. / M_E);
coeff = 1.f - expf(target / (time * sample_rate_));
}
else
coeff = 1.f; // instant change
}
}
float Adsr::Process(bool gate)
{
float out = 0.0f;
if (gate && !gate_) // rising edge
mode_ = ADSR_SEG_ATTACK;
else if (!gate && gate_) // falling edge
mode_ = ADSR_SEG_RELEASE;
gate_ = gate;
float D0(attackD0_);
if (mode_ == ADSR_SEG_DECAY)
D0 = decayD0_;
else if (mode_ == ADSR_SEG_RELEASE)
D0 = releaseD0_;
float target = mode_ == ADSR_SEG_DECAY ? sus_level_ : -0.01f;
switch (mode_)
{
case ADSR_SEG_IDLE: out = 0.0f; break;
case ADSR_SEG_ATTACK:
x_ += D0 * (attackTarget_ - x_);
out = x_;
if (out > 1.f)
{
x_ = out = 1.f;
mode_ = ADSR_SEG_DECAY;
}
break;
case ADSR_SEG_DECAY:
case ADSR_SEG_RELEASE:
x_ += D0 * (target - x_);
out = x_;
if (out < 0.0f)
{
x_ = out = 0.f;
mode_ = ADSR_SEG_IDLE;
}
default: break;
}
return out;
}