1 /*
2 * Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
3 * Copyright (c) 2015 Paul B Mahol
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * Audio (Sidechain) Compressor filter
25 */
26
27 #include "config_components.h"
28
33
40
43
66
70
71 #define OFFSET(x) offsetof(SidechainCompressContext, x)
72 #define A AV_OPT_FLAG_AUDIO_PARAM
73 #define F AV_OPT_FLAG_FILTERING_PARAM
74 #define R AV_OPT_FLAG_RUNTIME_PARAM
75
96 };
97
99 "acompressor/sidechaincompress",
101
102 // A fake infinity value (because real infinity may break some hosts)
103 #define FAKE_INFINITY (65536.0 * 65536.0)
104
105 // Check for infinity (with appropriate-ish tolerance)
106 #define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
107
108 static double output_gain(
double lin_slope,
double ratio,
double thres,
109 double knee, double knee_start, double knee_stop,
110 double compressed_knee_start,
111 double compressed_knee_stop,
112 int detection,
int mode)
113 {
114 double slope =
log(lin_slope);
115 double gain = 0.0;
117
118 if (detection)
119 slope *= 0.5;
120
122 gain = thres;
124 } else {
125 gain = (slope - thres) / ratio + thres;
127 }
128
130 if (knee > 1.0 && slope > knee_start)
132 knee_stop, compressed_knee_start,
134 } else {
135 if (knee > 1.0 && slope < knee_stop)
137 knee_start, compressed_knee_stop,
139 }
140
141 return exp(gain - slope);
142 }
143
145 {
148
149 s->thres =
log(
s->threshold);
150 s->lin_knee_start =
s->threshold / sqrt(
s->knee);
151 s->lin_knee_stop =
s->threshold * sqrt(
s->knee);
152 s->adj_knee_start =
s->lin_knee_start *
s->lin_knee_start;
153 s->adj_knee_stop =
s->lin_knee_stop *
s->lin_knee_stop;
154 s->knee_start =
log(
s->lin_knee_start);
155 s->knee_stop =
log(
s->lin_knee_stop);
156 s->compressed_knee_start = (
s->knee_start -
s->thres) /
s->ratio +
s->thres;
157 s->compressed_knee_stop = (
s->knee_stop -
s->thres) /
s->ratio +
s->thres;
158
161
162 return 0;
163 }
164
166 const double *
src,
double *dst,
const double *scsrc,
int nb_samples,
167 double level_in, double level_sc,
169 {
170 const double makeup =
s->makeup;
171 const double mix =
s->mix;
173
174 for (
i = 0;
i < nb_samples;
i++) {
175 double abs_sample, gain = 1.0;
176 double detector;
177 int detected;
178
179 abs_sample =
fabs(scsrc[0] * level_sc);
180
183 abs_sample =
FFMAX(
fabs(scsrc[
c] * level_sc), abs_sample);
184 } else {
186 abs_sample +=
fabs(scsrc[
c] * level_sc);
187
189 }
190
192 abs_sample *= abs_sample;
193
194 s->lin_slope += (abs_sample -
s->lin_slope) * (abs_sample >
s->lin_slope ?
s->attack_coeff :
s->release_coeff);
195
197 detector = (
s->detection ?
s->adj_knee_stop :
s->lin_knee_stop);
198 detected =
s->lin_slope < detector;
199 } else {
200 detector = (
s->detection ?
s->adj_knee_start :
s->lin_knee_start);
201 detected =
s->lin_slope > detector;
202 }
203
204 if (
s->lin_slope > 0.0 && detected)
206 s->knee_start,
s->knee_stop,
207 s->compressed_knee_start,
208 s->compressed_knee_stop,
209 s->detection,
s->mode);
210
211 for (
c = 0;
c <
inlink->ch_layout.nb_channels;
c++)
212 dst[
c] =
src[
c] * level_in * (gain * makeup *
mix + (1. -
mix));
213
215 dst +=
inlink->ch_layout.nb_channels;
217 }
218 }
219
221 char *res,
int res_len,
int flags)
222 {
224
228
230
231 return 0;
232 }
233
234 #if CONFIG_SIDECHAINCOMPRESS_FILTER
236 {
239 int ret,
i, nb_samples;
240 double *dst;
241
245 in[0]->nb_samples);
247 }
252 in[1]->nb_samples);
254 }
257
259 if (nb_samples) {
263 for (
i = 0;
i < 2;
i++) {
270 }
272 }
273
274 dst = (
double *)
out->data[0];
277
279 (
double *)in[1]->
data[0], nb_samples,
280 s->level_in,
s->level_sc,
281 ctx->inputs[0],
ctx->inputs[1]);
282
285
289 }
297 }
298 return 0;
299 }
300
302 {
306 };
308 &
ctx->inputs[1]->outcfg.channel_layouts);
311
312 /* This will link the channel properties of the main input and the output;
313 * it won't touch the second input as its channel_layouts is already set. */
316
319
321 }
322
324 {
327
329
332 if (!
s->fifo[0] || !
s->fifo[1])
334
336
337 return 0;
338 }
339
341 {
343
346 }
347
348 static const AVFilterPad sidechaincompress_inputs[] = {
349 {
352 },{
353 .name = "sidechain",
355 },
356 };
357
358 static const AVFilterPad sidechaincompress_outputs[] = {
359 {
363 },
364 };
365
367 .
name =
"sidechaincompress",
369 .priv_class = &sidechaincompress_acompressor_class,
377 };
378 #endif /* CONFIG_SIDECHAINCOMPRESS_FILTER */
379
380 #if CONFIG_ACOMPRESSOR_FILTER
382 {
383 const double *
src = (
const double *)in->
data[0];
388 double *dst;
389
392 } else {
397 }
399 }
400 dst = (
double *)
out->data[0];
401
403 s->level_in,
s->level_in,
405
409 }
410
412 {
415 .filter_frame = acompressor_filter_frame,
416 },
417 };
418
420 {
424 },
425 };
426
428 .
name =
"acompressor",
430 .priv_class = &sidechaincompress_acompressor_class,
436 };
437 #endif /* CONFIG_ACOMPRESSOR_FILTER */