GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/af_sidechaincompress.c Lines: 0 186 0.0 %
Date: 2020-09-25 23:16:12 Branches: 0 116 0.0 %

Line Branch Exec Source
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 "libavutil/audio_fifo.h"
28
#include "libavutil/avassert.h"
29
#include "libavutil/channel_layout.h"
30
#include "libavutil/common.h"
31
#include "libavutil/opt.h"
32
33
#include "audio.h"
34
#include "avfilter.h"
35
#include "filters.h"
36
#include "formats.h"
37
#include "hermite.h"
38
#include "internal.h"
39
40
typedef struct SidechainCompressContext {
41
    const AVClass *class;
42
43
    double level_in;
44
    double level_sc;
45
    double attack, attack_coeff;
46
    double release, release_coeff;
47
    double lin_slope;
48
    double ratio;
49
    double threshold;
50
    double makeup;
51
    double mix;
52
    double thres;
53
    double knee;
54
    double knee_start;
55
    double knee_stop;
56
    double lin_knee_start;
57
    double lin_knee_stop;
58
    double adj_knee_start;
59
    double adj_knee_stop;
60
    double compressed_knee_start;
61
    double compressed_knee_stop;
62
    int link;
63
    int detection;
64
    int mode;
65
66
    AVAudioFifo *fifo[2];
67
    int64_t pts;
68
} SidechainCompressContext;
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
75
static const AVOption options[] = {
76
    { "level_in",  "set input gain",     OFFSET(level_in),  AV_OPT_TYPE_DOUBLE, {.dbl=1},        0.015625,   64, A|F|R },
77
    { "mode",      "set mode",           OFFSET(mode),      AV_OPT_TYPE_INT,    {.i64=0},               0,    1, A|F|R, "mode" },
78
    {   "downward",0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=0},               0,    0, A|F|R, "mode" },
79
    {   "upward",  0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=1},               0,    0, A|F|R, "mode" },
80
    { "threshold", "set threshold",      OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0.000976563,    1, A|F|R },
81
    { "ratio",     "set ratio",          OFFSET(ratio),     AV_OPT_TYPE_DOUBLE, {.dbl=2},               1,   20, A|F|R },
82
    { "attack",    "set attack",         OFFSET(attack),    AV_OPT_TYPE_DOUBLE, {.dbl=20},           0.01, 2000, A|F|R },
83
    { "release",   "set release",        OFFSET(release),   AV_OPT_TYPE_DOUBLE, {.dbl=250},          0.01, 9000, A|F|R },
84
    { "makeup",    "set make up gain",   OFFSET(makeup),    AV_OPT_TYPE_DOUBLE, {.dbl=1},               1,   64, A|F|R },
85
    { "knee",      "set knee",           OFFSET(knee),      AV_OPT_TYPE_DOUBLE, {.dbl=2.82843},         1,    8, A|F|R },
86
    { "link",      "set link type",      OFFSET(link),      AV_OPT_TYPE_INT,    {.i64=0},               0,    1, A|F|R, "link" },
87
    {   "average", 0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=0},               0,    0, A|F|R, "link" },
88
    {   "maximum", 0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=1},               0,    0, A|F|R, "link" },
89
    { "detection", "set detection",      OFFSET(detection), AV_OPT_TYPE_INT,    {.i64=1},               0,    1, A|F|R, "detection" },
90
    {   "peak",    0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=0},               0,    0, A|F|R, "detection" },
91
    {   "rms",     0,                    0,                 AV_OPT_TYPE_CONST,  {.i64=1},               0,    0, A|F|R, "detection" },
92
    { "level_sc",  "set sidechain gain", OFFSET(level_sc),  AV_OPT_TYPE_DOUBLE, {.dbl=1},        0.015625,   64, A|F|R },
93
    { "mix",       "set mix",            OFFSET(mix),       AV_OPT_TYPE_DOUBLE, {.dbl=1},               0,    1, A|F|R },
94
    { NULL }
95
};
96
97
#define sidechaincompress_options options
98
AVFILTER_DEFINE_CLASS(sidechaincompress);
99
100
// A fake infinity value (because real infinity may break some hosts)
101
#define FAKE_INFINITY (65536.0 * 65536.0)
102
103
// Check for infinity (with appropriate-ish tolerance)
104
#define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
105
106
static double output_gain(double lin_slope, double ratio, double thres,
107
                          double knee, double knee_start, double knee_stop,
108
                          double compressed_knee_start,
109
                          double compressed_knee_stop,
110
                          int detection, int mode)
111
{
112
    double slope = log(lin_slope);
113
    double gain = 0.0;
114
    double delta = 0.0;
115
116
    if (detection)
117
        slope *= 0.5;
118
119
    if (IS_FAKE_INFINITY(ratio)) {
120
        gain = thres;
121
        delta = 0.0;
122
    } else {
123
        gain = (slope - thres) / ratio + thres;
124
        delta = 1.0 / ratio;
125
    }
126
127
    if (mode) {
128
        if (knee > 1.0 && slope > knee_start)
129
            gain = hermite_interpolation(slope, knee_stop, knee_start,
130
                                         knee_stop, compressed_knee_start,
131
                                         1.0, delta);
132
    } else {
133
        if (knee > 1.0 && slope < knee_stop)
134
            gain = hermite_interpolation(slope, knee_start, knee_stop,
135
                                         knee_start, compressed_knee_stop,
136
                                         1.0, delta);
137
    }
138
139
    return exp(gain - slope);
140
}
141
142
static int compressor_config_output(AVFilterLink *outlink)
143
{
144
    AVFilterContext *ctx = outlink->src;
145
    SidechainCompressContext *s = ctx->priv;
146
147
    s->thres = log(s->threshold);
148
    s->lin_knee_start = s->threshold / sqrt(s->knee);
149
    s->lin_knee_stop = s->threshold * sqrt(s->knee);
150
    s->adj_knee_start = s->lin_knee_start * s->lin_knee_start;
151
    s->adj_knee_stop = s->lin_knee_stop * s->lin_knee_stop;
152
    s->knee_start = log(s->lin_knee_start);
153
    s->knee_stop = log(s->lin_knee_stop);
154
    s->compressed_knee_start = (s->knee_start - s->thres) / s->ratio + s->thres;
155
    s->compressed_knee_stop = (s->knee_stop - s->thres) / s->ratio + s->thres;
156
157
    s->attack_coeff = FFMIN(1., 1. / (s->attack * outlink->sample_rate / 4000.));
158
    s->release_coeff = FFMIN(1., 1. / (s->release * outlink->sample_rate / 4000.));
159
160
    return 0;
161
}
162
163
static void compressor(SidechainCompressContext *s,
164
                       const double *src, double *dst, const double *scsrc, int nb_samples,
165
                       double level_in, double level_sc,
166
                       AVFilterLink *inlink, AVFilterLink *sclink)
167
{
168
    const double makeup = s->makeup;
169
    const double mix = s->mix;
170
    int i, c;
171
172
    for (i = 0; i < nb_samples; i++) {
173
        double abs_sample, gain = 1.0;
174
        double detector;
175
        int detected;
176
177
        abs_sample = fabs(scsrc[0] * level_sc);
178
179
        if (s->link == 1) {
180
            for (c = 1; c < sclink->channels; c++)
181
                abs_sample = FFMAX(fabs(scsrc[c] * level_sc), abs_sample);
182
        } else {
183
            for (c = 1; c < sclink->channels; c++)
184
                abs_sample += fabs(scsrc[c] * level_sc);
185
186
            abs_sample /= sclink->channels;
187
        }
188
189
        if (s->detection)
190
            abs_sample *= abs_sample;
191
192
        s->lin_slope += (abs_sample - s->lin_slope) * (abs_sample > s->lin_slope ? s->attack_coeff : s->release_coeff);
193
194
        if (s->mode) {
195
            detector = (s->detection ? s->adj_knee_stop : s->lin_knee_stop);
196
            detected = s->lin_slope < detector;
197
        } else {
198
            detector = (s->detection ? s->adj_knee_start : s->lin_knee_start);
199
            detected = s->lin_slope > detector;
200
        }
201
202
        if (s->lin_slope > 0.0 && detected)
203
            gain = output_gain(s->lin_slope, s->ratio, s->thres, s->knee,
204
                               s->knee_start, s->knee_stop,
205
                               s->compressed_knee_start,
206
                               s->compressed_knee_stop,
207
                               s->detection, s->mode);
208
209
        for (c = 0; c < inlink->channels; c++)
210
            dst[c] = src[c] * level_in * (gain * makeup * mix + (1. - mix));
211
212
        src += inlink->channels;
213
        dst += inlink->channels;
214
        scsrc += sclink->channels;
215
    }
216
}
217
218
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
219
                           char *res, int res_len, int flags)
220
{
221
    int ret;
222
223
    ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
224
    if (ret < 0)
225
        return ret;
226
227
    compressor_config_output(ctx->outputs[0]);
228
229
    return 0;
230
}
231
232
#if CONFIG_SIDECHAINCOMPRESS_FILTER
233
static int activate(AVFilterContext *ctx)
234
{
235
    SidechainCompressContext *s = ctx->priv;
236
    AVFrame *out = NULL, *in[2] = { NULL };
237
    int ret, i, nb_samples;
238
    double *dst;
239
240
    FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx);
241
    if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &in[0])) > 0) {
242
        av_audio_fifo_write(s->fifo[0], (void **)in[0]->extended_data,
243
                            in[0]->nb_samples);
244
        av_frame_free(&in[0]);
245
    }
246
    if (ret < 0)
247
        return ret;
248
    if ((ret = ff_inlink_consume_frame(ctx->inputs[1], &in[1])) > 0) {
249
        av_audio_fifo_write(s->fifo[1], (void **)in[1]->extended_data,
250
                            in[1]->nb_samples);
251
        av_frame_free(&in[1]);
252
    }
253
    if (ret < 0)
254
        return ret;
255
256
    nb_samples = FFMIN(av_audio_fifo_size(s->fifo[0]), av_audio_fifo_size(s->fifo[1]));
257
    if (nb_samples) {
258
        out = ff_get_audio_buffer(ctx->outputs[0], nb_samples);
259
        if (!out)
260
            return AVERROR(ENOMEM);
261
        for (i = 0; i < 2; i++) {
262
            in[i] = ff_get_audio_buffer(ctx->inputs[i], nb_samples);
263
            if (!in[i]) {
264
                av_frame_free(&in[0]);
265
                av_frame_free(&in[1]);
266
                av_frame_free(&out);
267
                return AVERROR(ENOMEM);
268
            }
269
            av_audio_fifo_read(s->fifo[i], (void **)in[i]->data, nb_samples);
270
        }
271
272
        dst = (double *)out->data[0];
273
        out->pts = s->pts;
274
        s->pts += av_rescale_q(nb_samples, (AVRational){1, ctx->outputs[0]->sample_rate}, ctx->outputs[0]->time_base);
275
276
        compressor(s, (double *)in[0]->data[0], dst,
277
                   (double *)in[1]->data[0], nb_samples,
278
                   s->level_in, s->level_sc,
279
                   ctx->inputs[0], ctx->inputs[1]);
280
281
        av_frame_free(&in[0]);
282
        av_frame_free(&in[1]);
283
284
        ret = ff_filter_frame(ctx->outputs[0], out);
285
        if (ret < 0)
286
            return ret;
287
    }
288
    FF_FILTER_FORWARD_STATUS(ctx->inputs[0], ctx->outputs[0]);
289
    FF_FILTER_FORWARD_STATUS(ctx->inputs[1], ctx->outputs[0]);
290
    if (ff_outlink_frame_wanted(ctx->outputs[0])) {
291
        if (!av_audio_fifo_size(s->fifo[0]))
292
            ff_inlink_request_frame(ctx->inputs[0]);
293
        if (!av_audio_fifo_size(s->fifo[1]))
294
            ff_inlink_request_frame(ctx->inputs[1]);
295
    }
296
    return 0;
297
}
298
299
static int query_formats(AVFilterContext *ctx)
300
{
301
    AVFilterFormats *formats;
302
    AVFilterChannelLayouts *layouts = NULL;
303
    static const enum AVSampleFormat sample_fmts[] = {
304
        AV_SAMPLE_FMT_DBL,
305
        AV_SAMPLE_FMT_NONE
306
    };
307
    int ret, i;
308
309
    if (!ctx->inputs[0]->incfg.channel_layouts ||
310
        !ctx->inputs[0]->incfg.channel_layouts->nb_channel_layouts) {
311
        av_log(ctx, AV_LOG_WARNING,
312
               "No channel layout for input 1\n");
313
            return AVERROR(EAGAIN);
314
    }
315
316
    if ((ret = ff_add_channel_layout(&layouts, ctx->inputs[0]->incfg.channel_layouts->channel_layouts[0])) < 0 ||
317
        (ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->incfg.channel_layouts)) < 0)
318
        return ret;
319
320
    for (i = 0; i < 2; i++) {
321
        layouts = ff_all_channel_counts();
322
        if ((ret = ff_channel_layouts_ref(layouts, &ctx->inputs[i]->outcfg.channel_layouts)) < 0)
323
            return ret;
324
    }
325
326
    formats = ff_make_format_list(sample_fmts);
327
    if ((ret = ff_set_common_formats(ctx, formats)) < 0)
328
        return ret;
329
330
    formats = ff_all_samplerates();
331
    return ff_set_common_samplerates(ctx, formats);
332
}
333
334
static int config_output(AVFilterLink *outlink)
335
{
336
    AVFilterContext *ctx = outlink->src;
337
    SidechainCompressContext *s = ctx->priv;
338
339
    if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
340
        av_log(ctx, AV_LOG_ERROR,
341
               "Inputs must have the same sample rate "
342
               "%d for in0 vs %d for in1\n",
343
               ctx->inputs[0]->sample_rate, ctx->inputs[1]->sample_rate);
344
        return AVERROR(EINVAL);
345
    }
346
347
    outlink->sample_rate = ctx->inputs[0]->sample_rate;
348
    outlink->time_base   = ctx->inputs[0]->time_base;
349
    outlink->channel_layout = ctx->inputs[0]->channel_layout;
350
    outlink->channels = ctx->inputs[0]->channels;
351
352
    s->fifo[0] = av_audio_fifo_alloc(ctx->inputs[0]->format, ctx->inputs[0]->channels, 1024);
353
    s->fifo[1] = av_audio_fifo_alloc(ctx->inputs[1]->format, ctx->inputs[1]->channels, 1024);
354
    if (!s->fifo[0] || !s->fifo[1])
355
        return AVERROR(ENOMEM);
356
357
    compressor_config_output(outlink);
358
359
    return 0;
360
}
361
362
static av_cold void uninit(AVFilterContext *ctx)
363
{
364
    SidechainCompressContext *s = ctx->priv;
365
366
    av_audio_fifo_free(s->fifo[0]);
367
    av_audio_fifo_free(s->fifo[1]);
368
}
369
370
static const AVFilterPad sidechaincompress_inputs[] = {
371
    {
372
        .name           = "main",
373
        .type           = AVMEDIA_TYPE_AUDIO,
374
    },{
375
        .name           = "sidechain",
376
        .type           = AVMEDIA_TYPE_AUDIO,
377
    },
378
    { NULL }
379
};
380
381
static const AVFilterPad sidechaincompress_outputs[] = {
382
    {
383
        .name          = "default",
384
        .type          = AVMEDIA_TYPE_AUDIO,
385
        .config_props  = config_output,
386
    },
387
    { NULL }
388
};
389
390
AVFilter ff_af_sidechaincompress = {
391
    .name           = "sidechaincompress",
392
    .description    = NULL_IF_CONFIG_SMALL("Sidechain compressor."),
393
    .priv_size      = sizeof(SidechainCompressContext),
394
    .priv_class     = &sidechaincompress_class,
395
    .query_formats  = query_formats,
396
    .activate       = activate,
397
    .uninit         = uninit,
398
    .inputs         = sidechaincompress_inputs,
399
    .outputs        = sidechaincompress_outputs,
400
    .process_command = process_command,
401
};
402
#endif  /* CONFIG_SIDECHAINCOMPRESS_FILTER */
403
404
#if CONFIG_ACOMPRESSOR_FILTER
405
static int acompressor_filter_frame(AVFilterLink *inlink, AVFrame *in)
406
{
407
    const double *src = (const double *)in->data[0];
408
    AVFilterContext *ctx = inlink->dst;
409
    SidechainCompressContext *s = ctx->priv;
410
    AVFilterLink *outlink = ctx->outputs[0];
411
    AVFrame *out;
412
    double *dst;
413
414
    if (av_frame_is_writable(in)) {
415
        out = in;
416
    } else {
417
        out = ff_get_audio_buffer(outlink, in->nb_samples);
418
        if (!out) {
419
            av_frame_free(&in);
420
            return AVERROR(ENOMEM);
421
        }
422
        av_frame_copy_props(out, in);
423
    }
424
    dst = (double *)out->data[0];
425
426
    compressor(s, src, dst, src, in->nb_samples,
427
               s->level_in, s->level_in,
428
               inlink, inlink);
429
430
    if (out != in)
431
        av_frame_free(&in);
432
    return ff_filter_frame(outlink, out);
433
}
434
435
static int acompressor_query_formats(AVFilterContext *ctx)
436
{
437
    AVFilterFormats *formats;
438
    AVFilterChannelLayouts *layouts;
439
    static const enum AVSampleFormat sample_fmts[] = {
440
        AV_SAMPLE_FMT_DBL,
441
        AV_SAMPLE_FMT_NONE
442
    };
443
    int ret;
444
445
    layouts = ff_all_channel_counts();
446
    if (!layouts)
447
        return AVERROR(ENOMEM);
448
    ret = ff_set_common_channel_layouts(ctx, layouts);
449
    if (ret < 0)
450
        return ret;
451
452
    formats = ff_make_format_list(sample_fmts);
453
    if (!formats)
454
        return AVERROR(ENOMEM);
455
    ret = ff_set_common_formats(ctx, formats);
456
    if (ret < 0)
457
        return ret;
458
459
    formats = ff_all_samplerates();
460
    if (!formats)
461
        return AVERROR(ENOMEM);
462
    return ff_set_common_samplerates(ctx, formats);
463
}
464
465
#define acompressor_options options
466
AVFILTER_DEFINE_CLASS(acompressor);
467
468
static const AVFilterPad acompressor_inputs[] = {
469
    {
470
        .name           = "default",
471
        .type           = AVMEDIA_TYPE_AUDIO,
472
        .filter_frame   = acompressor_filter_frame,
473
    },
474
    { NULL }
475
};
476
477
static const AVFilterPad acompressor_outputs[] = {
478
    {
479
        .name          = "default",
480
        .type          = AVMEDIA_TYPE_AUDIO,
481
        .config_props  = compressor_config_output,
482
    },
483
    { NULL }
484
};
485
486
AVFilter ff_af_acompressor = {
487
    .name           = "acompressor",
488
    .description    = NULL_IF_CONFIG_SMALL("Audio compressor."),
489
    .priv_size      = sizeof(SidechainCompressContext),
490
    .priv_class     = &acompressor_class,
491
    .query_formats  = acompressor_query_formats,
492
    .inputs         = acompressor_inputs,
493
    .outputs        = acompressor_outputs,
494
    .process_command = process_command,
495
};
496
#endif  /* CONFIG_ACOMPRESSOR_FILTER */