88 const double *kernel,
unsigned kernel_len,
91 assert(signal !=
nullptr);
92 assert(kernel !=
nullptr);
93 assert(out !=
nullptr);
94 assert(signal_len > 0);
95 assert(kernel_len > 0);
98 memset(out, 0, out_len *
sizeof(
double));
100 unsigned nfft =
next_pow2(2U * kernel_len);
101 if (nfft < 64U) nfft = 64U;
102 unsigned block_len = nfft - kernel_len + 1U;
104 unsigned num_bins = nfft / 2U + 1U;
106 double *h_time = calloc(nfft,
sizeof(
double));
107 double *x_time = calloc(nfft,
sizeof(
double));
108 double *y_time = calloc(nfft,
sizeof(
double));
109 fftw_complex *H = fftw_alloc_complex(num_bins);
110 fftw_complex *X = fftw_alloc_complex(num_bins);
111 fftw_complex *Y = fftw_alloc_complex(num_bins);
112 assert(h_time !=
nullptr);
113 assert(x_time !=
nullptr);
114 assert(y_time !=
nullptr);
115 assert(H !=
nullptr);
116 assert(X !=
nullptr);
117 assert(Y !=
nullptr);
119 memcpy(h_time, kernel, kernel_len *
sizeof(
double));
121 fftw_plan plan_h = fftw_plan_dft_r2c_1d((
int)nfft, h_time, H, FFTW_ESTIMATE);
122 fftw_plan plan_x = fftw_plan_dft_r2c_1d((
int)nfft, x_time, X, FFTW_ESTIMATE);
123 fftw_plan plan_y = fftw_plan_dft_c2r_1d((
int)nfft, Y, y_time, FFTW_ESTIMATE);
124 assert(plan_h !=
nullptr);
125 assert(plan_x !=
nullptr);
126 assert(plan_y !=
nullptr);
128 fftw_execute(plan_h);
130 for (
unsigned start = 0; start < signal_len; start += block_len) {
131 unsigned in_len = signal_len - start;
132 if (in_len > block_len) in_len = block_len;
134 memset(x_time, 0, nfft *
sizeof(
double));
135 memcpy(x_time, signal + start, in_len *
sizeof(
double));
137 fftw_execute(plan_x);
139 for (
unsigned k = 0; k < num_bins; k++) {
140 double xr = creal(X[k]);
141 double xi = cimag(X[k]);
142 double hr = creal(H[k]);
143 double hi = cimag(H[k]);
144 Y[k] = (xr * hr - xi * hi) + I * (xr * hi + xi * hr);
147 fftw_execute(plan_y);
149 double norm = 1.0 / (double)nfft;
150 for (
unsigned n = 0; n < nfft; n++) {
151 unsigned oi = start + n;
152 if (oi >= out_len)
break;
153 out[oi] += y_time[n] * norm;
157 fftw_destroy_plan(plan_h);
158 fftw_destroy_plan(plan_x);
159 fftw_destroy_plan(plan_y);
void MD_convolution_fft_ola(const double *signal, unsigned signal_len, const double *kernel, unsigned kernel_len, double *out)
Full linear convolution using FFT overlap-add (offline).
void MD_fir_filter(const double *signal, unsigned signal_len, const double *coeffs, unsigned num_taps, double *out)
Apply a causal FIR filter with arbitrary coefficients.
void MD_convolution_time(const double *signal, unsigned signal_len, const double *kernel, unsigned kernel_len, double *out)
Time-domain full linear convolution (direct sum-of-products).
void MD_moving_average(const double *signal, unsigned signal_len, unsigned window_len, double *out)
Causal moving-average FIR filter with zero-padded startup.