1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen, Damien Zammit and others |
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 "avfilter.h" |
23 |
|
|
#include "internal.h" |
24 |
|
|
#include "audio.h" |
25 |
|
|
|
26 |
|
|
typedef struct BiquadCoeffs { |
27 |
|
|
double a0, a1, a2, b1, b2; |
28 |
|
|
} BiquadCoeffs; |
29 |
|
|
|
30 |
|
|
typedef struct RIAACurve { |
31 |
|
|
BiquadCoeffs r1; |
32 |
|
|
BiquadCoeffs brickw; |
33 |
|
|
int use_brickw; |
34 |
|
|
} RIAACurve; |
35 |
|
|
|
36 |
|
|
typedef struct AudioEmphasisContext { |
37 |
|
|
const AVClass *class; |
38 |
|
|
int mode, type; |
39 |
|
|
double level_in, level_out; |
40 |
|
|
|
41 |
|
|
RIAACurve rc; |
42 |
|
|
|
43 |
|
|
AVFrame *w; |
44 |
|
|
} AudioEmphasisContext; |
45 |
|
|
|
46 |
|
|
#define OFFSET(x) offsetof(AudioEmphasisContext, x) |
47 |
|
|
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM |
48 |
|
|
|
49 |
|
|
static const AVOption aemphasis_options[] = { |
50 |
|
|
{ "level_in", "set input gain", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 64, FLAGS }, |
51 |
|
|
{ "level_out", "set output gain", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 64, FLAGS }, |
52 |
|
|
{ "mode", "set filter mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, |
53 |
|
|
{ "reproduction", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, |
54 |
|
|
{ "production", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, |
55 |
|
|
{ "type", "set filter type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=4}, 0, 8, FLAGS, "type" }, |
56 |
|
|
{ "col", "Columbia", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, |
57 |
|
|
{ "emi", "EMI", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, |
58 |
|
|
{ "bsi", "BSI (78RPM)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "type" }, |
59 |
|
|
{ "riaa", "RIAA", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "type" }, |
60 |
|
|
{ "cd", "Compact Disc (CD)", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "type" }, |
61 |
|
|
{ "50fm", "50µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, "type" }, |
62 |
|
|
{ "75fm", "75µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, "type" }, |
63 |
|
|
{ "50kf", "50µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, "type" }, |
64 |
|
|
{ "75kf", "75µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, "type" }, |
65 |
|
|
{ NULL } |
66 |
|
|
}; |
67 |
|
|
|
68 |
|
|
AVFILTER_DEFINE_CLASS(aemphasis); |
69 |
|
|
|
70 |
|
1554 |
static inline void biquad_process(BiquadCoeffs *bq, double *dst, const double *src, int nb_samples, |
71 |
|
|
double *w, double level_in, double level_out) |
72 |
|
|
{ |
73 |
|
1554 |
const double a0 = bq->a0; |
74 |
|
1554 |
const double a1 = bq->a1; |
75 |
|
1554 |
const double a2 = bq->a2; |
76 |
|
1554 |
const double b1 = bq->b1; |
77 |
|
1554 |
const double b2 = bq->b2; |
78 |
|
1554 |
double w1 = w[0]; |
79 |
|
1554 |
double w2 = w[1]; |
80 |
|
|
|
81 |
✓✓ |
1589154 |
for (int i = 0; i < nb_samples; i++) { |
82 |
|
1587600 |
double n = src[i] * level_in; |
83 |
|
1587600 |
double tmp = n - w1 * b1 - w2 * b2; |
84 |
|
1587600 |
double out = tmp * a0 + w1 * a1 + w2 * a2; |
85 |
|
|
|
86 |
|
1587600 |
w2 = w1; |
87 |
|
1587600 |
w1 = tmp; |
88 |
|
|
|
89 |
|
1587600 |
dst[i] = out * level_out; |
90 |
|
|
} |
91 |
|
|
|
92 |
|
1554 |
w[0] = w1; |
93 |
|
1554 |
w[1] = w2; |
94 |
|
1554 |
} |
95 |
|
|
|
96 |
|
|
typedef struct ThreadData { |
97 |
|
|
AVFrame *in, *out; |
98 |
|
|
} ThreadData; |
99 |
|
|
|
100 |
|
1036 |
static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
101 |
|
|
{ |
102 |
|
1036 |
AudioEmphasisContext *s = ctx->priv; |
103 |
|
1036 |
const double level_out = s->level_out; |
104 |
|
1036 |
const double level_in = s->level_in; |
105 |
|
1036 |
ThreadData *td = arg; |
106 |
|
1036 |
AVFrame *out = td->out; |
107 |
|
1036 |
AVFrame *in = td->in; |
108 |
|
1036 |
const int start = (in->channels * jobnr) / nb_jobs; |
109 |
|
1036 |
const int end = (in->channels * (jobnr+1)) / nb_jobs; |
110 |
|
|
|
111 |
✓✓ |
2072 |
for (int ch = start; ch < end; ch++) { |
112 |
|
1036 |
const double *src = (const double *)in->extended_data[ch]; |
113 |
|
1036 |
double *w = (double *)s->w->extended_data[ch]; |
114 |
|
1036 |
double *dst = (double *)out->extended_data[ch]; |
115 |
|
|
|
116 |
✓✓ |
1036 |
if (s->rc.use_brickw) { |
117 |
|
518 |
biquad_process(&s->rc.brickw, dst, src, in->nb_samples, w + 2, level_in, 1.); |
118 |
|
518 |
biquad_process(&s->rc.r1, dst, dst, in->nb_samples, w, 1., level_out); |
119 |
|
|
} else { |
120 |
|
518 |
biquad_process(&s->rc.r1, dst, src, in->nb_samples, w, level_in, level_out); |
121 |
|
|
} |
122 |
|
|
} |
123 |
|
|
|
124 |
|
1036 |
return 0; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
518 |
static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
128 |
|
|
{ |
129 |
|
518 |
AVFilterContext *ctx = inlink->dst; |
130 |
|
518 |
AVFilterLink *outlink = ctx->outputs[0]; |
131 |
|
|
ThreadData td; |
132 |
|
|
AVFrame *out; |
133 |
|
|
|
134 |
✓✗ |
518 |
if (av_frame_is_writable(in)) { |
135 |
|
518 |
out = in; |
136 |
|
|
} else { |
137 |
|
|
out = ff_get_audio_buffer(outlink, in->nb_samples); |
138 |
|
|
if (!out) { |
139 |
|
|
av_frame_free(&in); |
140 |
|
|
return AVERROR(ENOMEM); |
141 |
|
|
} |
142 |
|
|
av_frame_copy_props(out, in); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
518 |
td.in = in; td.out = out; |
146 |
✗✓ |
518 |
ctx->internal->execute(ctx, filter_channels, &td, NULL, FFMIN(inlink->channels, |
147 |
|
|
ff_filter_get_nb_threads(ctx))); |
148 |
|
|
|
149 |
✗✓ |
518 |
if (in != out) |
150 |
|
|
av_frame_free(&in); |
151 |
|
518 |
return ff_filter_frame(outlink, out); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
2 |
static int query_formats(AVFilterContext *ctx) |
155 |
|
|
{ |
156 |
|
|
AVFilterChannelLayouts *layouts; |
157 |
|
|
AVFilterFormats *formats; |
158 |
|
|
static const enum AVSampleFormat sample_fmts[] = { |
159 |
|
|
AV_SAMPLE_FMT_DBLP, |
160 |
|
|
AV_SAMPLE_FMT_NONE |
161 |
|
|
}; |
162 |
|
|
int ret; |
163 |
|
|
|
164 |
|
2 |
layouts = ff_all_channel_counts(); |
165 |
✗✓ |
2 |
if (!layouts) |
166 |
|
|
return AVERROR(ENOMEM); |
167 |
|
2 |
ret = ff_set_common_channel_layouts(ctx, layouts); |
168 |
✗✓ |
2 |
if (ret < 0) |
169 |
|
|
return ret; |
170 |
|
|
|
171 |
|
2 |
formats = ff_make_format_list(sample_fmts); |
172 |
✗✓ |
2 |
if (!formats) |
173 |
|
|
return AVERROR(ENOMEM); |
174 |
|
2 |
ret = ff_set_common_formats(ctx, formats); |
175 |
✗✓ |
2 |
if (ret < 0) |
176 |
|
|
return ret; |
177 |
|
|
|
178 |
|
2 |
formats = ff_all_samplerates(); |
179 |
✗✓ |
2 |
if (!formats) |
180 |
|
|
return AVERROR(ENOMEM); |
181 |
|
2 |
return ff_set_common_samplerates(ctx, formats); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
1 |
static inline void set_highshelf_rbj(BiquadCoeffs *bq, double freq, double q, double peak, double sr) |
185 |
|
|
{ |
186 |
|
1 |
double A = sqrt(peak); |
187 |
|
1 |
double w0 = freq * 2 * M_PI / sr; |
188 |
|
1 |
double alpha = sin(w0) / (2 * q); |
189 |
|
1 |
double cw0 = cos(w0); |
190 |
|
1 |
double tmp = 2 * sqrt(A) * alpha; |
191 |
|
1 |
double b0 = 0, ib0 = 0; |
192 |
|
|
|
193 |
|
1 |
bq->a0 = A*( (A+1) + (A-1)*cw0 + tmp); |
194 |
|
1 |
bq->a1 = -2*A*( (A-1) + (A+1)*cw0); |
195 |
|
1 |
bq->a2 = A*( (A+1) + (A-1)*cw0 - tmp); |
196 |
|
1 |
b0 = (A+1) - (A-1)*cw0 + tmp; |
197 |
|
1 |
bq->b1 = 2*( (A-1) - (A+1)*cw0); |
198 |
|
1 |
bq->b2 = (A+1) - (A-1)*cw0 - tmp; |
199 |
|
|
|
200 |
|
1 |
ib0 = 1 / b0; |
201 |
|
1 |
bq->b1 *= ib0; |
202 |
|
1 |
bq->b2 *= ib0; |
203 |
|
1 |
bq->a0 *= ib0; |
204 |
|
1 |
bq->a1 *= ib0; |
205 |
|
1 |
bq->a2 *= ib0; |
206 |
|
1 |
} |
207 |
|
|
|
208 |
|
2 |
static inline void set_lp_rbj(BiquadCoeffs *bq, double fc, double q, double sr, double gain) |
209 |
|
|
{ |
210 |
|
2 |
double omega = 2.0 * M_PI * fc / sr; |
211 |
|
2 |
double sn = sin(omega); |
212 |
|
2 |
double cs = cos(omega); |
213 |
|
2 |
double alpha = sn/(2 * q); |
214 |
|
2 |
double inv = 1.0/(1.0 + alpha); |
215 |
|
|
|
216 |
|
2 |
bq->a2 = bq->a0 = gain * inv * (1.0 - cs) * 0.5; |
217 |
|
2 |
bq->a1 = bq->a0 + bq->a0; |
218 |
|
2 |
bq->b1 = (-2.0 * cs * inv); |
219 |
|
2 |
bq->b2 = ((1.0 - alpha) * inv); |
220 |
|
2 |
} |
221 |
|
|
|
222 |
|
1 |
static double freq_gain(BiquadCoeffs *c, double freq, double sr) |
223 |
|
|
{ |
224 |
|
|
double zr, zi; |
225 |
|
|
|
226 |
|
1 |
freq *= 2.0 * M_PI / sr; |
227 |
|
1 |
zr = cos(freq); |
228 |
|
1 |
zi = -sin(freq); |
229 |
|
|
|
230 |
|
|
/* |(a0 + a1*z + a2*z^2)/(1 + b1*z + b2*z^2)| */ |
231 |
|
2 |
return hypot(c->a0 + c->a1*zr + c->a2*(zr*zr-zi*zi), c->a1*zi + 2*c->a2*zr*zi) / |
232 |
|
1 |
hypot(1 + c->b1*zr + c->b2*(zr*zr-zi*zi), c->b1*zi + 2*c->b2*zr*zi); |
233 |
|
|
} |
234 |
|
|
|
235 |
|
2 |
static int config_input(AVFilterLink *inlink) |
236 |
|
|
{ |
237 |
|
|
double i, j, k, g, t, a0, a1, a2, b1, b2, tau1, tau2, tau3; |
238 |
|
2 |
double cutfreq, gain1kHz, gc, sr = inlink->sample_rate; |
239 |
|
2 |
AVFilterContext *ctx = inlink->dst; |
240 |
|
2 |
AudioEmphasisContext *s = ctx->priv; |
241 |
|
|
BiquadCoeffs coeffs; |
242 |
|
|
|
243 |
✓✗ |
2 |
if (!s->w) |
244 |
|
2 |
s->w = ff_get_audio_buffer(inlink, 4); |
245 |
✗✓ |
2 |
if (!s->w) |
246 |
|
|
return AVERROR(ENOMEM); |
247 |
|
|
|
248 |
✗✗✗✓ ✗✓✗ |
2 |
switch (s->type) { |
249 |
|
|
case 0: //"Columbia" |
250 |
|
|
i = 100.; |
251 |
|
|
j = 500.; |
252 |
|
|
k = 1590.; |
253 |
|
|
break; |
254 |
|
|
case 1: //"EMI" |
255 |
|
|
i = 70.; |
256 |
|
|
j = 500.; |
257 |
|
|
k = 2500.; |
258 |
|
|
break; |
259 |
|
|
case 2: //"BSI(78rpm)" |
260 |
|
|
i = 50.; |
261 |
|
|
j = 353.; |
262 |
|
|
k = 3180.; |
263 |
|
|
break; |
264 |
|
1 |
case 3: //"RIAA" |
265 |
|
|
default: |
266 |
|
1 |
tau1 = 0.003180; |
267 |
|
1 |
tau2 = 0.000318; |
268 |
|
1 |
tau3 = 0.000075; |
269 |
|
1 |
i = 1. / (2. * M_PI * tau1); |
270 |
|
1 |
j = 1. / (2. * M_PI * tau2); |
271 |
|
1 |
k = 1. / (2. * M_PI * tau3); |
272 |
|
1 |
break; |
273 |
|
|
case 4: //"CD Mastering" |
274 |
|
|
tau1 = 0.000050; |
275 |
|
|
tau2 = 0.000015; |
276 |
|
|
tau3 = 0.0000001;// 1.6MHz out of audible range for null impact |
277 |
|
|
i = 1. / (2. * M_PI * tau1); |
278 |
|
|
j = 1. / (2. * M_PI * tau2); |
279 |
|
|
k = 1. / (2. * M_PI * tau3); |
280 |
|
|
break; |
281 |
|
1 |
case 5: //"50µs FM (Europe)" |
282 |
|
1 |
tau1 = 0.000050; |
283 |
|
1 |
tau2 = tau1 / 20;// not used |
284 |
|
1 |
tau3 = tau1 / 50;// |
285 |
|
1 |
i = 1. / (2. * M_PI * tau1); |
286 |
|
1 |
j = 1. / (2. * M_PI * tau2); |
287 |
|
1 |
k = 1. / (2. * M_PI * tau3); |
288 |
|
1 |
break; |
289 |
|
|
case 6: //"75µs FM (US)" |
290 |
|
|
tau1 = 0.000075; |
291 |
|
|
tau2 = tau1 / 20;// not used |
292 |
|
|
tau3 = tau1 / 50;// |
293 |
|
|
i = 1. / (2. * M_PI * tau1); |
294 |
|
|
j = 1. / (2. * M_PI * tau2); |
295 |
|
|
k = 1. / (2. * M_PI * tau3); |
296 |
|
|
break; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
2 |
i *= 2 * M_PI; |
300 |
|
2 |
j *= 2 * M_PI; |
301 |
|
2 |
k *= 2 * M_PI; |
302 |
|
|
|
303 |
|
2 |
t = 1. / sr; |
304 |
|
|
|
305 |
|
|
//swap a1 b1, a2 b2 |
306 |
✓✗✓✓
|
2 |
if (s->type == 7 || s->type == 8) { |
307 |
✗✓ |
1 |
double tau = (s->type == 7 ? 0.000050 : 0.000075); |
308 |
|
1 |
double f = 1.0 / (2 * M_PI * tau); |
309 |
|
1 |
double nyq = sr * 0.5; |
310 |
|
1 |
double gain = sqrt(1.0 + nyq * nyq / (f * f)); // gain at Nyquist |
311 |
|
1 |
double cfreq = sqrt((gain - 1.0) * f * f); // frequency |
312 |
|
1 |
double q = 1.0; |
313 |
|
|
|
314 |
✓✗ |
1 |
if (s->type == 8) |
315 |
|
1 |
q = pow((sr / 3269.0) + 19.5, -0.25); // somewhat poor curve-fit |
316 |
✗✓ |
1 |
if (s->type == 7) |
317 |
|
|
q = pow((sr / 4750.0) + 19.5, -0.25); |
318 |
✓✗ |
1 |
if (s->mode == 0) |
319 |
|
1 |
set_highshelf_rbj(&s->rc.r1, cfreq, q, 1. / gain, sr); |
320 |
|
|
else |
321 |
|
|
set_highshelf_rbj(&s->rc.r1, cfreq, q, gain, sr); |
322 |
|
1 |
s->rc.use_brickw = 0; |
323 |
|
|
} else { |
324 |
|
1 |
s->rc.use_brickw = 1; |
325 |
✓✗ |
1 |
if (s->mode == 0) { // Reproduction |
326 |
|
1 |
g = 1. / (4.+2.*i*t+2.*k*t+i*k*t*t); |
327 |
|
1 |
a0 = (2.*t+j*t*t)*g; |
328 |
|
1 |
a1 = (2.*j*t*t)*g; |
329 |
|
1 |
a2 = (-2.*t+j*t*t)*g; |
330 |
|
1 |
b1 = (-8.+2.*i*k*t*t)*g; |
331 |
|
1 |
b2 = (4.-2.*i*t-2.*k*t+i*k*t*t)*g; |
332 |
|
|
} else { // Production |
333 |
|
|
g = 1. / (2.*t+j*t*t); |
334 |
|
|
a0 = (4.+2.*i*t+2.*k*t+i*k*t*t)*g; |
335 |
|
|
a1 = (-8.+2.*i*k*t*t)*g; |
336 |
|
|
a2 = (4.-2.*i*t-2.*k*t+i*k*t*t)*g; |
337 |
|
|
b1 = (2.*j*t*t)*g; |
338 |
|
|
b2 = (-2.*t+j*t*t)*g; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
1 |
coeffs.a0 = a0; |
342 |
|
1 |
coeffs.a1 = a1; |
343 |
|
1 |
coeffs.a2 = a2; |
344 |
|
1 |
coeffs.b1 = b1; |
345 |
|
1 |
coeffs.b2 = b2; |
346 |
|
|
|
347 |
|
|
// the coeffs above give non-normalized value, so it should be normalized to produce 0dB at 1 kHz |
348 |
|
|
// find actual gain |
349 |
|
|
// Note: for FM emphasis, use 100 Hz for normalization instead |
350 |
|
1 |
gain1kHz = freq_gain(&coeffs, 1000.0, sr); |
351 |
|
|
// divide one filter's x[n-m] coefficients by that value |
352 |
|
1 |
gc = 1.0 / gain1kHz; |
353 |
|
1 |
s->rc.r1.a0 = coeffs.a0 * gc; |
354 |
|
1 |
s->rc.r1.a1 = coeffs.a1 * gc; |
355 |
|
1 |
s->rc.r1.a2 = coeffs.a2 * gc; |
356 |
|
1 |
s->rc.r1.b1 = coeffs.b1; |
357 |
|
1 |
s->rc.r1.b2 = coeffs.b2; |
358 |
|
|
} |
359 |
|
|
|
360 |
✗✓ |
2 |
cutfreq = FFMIN(0.45 * sr, 21000.); |
361 |
|
2 |
set_lp_rbj(&s->rc.brickw, cutfreq, 0.707, sr, 1.); |
362 |
|
|
|
363 |
|
2 |
return 0; |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
367 |
|
|
char *res, int res_len, int flags) |
368 |
|
|
{ |
369 |
|
|
int ret; |
370 |
|
|
|
371 |
|
|
ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); |
372 |
|
|
if (ret < 0) |
373 |
|
|
return ret; |
374 |
|
|
|
375 |
|
|
return config_input(ctx->inputs[0]); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
2 |
static av_cold void uninit(AVFilterContext *ctx) |
379 |
|
|
{ |
380 |
|
2 |
AudioEmphasisContext *s = ctx->priv; |
381 |
|
|
|
382 |
|
2 |
av_frame_free(&s->w); |
383 |
|
2 |
} |
384 |
|
|
|
385 |
|
|
static const AVFilterPad avfilter_af_aemphasis_inputs[] = { |
386 |
|
|
{ |
387 |
|
|
.name = "default", |
388 |
|
|
.type = AVMEDIA_TYPE_AUDIO, |
389 |
|
|
.config_props = config_input, |
390 |
|
|
.filter_frame = filter_frame, |
391 |
|
|
}, |
392 |
|
|
{ NULL } |
393 |
|
|
}; |
394 |
|
|
|
395 |
|
|
static const AVFilterPad avfilter_af_aemphasis_outputs[] = { |
396 |
|
|
{ |
397 |
|
|
.name = "default", |
398 |
|
|
.type = AVMEDIA_TYPE_AUDIO, |
399 |
|
|
}, |
400 |
|
|
{ NULL } |
401 |
|
|
}; |
402 |
|
|
|
403 |
|
|
AVFilter ff_af_aemphasis = { |
404 |
|
|
.name = "aemphasis", |
405 |
|
|
.description = NULL_IF_CONFIG_SMALL("Audio emphasis."), |
406 |
|
|
.priv_size = sizeof(AudioEmphasisContext), |
407 |
|
|
.priv_class = &aemphasis_class, |
408 |
|
|
.uninit = uninit, |
409 |
|
|
.query_formats = query_formats, |
410 |
|
|
.inputs = avfilter_af_aemphasis_inputs, |
411 |
|
|
.outputs = avfilter_af_aemphasis_outputs, |
412 |
|
|
.process_command = process_command, |
413 |
|
|
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | |
414 |
|
|
AVFILTER_FLAG_SLICE_THREADS, |
415 |
|
|
}; |