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