You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fir.hpp 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #pragma once
  2. #include "dsp/common.hpp"
  3. #include <pffft.h>
  4. namespace rack {
  5. namespace dsp {
  6. /** Performs a direct sum convolution */
  7. inline float convolveNaive(const float *in, const float *kernel, int len) {
  8. float y = 0.f;
  9. for (int i = 0; i < len; i++) {
  10. y += in[len - 1 - i] * kernel[i];
  11. }
  12. return y;
  13. }
  14. /** Computes the impulse response of a boxcar lowpass filter */
  15. inline void boxcarLowpassIR(float *out, int len, float cutoff = 0.5f) {
  16. for (int i = 0; i < len; i++) {
  17. float t = i - (len - 1) / 2.f;
  18. out[i] = 2 * cutoff * sinc(2 * cutoff * t);
  19. }
  20. }
  21. inline void blackmanHarrisWindow(float *x, int len) {
  22. // Constants from https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window
  23. const float a0 = 0.35875f;
  24. const float a1 = 0.48829f;
  25. const float a2 = 0.14128f;
  26. const float a3 = 0.01168f;
  27. float factor = 2*M_PI / (len - 1);
  28. for (int i = 0; i < len; i++) {
  29. x[i] *=
  30. + a0
  31. - a1 * cosf(1*factor * i)
  32. + a2 * cosf(2*factor * i)
  33. - a3 * cosf(3*factor * i);
  34. }
  35. }
  36. struct RealTimeConvolver {
  37. // `kernelBlocks` number of contiguous FFT blocks of size `blockSize`
  38. // indexed by [i * blockSize*2 + j]
  39. float *kernelFfts = NULL;
  40. float *inputFfts = NULL;
  41. float *outputTail = NULL;
  42. float *tmpBlock = NULL;
  43. size_t blockSize;
  44. size_t kernelBlocks = 0;
  45. size_t inputPos = 0;
  46. PFFFT_Setup *pffft;
  47. /** `blockSize` is the size of each FFT block. It should be >=32 and a power of 2. */
  48. RealTimeConvolver(size_t blockSize) {
  49. this->blockSize = blockSize;
  50. pffft = pffft_new_setup(blockSize*2, PFFFT_REAL);
  51. outputTail = new float[blockSize];
  52. memset(outputTail, 0, blockSize * sizeof(float));
  53. tmpBlock = new float[blockSize*2];
  54. memset(tmpBlock, 0, blockSize*2 * sizeof(float));
  55. }
  56. ~RealTimeConvolver() {
  57. setKernel(NULL, 0);
  58. delete[] outputTail;
  59. delete[] tmpBlock;
  60. pffft_destroy_setup(pffft);
  61. }
  62. void setKernel(const float *kernel, size_t length) {
  63. // Clear existing kernel
  64. if (kernelFfts) {
  65. pffft_aligned_free(kernelFfts);
  66. kernelFfts = NULL;
  67. }
  68. if (inputFfts) {
  69. pffft_aligned_free(inputFfts);
  70. inputFfts = NULL;
  71. }
  72. kernelBlocks = 0;
  73. inputPos = 0;
  74. if (kernel && length > 0) {
  75. // Round up to the nearest factor of `blockSize`
  76. kernelBlocks = (length - 1) / blockSize + 1;
  77. // Allocate blocks
  78. kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks);
  79. inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks);
  80. memset(inputFfts, 0, sizeof(float) * blockSize*2 * kernelBlocks);
  81. for (size_t i = 0; i < kernelBlocks; i++) {
  82. // Pad each block with zeros
  83. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  84. size_t len = std::min((int) blockSize, (int) (length - i*blockSize));
  85. memcpy(tmpBlock, &kernel[i*blockSize], sizeof(float)*len);
  86. // Compute fft
  87. pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize*2 * i], NULL, PFFFT_FORWARD);
  88. }
  89. }
  90. }
  91. /** Applies reverb to input
  92. input and output must be of size `blockSize`
  93. */
  94. void processBlock(const float *input, float *output) {
  95. if (kernelBlocks == 0) {
  96. memset(output, 0, sizeof(float) * blockSize);
  97. return;
  98. }
  99. // Step input position
  100. inputPos = (inputPos + 1) % kernelBlocks;
  101. // Pad block with zeros
  102. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  103. memcpy(tmpBlock, input, sizeof(float) * blockSize);
  104. // Compute input fft
  105. pffft_transform(pffft, tmpBlock, &inputFfts[blockSize*2 * inputPos], NULL, PFFFT_FORWARD);
  106. // Create output fft
  107. memset(tmpBlock, 0, sizeof(float) * blockSize*2);
  108. // convolve input fft by kernel fft
  109. // Note: This is the CPU bottleneck loop
  110. for (size_t i = 0; i < kernelBlocks; i++) {
  111. size_t pos = (inputPos - i + kernelBlocks) % kernelBlocks;
  112. pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize*2 * i], &inputFfts[blockSize*2 * pos], tmpBlock, 1.f);
  113. }
  114. // Compute output
  115. pffft_transform(pffft, tmpBlock, tmpBlock, NULL, PFFFT_BACKWARD);
  116. // Add block tail from last output block
  117. for (size_t i = 0; i < blockSize; i++) {
  118. tmpBlock[i] += outputTail[i];
  119. }
  120. // Copy output block to output
  121. float scale = 1.f / (blockSize*2);
  122. for (size_t i = 0; i < blockSize; i++) {
  123. // Scale based on FFT
  124. output[i] = tmpBlock[i] * scale;
  125. }
  126. // Set tail
  127. for (size_t i = 0; i < blockSize; i++) {
  128. outputTail[i] = tmpBlock[i + blockSize];
  129. }
  130. }
  131. };
  132. } // namespace dsp
  133. } // namespace rack