miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
minidsp_fir.c
Go to the documentation of this file.
1
7#include "minidsp.h"
8
10static unsigned next_pow2(unsigned n)
11{
12 unsigned p = 1;
13 while (p < n) p <<= 1;
14 return p;
15}
16
17unsigned MD_convolution_num_samples(unsigned signal_len, unsigned kernel_len)
18{
19 assert(signal_len > 0);
20 assert(kernel_len > 0);
21 return signal_len + kernel_len - 1;
22}
23
24void MD_convolution_time(const double *signal, unsigned signal_len,
25 const double *kernel, unsigned kernel_len,
26 double *out)
27{
28 assert(signal != nullptr);
29 assert(kernel != nullptr);
30 assert(out != nullptr);
31 assert(signal_len > 0);
32 assert(kernel_len > 0);
33
34 unsigned out_len = MD_convolution_num_samples(signal_len, kernel_len);
35 for (unsigned n = 0; n < out_len; n++) {
36 double acc = 0.0;
37 for (unsigned k = 0; k < kernel_len; k++) {
38 if (k > n) break; /* signal index would be negative */
39 unsigned si = n - k;
40 if (si >= signal_len) continue; /* past end of input signal */
41 acc += signal[si] * kernel[k];
42 }
43 out[n] = acc;
44 }
45}
46
47void MD_moving_average(const double *signal, unsigned signal_len,
48 unsigned window_len, double *out)
49{
50 assert(signal != nullptr);
51 assert(out != nullptr);
52 assert(signal_len > 0);
53 assert(window_len > 0);
54
55 double sum = 0.0;
56 double inv_win = 1.0 / (double)window_len;
57
58 for (unsigned n = 0; n < signal_len; n++) {
59 sum += signal[n];
60 if (n >= window_len) {
61 sum -= signal[n - window_len];
62 }
63 out[n] = sum * inv_win;
64 }
65}
66
67void MD_fir_filter(const double *signal, unsigned signal_len,
68 const double *coeffs, unsigned num_taps,
69 double *out)
70{
71 assert(signal != nullptr);
72 assert(coeffs != nullptr);
73 assert(out != nullptr);
74 assert(signal_len > 0);
75 assert(num_taps > 0);
76
77 for (unsigned n = 0; n < signal_len; n++) {
78 double acc = 0.0;
79 for (unsigned k = 0; k < num_taps; k++) {
80 if (k > n) break; /* zero-padded causal startup */
81 acc += coeffs[k] * signal[n - k];
82 }
83 out[n] = acc;
84 }
85}
86
87void MD_convolution_fft_ola(const double *signal, unsigned signal_len,
88 const double *kernel, unsigned kernel_len,
89 double *out)
90{
91 assert(signal != nullptr);
92 assert(kernel != nullptr);
93 assert(out != nullptr);
94 assert(signal_len > 0);
95 assert(kernel_len > 0);
96
97 unsigned out_len = MD_convolution_num_samples(signal_len, kernel_len);
98 memset(out, 0, out_len * sizeof(double));
99
100 unsigned nfft = next_pow2(2U * kernel_len);
101 if (nfft < 64U) nfft = 64U;
102 unsigned block_len = nfft - kernel_len + 1U;
103
104 unsigned num_bins = nfft / 2U + 1U;
105
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);
118
119 memcpy(h_time, kernel, kernel_len * sizeof(double));
120
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);
127
128 fftw_execute(plan_h); /* kernel FFT once */
129
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;
133
134 memset(x_time, 0, nfft * sizeof(double));
135 memcpy(x_time, signal + start, in_len * sizeof(double));
136
137 fftw_execute(plan_x);
138
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);
145 }
146
147 fftw_execute(plan_y);
148
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;
154 }
155 }
156
157 fftw_destroy_plan(plan_h);
158 fftw_destroy_plan(plan_x);
159 fftw_destroy_plan(plan_y);
160 fftw_free(Y);
161 fftw_free(X);
162 fftw_free(H);
163 free(y_time);
164 free(x_time);
165 free(h_time);
166}
A mini library of DSP (Digital Signal Processing) routines.
static unsigned next_pow2(unsigned n)
Return the next power-of-two >= n (n must be > 0).
Definition minidsp_fir.c:10
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).
Definition minidsp_fir.c:87
unsigned MD_convolution_num_samples(unsigned signal_len, unsigned kernel_len)
Compute the output length of a full linear convolution.
Definition minidsp_fir.c:17
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.
Definition minidsp_fir.c:67
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).
Definition minidsp_fir.c:24
void MD_moving_average(const double *signal, unsigned signal_len, unsigned window_len, double *out)
Causal moving-average FIR filter with zero-padded startup.
Definition minidsp_fir.c:47