|
|
|
@@ -6,128 +6,168 @@ namespace rack { |
|
|
|
namespace dsp { |
|
|
|
|
|
|
|
|
|
|
|
struct RCFilter { |
|
|
|
float c = 0.f; |
|
|
|
float xstate[1] = {}; |
|
|
|
float ystate[1] = {}; |
|
|
|
/** The simplest possible analog filter using an Euler solver. |
|
|
|
https://en.wikipedia.org/wiki/RC_circuit |
|
|
|
Use two RC filters in series for a bandpass filter. |
|
|
|
*/ |
|
|
|
template <typename T = float> |
|
|
|
struct TRCFilter { |
|
|
|
T c = 0.f; |
|
|
|
T xstate[1]; |
|
|
|
T ystate[1]; |
|
|
|
|
|
|
|
TRCFilter() { |
|
|
|
reset(); |
|
|
|
} |
|
|
|
|
|
|
|
void reset() { |
|
|
|
xstate[0] = 0.f; |
|
|
|
ystate[0] = 0.f; |
|
|
|
} |
|
|
|
|
|
|
|
// `r` is the ratio between the cutoff frequency and sample rate, i.e. r = f_c / f_s |
|
|
|
void setCutoff(float r) { |
|
|
|
/** Sets the cutoff frequency. |
|
|
|
`r` is the ratio between the cutoff frequency and sample rate, i.e. r = f_c / f_s |
|
|
|
*/ |
|
|
|
void setCutoff(T r) { |
|
|
|
c = 2.f / r; |
|
|
|
} |
|
|
|
void process(float x) { |
|
|
|
float y = (x + xstate[0] - ystate[0] * (1 - c)) / (1 + c); |
|
|
|
void process(T x) { |
|
|
|
T y = (x + xstate[0] - ystate[0] * (1 - c)) / (1 + c); |
|
|
|
xstate[0] = x; |
|
|
|
ystate[0] = y; |
|
|
|
} |
|
|
|
float lowpass() { |
|
|
|
T lowpass() { |
|
|
|
return ystate[0]; |
|
|
|
} |
|
|
|
float highpass() { |
|
|
|
T highpass() { |
|
|
|
return xstate[0] - ystate[0]; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
typedef TRCFilter<> RCFilter; |
|
|
|
|
|
|
|
|
|
|
|
struct PeakFilter { |
|
|
|
float state = 0.f; |
|
|
|
float c = 0.f; |
|
|
|
/** Applies exponential smoothing to a signal with the ODE |
|
|
|
\f$ \frac{dy}{dt} = x \lambda \f$. |
|
|
|
*/ |
|
|
|
template <typename T = float> |
|
|
|
struct TExponentialFilter { |
|
|
|
T out = 0.f; |
|
|
|
T lambda = 0.f; |
|
|
|
|
|
|
|
void reset() { |
|
|
|
out = 0.f; |
|
|
|
} |
|
|
|
|
|
|
|
/** Rate is lambda / sampleRate */ |
|
|
|
void setRate(float r) { |
|
|
|
c = 1.f - r; |
|
|
|
void setLambda(T lambda) { |
|
|
|
this->lambda = lambda; |
|
|
|
} |
|
|
|
void process(float x) { |
|
|
|
if (x > state) |
|
|
|
state = x; |
|
|
|
state *= c; |
|
|
|
|
|
|
|
T process(T deltaTime, T in) { |
|
|
|
T y = out + (in - out) * lambda * deltaTime; |
|
|
|
// If no change was made between the old and new output, assume T granularity is too small and snap output to input |
|
|
|
out = simd::ifelse(out == y, in, y); |
|
|
|
return out; |
|
|
|
} |
|
|
|
float peak() { |
|
|
|
return state; |
|
|
|
|
|
|
|
DEPRECATED T process(T in) { |
|
|
|
return process(1.f, in); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
typedef TExponentialFilter<> ExponentialFilter; |
|
|
|
|
|
|
|
|
|
|
|
/** Like ExponentialFilter but jumps immediately to higher values. |
|
|
|
*/ |
|
|
|
template <typename T = float> |
|
|
|
struct TPeakFilter { |
|
|
|
T out = 0.f; |
|
|
|
T lambda = 0.f; |
|
|
|
|
|
|
|
void reset() { |
|
|
|
out = 0.f; |
|
|
|
} |
|
|
|
|
|
|
|
struct SlewLimiter { |
|
|
|
float rise = 0.f; |
|
|
|
float fall = 0.f; |
|
|
|
float out = NAN; |
|
|
|
|
|
|
|
float process(float deltaTime, float in) { |
|
|
|
if (std::isnan(out)) { |
|
|
|
out = in; |
|
|
|
} |
|
|
|
else if (out < in) { |
|
|
|
float y = out + rise * deltaTime; |
|
|
|
out = std::fmin(y, in); |
|
|
|
} |
|
|
|
else if (out > in) { |
|
|
|
float y = out - fall * deltaTime; |
|
|
|
out = std::fmax(y, in); |
|
|
|
} |
|
|
|
void setLambda(T lambda) { |
|
|
|
this->lambda = lambda; |
|
|
|
} |
|
|
|
|
|
|
|
T process(T deltaTime, T in) { |
|
|
|
T y = out + (in - out) * lambda * deltaTime; |
|
|
|
out = simd::fmax(y, in); |
|
|
|
return out; |
|
|
|
} |
|
|
|
DEPRECATED float process(float in) { |
|
|
|
return process(1.f, in); |
|
|
|
/** Use the return value of process() instead. */ |
|
|
|
DEPRECATED T peak() { |
|
|
|
return out; |
|
|
|
} |
|
|
|
DEPRECATED void setRiseFall(float rise, float fall) { |
|
|
|
this->rise = rise; |
|
|
|
this->fall = fall; |
|
|
|
/** Use setLambda() instead. */ |
|
|
|
DEPRECATED void setRate(T r) { |
|
|
|
lambda = 1.f - r; |
|
|
|
} |
|
|
|
DEPRECATED T process(T x) { |
|
|
|
return process(1.f, x); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
typedef TPeakFilter<> PeakFilter; |
|
|
|
|
|
|
|
|
|
|
|
template <typename T = float> |
|
|
|
struct TSlewLimiter { |
|
|
|
T out = 0.f; |
|
|
|
T rise = 0.f; |
|
|
|
T fall = 0.f; |
|
|
|
|
|
|
|
struct ExponentialSlewLimiter { |
|
|
|
float riseLambda = 0.f; |
|
|
|
float fallLambda = 0.f; |
|
|
|
float out = NAN; |
|
|
|
|
|
|
|
float process(float deltaTime, float in) { |
|
|
|
if (std::isnan(out)) { |
|
|
|
out = in; |
|
|
|
} |
|
|
|
else if (out < in) { |
|
|
|
float y = out + (in - out) * riseLambda * deltaTime; |
|
|
|
out = (out == y) ? in : y; |
|
|
|
} |
|
|
|
else if (out > in) { |
|
|
|
float y = out + (in - out) * fallLambda * deltaTime; |
|
|
|
out = (out == y) ? in : y; |
|
|
|
} |
|
|
|
void reset() { |
|
|
|
out = 0.f; |
|
|
|
} |
|
|
|
|
|
|
|
void setRiseFall(T rise, T fall) { |
|
|
|
this->rise = rise; |
|
|
|
this->fall = fall; |
|
|
|
} |
|
|
|
T process(T deltaTime, T in) { |
|
|
|
out = simd::clamp(in, out - fall * deltaTime, out + rise * deltaTime); |
|
|
|
return out; |
|
|
|
} |
|
|
|
DEPRECATED float process(float in) { |
|
|
|
DEPRECATED T process(T in) { |
|
|
|
return process(1.f, in); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
typedef TSlewLimiter<> SlewLimiter; |
|
|
|
|
|
|
|
/** Applies exponential smoothing to a signal with the ODE |
|
|
|
\f$ \frac{dy}{dt} = x \lambda \f$. |
|
|
|
*/ |
|
|
|
struct ExponentialFilter { |
|
|
|
float out = 0.f; |
|
|
|
float lambda = 0.f; |
|
|
|
|
|
|
|
template <typename T = float> |
|
|
|
struct TExponentialSlewLimiter { |
|
|
|
T out = 0.f; |
|
|
|
T riseLambda = 0.f; |
|
|
|
T fallLambda = 0.f; |
|
|
|
|
|
|
|
void reset() { |
|
|
|
out = 0.f; |
|
|
|
} |
|
|
|
|
|
|
|
float process(float deltaTime, float in) { |
|
|
|
float y = out + (in - out) * lambda * deltaTime; |
|
|
|
// If no change was made between the old and new output, assume float granularity is too small and snap output to input |
|
|
|
if (out == y) |
|
|
|
out = in; |
|
|
|
else |
|
|
|
out = y; |
|
|
|
void setRiseFall(T riseLambda, T fallLambda) { |
|
|
|
this->riseLambda = riseLambda; |
|
|
|
this->fallLambda = fallLambda; |
|
|
|
} |
|
|
|
T process(T deltaTime, T in) { |
|
|
|
T lambda = simd::ifelse(in > out, riseLambda, fallLambda); |
|
|
|
T y = out + (in - out) * lambda * deltaTime; |
|
|
|
// If the change from the old out to the new out is too small for floats, set `in` directly. |
|
|
|
out = simd::ifelse(out == y, in, y); |
|
|
|
return out; |
|
|
|
} |
|
|
|
|
|
|
|
DEPRECATED float process(float in) { |
|
|
|
DEPRECATED T process(T in) { |
|
|
|
return process(1.f, in); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
typedef TExponentialSlewLimiter<> ExponentialSlewLimiter; |
|
|
|
|
|
|
|
|
|
|
|
} // namespace dsp |
|
|
|
} // namespace rack |