|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- #pragma once
- #include <dsp/common.hpp>
-
-
- namespace rack {
- namespace dsp {
-
-
- /** 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;
- }
-
- /** Sets the cutoff angular frequency in radians.
- */
- void setCutoff(T r) {
- c = 2.f / r;
- }
- /** Sets the cutoff frequency.
- `f` is the ratio between the cutoff frequency and sample rate, i.e. f = f_c / f_s
- */
- void setCutoffFreq(T f) {
- setCutoff(2.f * M_PI * f);
- }
- void process(T x) {
- T y = (x + xstate[0] - ystate[0] * (1 - c)) / (1 + c);
- xstate[0] = x;
- ystate[0] = y;
- }
- T lowpass() {
- return ystate[0];
- }
- T highpass() {
- return xstate[0] - ystate[0];
- }
- };
-
- typedef TRCFilter<> RCFilter;
-
-
- /** 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;
- }
-
- void setLambda(T lambda) {
- this->lambda = lambda;
- }
-
- /** Sets \f$ \lambda = 1 / \tau \f$. */
- void setTau(T tau) {
- this->lambda = 1 / tau;
- }
-
- 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;
- }
-
- 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;
- }
-
- void setLambda(T lambda) {
- this->lambda = lambda;
- }
-
- void setTau(T tau) {
- this->lambda = 1 / tau;
- }
-
- T process(T deltaTime, T in) {
- T y = out + (in - out) * lambda * deltaTime;
- out = simd::fmax(y, in);
- return out;
- }
- /** Use the return value of process() instead. */
- DEPRECATED T peak() {
- return out;
- }
- /** 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;
-
-
- /** Limits the derivative of the output by a rise and fall speed, in units/s. */
- template <typename T = float>
- struct TSlewLimiter {
- T out = 0.f;
- T rise = 0.f;
- T fall = 0.f;
-
- 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 T process(T in) {
- return process(1.f, in);
- }
- };
-
- typedef TSlewLimiter<> SlewLimiter;
-
-
- /** Behaves like ExponentialFilter but with different lambas when the RHS of the ODE is positive or negative. */
- template <typename T = float>
- struct TExponentialSlewLimiter {
- T out = 0.f;
- T riseLambda = 0.f;
- T fallLambda = 0.f;
-
- void reset() {
- out = 0.f;
- }
-
- void setRiseFall(T riseLambda, T fallLambda) {
- this->riseLambda = riseLambda;
- this->fallLambda = fallLambda;
- }
- void setRiseFallTau(T riseTau, T fallTau) {
- this->riseLambda = 1 / riseTau;
- this->fallLambda = 1 / fallTau;
- }
- 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 T process(T in) {
- return process(1.f, in);
- }
- };
-
- typedef TExponentialSlewLimiter<> ExponentialSlewLimiter;
-
-
- /** Digital IIR filter processor.
- https://en.wikipedia.org/wiki/Infinite_impulse_response
- */
- template <int B_ORDER, int A_ORDER, typename T = float>
- struct IIRFilter {
- /** transfer function numerator coefficients: b_0, b_1, etc.
- */
- T b[B_ORDER] = {};
- /** transfer function denominator coefficients: a_1, a_2, etc.
- a_0 is fixed to 1 and omitted from the `a` array, so its indices are shifted down by 1.
- */
- T a[A_ORDER - 1] = {};
- /** input state
- x[0] = x_{n-1}
- x[1] = x_{n-2}
- etc.
- */
- T x[B_ORDER - 1];
- /** output state */
- T y[A_ORDER - 1];
-
- IIRFilter() {
- reset();
- }
-
- void reset() {
- for (int i = 1; i < B_ORDER; i++) {
- x[i - 1] = 0.f;
- }
- for (int i = 1; i < A_ORDER; i++) {
- y[i - 1] = 0.f;
- }
- }
-
- void setCoefficients(const T* b, const T* a) {
- for (int i = 0; i < B_ORDER; i++) {
- this->b[i] = b[i];
- }
- for (int i = 1; i < A_ORDER; i++) {
- this->a[i - 1] = a[i - 1];
- }
- }
-
- T process(T in) {
- T out = 0.f;
- // Add x state
- if (0 < B_ORDER) {
- out = b[0] * in;
- }
- for (int i = 1; i < B_ORDER; i++) {
- out += b[i] * x[i - 1];
- }
- // Subtract y state
- for (int i = 1; i < A_ORDER; i++) {
- out -= a[i - 1] * y[i - 1];
- }
- // Shift x state
- for (int i = B_ORDER - 1; i >= 2; i--) {
- x[i - 1] = x[i - 2];
- }
- x[0] = in;
- // Shift y state
- for (int i = A_ORDER - 1; i >= 2; i--) {
- y[i - 1] = y[i - 2];
- }
- y[0] = out;
- return out;
- }
-
- /** Computes the complex transfer function $H(s)$ at a particular frequency
- s: normalized angular frequency equal to $2 \pi f / f_{sr}$ ($\pi$ is the Nyquist frequency)
- */
- std::complex<T> getTransferFunction(T s) {
- // Compute sum(a_k z^-k) / sum(b_k z^-k) where z = e^(i s)
- std::complex<T> bSum(b[0], 0);
- std::complex<T> aSum(1, 0);
- for (int i = 1; i < std::max(B_ORDER, A_ORDER); i++) {
- T p = -i * s;
- std::complex<T> z(simd::cos(p), simd::sin(p));
- if (i < B_ORDER)
- bSum += b[i] * z;
- if (i < A_ORDER)
- aSum += a[i - 1] * z;
- }
- return bSum / aSum;
- }
-
- T getFrequencyResponse(T f) {
- // T hReal, hImag;
- // getTransferFunction(2 * M_PI * f, &hReal, &hImag);
- // return simd::hypot(hReal, hImag);
- return simd::abs(getTransferFunction(2 * M_PI * f));
- }
-
- T getFrequencyPhase(T f) {
- return simd::arg(getTransferFunction(2 * M_PI * f));
- }
- };
-
-
- template <typename T = float>
- struct TBiquadFilter : IIRFilter<3, 3, T> {
- enum Type {
- LOWPASS_1POLE,
- HIGHPASS_1POLE,
- LOWPASS,
- HIGHPASS,
- LOWSHELF,
- HIGHSHELF,
- BANDPASS,
- PEAK,
- NOTCH,
- NUM_TYPES
- };
-
- TBiquadFilter() {
- setParameters(LOWPASS, 0.f, 0.f, 1.f);
- }
-
- /** Calculates and sets the biquad transfer function coefficients.
- f: normalized frequency (cutoff frequency / sample rate), must be less than 0.5
- Q: quality factor
- V: gain
- */
- void setParameters(Type type, float f, float Q, float V) {
- float K = std::tan(M_PI * f);
- switch (type) {
- case LOWPASS_1POLE: {
- this->a[0] = -std::exp(-2.f * M_PI * f);
- this->a[1] = 0.f;
- this->b[0] = 1.f + this->a[0];
- this->b[1] = 0.f;
- this->b[2] = 0.f;
- } break;
-
- case HIGHPASS_1POLE: {
- this->a[0] = std::exp(-2.f * M_PI * (0.5f - f));
- this->a[1] = 0.f;
- this->b[0] = 1.f - this->a[0];
- this->b[1] = 0.f;
- this->b[2] = 0.f;
- } break;
-
- case LOWPASS: {
- float norm = 1.f / (1.f + K / Q + K * K);
- this->b[0] = K * K * norm;
- this->b[1] = 2.f * this->b[0];
- this->b[2] = this->b[0];
- this->a[0] = 2.f * (K * K - 1.f) * norm;
- this->a[1] = (1.f - K / Q + K * K) * norm;
- } break;
-
- case HIGHPASS: {
- float norm = 1.f / (1.f + K / Q + K * K);
- this->b[0] = norm;
- this->b[1] = -2.f * this->b[0];
- this->b[2] = this->b[0];
- this->a[0] = 2.f * (K * K - 1.f) * norm;
- this->a[1] = (1.f - K / Q + K * K) * norm;
-
- } break;
-
- case LOWSHELF: {
- float sqrtV = std::sqrt(V);
- if (V >= 1.f) {
- float norm = 1.f / (1.f + M_SQRT2 * K + K * K);
- this->b[0] = (1.f + M_SQRT2 * sqrtV * K + V * K * K) * norm;
- this->b[1] = 2.f * (V * K * K - 1.f) * norm;
- this->b[2] = (1.f - M_SQRT2 * sqrtV * K + V * K * K) * norm;
- this->a[0] = 2.f * (K * K - 1.f) * norm;
- this->a[1] = (1.f - M_SQRT2 * K + K * K) * norm;
- }
- else {
- float norm = 1.f / (1.f + M_SQRT2 / sqrtV * K + K * K / V);
- this->b[0] = (1.f + M_SQRT2 * K + K * K) * norm;
- this->b[1] = 2.f * (K * K - 1) * norm;
- this->b[2] = (1.f - M_SQRT2 * K + K * K) * norm;
- this->a[0] = 2.f * (K * K / V - 1.f) * norm;
- this->a[1] = (1.f - M_SQRT2 / sqrtV * K + K * K / V) * norm;
- }
- } break;
-
- case HIGHSHELF: {
- float sqrtV = std::sqrt(V);
- if (V >= 1.f) {
- float norm = 1.f / (1.f + M_SQRT2 * K + K * K);
- this->b[0] = (V + M_SQRT2 * sqrtV * K + K * K) * norm;
- this->b[1] = 2.f * (K * K - V) * norm;
- this->b[2] = (V - M_SQRT2 * sqrtV * K + K * K) * norm;
- this->a[0] = 2.f * (K * K - 1.f) * norm;
- this->a[1] = (1.f - M_SQRT2 * K + K * K) * norm;
- }
- else {
- float norm = 1.f / (1.f / V + M_SQRT2 / sqrtV * K + K * K);
- this->b[0] = (1.f + M_SQRT2 * K + K * K) * norm;
- this->b[1] = 2.f * (K * K - 1.f) * norm;
- this->b[2] = (1.f - M_SQRT2 * K + K * K) * norm;
- this->a[0] = 2.f * (K * K - 1.f / V) * norm;
- this->a[1] = (1.f / V - M_SQRT2 / sqrtV * K + K * K) * norm;
- }
- } break;
-
- case BANDPASS: {
- float norm = 1.f / (1.f + K / Q + K * K);
- this->b[0] = K / Q * norm;
- this->b[1] = 0.f;
- this->b[2] = -this->b[0];
- this->a[0] = 2.f * (K * K - 1.f) * norm;
- this->a[1] = (1.f - K / Q + K * K) * norm;
- } break;
-
- case PEAK: {
- if (V >= 1.f) {
- float norm = 1.f / (1.f + K / Q + K * K);
- this->b[0] = (1.f + K / Q * V + K * K) * norm;
- this->b[1] = 2.f * (K * K - 1.f) * norm;
- this->b[2] = (1.f - K / Q * V + K * K) * norm;
- this->a[0] = this->b[1];
- this->a[1] = (1.f - K / Q + K * K) * norm;
- }
- else {
- float norm = 1.f / (1.f + K / Q / V + K * K);
- this->b[0] = (1.f + K / Q + K * K) * norm;
- this->b[1] = 2.f * (K * K - 1.f) * norm;
- this->b[2] = (1.f - K / Q + K * K) * norm;
- this->a[0] = this->b[1];
- this->a[1] = (1.f - K / Q / V + K * K) * norm;
- }
- } break;
-
- case NOTCH: {
- float norm = 1.f / (1.f + K / Q + K * K);
- this->b[0] = (1.f + K * K) * norm;
- this->b[1] = 2.f * (K * K - 1.f) * norm;
- this->b[2] = this->b[0];
- this->a[0] = this->b[1];
- this->a[1] = (1.f - K / Q + K * K) * norm;
- } break;
-
- default: break;
- }
- }
- };
-
- typedef TBiquadFilter<> BiquadFilter;
-
-
- } // namespace dsp
- } // namespace rack
|