14 while (p < n) p <<= 1;
22 return signal_len + kernel_len - 1;
26 const double *kernel,
unsigned kernel_len,
36 for (
unsigned n = 0; n < out_len; n++) {
38 for (
unsigned k = 0; k < kernel_len; k++) {
41 if (si >= signal_len)
continue;
42 acc += signal[si] * kernel[k];
49 unsigned window_len,
double *out)
57 double inv_win = 1.0 / (double)window_len;
59 for (
unsigned n = 0; n < signal_len; n++) {
61 if (n >= window_len) {
62 sum -= signal[n - window_len];
64 out[n] = sum * inv_win;
69 const double *coeffs,
unsigned num_taps,
78 for (
unsigned n = 0; n < signal_len; n++) {
80 for (
unsigned k = 0; k < num_taps; k++) {
82 acc += coeffs[k] * signal[n - k];
89 double cutoff_freq,
double sample_rate,
98 double fc = cutoff_freq / sample_rate;
99 double center = (double)(num_taps - 1) / 2.0;
102 double *kaiser = malloc(num_taps *
sizeof(
double));
108 for (
unsigned i = 0; i < num_taps; i++) {
109 double x = (double)i - center;
110 coeffs[i] = 2.0 * fc *
MD_sinc(2.0 * fc * x) * kaiser[i];
117 if (fabs(sum) > 1e-30) {
118 for (
unsigned i = 0; i < num_taps; i++) {
125 const double *kernel,
unsigned kernel_len,
135 memset(out, 0, out_len *
sizeof(
double));
137 unsigned nfft =
next_pow2(2U * kernel_len);
138 if (nfft < 64U) nfft = 64U;
139 unsigned block_len = nfft - kernel_len + 1U;
141 unsigned num_bins = nfft / 2U + 1U;
143 double *h_time = calloc(nfft,
sizeof(
double));
144 double *x_time = calloc(nfft,
sizeof(
double));
145 double *y_time = calloc(nfft,
sizeof(
double));
146 fftw_complex *H = fftw_alloc_complex(num_bins);
147 fftw_complex *X = fftw_alloc_complex(num_bins);
148 fftw_complex *Y = fftw_alloc_complex(num_bins);
156 memcpy(h_time, kernel, kernel_len *
sizeof(
double));
158 fftw_plan plan_h = fftw_plan_dft_r2c_1d((
int)nfft, h_time, H, FFTW_ESTIMATE);
159 fftw_plan plan_x = fftw_plan_dft_r2c_1d((
int)nfft, x_time, X, FFTW_ESTIMATE);
160 fftw_plan plan_y = fftw_plan_dft_c2r_1d((
int)nfft, Y, y_time, FFTW_ESTIMATE);
165 fftw_execute(plan_h);
167 for (
unsigned start = 0; start < signal_len; start += block_len) {
168 unsigned in_len = signal_len - start;
169 if (in_len > block_len) in_len = block_len;
171 memset(x_time, 0, nfft *
sizeof(
double));
172 memcpy(x_time, signal + start, in_len *
sizeof(
double));
174 fftw_execute(plan_x);
176 for (
unsigned k = 0; k < num_bins; k++) {
177 double xr = creal(X[k]);
178 double xi = cimag(X[k]);
179 double hr = creal(H[k]);
180 double hi = cimag(H[k]);
181 Y[k] = (xr * hr - xi * hi) + I * (xr * hi + xi * hr);
184 fftw_execute(plan_y);
186 double norm = 1.0 / (double)nfft;
187 for (
unsigned n = 0; n < nfft; n++) {
188 unsigned oi = start + n;
189 if (oi >= out_len)
break;
190 out[oi] += y_time[n] * norm;
194 fftw_destroy_plan(plan_h);
195 fftw_destroy_plan(plan_x);
196 fftw_destroy_plan(plan_y);
A mini library of DSP (Digital Signal Processing) routines.
void MD_Gen_Kaiser_Win(double *out, unsigned n, double beta)
Generate a Kaiser window of length n with shape parameter beta.
@ MD_ERR_INVALID_SIZE
A size or count argument is invalid (e.g.
@ MD_ERR_INVALID_RANGE
A range or bound is invalid (e.g.
@ MD_ERR_ALLOC_FAILED
A memory allocation failed.
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
double MD_sinc(double x)
Normalized sinc function: .
static unsigned next_pow2(unsigned n)
Return the next power-of-two >= n (n must be > 0).
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).
unsigned MD_convolution_num_samples(unsigned signal_len, unsigned kernel_len)
Compute the output length of a full linear convolution.
void MD_design_lowpass_fir(double *coeffs, unsigned num_taps, double cutoff_freq, double sample_rate, double kaiser_beta)
Design a Kaiser-windowed sinc lowpass FIR filter.
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.
Internal header for cross-file dependencies within the minidsp module.
#define MD_CHECK(cond, code, msg, retval)
Check a precondition in a function that returns a value.
#define MD_CHECK_VOID(cond, code, msg)
Check a precondition in a void function.