/** Helpful defines, functions, and other utilities for use in/with daisysp modules. */ #pragma once #ifndef DSY_CORE_DSP #define DSY_CORE_DSP #include #include #include #include /** PIs */ #define PI_F 3.1415927410125732421875f #define TWOPI_F (2.0f * PI_F) #define HALFPI_F (PI_F * 0.5f) #define DSY_MIN(in, mn) (in < mn ? in : mn) #define DSY_MAX(in, mx) (in > mx ? in : mx) #define DSY_CLAMP(in, mn, mx) (DSY_MIN(DSY_MAX(in, mn), mx)) #define DSY_COUNTOF(_arr) (sizeof(_arr) / sizeof(_arr[0])) namespace daisysp { //Avoids division for random floats. e.g. rand() * kRandFrac static constexpr float kRandFrac = 1.f / (float)RAND_MAX; //Convert from semitones to other units. e.g. 2 ^ (kOneTwelfth * x) static constexpr float kOneTwelfth = 1.f / 12.f; /** efficient floating point min/max c/o stephen mccaul */ inline float fmax(float a, float b) { float r; #ifdef __arm__ asm("vmaxnm.f32 %[d], %[n], %[m]" : [d] "=t"(r) : [n] "t"(a), [m] "t"(b) :); #else r = (a > b) ? a : b; #endif // __arm__ return r; } inline float fmin(float a, float b) { float r; #ifdef __arm__ asm("vminnm.f32 %[d], %[n], %[m]" : [d] "=t"(r) : [n] "t"(a), [m] "t"(b) :); #else r = (a < b) ? a : b; #endif // __arm__ return r; } /** quick fp clamp */ inline float fclamp(float in, float min, float max) { return fmin(fmax(in, min), max); } /** From Musicdsp.org "Fast power and root estimates for 32bit floats) Original code by Stefan Stenzel These are approximations */ inline float fastpower(float f, int n) { long *lp, l; lp = (long *)(&f); l = *lp; l -= 0x3F800000; l <<= (n - 1); l += 0x3F800000; *lp = l; return f; } inline float fastroot(float f, int n) { long *lp, l; lp = (long *)(&f); l = *lp; l -= 0x3F800000; l >>= (n = 1); l += 0x3F800000; *lp = l; return f; } /** From http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html No approximation, pow10f(x) gives a 90% speed increase over powf(10.f, x) */ inline float pow10f(float f) { return expf(2.302585092994046f * f); } /* Original code for fastlog2f by Dr. Paul Beckmann from the ARM community forum, adapted from the CMSIS-DSP library About 25% performance increase over std::log10f */ inline float fastlog2f(float f) { float frac; int exp; frac = frexpf(fabsf(f), &exp); f = 1.23149591368684f; f *= frac; f += -4.11852516267426f; f *= frac; f += 6.02197014179219f; f *= frac; f += -3.13396450166353f; f += exp; return (f); } inline float fastlog10f(float f) { return fastlog2f(f) * 0.3010299956639812f; } /** Midi to frequency helper */ inline float mtof(float m) { return powf(2, (m - 69.0f) / 12.0f) * 440.0f; } /** one pole lpf out is passed by reference, and must be retained between calls to properly filter the signal coeff can be calculated: coeff = 1.0 / (time * sample_rate) ; where time is in seconds */ inline void fonepole(float &out, float in, float coeff) { out += coeff * (in - out); } /** Simple 3-point median filter c/o stephen mccaul */ template T median(T a, T b, T c) { return (b < a) ? (b < c) ? (c < a) ? c : a : b : (a < c) ? (c < b) ? c : b : a; } /** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h */ inline float ThisBlepSample(float t) { return 0.5f * t * t; } /** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h */ inline float NextBlepSample(float t) { t = 1.0f - t; return -0.5f * t * t; } /** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h */ inline float NextIntegratedBlepSample(float t) { const float t1 = 0.5f * t; const float t2 = t1 * t1; const float t4 = t2 * t2; return 0.1875f - t1 + 1.5f * t2 - t4; } /** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h */ inline float ThisIntegratedBlepSample(float t) { return NextIntegratedBlepSample(1.0f - t); } /** Soft Limiting function ported extracted from pichenettes/stmlib */ inline float SoftLimit(float x) { return x * (27.f + x * x) / (27.f + 9.f * x * x); } /** Soft Clipping function extracted from pichenettes/stmlib */ inline float SoftClip(float x) { if(x < -3.0f) return -1.0f; else if(x > 3.0f) return 1.0f; else return SoftLimit(x); } /** Quick check for Invalid float values (NaN, Inf, out of range) ** \param x value passed by reference, replaced by y if invalid. ** \param y value to replace x if invalidity is found. ** ** When DEBUG is true in the build, this will halt ** execution for tracing the reason for the invalidity. */ inline void TestFloat(float &x, float y = 0.f) { if(!std::isnormal(x) && x != 0) { #ifdef DEBUG asm("bkpt 255"); #else x = y; #endif } } /** Based on soft saturate from: [musicdsp.org](musicdsp.org/en/latest/Effects/42-soft-saturation.html) Bram de Jong (2002-01-17) This still needs to be tested/fixed. Definitely does some weird stuff described as: x < a: f(x) = x x > a: f(x) = a + (x-a)/(1+((x-a)/(1-a))^2) x > 1: f(x) = (a + 1)/2 */ inline float soft_saturate(float in, float thresh) { bool flip; float val, out; //val = fabsf(in); out = 0.f; flip = in < 0.0f; val = flip ? -in : in; if(val < thresh) { out = in; } else if(val > 1.0f) { out = (thresh + 1.0f) / 2.0f; if(flip) out *= -1.0f; } else if(val > thresh) { float temp; temp = (val - thresh) / (1 - thresh); out = thresh + (val - thresh) / (1.0f + (temp * temp)); if(flip) out *= -1.0f; } return out; // return val < thresh // ? val // : val > 1.0f // ? (thresh + 1.0f) / 2.0f // : thresh // + (val - thresh) // / (1.0f // + (((val - thresh) / (1.0f - thresh)) // * ((val - thresh) / (1.0f - thresh)))); } constexpr bool is_power2(uint32_t x) { return ((x - 1) & x) == 0; } constexpr uint32_t get_next_power2(uint32_t x) { x--; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x++; assert(is_power2(x)); return x; } } // namespace daisysp #endif #ifdef DSY_CUSTOM_DSP #include "custom_dsp.h" #endif