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
39
42
65
69
70 #define OFFSET(x) offsetof(SidechainCompressContext, x)
71 #define A AV_OPT_FLAG_AUDIO_PARAM
72 #define F AV_OPT_FLAG_FILTERING_PARAM
73 #define R AV_OPT_FLAG_RUNTIME_PARAM
74
95 };
96
98 "acompressor/sidechaincompress",
100
101 // A fake infinity value (because real infinity may break some hosts)
102 #define FAKE_INFINITY (65536.0 * 65536.0)
103
104 // Check for infinity (with appropriate-ish tolerance)
105 #define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
106
107 static double output_gain(
double lin_slope,
double ratio,
double thres,
108 double knee, double knee_start, double knee_stop,
109 double compressed_knee_start,
110 double compressed_knee_stop,
111 int detection,
int mode)
112 {
113 double slope =
log(lin_slope);
114 double gain = 0.0;
116
117 if (detection)
118 slope *= 0.5;
119
121 gain = thres;
123 } else {
124 gain = (slope - thres) / ratio + thres;
126 }
127
129 if (knee > 1.0 && slope > knee_start)
131 knee_stop, compressed_knee_start,
133 } else {
134 if (knee > 1.0 && slope < knee_stop)
136 knee_start, compressed_knee_stop,
138 }
139
140 return exp(gain - slope);
141 }
142
144 {
147
148 s->thres =
log(
s->threshold);
149 s->lin_knee_start =
s->threshold / sqrt(
s->knee);
150 s->lin_knee_stop =
s->threshold * sqrt(
s->knee);
151 s->adj_knee_start =
s->lin_knee_start *
s->lin_knee_start;
152 s->adj_knee_stop =
s->lin_knee_stop *
s->lin_knee_stop;
153 s->knee_start =
log(
s->lin_knee_start);
154 s->knee_stop =
log(
s->lin_knee_stop);
155 s->compressed_knee_start = (
s->knee_start -
s->thres) /
s->ratio +
s->thres;
156 s->compressed_knee_stop = (
s->knee_stop -
s->thres) /
s->ratio +
s->thres;
157
160
161 return 0;
162 }
163
165 const double *
src,
double *
dst,
const double *scsrc,
int nb_samples,
166 double level_in, double level_sc,
168 {
169 const double makeup =
s->makeup;
170 const double mix =
s->mix;
172
173 for (
i = 0;
i < nb_samples;
i++) {
174 double abs_sample, gain = 1.0;
175 double detector;
176 int detected;
177
178 abs_sample =
fabs(scsrc[0] * level_sc);
179
182 abs_sample =
FFMAX(
fabs(scsrc[
c] * level_sc), abs_sample);
183 } else {
185 abs_sample +=
fabs(scsrc[
c] * level_sc);
186
188 }
189
191 abs_sample *= abs_sample;
192
193 s->lin_slope += (abs_sample -
s->lin_slope) * (abs_sample >
s->lin_slope ?
s->attack_coeff :
s->release_coeff);
194
196 detector = (
s->detection ?
s->adj_knee_stop :
s->lin_knee_stop);
197 detected =
s->lin_slope < detector;
198 } else {
199 detector = (
s->detection ?
s->adj_knee_start :
s->lin_knee_start);
200 detected =
s->lin_slope > detector;
201 }
202
203 if (
s->lin_slope > 0.0 && detected)
205 s->knee_start,
s->knee_stop,
206 s->compressed_knee_start,
207 s->compressed_knee_stop,
208 s->detection,
s->mode);
209
210 for (
c = 0;
c <
inlink->ch_layout.nb_channels;
c++)
212
216 }
217 }
218
220 char *res,
int res_len,
int flags)
221 {
223
227
229
230 return 0;
231 }
232
233 #if CONFIG_SIDECHAINCOMPRESS_FILTER
235 {
238 int ret,
i, nb_samples;
240
244 in[0]->nb_samples);
246 }
251 in[1]->nb_samples);
253 }
256
258 if (nb_samples) {
262 for (
i = 0;
i < 2;
i++) {
269 }
271 }
272
273 dst = (
double *)
out->data[0];
276
278 (
double *)in[1]->
data[0], nb_samples,
279 s->level_in,
s->level_sc,
280 ctx->inputs[0],
ctx->inputs[1]);
281
284
288 }
296 }
297 return 0;
298 }
299
303 {
307 };
309
310 /* Generic code will link the channel properties of the main input and the output;
311 * it won't touch the second input as its channel_layouts is already set. */
316
319
320 return 0;
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];
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 */