#include "DSPMath.hpp" namespace dsp { /** * @brief Clip signal at bottom by value * @param in Sample input * @param clip Clipping value * @return Clipped sample */ float clipl(float in, float clip) { if (in < clip) return clip; else return in; } /** * @brief Clip signal at top by value * @param in Sample input * @param clip Clipping value * @return Clipped sample */ float cliph(float in, float clip) { if (in > clip) return clip; else return in; } /** * @brief Wrap input number between -PI..PI * @param n Input number * @return Wrapped value */ float wrapTWOPI(float n) { float b = 1.f / TWOPI * n; return (b - lroundf(b)) * TWOPI; } /** * @brief Get PLL increment depending on frequency * @param frq Frequency * @return PLL increment */ float getPhaseIncrement(float frq) { return TWOPI * frq / engineGetSampleRate(); } /** * @brief Actual BLIT core computation * @param N Harmonics * @param phase Current phase value * @return */ float BLITcore(float N, float phase) { float a = wrapTWOPI((clipl(N - 1, 0.f) + 0.5f) * phase); float x = fastSin(a) * 1.f / fastSin(0.5f * phase); return (x - 1.f) * 2.f; } /** * @brief BLIT generator based on current phase * @param N Harmonics * @param phase Current phase of PLL * @return */ float BLIT(float N, float phase) { if (phase == 0.f) return 1.f; else return BLITcore(N, phase); } /** * @brief Add value to integrator * @param x Input * @param Fn * @return */ float Integrator::add(float x, float Fn) { value = (x - value) * (d * Fn) + value; return value; } /** * @brief Filter function for DC block * @param x Input sample * @return Filtered sample */ double DCBlocker::filter(double x) { double y = x - xm1 + r * ym1; xm1 = x; ym1 = y; return y; } DCBlocker::DCBlocker(double r) : r(r) {} /** * @brief Filter function for simple 6dB lowpass filter * @param x Input sample * @return */ float LP6DBFilter::filter(float x) { float y = y0 + (alpha * (x - y0)); y0 = y; return y; } /** * @brief Update filter parameter * @param fc Cutoff frequency */ void LP6DBFilter::updateFrequency(float fc, int factor) { LP6DBFilter::fc = fc; RC = 1.f / (LP6DBFilter::fc * TWOPI); dt = 1.f / engineGetSampleRate() * factor; alpha = dt / (RC + dt); } /** * @brief Shaper type 1 (Saturate) * @param a Amount from 0 - x * @param x Input sample * @return */ float shape1(float a, float x) { float k = 2 * a / (1 - a); float b = (1 + k) * (x * 0.5f) / (1 + k * fabsf(x * 0.5f)); return b * 4; } /** * @brief Waveshaper as used in ReShaper. Input should be in the range -1..+1 * @param a Shaping factor * @param x Input sample * @return */ float shape2(float a, float x) { return atanf(x * a);//x * (fabs(x) + a) / (x * x + (a - 1) * fabs(x) + 1); } /** * @brief Soft saturating with a clip of a. Works only with positive values so use 'b' as helper here. * @param x Input sample * @param a Saturating threshold * @return */ double saturate(double x, double a) { double b = 1; /* turn negative values positive and remind in b as coefficient */ if (x < 0) { b = -1; x *= -1; } // nothing to do if (x <= a) return x * b; double d = (a + (x - a) / (1 + pow((x - a) / (1 - a), 2))); if (d > 1) { return (a + 1) / 2 * b; } else { return d * b; } } /** * @brief * @param input * @return */ double overdrive(double input) { const double x = input * 0.686306; const double a = 1 + exp(sqrt(fabs(x)) * -0.75); return (exp(x) - exp(-x * a)) / (exp(x) + exp(-x)); } } // namespace rack_plugin_LindenbergResearch