#include "fl/time_alpha.h" #include "fl/math_macros.h" /// Tracks a smoothed peak with attack, decay, and output-inertia time-constants. class MaxFadeTracker { public: /// @param attackTimeSec τ₁: how quickly to rise toward a new peak. /// @param decayTimeSec τ₂: how quickly to decay to 1/e of value. /// @param outputTimeSec τ₃: how quickly the returned value follows currentLevel_. /// @param sampleRate audio sample rate (e.g. 44100 or 48000). MaxFadeTracker(float attackTimeSec, float decayTimeSec, float outputTimeSec, float sampleRate) : attackRate_(1.0f / attackTimeSec) , decayRate_(1.0f / decayTimeSec) , outputRate_(1.0f / outputTimeSec) , sampleRate_(sampleRate) , currentLevel_(0.0f) , smoothedOutput_(0.0f) {} void setAttackTime(float t){ attackRate_ = 1.0f/t; } void setDecayTime (float t){ decayRate_ = 1.0f/t; } void setOutputTime(float t){ outputRate_ = 1.0f/t; } /// Process one 512-sample block; returns [0…1] with inertia. float operator()(const int16_t* samples, size_t length) { assert(length == 512); // 1) block peak float peak = 0.0f; for (size_t i = 0; i < length; ++i) { float v = ABS(samples[i]) * (1.0f/32768.0f); peak = MAX(peak, v); } // 2) time delta float dt = static_cast(length) / sampleRate_; // 3) update currentLevel_ with attack/decay if (peak > currentLevel_) { float riseFactor = 1.0f - exp(-attackRate_ * dt); currentLevel_ += (peak - currentLevel_) * riseFactor; } else { float decayFactor = exp(-decayRate_ * dt); currentLevel_ *= decayFactor; } // 4) output inertia: smooth smoothedOutput_ → currentLevel_ float outFactor = 1.0f - exp(-outputRate_ * dt); smoothedOutput_ += (currentLevel_ - smoothedOutput_) * outFactor; return smoothedOutput_; } private: float attackRate_; // = 1/τ₁ float decayRate_; // = 1/τ₂ float outputRate_; // = 1/τ₃ float sampleRate_; float currentLevel_; // instantaneous peak with attack/decay float smoothedOutput_; // returned value with inertia };