2 * Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen, Damien Zammit and others
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/opt.h"
26 typedef struct BiquadCoeffs
{
27 double a0
, a1
, a2
, b1
, b2
;
30 typedef struct RIAACurve
{
36 typedef struct AudioEmphasisContext
{
39 double level_in
, level_out
;
44 } AudioEmphasisContext
;
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
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
, .unit
= "mode" },
53 { "reproduction", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, .unit
= "mode" },
54 { "production", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, .unit
= "mode" },
55 { "type", "set filter type", OFFSET(type
), AV_OPT_TYPE_INT
, {.i64
=4}, 0, 8, FLAGS
, .unit
= "type" },
56 { "col", "Columbia", 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, .unit
= "type" },
57 { "emi", "EMI", 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, .unit
= "type" },
58 { "bsi", "BSI (78RPM)", 0, AV_OPT_TYPE_CONST
, {.i64
=2}, 0, 0, FLAGS
, .unit
= "type" },
59 { "riaa", "RIAA", 0, AV_OPT_TYPE_CONST
, {.i64
=3}, 0, 0, FLAGS
, .unit
= "type" },
60 { "cd", "Compact Disc (CD)", 0, AV_OPT_TYPE_CONST
, {.i64
=4}, 0, 0, FLAGS
, .unit
= "type" },
61 { "50fm", "50µs (FM)", 0, AV_OPT_TYPE_CONST
, {.i64
=5}, 0, 0, FLAGS
, .unit
= "type" },
62 { "75fm", "75µs (FM)", 0, AV_OPT_TYPE_CONST
, {.i64
=6}, 0, 0, FLAGS
, .unit
= "type" },
63 { "50kf", "50µs (FM-KF)", 0, AV_OPT_TYPE_CONST
, {.i64
=7}, 0, 0, FLAGS
, .unit
= "type" },
64 { "75kf", "75µs (FM-KF)", 0, AV_OPT_TYPE_CONST
, {.i64
=8}, 0, 0, FLAGS
, .unit
= "type" },
68 AVFILTER_DEFINE_CLASS(aemphasis
);
70 static inline void biquad_process(BiquadCoeffs
*bq
, double *dst
, const double *src
, int nb_samples
,
71 double *w
, double level_in
, double level_out
)
73 const double a0
= bq
->a0
;
74 const double a1
= bq
->a1
;
75 const double a2
= bq
->a2
;
76 const double b1
= bq
->b1
;
77 const double b2
= bq
->b2
;
81 for (int i
= 0; i
< nb_samples
; i
++) {
82 double n
= src
[i
] * level_in
;
83 double tmp
= n
- w1
* b1
- w2
* b2
;
84 double out
= tmp
* a0
+ w1
* a1
+ w2
* a2
;
89 dst
[i
] = out
* level_out
;
96 typedef struct ThreadData
{
100 static int filter_channels(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
102 AudioEmphasisContext
*s
= ctx
->priv
;
103 const double level_out
= s
->level_out
;
104 const double level_in
= s
->level_in
;
105 ThreadData
*td
= arg
;
106 AVFrame
*out
= td
->out
;
107 AVFrame
*in
= td
->in
;
108 const int start
= (in
->ch_layout
.nb_channels
* jobnr
) / nb_jobs
;
109 const int end
= (in
->ch_layout
.nb_channels
* (jobnr
+1)) / nb_jobs
;
111 for (int ch
= start
; ch
< end
; ch
++) {
112 const double *src
= (const double *)in
->extended_data
[ch
];
113 double *w
= (double *)s
->w
->extended_data
[ch
];
114 double *dst
= (double *)out
->extended_data
[ch
];
116 if (s
->rc
.use_brickw
) {
117 biquad_process(&s
->rc
.brickw
, dst
, src
, in
->nb_samples
, w
+ 2, level_in
, 1.);
118 biquad_process(&s
->rc
.r1
, dst
, dst
, in
->nb_samples
, w
, 1., level_out
);
120 biquad_process(&s
->rc
.r1
, dst
, src
, in
->nb_samples
, w
, level_in
, level_out
);
127 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
129 AVFilterContext
*ctx
= inlink
->dst
;
130 AVFilterLink
*outlink
= ctx
->outputs
[0];
134 if (av_frame_is_writable(in
)) {
137 out
= ff_get_audio_buffer(outlink
, in
->nb_samples
);
140 return AVERROR(ENOMEM
);
142 av_frame_copy_props(out
, in
);
145 td
.in
= in
; td
.out
= out
;
146 ff_filter_execute(ctx
, filter_channels
, &td
, NULL
,
147 FFMIN(inlink
->ch_layout
.nb_channels
, ff_filter_get_nb_threads(ctx
)));
151 return ff_filter_frame(outlink
, out
);
154 static inline void set_highshelf_rbj(BiquadCoeffs
*bq
, double freq
, double q
, double peak
, double sr
)
156 double A
= sqrt(peak
);
157 double w0
= freq
* 2 * M_PI
/ sr
;
158 double alpha
= sin(w0
) / (2 * q
);
159 double cw0
= cos(w0
);
160 double tmp
= 2 * sqrt(A
) * alpha
;
161 double b0
= 0, ib0
= 0;
163 bq
->a0
= A
*( (A
+1) + (A
-1)*cw0
+ tmp
);
164 bq
->a1
= -2*A
*( (A
-1) + (A
+1)*cw0
);
165 bq
->a2
= A
*( (A
+1) + (A
-1)*cw0
- tmp
);
166 b0
= (A
+1) - (A
-1)*cw0
+ tmp
;
167 bq
->b1
= 2*( (A
-1) - (A
+1)*cw0
);
168 bq
->b2
= (A
+1) - (A
-1)*cw0
- tmp
;
178 static inline void set_lp_rbj(BiquadCoeffs
*bq
, double fc
, double q
, double sr
, double gain
)
180 double omega
= 2.0 * M_PI
* fc
/ sr
;
181 double sn
= sin(omega
);
182 double cs
= cos(omega
);
183 double alpha
= sn
/(2 * q
);
184 double inv
= 1.0/(1.0 + alpha
);
186 bq
->a2
= bq
->a0
= gain
* inv
* (1.0 - cs
) * 0.5;
187 bq
->a1
= bq
->a0
+ bq
->a0
;
188 bq
->b1
= (-2.0 * cs
* inv
);
189 bq
->b2
= ((1.0 - alpha
) * inv
);
192 static double freq_gain(BiquadCoeffs
*c
, double freq
, double sr
)
196 freq
*= 2.0 * M_PI
/ sr
;
200 /* |(a0 + a1*z + a2*z^2)/(1 + b1*z + b2*z^2)| */
201 return hypot(c
->a0
+ c
->a1
*zr
+ c
->a2
*(zr
*zr
-zi
*zi
), c
->a1
*zi
+ 2*c
->a2
*zr
*zi
) /
202 hypot(1 + c
->b1
*zr
+ c
->b2
*(zr
*zr
-zi
*zi
), c
->b1
*zi
+ 2*c
->b2
*zr
*zi
);
205 static int config_input(AVFilterLink
*inlink
)
207 double i
, j
, k
, g
, t
, a0
, a1
, a2
, b1
, b2
, tau1
, tau2
, tau3
;
208 double cutfreq
, gain1kHz
, gc
, sr
= inlink
->sample_rate
;
209 AVFilterContext
*ctx
= inlink
->dst
;
210 AudioEmphasisContext
*s
= ctx
->priv
;
214 s
->w
= ff_get_audio_buffer(inlink
, 4);
216 return AVERROR(ENOMEM
);
229 case 2: //"BSI(78rpm)"
239 i
= 1. / (2. * M_PI
* tau1
);
240 j
= 1. / (2. * M_PI
* tau2
);
241 k
= 1. / (2. * M_PI
* tau3
);
243 case 4: //"CD Mastering"
246 tau3
= 0.0000001;// 1.6MHz out of audible range for null impact
247 i
= 1. / (2. * M_PI
* tau1
);
248 j
= 1. / (2. * M_PI
* tau2
);
249 k
= 1. / (2. * M_PI
* tau3
);
251 case 5: //"50µs FM (Europe)"
253 tau2
= tau1
/ 20;// not used
255 i
= 1. / (2. * M_PI
* tau1
);
256 j
= 1. / (2. * M_PI
* tau2
);
257 k
= 1. / (2. * M_PI
* tau3
);
259 case 6: //"75µs FM (US)"
261 tau2
= tau1
/ 20;// not used
263 i
= 1. / (2. * M_PI
* tau1
);
264 j
= 1. / (2. * M_PI
* tau2
);
265 k
= 1. / (2. * M_PI
* tau3
);
276 if (s
->type
== 7 || s
->type
== 8) {
277 double tau
= (s
->type
== 7 ? 0.000050 : 0.000075);
278 double f
= 1.0 / (2 * M_PI
* tau
);
279 double nyq
= sr
* 0.5;
280 double gain
= sqrt(1.0 + nyq
* nyq
/ (f
* f
)); // gain at Nyquist
281 double cfreq
= sqrt((gain
- 1.0) * f
* f
); // frequency
285 q
= pow((sr
/ 3269.0) + 19.5, -0.25); // somewhat poor curve-fit
287 q
= pow((sr
/ 4750.0) + 19.5, -0.25);
289 set_highshelf_rbj(&s
->rc
.r1
, cfreq
, q
, 1. / gain
, sr
);
291 set_highshelf_rbj(&s
->rc
.r1
, cfreq
, q
, gain
, sr
);
292 s
->rc
.use_brickw
= 0;
294 s
->rc
.use_brickw
= 1;
295 if (s
->mode
== 0) { // Reproduction
296 g
= 1. / (4.+2.*i
*t
+2.*k
*t
+i
*k
*t
*t
);
299 a2
= (-2.*t
+j
*t
*t
)*g
;
300 b1
= (-8.+2.*i
*k
*t
*t
)*g
;
301 b2
= (4.-2.*i
*t
-2.*k
*t
+i
*k
*t
*t
)*g
;
302 } else { // Production
303 g
= 1. / (2.*t
+j
*t
*t
);
304 a0
= (4.+2.*i
*t
+2.*k
*t
+i
*k
*t
*t
)*g
;
305 a1
= (-8.+2.*i
*k
*t
*t
)*g
;
306 a2
= (4.-2.*i
*t
-2.*k
*t
+i
*k
*t
*t
)*g
;
308 b2
= (-2.*t
+j
*t
*t
)*g
;
317 // the coeffs above give non-normalized value, so it should be normalized to produce 0dB at 1 kHz
319 // Note: for FM emphasis, use 100 Hz for normalization instead
320 gain1kHz
= freq_gain(&coeffs
, 1000.0, sr
);
321 // divide one filter's x[n-m] coefficients by that value
323 s
->rc
.r1
.a0
= coeffs
.a0
* gc
;
324 s
->rc
.r1
.a1
= coeffs
.a1
* gc
;
325 s
->rc
.r1
.a2
= coeffs
.a2
* gc
;
326 s
->rc
.r1
.b1
= coeffs
.b1
;
327 s
->rc
.r1
.b2
= coeffs
.b2
;
330 cutfreq
= FFMIN(0.45 * sr
, 21000.);
331 set_lp_rbj(&s
->rc
.brickw
, cutfreq
, 0.707, sr
, 1.);
336 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
337 char *res
, int res_len
, int flags
)
341 ret
= ff_filter_process_command(ctx
, cmd
, args
, res
, res_len
, flags
);
345 return config_input(ctx
->inputs
[0]);
348 static av_cold
void uninit(AVFilterContext
*ctx
)
350 AudioEmphasisContext
*s
= ctx
->priv
;
352 av_frame_free(&s
->w
);
355 static const AVFilterPad avfilter_af_aemphasis_inputs
[] = {
358 .type
= AVMEDIA_TYPE_AUDIO
,
359 .config_props
= config_input
,
360 .filter_frame
= filter_frame
,
364 const FFFilter ff_af_aemphasis
= {
365 .p
.name
= "aemphasis",
366 .p
.description
= NULL_IF_CONFIG_SMALL("Audio emphasis."),
367 .p
.priv_class
= &aemphasis_class
,
368 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
|
369 AVFILTER_FLAG_SLICE_THREADS
,
370 .priv_size
= sizeof(AudioEmphasisContext
),
372 FILTER_INPUTS(avfilter_af_aemphasis_inputs
),
373 FILTER_OUTPUTS(ff_audio_default_filterpad
),
374 FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP
),
375 .process_command
= process_command
,