GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* |
||
2 |
* Copyright (c) 2016 Muhammad Faiz <mfcc64@gmail.com> |
||
3 |
* |
||
4 |
* This file is part of FFmpeg. |
||
5 |
* |
||
6 |
* FFmpeg is free software; you can redistribute it and/or |
||
7 |
* modify it under the terms of the GNU Lesser General Public |
||
8 |
* License as published by the Free Software Foundation; either |
||
9 |
* version 2.1 of the License, or (at your option) any later version. |
||
10 |
* |
||
11 |
* FFmpeg is distributed in the hope that it will be useful, |
||
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 |
* Lesser General Public License for more details. |
||
15 |
* |
||
16 |
* You should have received a copy of the GNU Lesser General Public |
||
17 |
* License along with FFmpeg; if not, write to the Free Software |
||
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
19 |
*/ |
||
20 |
|||
21 |
#include "libavutil/opt.h" |
||
22 |
#include "libavutil/eval.h" |
||
23 |
#include "libavutil/avassert.h" |
||
24 |
#include "libavcodec/avfft.h" |
||
25 |
#include "avfilter.h" |
||
26 |
#include "internal.h" |
||
27 |
#include "audio.h" |
||
28 |
|||
29 |
#define RDFT_BITS_MIN 4 |
||
30 |
#define RDFT_BITS_MAX 16 |
||
31 |
|||
32 |
enum WindowFunc { |
||
33 |
WFUNC_RECTANGULAR, |
||
34 |
WFUNC_HANN, |
||
35 |
WFUNC_HAMMING, |
||
36 |
WFUNC_BLACKMAN, |
||
37 |
WFUNC_NUTTALL3, |
||
38 |
WFUNC_MNUTTALL3, |
||
39 |
WFUNC_NUTTALL, |
||
40 |
WFUNC_BNUTTALL, |
||
41 |
WFUNC_BHARRIS, |
||
42 |
WFUNC_TUKEY, |
||
43 |
NB_WFUNC |
||
44 |
}; |
||
45 |
|||
46 |
enum Scale { |
||
47 |
SCALE_LINLIN, |
||
48 |
SCALE_LINLOG, |
||
49 |
SCALE_LOGLIN, |
||
50 |
SCALE_LOGLOG, |
||
51 |
NB_SCALE |
||
52 |
}; |
||
53 |
|||
54 |
#define NB_GAIN_ENTRY_MAX 4096 |
||
55 |
typedef struct GainEntry { |
||
56 |
double freq; |
||
57 |
double gain; |
||
58 |
} GainEntry; |
||
59 |
|||
60 |
typedef struct OverlapIndex { |
||
61 |
int buf_idx; |
||
62 |
int overlap_idx; |
||
63 |
} OverlapIndex; |
||
64 |
|||
65 |
typedef struct FIREqualizerContext { |
||
66 |
const AVClass *class; |
||
67 |
|||
68 |
RDFTContext *analysis_rdft; |
||
69 |
RDFTContext *analysis_irdft; |
||
70 |
RDFTContext *rdft; |
||
71 |
RDFTContext *irdft; |
||
72 |
FFTContext *fft_ctx; |
||
73 |
RDFTContext *cepstrum_rdft; |
||
74 |
RDFTContext *cepstrum_irdft; |
||
75 |
int analysis_rdft_len; |
||
76 |
int rdft_len; |
||
77 |
int cepstrum_len; |
||
78 |
|||
79 |
float *analysis_buf; |
||
80 |
float *dump_buf; |
||
81 |
float *kernel_tmp_buf; |
||
82 |
float *kernel_buf; |
||
83 |
float *cepstrum_buf; |
||
84 |
float *conv_buf; |
||
85 |
OverlapIndex *conv_idx; |
||
86 |
int fir_len; |
||
87 |
int nsamples_max; |
||
88 |
int64_t next_pts; |
||
89 |
int frame_nsamples_max; |
||
90 |
int remaining; |
||
91 |
|||
92 |
char *gain_cmd; |
||
93 |
char *gain_entry_cmd; |
||
94 |
const char *gain; |
||
95 |
const char *gain_entry; |
||
96 |
double delay; |
||
97 |
double accuracy; |
||
98 |
int wfunc; |
||
99 |
int fixed; |
||
100 |
int multi; |
||
101 |
int zero_phase; |
||
102 |
int scale; |
||
103 |
char *dumpfile; |
||
104 |
int dumpscale; |
||
105 |
int fft2; |
||
106 |
int min_phase; |
||
107 |
|||
108 |
int nb_gain_entry; |
||
109 |
int gain_entry_err; |
||
110 |
GainEntry gain_entry_tbl[NB_GAIN_ENTRY_MAX]; |
||
111 |
} FIREqualizerContext; |
||
112 |
|||
113 |
#define OFFSET(x) offsetof(FIREqualizerContext, x) |
||
114 |
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
||
115 |
#define TFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM |
||
116 |
|||
117 |
static const AVOption firequalizer_options[] = { |
||
118 |
{ "gain", "set gain curve", OFFSET(gain), AV_OPT_TYPE_STRING, { .str = "gain_interpolate(f)" }, 0, 0, TFLAGS }, |
||
119 |
{ "gain_entry", "set gain entry", OFFSET(gain_entry), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, TFLAGS }, |
||
120 |
{ "delay", "set delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.0, 1e10, FLAGS }, |
||
121 |
{ "accuracy", "set accuracy", OFFSET(accuracy), AV_OPT_TYPE_DOUBLE, { .dbl = 5.0 }, 0.0, 1e10, FLAGS }, |
||
122 |
{ "wfunc", "set window function", OFFSET(wfunc), AV_OPT_TYPE_INT, { .i64 = WFUNC_HANN }, 0, NB_WFUNC-1, FLAGS, "wfunc" }, |
||
123 |
{ "rectangular", "rectangular window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_RECTANGULAR }, 0, 0, FLAGS, "wfunc" }, |
||
124 |
{ "hann", "hann window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HANN }, 0, 0, FLAGS, "wfunc" }, |
||
125 |
{ "hamming", "hamming window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HAMMING }, 0, 0, FLAGS, "wfunc" }, |
||
126 |
{ "blackman", "blackman window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BLACKMAN }, 0, 0, FLAGS, "wfunc" }, |
||
127 |
{ "nuttall3", "3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL3 }, 0, 0, FLAGS, "wfunc" }, |
||
128 |
{ "mnuttall3", "minimum 3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_MNUTTALL3 }, 0, 0, FLAGS, "wfunc" }, |
||
129 |
{ "nuttall", "nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL }, 0, 0, FLAGS, "wfunc" }, |
||
130 |
{ "bnuttall", "blackman-nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BNUTTALL }, 0, 0, FLAGS, "wfunc" }, |
||
131 |
{ "bharris", "blackman-harris window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BHARRIS }, 0, 0, FLAGS, "wfunc" }, |
||
132 |
{ "tukey", "tukey window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_TUKEY }, 0, 0, FLAGS, "wfunc" }, |
||
133 |
{ "fixed", "set fixed frame samples", OFFSET(fixed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
||
134 |
{ "multi", "set multi channels mode", OFFSET(multi), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
||
135 |
{ "zero_phase", "set zero phase mode", OFFSET(zero_phase), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
||
136 |
{ "scale", "set gain scale", OFFSET(scale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" }, |
||
137 |
{ "linlin", "linear-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLIN }, 0, 0, FLAGS, "scale" }, |
||
138 |
{ "linlog", "linear-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLOG }, 0, 0, FLAGS, "scale" }, |
||
139 |
{ "loglin", "logarithmic-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLIN }, 0, 0, FLAGS, "scale" }, |
||
140 |
{ "loglog", "logarithmic-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLOG }, 0, 0, FLAGS, "scale" }, |
||
141 |
{ "dumpfile", "set dump file", OFFSET(dumpfile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, |
||
142 |
{ "dumpscale", "set dump scale", OFFSET(dumpscale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" }, |
||
143 |
{ "fft2", "set 2-channels fft", OFFSET(fft2), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
||
144 |
{ "min_phase", "set minimum phase mode", OFFSET(min_phase), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
||
145 |
{ NULL } |
||
146 |
}; |
||
147 |
|||
148 |
AVFILTER_DEFINE_CLASS(firequalizer); |
||
149 |
|||
150 |
10 |
static void common_uninit(FIREqualizerContext *s) |
|
151 |
{ |
||
152 |
10 |
av_rdft_end(s->analysis_rdft); |
|
153 |
10 |
av_rdft_end(s->analysis_irdft); |
|
154 |
10 |
av_rdft_end(s->rdft); |
|
155 |
10 |
av_rdft_end(s->irdft); |
|
156 |
10 |
av_fft_end(s->fft_ctx); |
|
157 |
10 |
av_rdft_end(s->cepstrum_rdft); |
|
158 |
10 |
av_rdft_end(s->cepstrum_irdft); |
|
159 |
10 |
s->analysis_rdft = s->analysis_irdft = s->rdft = s->irdft = NULL; |
|
160 |
10 |
s->fft_ctx = NULL; |
|
161 |
10 |
s->cepstrum_rdft = NULL; |
|
162 |
10 |
s->cepstrum_irdft = NULL; |
|
163 |
|||
164 |
10 |
av_freep(&s->analysis_buf); |
|
165 |
10 |
av_freep(&s->dump_buf); |
|
166 |
10 |
av_freep(&s->kernel_tmp_buf); |
|
167 |
10 |
av_freep(&s->kernel_buf); |
|
168 |
10 |
av_freep(&s->cepstrum_buf); |
|
169 |
10 |
av_freep(&s->conv_buf); |
|
170 |
10 |
av_freep(&s->conv_idx); |
|
171 |
10 |
} |
|
172 |
|||
173 |
5 |
static av_cold void uninit(AVFilterContext *ctx) |
|
174 |
{ |
||
175 |
5 |
FIREqualizerContext *s = ctx->priv; |
|
176 |
|||
177 |
5 |
common_uninit(s); |
|
178 |
5 |
av_freep(&s->gain_cmd); |
|
179 |
5 |
av_freep(&s->gain_entry_cmd); |
|
180 |
5 |
} |
|
181 |
|||
182 |
5 |
static int query_formats(AVFilterContext *ctx) |
|
183 |
{ |
||
184 |
AVFilterChannelLayouts *layouts; |
||
185 |
AVFilterFormats *formats; |
||
186 |
static const enum AVSampleFormat sample_fmts[] = { |
||
187 |
AV_SAMPLE_FMT_FLTP, |
||
188 |
AV_SAMPLE_FMT_NONE |
||
189 |
}; |
||
190 |
int ret; |
||
191 |
|||
192 |
5 |
layouts = ff_all_channel_counts(); |
|
193 |
✗✓ | 5 |
if (!layouts) |
194 |
return AVERROR(ENOMEM); |
||
195 |
5 |
ret = ff_set_common_channel_layouts(ctx, layouts); |
|
196 |
✗✓ | 5 |
if (ret < 0) |
197 |
return ret; |
||
198 |
|||
199 |
5 |
formats = ff_make_format_list(sample_fmts); |
|
200 |
✗✓ | 5 |
if (!formats) |
201 |
return AVERROR(ENOMEM); |
||
202 |
5 |
ret = ff_set_common_formats(ctx, formats); |
|
203 |
✗✓ | 5 |
if (ret < 0) |
204 |
return ret; |
||
205 |
|||
206 |
5 |
formats = ff_all_samplerates(); |
|
207 |
✗✓ | 5 |
if (!formats) |
208 |
return AVERROR(ENOMEM); |
||
209 |
5 |
return ff_set_common_samplerates(ctx, formats); |
|
210 |
} |
||
211 |
|||
212 |
914 |
static void fast_convolute(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, float *av_restrict conv_buf, |
|
213 |
OverlapIndex *av_restrict idx, float *av_restrict data, int nsamples) |
||
214 |
{ |
||
215 |
✓✓ | 914 |
if (nsamples <= s->nsamples_max) { |
216 |
760 |
float *buf = conv_buf + idx->buf_idx * s->rdft_len; |
|
217 |
760 |
float *obuf = conv_buf + !idx->buf_idx * s->rdft_len + idx->overlap_idx; |
|
218 |
760 |
int center = s->fir_len/2; |
|
219 |
int k; |
||
220 |
|||
221 |
760 |
memset(buf, 0, center * sizeof(*data)); |
|
222 |
760 |
memcpy(buf + center, data, nsamples * sizeof(*data)); |
|
223 |
760 |
memset(buf + center + nsamples, 0, (s->rdft_len - nsamples - center) * sizeof(*data)); |
|
224 |
760 |
av_rdft_calc(s->rdft, buf); |
|
225 |
|||
226 |
760 |
buf[0] *= kernel_buf[0]; |
|
227 |
760 |
buf[1] *= kernel_buf[s->rdft_len/2]; |
|
228 |
✓✓ | 1802240 |
for (k = 1; k < s->rdft_len/2; k++) { |
229 |
1801480 |
buf[2*k] *= kernel_buf[k]; |
|
230 |
1801480 |
buf[2*k+1] *= kernel_buf[k]; |
|
231 |
} |
||
232 |
|||
233 |
760 |
av_rdft_calc(s->irdft, buf); |
|
234 |
✓✓ | 1924014 |
for (k = 0; k < s->rdft_len - idx->overlap_idx; k++) |
235 |
1923254 |
buf[k] += obuf[k]; |
|
236 |
760 |
memcpy(data, buf, nsamples * sizeof(*data)); |
|
237 |
760 |
idx->buf_idx = !idx->buf_idx; |
|
238 |
760 |
idx->overlap_idx = nsamples; |
|
239 |
} else { |
||
240 |
✓✓ | 522 |
while (nsamples > s->nsamples_max * 2) { |
241 |
368 |
fast_convolute(s, kernel_buf, conv_buf, idx, data, s->nsamples_max); |
|
242 |
368 |
data += s->nsamples_max; |
|
243 |
368 |
nsamples -= s->nsamples_max; |
|
244 |
} |
||
245 |
154 |
fast_convolute(s, kernel_buf, conv_buf, idx, data, nsamples/2); |
|
246 |
154 |
fast_convolute(s, kernel_buf, conv_buf, idx, data + nsamples/2, nsamples - nsamples/2); |
|
247 |
} |
||
248 |
914 |
} |
|
249 |
|||
250 |
static void fast_convolute_nonlinear(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, |
||
251 |
float *av_restrict conv_buf, OverlapIndex *av_restrict idx, |
||
252 |
float *av_restrict data, int nsamples) |
||
253 |
{ |
||
254 |
if (nsamples <= s->nsamples_max) { |
||
255 |
float *buf = conv_buf + idx->buf_idx * s->rdft_len; |
||
256 |
float *obuf = conv_buf + !idx->buf_idx * s->rdft_len + idx->overlap_idx; |
||
257 |
int k; |
||
258 |
|||
259 |
memcpy(buf, data, nsamples * sizeof(*data)); |
||
260 |
memset(buf + nsamples, 0, (s->rdft_len - nsamples) * sizeof(*data)); |
||
261 |
av_rdft_calc(s->rdft, buf); |
||
262 |
|||
263 |
buf[0] *= kernel_buf[0]; |
||
264 |
buf[1] *= kernel_buf[1]; |
||
265 |
for (k = 2; k < s->rdft_len; k += 2) { |
||
266 |
float re, im; |
||
267 |
re = buf[k] * kernel_buf[k] - buf[k+1] * kernel_buf[k+1]; |
||
268 |
im = buf[k] * kernel_buf[k+1] + buf[k+1] * kernel_buf[k]; |
||
269 |
buf[k] = re; |
||
270 |
buf[k+1] = im; |
||
271 |
} |
||
272 |
|||
273 |
av_rdft_calc(s->irdft, buf); |
||
274 |
for (k = 0; k < s->rdft_len - idx->overlap_idx; k++) |
||
275 |
buf[k] += obuf[k]; |
||
276 |
memcpy(data, buf, nsamples * sizeof(*data)); |
||
277 |
idx->buf_idx = !idx->buf_idx; |
||
278 |
idx->overlap_idx = nsamples; |
||
279 |
} else { |
||
280 |
while (nsamples > s->nsamples_max * 2) { |
||
281 |
fast_convolute_nonlinear(s, kernel_buf, conv_buf, idx, data, s->nsamples_max); |
||
282 |
data += s->nsamples_max; |
||
283 |
nsamples -= s->nsamples_max; |
||
284 |
} |
||
285 |
fast_convolute_nonlinear(s, kernel_buf, conv_buf, idx, data, nsamples/2); |
||
286 |
fast_convolute_nonlinear(s, kernel_buf, conv_buf, idx, data + nsamples/2, nsamples - nsamples/2); |
||
287 |
} |
||
288 |
} |
||
289 |
|||
290 |
561 |
static void fast_convolute2(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, FFTComplex *av_restrict conv_buf, |
|
291 |
OverlapIndex *av_restrict idx, float *av_restrict data0, float *av_restrict data1, int nsamples) |
||
292 |
{ |
||
293 |
✓✓ | 561 |
if (nsamples <= s->nsamples_max) { |
294 |
523 |
FFTComplex *buf = conv_buf + idx->buf_idx * s->rdft_len; |
|
295 |
523 |
FFTComplex *obuf = conv_buf + !idx->buf_idx * s->rdft_len + idx->overlap_idx; |
|
296 |
523 |
int center = s->fir_len/2; |
|
297 |
int k; |
||
298 |
float tmp; |
||
299 |
|||
300 |
523 |
memset(buf, 0, center * sizeof(*buf)); |
|
301 |
✓✓ | 548245 |
for (k = 0; k < nsamples; k++) { |
302 |
547722 |
buf[center+k].re = data0[k]; |
|
303 |
547722 |
buf[center+k].im = data1[k]; |
|
304 |
} |
||
305 |
523 |
memset(buf + center + nsamples, 0, (s->rdft_len - nsamples - center) * sizeof(*buf)); |
|
306 |
523 |
av_fft_permute(s->fft_ctx, buf); |
|
307 |
523 |
av_fft_calc(s->fft_ctx, buf); |
|
308 |
|||
309 |
/* swap re <-> im, do backward fft using forward fft_ctx */ |
||
310 |
/* normalize with 0.5f */ |
||
311 |
523 |
tmp = buf[0].re; |
|
312 |
523 |
buf[0].re = 0.5f * kernel_buf[0] * buf[0].im; |
|
313 |
523 |
buf[0].im = 0.5f * kernel_buf[0] * tmp; |
|
314 |
✓✓ | 1346560 |
for (k = 1; k < s->rdft_len/2; k++) { |
315 |
1346037 |
int m = s->rdft_len - k; |
|
316 |
1346037 |
tmp = buf[k].re; |
|
317 |
1346037 |
buf[k].re = 0.5f * kernel_buf[k] * buf[k].im; |
|
318 |
1346037 |
buf[k].im = 0.5f * kernel_buf[k] * tmp; |
|
319 |
1346037 |
tmp = buf[m].re; |
|
320 |
1346037 |
buf[m].re = 0.5f * kernel_buf[k] * buf[m].im; |
|
321 |
1346037 |
buf[m].im = 0.5f * kernel_buf[k] * tmp; |
|
322 |
} |
||
323 |
523 |
tmp = buf[k].re; |
|
324 |
523 |
buf[k].re = 0.5f * kernel_buf[k] * buf[k].im; |
|
325 |
523 |
buf[k].im = 0.5f * kernel_buf[k] * tmp; |
|
326 |
|||
327 |
523 |
av_fft_permute(s->fft_ctx, buf); |
|
328 |
523 |
av_fft_calc(s->fft_ctx, buf); |
|
329 |
|||
330 |
✓✓ | 2147117 |
for (k = 0; k < s->rdft_len - idx->overlap_idx; k++) { |
331 |
2146594 |
buf[k].re += obuf[k].re; |
|
332 |
2146594 |
buf[k].im += obuf[k].im; |
|
333 |
} |
||
334 |
|||
335 |
/* swapped re <-> im */ |
||
336 |
✓✓ | 548245 |
for (k = 0; k < nsamples; k++) { |
337 |
547722 |
data0[k] = buf[k].im; |
|
338 |
547722 |
data1[k] = buf[k].re; |
|
339 |
} |
||
340 |
523 |
idx->buf_idx = !idx->buf_idx; |
|
341 |
523 |
idx->overlap_idx = nsamples; |
|
342 |
} else { |
||
343 |
✓✓ | 220 |
while (nsamples > s->nsamples_max * 2) { |
344 |
182 |
fast_convolute2(s, kernel_buf, conv_buf, idx, data0, data1, s->nsamples_max); |
|
345 |
182 |
data0 += s->nsamples_max; |
|
346 |
182 |
data1 += s->nsamples_max; |
|
347 |
182 |
nsamples -= s->nsamples_max; |
|
348 |
} |
||
349 |
38 |
fast_convolute2(s, kernel_buf, conv_buf, idx, data0, data1, nsamples/2); |
|
350 |
38 |
fast_convolute2(s, kernel_buf, conv_buf, idx, data0 + nsamples/2, data1 + nsamples/2, nsamples - nsamples/2); |
|
351 |
} |
||
352 |
561 |
} |
|
353 |
|||
354 |
static void dump_fir(AVFilterContext *ctx, FILE *fp, int ch) |
||
355 |
{ |
||
356 |
FIREqualizerContext *s = ctx->priv; |
||
357 |
int rate = ctx->inputs[0]->sample_rate; |
||
358 |
int xlog = s->dumpscale == SCALE_LOGLIN || s->dumpscale == SCALE_LOGLOG; |
||
359 |
int ylog = s->dumpscale == SCALE_LINLOG || s->dumpscale == SCALE_LOGLOG; |
||
360 |
int x; |
||
361 |
int center = s->fir_len / 2; |
||
362 |
double delay = s->zero_phase ? 0.0 : (double) center / rate; |
||
363 |
double vx, ya, yb; |
||
364 |
|||
365 |
if (!s->min_phase) { |
||
366 |
s->analysis_buf[0] *= s->rdft_len/2; |
||
367 |
for (x = 1; x <= center; x++) { |
||
368 |
s->analysis_buf[x] *= s->rdft_len/2; |
||
369 |
s->analysis_buf[s->analysis_rdft_len - x] *= s->rdft_len/2; |
||
370 |
} |
||
371 |
} else { |
||
372 |
for (x = 0; x < s->fir_len; x++) |
||
373 |
s->analysis_buf[x] *= s->rdft_len/2; |
||
374 |
} |
||
375 |
|||
376 |
if (ch) |
||
377 |
fprintf(fp, "\n\n"); |
||
378 |
|||
379 |
fprintf(fp, "# time[%d] (time amplitude)\n", ch); |
||
380 |
|||
381 |
if (!s->min_phase) { |
||
382 |
for (x = center; x > 0; x--) |
||
383 |
fprintf(fp, "%15.10f %15.10f\n", delay - (double) x / rate, (double) s->analysis_buf[s->analysis_rdft_len - x]); |
||
384 |
|||
385 |
for (x = 0; x <= center; x++) |
||
386 |
fprintf(fp, "%15.10f %15.10f\n", delay + (double)x / rate , (double) s->analysis_buf[x]); |
||
387 |
} else { |
||
388 |
for (x = 0; x < s->fir_len; x++) |
||
389 |
fprintf(fp, "%15.10f %15.10f\n", (double)x / rate, (double) s->analysis_buf[x]); |
||
390 |
} |
||
391 |
|||
392 |
av_rdft_calc(s->analysis_rdft, s->analysis_buf); |
||
393 |
|||
394 |
fprintf(fp, "\n\n# freq[%d] (frequency desired_gain actual_gain)\n", ch); |
||
395 |
|||
396 |
for (x = 0; x <= s->analysis_rdft_len/2; x++) { |
||
397 |
int i = (x == s->analysis_rdft_len/2) ? 1 : 2 * x; |
||
398 |
vx = (double)x * rate / s->analysis_rdft_len; |
||
399 |
if (xlog) |
||
400 |
vx = log2(0.05*vx); |
||
401 |
ya = s->dump_buf[i]; |
||
402 |
yb = s->min_phase && (i > 1) ? hypotf(s->analysis_buf[i], s->analysis_buf[i+1]) : s->analysis_buf[i]; |
||
403 |
if (s->min_phase) |
||
404 |
yb = fabs(yb); |
||
405 |
if (ylog) { |
||
406 |
ya = 20.0 * log10(fabs(ya)); |
||
407 |
yb = 20.0 * log10(fabs(yb)); |
||
408 |
} |
||
409 |
fprintf(fp, "%17.10f %17.10f %17.10f\n", vx, ya, yb); |
||
410 |
} |
||
411 |
} |
||
412 |
|||
413 |
6 |
static double entry_func(void *p, double freq, double gain) |
|
414 |
{ |
||
415 |
6 |
AVFilterContext *ctx = p; |
|
416 |
6 |
FIREqualizerContext *s = ctx->priv; |
|
417 |
|||
418 |
✗✓ | 6 |
if (s->nb_gain_entry >= NB_GAIN_ENTRY_MAX) { |
419 |
av_log(ctx, AV_LOG_ERROR, "entry table overflow.\n"); |
||
420 |
s->gain_entry_err = AVERROR(EINVAL); |
||
421 |
return 0; |
||
422 |
} |
||
423 |
|||
424 |
✗✓ | 6 |
if (isnan(freq)) { |
425 |
av_log(ctx, AV_LOG_ERROR, "nan frequency (%g, %g).\n", freq, gain); |
||
426 |
s->gain_entry_err = AVERROR(EINVAL); |
||
427 |
return 0; |
||
428 |
} |
||
429 |
|||
430 |
✓✓✗✓ |
6 |
if (s->nb_gain_entry > 0 && freq <= s->gain_entry_tbl[s->nb_gain_entry - 1].freq) { |
431 |
av_log(ctx, AV_LOG_ERROR, "unsorted frequency (%g, %g).\n", freq, gain); |
||
432 |
s->gain_entry_err = AVERROR(EINVAL); |
||
433 |
return 0; |
||
434 |
} |
||
435 |
|||
436 |
6 |
s->gain_entry_tbl[s->nb_gain_entry].freq = freq; |
|
437 |
6 |
s->gain_entry_tbl[s->nb_gain_entry].gain = gain; |
|
438 |
6 |
s->nb_gain_entry++; |
|
439 |
6 |
return 0; |
|
440 |
} |
||
441 |
|||
442 |
9660 |
static int gain_entry_compare(const void *key, const void *memb) |
|
443 |
{ |
||
444 |
9660 |
const double *freq = key; |
|
445 |
9660 |
const GainEntry *entry = memb; |
|
446 |
|||
447 |
✓✓ | 9660 |
if (*freq < entry[0].freq) |
448 |
2972 |
return -1; |
|
449 |
✗✓ | 6688 |
if (*freq > entry[1].freq) |
450 |
return 1; |
||
451 |
6688 |
return 0; |
|
452 |
} |
||
453 |
|||
454 |
16386 |
static double gain_interpolate_func(void *p, double freq) |
|
455 |
{ |
||
456 |
16386 |
AVFilterContext *ctx = p; |
|
457 |
16386 |
FIREqualizerContext *s = ctx->priv; |
|
458 |
GainEntry *res; |
||
459 |
double d0, d1, d; |
||
460 |
|||
461 |
✗✓ | 16386 |
if (isnan(freq)) |
462 |
return freq; |
||
463 |
|||
464 |
✗✓ | 16386 |
if (!s->nb_gain_entry) |
465 |
return 0; |
||
466 |
|||
467 |
✓✓ | 16386 |
if (freq <= s->gain_entry_tbl[0].freq) |
468 |
744 |
return s->gain_entry_tbl[0].gain; |
|
469 |
|||
470 |
✓✓ | 15642 |
if (freq >= s->gain_entry_tbl[s->nb_gain_entry-1].freq) |
471 |
8954 |
return s->gain_entry_tbl[s->nb_gain_entry-1].gain; |
|
472 |
|||
473 |
6688 |
res = bsearch(&freq, &s->gain_entry_tbl, s->nb_gain_entry - 1, sizeof(*res), gain_entry_compare); |
|
474 |
✗✓ | 6688 |
av_assert0(res); |
475 |
|||
476 |
6688 |
d = res[1].freq - res[0].freq; |
|
477 |
6688 |
d0 = freq - res[0].freq; |
|
478 |
6688 |
d1 = res[1].freq - freq; |
|
479 |
|||
480 |
✓✗✓✗ |
6688 |
if (d0 && d1) |
481 |
6688 |
return (d0 * res[1].gain + d1 * res[0].gain) / d; |
|
482 |
|||
483 |
if (d0) |
||
484 |
return res[1].gain; |
||
485 |
|||
486 |
return res[0].gain; |
||
487 |
} |
||
488 |
|||
489 |
static double cubic_interpolate_func(void *p, double freq) |
||
490 |
{ |
||
491 |
AVFilterContext *ctx = p; |
||
492 |
FIREqualizerContext *s = ctx->priv; |
||
493 |
GainEntry *res; |
||
494 |
double x, x2, x3; |
||
495 |
double a, b, c, d; |
||
496 |
double m0, m1, m2, msum, unit; |
||
497 |
|||
498 |
if (!s->nb_gain_entry) |
||
499 |
return 0; |
||
500 |
|||
501 |
if (freq <= s->gain_entry_tbl[0].freq) |
||
502 |
return s->gain_entry_tbl[0].gain; |
||
503 |
|||
504 |
if (freq >= s->gain_entry_tbl[s->nb_gain_entry-1].freq) |
||
505 |
return s->gain_entry_tbl[s->nb_gain_entry-1].gain; |
||
506 |
|||
507 |
res = bsearch(&freq, &s->gain_entry_tbl, s->nb_gain_entry - 1, sizeof(*res), gain_entry_compare); |
||
508 |
av_assert0(res); |
||
509 |
|||
510 |
unit = res[1].freq - res[0].freq; |
||
511 |
m0 = res != s->gain_entry_tbl ? |
||
512 |
unit * (res[0].gain - res[-1].gain) / (res[0].freq - res[-1].freq) : 0; |
||
513 |
m1 = res[1].gain - res[0].gain; |
||
514 |
m2 = res != s->gain_entry_tbl + s->nb_gain_entry - 2 ? |
||
515 |
unit * (res[2].gain - res[1].gain) / (res[2].freq - res[1].freq) : 0; |
||
516 |
|||
517 |
msum = fabs(m0) + fabs(m1); |
||
518 |
m0 = msum > 0 ? (fabs(m0) * m1 + fabs(m1) * m0) / msum : 0; |
||
519 |
msum = fabs(m1) + fabs(m2); |
||
520 |
m1 = msum > 0 ? (fabs(m1) * m2 + fabs(m2) * m1) / msum : 0; |
||
521 |
|||
522 |
d = res[0].gain; |
||
523 |
c = m0; |
||
524 |
b = 3 * res[1].gain - m1 - 2 * c - 3 * d; |
||
525 |
a = res[1].gain - b - c - d; |
||
526 |
|||
527 |
x = (freq - res[0].freq) / unit; |
||
528 |
x2 = x * x; |
||
529 |
x3 = x2 * x; |
||
530 |
|||
531 |
return a * x3 + b * x2 + c * x + d; |
||
532 |
} |
||
533 |
|||
534 |
static const char *const var_names[] = { |
||
535 |
"f", |
||
536 |
"sr", |
||
537 |
"ch", |
||
538 |
"chid", |
||
539 |
"chs", |
||
540 |
"chlayout", |
||
541 |
NULL |
||
542 |
}; |
||
543 |
|||
544 |
enum VarOffset { |
||
545 |
VAR_F, |
||
546 |
VAR_SR, |
||
547 |
VAR_CH, |
||
548 |
VAR_CHID, |
||
549 |
VAR_CHS, |
||
550 |
VAR_CHLAYOUT, |
||
551 |
VAR_NB |
||
552 |
}; |
||
553 |
|||
554 |
static void generate_min_phase_kernel(FIREqualizerContext *s, float *rdft_buf) |
||
555 |
{ |
||
556 |
int k, cepstrum_len = s->cepstrum_len, rdft_len = s->rdft_len; |
||
557 |
double norm = 2.0 / cepstrum_len; |
||
558 |
double minval = 1e-7 / rdft_len; |
||
559 |
|||
560 |
memset(s->cepstrum_buf, 0, cepstrum_len * sizeof(*s->cepstrum_buf)); |
||
561 |
memcpy(s->cepstrum_buf, rdft_buf, rdft_len/2 * sizeof(*rdft_buf)); |
||
562 |
memcpy(s->cepstrum_buf + cepstrum_len - rdft_len/2, rdft_buf + rdft_len/2, rdft_len/2 * sizeof(*rdft_buf)); |
||
563 |
|||
564 |
av_rdft_calc(s->cepstrum_rdft, s->cepstrum_buf); |
||
565 |
|||
566 |
s->cepstrum_buf[0] = log(FFMAX(s->cepstrum_buf[0], minval)); |
||
567 |
s->cepstrum_buf[1] = log(FFMAX(s->cepstrum_buf[1], minval)); |
||
568 |
|||
569 |
for (k = 2; k < cepstrum_len; k += 2) { |
||
570 |
s->cepstrum_buf[k] = log(FFMAX(s->cepstrum_buf[k], minval)); |
||
571 |
s->cepstrum_buf[k+1] = 0; |
||
572 |
} |
||
573 |
|||
574 |
av_rdft_calc(s->cepstrum_irdft, s->cepstrum_buf); |
||
575 |
|||
576 |
memset(s->cepstrum_buf + cepstrum_len/2 + 1, 0, (cepstrum_len/2 - 1) * sizeof(*s->cepstrum_buf)); |
||
577 |
for (k = 1; k < cepstrum_len/2; k++) |
||
578 |
s->cepstrum_buf[k] *= 2; |
||
579 |
|||
580 |
av_rdft_calc(s->cepstrum_rdft, s->cepstrum_buf); |
||
581 |
|||
582 |
s->cepstrum_buf[0] = exp(s->cepstrum_buf[0] * norm) * norm; |
||
583 |
s->cepstrum_buf[1] = exp(s->cepstrum_buf[1] * norm) * norm; |
||
584 |
for (k = 2; k < cepstrum_len; k += 2) { |
||
585 |
double mag = exp(s->cepstrum_buf[k] * norm) * norm; |
||
586 |
double ph = s->cepstrum_buf[k+1] * norm; |
||
587 |
s->cepstrum_buf[k] = mag * cos(ph); |
||
588 |
s->cepstrum_buf[k+1] = mag * sin(ph); |
||
589 |
} |
||
590 |
|||
591 |
av_rdft_calc(s->cepstrum_irdft, s->cepstrum_buf); |
||
592 |
memset(rdft_buf, 0, s->rdft_len * sizeof(*rdft_buf)); |
||
593 |
memcpy(rdft_buf, s->cepstrum_buf, s->fir_len * sizeof(*rdft_buf)); |
||
594 |
|||
595 |
if (s->dumpfile) { |
||
596 |
memset(s->analysis_buf, 0, s->analysis_rdft_len * sizeof(*s->analysis_buf)); |
||
597 |
memcpy(s->analysis_buf, s->cepstrum_buf, s->fir_len * sizeof(*s->analysis_buf)); |
||
598 |
} |
||
599 |
|||
600 |
} |
||
601 |
|||
602 |
5 |
static int generate_kernel(AVFilterContext *ctx, const char *gain, const char *gain_entry) |
|
603 |
{ |
||
604 |
5 |
FIREqualizerContext *s = ctx->priv; |
|
605 |
5 |
AVFilterLink *inlink = ctx->inputs[0]; |
|
606 |
5 |
const char *gain_entry_func_names[] = { "entry", NULL }; |
|
607 |
5 |
const char *gain_func_names[] = { "gain_interpolate", "cubic_interpolate", NULL }; |
|
608 |
5 |
double (*gain_entry_funcs[])(void *, double, double) = { entry_func, NULL }; |
|
609 |
5 |
double (*gain_funcs[])(void *, double) = { gain_interpolate_func, cubic_interpolate_func, NULL }; |
|
610 |
double vars[VAR_NB]; |
||
611 |
AVExpr *gain_expr; |
||
612 |
int ret, k, center, ch; |
||
613 |
✓✗✗✓ |
5 |
int xlog = s->scale == SCALE_LOGLIN || s->scale == SCALE_LOGLOG; |
614 |
✗✓✗✗ |
5 |
int ylog = s->scale == SCALE_LINLOG || s->scale == SCALE_LOGLOG; |
615 |
5 |
FILE *dump_fp = NULL; |
|
616 |
|||
617 |
5 |
s->nb_gain_entry = 0; |
|
618 |
5 |
s->gain_entry_err = 0; |
|
619 |
✓✓ | 5 |
if (gain_entry) { |
620 |
2 |
double result = 0.0; |
|
621 |
2 |
ret = av_expr_parse_and_eval(&result, gain_entry, NULL, NULL, NULL, NULL, |
|
622 |
gain_entry_func_names, gain_entry_funcs, ctx, 0, ctx); |
||
623 |
✗✓ | 2 |
if (ret < 0) |
624 |
return ret; |
||
625 |
✗✓ | 2 |
if (s->gain_entry_err < 0) |
626 |
return s->gain_entry_err; |
||
627 |
} |
||
628 |
|||
629 |
5 |
av_log(ctx, AV_LOG_DEBUG, "nb_gain_entry = %d.\n", s->nb_gain_entry); |
|
630 |
|||
631 |
5 |
ret = av_expr_parse(&gain_expr, gain, var_names, |
|
632 |
gain_func_names, gain_funcs, NULL, NULL, 0, ctx); |
||
633 |
✗✓ | 5 |
if (ret < 0) |
634 |
return ret; |
||
635 |
|||
636 |
✗✓✗✗ ✗✗✗✗ |
5 |
if (s->dumpfile && (!s->dump_buf || !s->analysis_rdft || !(dump_fp = fopen(s->dumpfile, "w")))) |
637 |
av_log(ctx, AV_LOG_WARNING, "dumping failed.\n"); |
||
638 |
|||
639 |
5 |
vars[VAR_CHS] = inlink->channels; |
|
640 |
5 |
vars[VAR_CHLAYOUT] = inlink->channel_layout; |
|
641 |
5 |
vars[VAR_SR] = inlink->sample_rate; |
|
642 |
✓✓ | 9 |
for (ch = 0; ch < inlink->channels; ch++) { |
643 |
7 |
float *rdft_buf = s->kernel_tmp_buf + ch * s->rdft_len; |
|
644 |
double result; |
||
645 |
7 |
vars[VAR_CH] = ch; |
|
646 |
7 |
vars[VAR_CHID] = av_channel_layout_extract_channel(inlink->channel_layout, ch); |
|
647 |
7 |
vars[VAR_F] = 0.0; |
|
648 |
✗✓ | 7 |
if (xlog) |
649 |
vars[VAR_F] = log2(0.05 * vars[VAR_F]); |
||
650 |
7 |
result = av_expr_eval(gain_expr, vars, ctx); |
|
651 |
✓✗ | 7 |
s->analysis_buf[0] = ylog ? pow(10.0, 0.05 * result) : result; |
652 |
|||
653 |
7 |
vars[VAR_F] = 0.5 * inlink->sample_rate; |
|
654 |
✗✓ | 7 |
if (xlog) |
655 |
vars[VAR_F] = log2(0.05 * vars[VAR_F]); |
||
656 |
7 |
result = av_expr_eval(gain_expr, vars, ctx); |
|
657 |
✓✗ | 7 |
s->analysis_buf[1] = ylog ? pow(10.0, 0.05 * result) : result; |
658 |
|||
659 |
✓✓ | 57344 |
for (k = 1; k < s->analysis_rdft_len/2; k++) { |
660 |
57337 |
vars[VAR_F] = k * ((double)inlink->sample_rate /(double)s->analysis_rdft_len); |
|
661 |
✗✓ | 57337 |
if (xlog) |
662 |
vars[VAR_F] = log2(0.05 * vars[VAR_F]); |
||
663 |
57337 |
result = av_expr_eval(gain_expr, vars, ctx); |
|
664 |
✓✗✗✗ |
57337 |
s->analysis_buf[2*k] = ylog ? pow(10.0, 0.05 * result) : s->min_phase ? fabs(result) : result; |
665 |
57337 |
s->analysis_buf[2*k+1] = 0.0; |
|
666 |
} |
||
667 |
|||
668 |
✗✓ | 7 |
if (s->dump_buf) |
669 |
memcpy(s->dump_buf, s->analysis_buf, s->analysis_rdft_len * sizeof(*s->analysis_buf)); |
||
670 |
|||
671 |
7 |
av_rdft_calc(s->analysis_irdft, s->analysis_buf); |
|
672 |
7 |
center = s->fir_len / 2; |
|
673 |
|||
674 |
✓✓ | 16331 |
for (k = 0; k <= center; k++) { |
675 |
16324 |
double u = k * (M_PI/center); |
|
676 |
double win; |
||
677 |
✗✓✗✗ ✗✗✓✗ ✗✗✗ |
16324 |
switch (s->wfunc) { |
678 |
case WFUNC_RECTANGULAR: |
||
679 |
win = 1.0; |
||
680 |
break; |
||
681 |
7502 |
case WFUNC_HANN: |
|
682 |
7502 |
win = 0.5 + 0.5 * cos(u); |
|
683 |
7502 |
break; |
|
684 |
case WFUNC_HAMMING: |
||
685 |
win = 0.53836 + 0.46164 * cos(u); |
||
686 |
break; |
||
687 |
case WFUNC_BLACKMAN: |
||
688 |
win = 0.42 + 0.5 * cos(u) + 0.08 * cos(2*u); |
||
689 |
break; |
||
690 |
case WFUNC_NUTTALL3: |
||
691 |
win = 0.40897 + 0.5 * cos(u) + 0.09103 * cos(2*u); |
||
692 |
break; |
||
693 |
case WFUNC_MNUTTALL3: |
||
694 |
win = 0.4243801 + 0.4973406 * cos(u) + 0.0782793 * cos(2*u); |
||
695 |
break; |
||
696 |
8822 |
case WFUNC_NUTTALL: |
|
697 |
8822 |
win = 0.355768 + 0.487396 * cos(u) + 0.144232 * cos(2*u) + 0.012604 * cos(3*u); |
|
698 |
8822 |
break; |
|
699 |
case WFUNC_BNUTTALL: |
||
700 |
win = 0.3635819 + 0.4891775 * cos(u) + 0.1365995 * cos(2*u) + 0.0106411 * cos(3*u); |
||
701 |
break; |
||
702 |
case WFUNC_BHARRIS: |
||
703 |
win = 0.35875 + 0.48829 * cos(u) + 0.14128 * cos(2*u) + 0.01168 * cos(3*u); |
||
704 |
break; |
||
705 |
case WFUNC_TUKEY: |
||
706 |
win = (u <= 0.5 * M_PI) ? 1.0 : (0.5 + 0.5 * cos(2*u - M_PI)); |
||
707 |
break; |
||
708 |
default: |
||
709 |
av_assert0(0); |
||
710 |
} |
||
711 |
16324 |
s->analysis_buf[k] *= (2.0/s->analysis_rdft_len) * (2.0/s->rdft_len) * win; |
|
712 |
✓✓ | 16324 |
if (k) |
713 |
16317 |
s->analysis_buf[s->analysis_rdft_len - k] = s->analysis_buf[k]; |
|
714 |
} |
||
715 |
|||
716 |
7 |
memset(s->analysis_buf + center + 1, 0, (s->analysis_rdft_len - s->fir_len) * sizeof(*s->analysis_buf)); |
|
717 |
7 |
memcpy(rdft_buf, s->analysis_buf, s->rdft_len/2 * sizeof(*s->analysis_buf)); |
|
718 |
7 |
memcpy(rdft_buf + s->rdft_len/2, s->analysis_buf + s->analysis_rdft_len - s->rdft_len/2, s->rdft_len/2 * sizeof(*s->analysis_buf)); |
|
719 |
✗✓ | 7 |
if (s->min_phase) |
720 |
generate_min_phase_kernel(s, rdft_buf); |
||
721 |
7 |
av_rdft_calc(s->rdft, rdft_buf); |
|
722 |
|||
723 |
✓✓ | 61447 |
for (k = 0; k < s->rdft_len; k++) { |
724 |
✓✗✗✓ |
61440 |
if (isnan(rdft_buf[k]) || isinf(rdft_buf[k])) { |
725 |
av_log(ctx, AV_LOG_ERROR, "filter kernel contains nan or infinity.\n"); |
||
726 |
av_expr_free(gain_expr); |
||
727 |
if (dump_fp) |
||
728 |
fclose(dump_fp); |
||
729 |
return AVERROR(EINVAL); |
||
730 |
} |
||
731 |
} |
||
732 |
|||
733 |
✓✗ | 7 |
if (!s->min_phase) { |
734 |
7 |
rdft_buf[s->rdft_len-1] = rdft_buf[1]; |
|
735 |
✓✓ | 30727 |
for (k = 0; k < s->rdft_len/2; k++) |
736 |
30720 |
rdft_buf[k] = rdft_buf[2*k]; |
|
737 |
7 |
rdft_buf[s->rdft_len/2] = rdft_buf[s->rdft_len-1]; |
|
738 |
} |
||
739 |
|||
740 |
✗✓ | 7 |
if (dump_fp) |
741 |
dump_fir(ctx, dump_fp, ch); |
||
742 |
|||
743 |
✓✓ | 7 |
if (!s->multi) |
744 |
3 |
break; |
|
745 |
} |
||
746 |
|||
747 |
✓✓ | 5 |
memcpy(s->kernel_buf, s->kernel_tmp_buf, (s->multi ? inlink->channels : 1) * s->rdft_len * sizeof(*s->kernel_buf)); |
748 |
5 |
av_expr_free(gain_expr); |
|
749 |
✗✓ | 5 |
if (dump_fp) |
750 |
fclose(dump_fp); |
||
751 |
5 |
return 0; |
|
752 |
} |
||
753 |
|||
754 |
#define SELECT_GAIN(s) (s->gain_cmd ? s->gain_cmd : s->gain) |
||
755 |
#define SELECT_GAIN_ENTRY(s) (s->gain_entry_cmd ? s->gain_entry_cmd : s->gain_entry) |
||
756 |
|||
757 |
5 |
static int config_input(AVFilterLink *inlink) |
|
758 |
{ |
||
759 |
5 |
AVFilterContext *ctx = inlink->dst; |
|
760 |
5 |
FIREqualizerContext *s = ctx->priv; |
|
761 |
int rdft_bits; |
||
762 |
|||
763 |
5 |
common_uninit(s); |
|
764 |
|||
765 |
5 |
s->next_pts = 0; |
|
766 |
5 |
s->frame_nsamples_max = 0; |
|
767 |
|||
768 |
5 |
s->fir_len = FFMAX(2 * (int)(inlink->sample_rate * s->delay) + 1, 3); |
|
769 |
5 |
s->remaining = s->fir_len - 1; |
|
770 |
|||
771 |
✓✗ | 47 |
for (rdft_bits = RDFT_BITS_MIN; rdft_bits <= RDFT_BITS_MAX; rdft_bits++) { |
772 |
47 |
s->rdft_len = 1 << rdft_bits; |
|
773 |
47 |
s->nsamples_max = s->rdft_len - s->fir_len + 1; |
|
774 |
✓✓ | 47 |
if (s->nsamples_max * 2 >= s->fir_len) |
775 |
5 |
break; |
|
776 |
} |
||
777 |
|||
778 |
✗✓ | 5 |
if (rdft_bits > RDFT_BITS_MAX) { |
779 |
av_log(ctx, AV_LOG_ERROR, "too large delay, please decrease it.\n"); |
||
780 |
return AVERROR(EINVAL); |
||
781 |
} |
||
782 |
|||
783 |
✓✗✗✓ |
5 |
if (!(s->rdft = av_rdft_init(rdft_bits, DFT_R2C)) || !(s->irdft = av_rdft_init(rdft_bits, IDFT_C2R))) |
784 |
return AVERROR(ENOMEM); |
||
785 |
|||
786 |
✓✓✓✗ ✓✗✗✓ |
5 |
if (s->fft2 && !s->multi && inlink->channels > 1 && !(s->fft_ctx = av_fft_init(rdft_bits, 0))) |
787 |
return AVERROR(ENOMEM); |
||
788 |
|||
789 |
✗✓ | 5 |
if (s->min_phase) { |
790 |
int cepstrum_bits = rdft_bits + 2; |
||
791 |
if (cepstrum_bits > RDFT_BITS_MAX) { |
||
792 |
av_log(ctx, AV_LOG_ERROR, "too large delay, please decrease it.\n"); |
||
793 |
return AVERROR(EINVAL); |
||
794 |
} |
||
795 |
|||
796 |
cepstrum_bits = FFMIN(RDFT_BITS_MAX, cepstrum_bits + 1); |
||
797 |
s->cepstrum_rdft = av_rdft_init(cepstrum_bits, DFT_R2C); |
||
798 |
s->cepstrum_irdft = av_rdft_init(cepstrum_bits, IDFT_C2R); |
||
799 |
if (!s->cepstrum_rdft || !s->cepstrum_irdft) |
||
800 |
return AVERROR(ENOMEM); |
||
801 |
|||
802 |
s->cepstrum_len = 1 << cepstrum_bits; |
||
803 |
s->cepstrum_buf = av_malloc_array(s->cepstrum_len, sizeof(*s->cepstrum_buf)); |
||
804 |
if (!s->cepstrum_buf) |
||
805 |
return AVERROR(ENOMEM); |
||
806 |
} |
||
807 |
|||
808 |
✓✗ | 13 |
for ( ; rdft_bits <= RDFT_BITS_MAX; rdft_bits++) { |
809 |
13 |
s->analysis_rdft_len = 1 << rdft_bits; |
|
810 |
✓✓ | 13 |
if (inlink->sample_rate <= s->accuracy * s->analysis_rdft_len) |
811 |
5 |
break; |
|
812 |
} |
||
813 |
|||
814 |
✗✓ | 5 |
if (rdft_bits > RDFT_BITS_MAX) { |
815 |
av_log(ctx, AV_LOG_ERROR, "too small accuracy, please increase it.\n"); |
||
816 |
return AVERROR(EINVAL); |
||
817 |
} |
||
818 |
|||
819 |
✗✓ | 5 |
if (!(s->analysis_irdft = av_rdft_init(rdft_bits, IDFT_C2R))) |
820 |
return AVERROR(ENOMEM); |
||
821 |
|||
822 |
✗✓ | 5 |
if (s->dumpfile) { |
823 |
s->analysis_rdft = av_rdft_init(rdft_bits, DFT_R2C); |
||
824 |
s->dump_buf = av_malloc_array(s->analysis_rdft_len, sizeof(*s->dump_buf)); |
||
825 |
} |
||
826 |
|||
827 |
5 |
s->analysis_buf = av_malloc_array(s->analysis_rdft_len, sizeof(*s->analysis_buf)); |
|
828 |
✓✓ | 5 |
s->kernel_tmp_buf = av_malloc_array(s->rdft_len * (s->multi ? inlink->channels : 1), sizeof(*s->kernel_tmp_buf)); |
829 |
✓✓ | 5 |
s->kernel_buf = av_malloc_array(s->rdft_len * (s->multi ? inlink->channels : 1), sizeof(*s->kernel_buf)); |
830 |
5 |
s->conv_buf = av_calloc(2 * s->rdft_len * inlink->channels, sizeof(*s->conv_buf)); |
|
831 |
5 |
s->conv_idx = av_calloc(inlink->channels, sizeof(*s->conv_idx)); |
|
832 |
✓✗✓✗ ✓✗✓✗ ✗✓ |
5 |
if (!s->analysis_buf || !s->kernel_tmp_buf || !s->kernel_buf || !s->conv_buf || !s->conv_idx) |
833 |
return AVERROR(ENOMEM); |
||
834 |
|||
835 |
5 |
av_log(ctx, AV_LOG_DEBUG, "sample_rate = %d, channels = %d, analysis_rdft_len = %d, rdft_len = %d, fir_len = %d, nsamples_max = %d.\n", |
|
836 |
inlink->sample_rate, inlink->channels, s->analysis_rdft_len, s->rdft_len, s->fir_len, s->nsamples_max); |
||
837 |
|||
838 |
✓✓ | 5 |
if (s->fixed) |
839 |
1 |
inlink->min_samples = inlink->max_samples = inlink->partial_buf_size = s->nsamples_max; |
|
840 |
|||
841 |
✗✓✗✓ |
5 |
return generate_kernel(ctx, SELECT_GAIN(s), SELECT_GAIN_ENTRY(s)); |
842 |
} |
||
843 |
|||
844 |
422 |
static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
|
845 |
{ |
||
846 |
422 |
AVFilterContext *ctx = inlink->dst; |
|
847 |
422 |
FIREqualizerContext *s = ctx->priv; |
|
848 |
int ch; |
||
849 |
|||
850 |
✓✗ | 422 |
if (!s->min_phase) { |
851 |
✓✓✓✓ |
725 |
for (ch = 0; ch + 1 < inlink->channels && s->fft_ctx; ch += 2) { |
852 |
303 |
fast_convolute2(s, s->kernel_buf, (FFTComplex *)(s->conv_buf + 2 * ch * s->rdft_len), |
|
853 |
303 |
s->conv_idx + ch, (float *) frame->extended_data[ch], |
|
854 |
303 |
(float *) frame->extended_data[ch+1], frame->nb_samples); |
|
855 |
} |
||
856 |
|||
857 |
✓✓ | 660 |
for ( ; ch < inlink->channels; ch++) { |
858 |
238 |
fast_convolute(s, s->kernel_buf + (s->multi ? ch * s->rdft_len : 0), |
|
859 |
238 |
s->conv_buf + 2 * ch * s->rdft_len, s->conv_idx + ch, |
|
860 |
✓✓ | 238 |
(float *) frame->extended_data[ch], frame->nb_samples); |
861 |
} |
||
862 |
} else { |
||
863 |
for (ch = 0; ch < inlink->channels; ch++) { |
||
864 |
fast_convolute_nonlinear(s, s->kernel_buf + (s->multi ? ch * s->rdft_len : 0), |
||
865 |
s->conv_buf + 2 * ch * s->rdft_len, s->conv_idx + ch, |
||
866 |
(float *) frame->extended_data[ch], frame->nb_samples); |
||
867 |
} |
||
868 |
} |
||
869 |
|||
870 |
422 |
s->next_pts = AV_NOPTS_VALUE; |
|
871 |
✓✗ | 422 |
if (frame->pts != AV_NOPTS_VALUE) { |
872 |
422 |
s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, av_make_q(1, inlink->sample_rate), inlink->time_base); |
|
873 |
✓✓✓✗ |
422 |
if (s->zero_phase && !s->min_phase) |
874 |
38 |
frame->pts -= av_rescale_q(s->fir_len/2, av_make_q(1, inlink->sample_rate), inlink->time_base); |
|
875 |
} |
||
876 |
422 |
s->frame_nsamples_max = FFMAX(s->frame_nsamples_max, frame->nb_samples); |
|
877 |
422 |
return ff_filter_frame(ctx->outputs[0], frame); |
|
878 |
} |
||
879 |
|||
880 |
655 |
static int request_frame(AVFilterLink *outlink) |
|
881 |
{ |
||
882 |
655 |
AVFilterContext *ctx = outlink->src; |
|
883 |
655 |
FIREqualizerContext *s= ctx->priv; |
|
884 |
int ret; |
||
885 |
|||
886 |
655 |
ret = ff_request_frame(ctx->inputs[0]); |
|
887 |
✓✓✓✓ ✓✗ |
655 |
if (ret == AVERROR_EOF && s->remaining > 0 && s->frame_nsamples_max > 0) { |
888 |
10 |
AVFrame *frame = ff_get_audio_buffer(outlink, FFMIN(s->remaining, s->frame_nsamples_max)); |
|
889 |
|||
890 |
✗✓ | 10 |
if (!frame) |
891 |
return AVERROR(ENOMEM); |
||
892 |
|||
893 |
10 |
av_samples_set_silence(frame->extended_data, 0, frame->nb_samples, outlink->channels, frame->format); |
|
894 |
10 |
frame->pts = s->next_pts; |
|
895 |
10 |
s->remaining -= frame->nb_samples; |
|
896 |
10 |
ret = filter_frame(ctx->inputs[0], frame); |
|
897 |
} |
||
898 |
|||
899 |
655 |
return ret; |
|
900 |
} |
||
901 |
|||
902 |
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
||
903 |
char *res, int res_len, int flags) |
||
904 |
{ |
||
905 |
FIREqualizerContext *s = ctx->priv; |
||
906 |
int ret = AVERROR(ENOSYS); |
||
907 |
|||
908 |
if (!strcmp(cmd, "gain")) { |
||
909 |
char *gain_cmd; |
||
910 |
|||
911 |
if (SELECT_GAIN(s) && !strcmp(SELECT_GAIN(s), args)) { |
||
912 |
av_log(ctx, AV_LOG_DEBUG, "equal gain, do not rebuild.\n"); |
||
913 |
return 0; |
||
914 |
} |
||
915 |
|||
916 |
gain_cmd = av_strdup(args); |
||
917 |
if (!gain_cmd) |
||
918 |
return AVERROR(ENOMEM); |
||
919 |
|||
920 |
ret = generate_kernel(ctx, gain_cmd, SELECT_GAIN_ENTRY(s)); |
||
921 |
if (ret >= 0) { |
||
922 |
av_freep(&s->gain_cmd); |
||
923 |
s->gain_cmd = gain_cmd; |
||
924 |
} else { |
||
925 |
av_freep(&gain_cmd); |
||
926 |
} |
||
927 |
} else if (!strcmp(cmd, "gain_entry")) { |
||
928 |
char *gain_entry_cmd; |
||
929 |
|||
930 |
if (SELECT_GAIN_ENTRY(s) && !strcmp(SELECT_GAIN_ENTRY(s), args)) { |
||
931 |
av_log(ctx, AV_LOG_DEBUG, "equal gain_entry, do not rebuild.\n"); |
||
932 |
return 0; |
||
933 |
} |
||
934 |
|||
935 |
gain_entry_cmd = av_strdup(args); |
||
936 |
if (!gain_entry_cmd) |
||
937 |
return AVERROR(ENOMEM); |
||
938 |
|||
939 |
ret = generate_kernel(ctx, SELECT_GAIN(s), gain_entry_cmd); |
||
940 |
if (ret >= 0) { |
||
941 |
av_freep(&s->gain_entry_cmd); |
||
942 |
s->gain_entry_cmd = gain_entry_cmd; |
||
943 |
} else { |
||
944 |
av_freep(&gain_entry_cmd); |
||
945 |
} |
||
946 |
} |
||
947 |
|||
948 |
return ret; |
||
949 |
} |
||
950 |
|||
951 |
static const AVFilterPad firequalizer_inputs[] = { |
||
952 |
{ |
||
953 |
.name = "default", |
||
954 |
.config_props = config_input, |
||
955 |
.filter_frame = filter_frame, |
||
956 |
.type = AVMEDIA_TYPE_AUDIO, |
||
957 |
.needs_writable = 1, |
||
958 |
}, |
||
959 |
{ NULL } |
||
960 |
}; |
||
961 |
|||
962 |
static const AVFilterPad firequalizer_outputs[] = { |
||
963 |
{ |
||
964 |
.name = "default", |
||
965 |
.request_frame = request_frame, |
||
966 |
.type = AVMEDIA_TYPE_AUDIO, |
||
967 |
}, |
||
968 |
{ NULL } |
||
969 |
}; |
||
970 |
|||
971 |
AVFilter ff_af_firequalizer = { |
||
972 |
.name = "firequalizer", |
||
973 |
.description = NULL_IF_CONFIG_SMALL("Finite Impulse Response Equalizer."), |
||
974 |
.uninit = uninit, |
||
975 |
.query_formats = query_formats, |
||
976 |
.process_command = process_command, |
||
977 |
.priv_size = sizeof(FIREqualizerContext), |
||
978 |
.inputs = firequalizer_inputs, |
||
979 |
.outputs = firequalizer_outputs, |
||
980 |
.priv_class = &firequalizer_class, |
||
981 |
}; |
Generated by: GCOVR (Version 4.2) |