#include #include #include namespace rack { namespace dsp { void minBlepImpulse(int z, int o, float* output) { // Symmetric sinc array with `z` zero-crossings on each side int n = 2 * z * o; float* x = new float[n]; for (int i = 0; i < n; i++) { float p = math::rescale((float) i, 0.f, (float)(n - 1), (float) - z, (float) z); x[i] = sinc(p); } // Apply window blackmanHarrisWindow(x, n); // Real cepstrum float* fx = new float[2 * n]; // Valgrind complains that the array is uninitialized for some reason, unless we clear it. std::memset(fx, 0, sizeof(float) * 2 * n); RealFFT rfft(n); rfft.rfft(x, fx); // fx = log(abs(fx)) fx[0] = std::log(std::fabs(fx[0])); for (int i = 1; i < n; i++) { fx[2 * i] = std::log(std::hypot(fx[2 * i], fx[2 * i + 1])); fx[2 * i + 1] = 0.f; } fx[1] = std::log(std::fabs(fx[1])); // Clamp values in case we have -inf for (int i = 0; i < 2 * n; i++) { fx[i] = std::fmax(-30.f, fx[i]); } rfft.irfft(fx, x); rfft.scale(x); // Minimum-phase reconstruction for (int i = 1; i < n / 2; i++) { x[i] *= 2.f; } for (int i = (n + 1) / 2; i < n; i++) { x[i] = 0.f; } rfft.rfft(x, fx); // fx = exp(fx) fx[0] = std::exp(fx[0]); for (int i = 1; i < n; i++) { float re = std::exp(fx[2 * i]); float im = fx[2 * i + 1]; fx[2 * i] = re * std::cos(im); fx[2 * i + 1] = re * std::sin(im); } fx[1] = std::exp(fx[1]); rfft.irfft(fx, x); rfft.scale(x); // Integrate float total = 0.f; for (int i = 0; i < n; i++) { total += x[i]; x[i] = total; } // Normalize float norm = 1.f / x[n - 1]; for (int i = 0; i < n; i++) { x[i] *= norm; } std::memcpy(output, x, n * sizeof(float)); // Cleanup delete[] x; delete[] fx; } } // namespace dsp } // namespace rack