| @@ -15,6 +15,9 @@ | |||||
| #include <string> | #include <string> | ||||
| namespace rack { | |||||
| /** Deprecation notice for functions | /** Deprecation notice for functions | ||||
| E.g. | E.g. | ||||
| DEPRECATED void foo(); | DEPRECATED void foo(); | ||||
| @@ -130,4 +133,7 @@ DeferWrapper<F> deferWrapper(F f) { | |||||
| return DeferWrapper<F>(f); | return DeferWrapper<F>(f); | ||||
| } | } | ||||
| #define DEFER(code) auto CONCAT(_defer_, __COUNTER__) = deferWrapper([&]() code) | |||||
| #define DEFER(code) auto CONCAT(_defer_, __COUNTER__) = rack::deferWrapper([&]() code) | |||||
| } // namespace rack | |||||
| @@ -6,11 +6,15 @@ namespace rack { | |||||
| namespace dsp { | namespace dsp { | ||||
| // Useful constants | |||||
| static const float FREQ_C4 = 261.6256f; | static const float FREQ_C4 = 261.6256f; | ||||
| static const float FREQ_A4 = 440.0000f; | static const float FREQ_A4 = 440.0000f; | ||||
| /** The normalized sinc function. https://en.wikipedia.org/wiki/Sinc_function */ | |||||
| /** The normalized sinc function | |||||
| See https://en.wikipedia.org/wiki/Sinc_function | |||||
| */ | |||||
| inline float sinc(float x) { | inline float sinc(float x) { | ||||
| if (x == 0.f) | if (x == 0.f) | ||||
| return 1.f; | return 1.f; | ||||
| @@ -18,6 +22,8 @@ inline float sinc(float x) { | |||||
| return std::sin(x) / x; | return std::sin(x) / x; | ||||
| } | } | ||||
| // Functions for parameter scaling | |||||
| inline float quadraticBipolar(float x) { | inline float quadraticBipolar(float x) { | ||||
| float x2 = x*x; | float x2 = x*x; | ||||
| return (x >= 0.f) ? x2 : -x2; | return (x >= 0.f) ? x2 : -x2; | ||||
| @@ -33,7 +39,7 @@ inline float quarticBipolar(float x) { | |||||
| } | } | ||||
| inline float quintic(float x) { | inline float quintic(float x) { | ||||
| // optimal with --fast-math | |||||
| // optimal with -fassociative-math | |||||
| return x*x*x*x*x; | return x*x*x*x*x; | ||||
| } | } | ||||
| @@ -47,6 +53,8 @@ inline float exponentialBipolar(float b, float x) { | |||||
| return (std::pow(b, x) - std::pow(b, -x)) / a; | return (std::pow(b, x) - std::pow(b, -x)) / a; | ||||
| } | } | ||||
| // Useful conversion functions | |||||
| inline float amplitudeToDb(float amp) { | inline float amplitudeToDb(float amp) { | ||||
| return std::log10(amp) * 20.f; | return std::log10(amp) * 20.f; | ||||
| } | } | ||||
| @@ -0,0 +1,134 @@ | |||||
| #pragma once | |||||
| #include "dsp/common.hpp" | |||||
| #include <pffft.h> | |||||
| namespace rack { | |||||
| namespace dsp { | |||||
| template<typename T> | |||||
| T *alignedNew(size_t len) { | |||||
| return pffft_aligned_malloc(len * sizeof(T)); | |||||
| } | |||||
| template<typename T> | |||||
| void alignedDelete(T *p) { | |||||
| pffft_aligned_free(p); | |||||
| } | |||||
| /** Real-valued FFT context | |||||
| Wraps PFFFT (https://bitbucket.org/jpommier/pffft/) | |||||
| */ | |||||
| struct RealFFT { | |||||
| PFFFT_Setup *setup; | |||||
| int length; | |||||
| RealFFT(size_t length) { | |||||
| this->length = length; | |||||
| setup = pffft_new_setup(length, PFFFT_REAL); | |||||
| } | |||||
| ~RealFFT() { | |||||
| pffft_destroy_setup(setup); | |||||
| } | |||||
| /** Performs the real FFT. | |||||
| Input and output must be aligned using the above align*() functions. | |||||
| Input is `length` elements. Output is `2*length` elements. | |||||
| Output is arbitrarily ordered for performance reasons. | |||||
| However, this ordering is consistent, so element-wise multiplication with line up with other results, and the inverse FFT will return a correctly ordered result. | |||||
| */ | |||||
| void rfftUnordered(const float *input, float *output) { | |||||
| pffft_transform(setup, input, output, NULL, PFFFT_FORWARD); | |||||
| } | |||||
| /** Performs the inverse real FFT. | |||||
| Input is `2*length` elements. Output is `length` elements. | |||||
| Scaling is such that IRFFT(RFFT(x)) = N*x. | |||||
| */ | |||||
| void irfftUnordered(const float *input, float *output) { | |||||
| pffft_transform(setup, input, output, NULL, PFFFT_BACKWARD); | |||||
| } | |||||
| /** Slower than the above methods, but returns results in the "canonical" FFT order as follows. | |||||
| output[0] = F(0) | |||||
| output[1] = F(n/2) | |||||
| output[2] = real(F(1)) | |||||
| output[3] = imag(F(1)) | |||||
| output[4] = real(F(2)) | |||||
| output[5] = imag(F(2)) | |||||
| ... | |||||
| output[length - 2] = real(F(n/2 - 1)) | |||||
| output[length - 1] = imag(F(n/2 - 1)) | |||||
| */ | |||||
| void rfft(const float *input, float *output) { | |||||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_FORWARD); | |||||
| } | |||||
| void irfft(const float *input, float *output) { | |||||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); | |||||
| } | |||||
| /** Scales the RFFT so that | |||||
| scale(IFFT(FFT(x))) = x | |||||
| */ | |||||
| void scale(float *x) { | |||||
| float a = 1.f / length; | |||||
| for (int i = 0; i < length; i++) { | |||||
| x[i] *= a; | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct ComplexFFT { | |||||
| PFFFT_Setup *setup; | |||||
| int length; | |||||
| ComplexFFT(size_t length) { | |||||
| this->length = length; | |||||
| setup = pffft_new_setup(length, PFFFT_COMPLEX); | |||||
| } | |||||
| ~ComplexFFT() { | |||||
| pffft_destroy_setup(setup); | |||||
| } | |||||
| /** Performs the complex FFT. | |||||
| Input and output must be aligned using the above align*() functions. | |||||
| Input is `2*length` elements. Output is `2*length` elements. | |||||
| */ | |||||
| void fftUnordered(const float *input, float *output) { | |||||
| pffft_transform(setup, input, output, NULL, PFFFT_FORWARD); | |||||
| } | |||||
| /** Performs the inverse complex FFT. | |||||
| Input is `2*length` elements. Output is `2*length` elements. | |||||
| Scaling is such that FFT(IFFT(x)) = N*x. | |||||
| */ | |||||
| void ifftUnordered(const float *input, float *output) { | |||||
| pffft_transform(setup, input, output, NULL, PFFFT_BACKWARD); | |||||
| } | |||||
| void fft(const float *input, float *output) { | |||||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_FORWARD); | |||||
| } | |||||
| void ifft(const float *input, float *output) { | |||||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); | |||||
| } | |||||
| void scale(float *x) { | |||||
| float a = 1.f / length; | |||||
| for (int i = 0; i < length; i++) { | |||||
| x[2*i+0] *= a; | |||||
| x[2*i+1] *= a; | |||||
| } | |||||
| } | |||||
| }; | |||||
| } // namespace dsp | |||||
| } // namespace rack | |||||
| @@ -24,7 +24,7 @@ inline void boxcarLowpassIR(float *out, int len, float cutoff = 0.5f) { | |||||
| } | } | ||||
| } | } | ||||
| inline void blackmanHarrisWindow(float *x, int len) { | |||||
| inline void blackmanHarris(float *x, int len) { | |||||
| // Constants from https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | // Constants from https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | ||||
| const float a0 = 0.35875f; | const float a0 = 0.35875f; | ||||
| const float a1 = 0.48829f; | const float a1 = 0.48829f; | ||||
| @@ -34,9 +34,9 @@ inline void blackmanHarrisWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | for (int i = 0; i < len; i++) { | ||||
| x[i] *= | x[i] *= | ||||
| + a0 | + a0 | ||||
| - a1 * cosf(1*factor * i) | |||||
| + a2 * cosf(2*factor * i) | |||||
| - a3 * cosf(3*factor * i); | |||||
| - a1 * std::cos(1*factor * i) | |||||
| + a2 * std::cos(2*factor * i) | |||||
| - a3 * std::cos(3*factor * i); | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,31 +6,28 @@ namespace rack { | |||||
| namespace dsp { | namespace dsp { | ||||
| // Pre-made minBLEP samples in minBLEP.cpp | |||||
| extern const float minblep_16_32[]; | |||||
| template<int ZERO_CROSSINGS> | template<int ZERO_CROSSINGS> | ||||
| struct MinBLEP { | struct MinBLEP { | ||||
| float buf[2*ZERO_CROSSINGS] = {}; | |||||
| float buf[2 * ZERO_CROSSINGS] = {}; | |||||
| int pos = 0; | int pos = 0; | ||||
| const float *minblep; | const float *minblep; | ||||
| int oversample; | int oversample; | ||||
| /** Places a discontinuity with magnitude dx at -1 < p <= 0 relative to the current frame */ | |||||
| void jump(float p, float dx) { | |||||
| if (p <= -1 || 0 < p) | |||||
| /** Places a discontinuity with magnitude `x` at -1 < p <= 0 relative to the current frame */ | |||||
| void insertDiscontinuity(float p, float x) { | |||||
| if (!(-1 < p && p <= 0)) | |||||
| return; | return; | ||||
| for (int j = 0; j < 2*ZERO_CROSSINGS; j++) { | |||||
| for (int j = 0; j < 2 * ZERO_CROSSINGS; j++) { | |||||
| float minblepIndex = ((float)j - p) * oversample; | float minblepIndex = ((float)j - p) * oversample; | ||||
| int index = (pos + j) % (2*ZERO_CROSSINGS); | |||||
| buf[index] += dx * (-1.0 + math::interpolateLinear(minblep, minblepIndex)); | |||||
| int index = (pos + j) % (2 * ZERO_CROSSINGS); | |||||
| buf[index] += x * (-1.f + math::interpolateLinear(minblep, minblepIndex)); | |||||
| } | } | ||||
| } | } | ||||
| float shift() { | |||||
| float process() { | |||||
| float v = buf[pos]; | float v = buf[pos]; | ||||
| buf[pos] = 0.0; | |||||
| pos = (pos + 1) % (2*ZERO_CROSSINGS); | |||||
| buf[pos] = 0.f; | |||||
| pos = (pos + 1) % (2 * ZERO_CROSSINGS); | |||||
| return v; | return v; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -109,7 +109,7 @@ struct Decimator { | |||||
| Decimator(float cutoff = 0.9f) { | Decimator(float cutoff = 0.9f) { | ||||
| boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE); | boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE); | ||||
| blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY); | |||||
| blackmanHarris(kernel, OVERSAMPLE*QUALITY); | |||||
| reset(); | reset(); | ||||
| } | } | ||||
| void reset() { | void reset() { | ||||
| @@ -1,17 +1,6 @@ | |||||
| #pragma once | #pragma once | ||||
| /** Example usage: | |||||
| DEBUG("error: %d", errno); | |||||
| will print something like | |||||
| [0.123 debug myfile.cpp:45] error: 67 | |||||
| */ | |||||
| #define DEBUG(format, ...) rack::logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define INFO(format, ...) rack::logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define WARN(format, ...) rack::logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define FATAL(format, ...) rack::logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| namespace rack { | namespace rack { | ||||
| namespace logger { | namespace logger { | ||||
| @@ -29,5 +18,16 @@ void destroy(); | |||||
| void log(Level level, const char *filename, int line, const char *format, ...); | void log(Level level, const char *filename, int line, const char *format, ...); | ||||
| /** Example usage: | |||||
| DEBUG("error: %d", errno); | |||||
| will print something like | |||||
| [0.123 debug myfile.cpp:45] error: 67 | |||||
| */ | |||||
| #define DEBUG(format, ...) rack::logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define INFO(format, ...) rack::logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define WARN(format, ...) rack::logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define FATAL(format, ...) rack::logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| } // namespace logger | } // namespace logger | ||||
| } // namespace rack | } // namespace rack | ||||
| @@ -119,15 +119,15 @@ See https://en.wikipedia.org/wiki/Euclidean_division | |||||
| */ | */ | ||||
| inline float eucMod(float a, float base) { | inline float eucMod(float a, float base) { | ||||
| float mod = std::fmod(a, base); | float mod = std::fmod(a, base); | ||||
| return (mod >= 0.0f) ? mod : mod + base; | |||||
| return (mod >= 0.f) ? mod : mod + base; | |||||
| } | } | ||||
| inline bool isNear(float a, float b, float epsilon = 1.0e-6f) { | |||||
| inline bool isNear(float a, float b, float epsilon = 1e-6f) { | |||||
| return std::abs(a - b) <= epsilon; | return std::abs(a - b) <= epsilon; | ||||
| } | } | ||||
| /** If the magnitude of x if less than epsilon, return 0 */ | /** If the magnitude of x if less than epsilon, return 0 */ | ||||
| inline float chop(float x, float epsilon = 1.0e-6f) { | |||||
| inline float chop(float x, float epsilon = 1e-6f) { | |||||
| return isNear(x, 0.f, epsilon) ? 0.f : x; | return isNear(x, 0.f, epsilon) ? 0.f : x; | ||||
| } | } | ||||
| @@ -157,7 +157,6 @@ inline void complexMult(float *cr, float *ci, float ar, float ai, float br, floa | |||||
| *ci = ar * bi + ai * br; | *ci = ar * bi + ai * br; | ||||
| } | } | ||||
| //////////////////// | //////////////////// | ||||
| // 2D vector and rectangle | // 2D vector and rectangle | ||||
| //////////////////// | //////////////////// | ||||
| @@ -232,7 +231,7 @@ struct Vec { | |||||
| return x == b.x && y == b.y; | return x == b.x && y == b.y; | ||||
| } | } | ||||
| bool isZero() const { | bool isZero() const { | ||||
| return x == 0.0f && y == 0.0f; | |||||
| return x == 0.f && y == 0.f; | |||||
| } | } | ||||
| bool isFinite() const { | bool isFinite() const { | ||||
| return std::isfinite(x) && std::isfinite(y); | return std::isfinite(x) && std::isfinite(y); | ||||
| @@ -79,6 +79,7 @@ | |||||
| #include "dsp/common.hpp" | #include "dsp/common.hpp" | ||||
| #include "dsp/digital.hpp" | #include "dsp/digital.hpp" | ||||
| #include "dsp/fft.hpp" | |||||
| #include "dsp/filter.hpp" | #include "dsp/filter.hpp" | ||||
| #include "dsp/fir.hpp" | #include "dsp/fir.hpp" | ||||
| #include "dsp/frame.hpp" | #include "dsp/frame.hpp" | ||||