mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avfilter: add audio compressor filter
Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
parent
3f895dcb0d
commit
1685a781cd
@ -35,6 +35,7 @@ version <next>:
|
||||
- anoisesrc audio filter source
|
||||
- IVR demuxer
|
||||
- compensationdelay filter
|
||||
- acompressor filter
|
||||
|
||||
|
||||
version 2.8:
|
||||
|
@ -318,6 +318,78 @@ build.
|
||||
|
||||
Below is a description of the currently available audio filters.
|
||||
|
||||
@section acompressor
|
||||
|
||||
A compressor is mainly used to reduce the dynamic range of a signal.
|
||||
Especially modern music is mostly compressed at a high ratio to
|
||||
improve the overall loudness. It's done to get the highest attention
|
||||
of a listener, "fatten" the sound and bring more "power" to the track.
|
||||
If a signal is compressed too much it may sound dull or "dead"
|
||||
afterwards or it may start to "pump" (which could be a powerful effect
|
||||
but can also destroy a track completely).
|
||||
The right compression is the key to reach a professional sound and is
|
||||
the high art of mixing and mastering. Because of its complex settings
|
||||
it may take a long time to get the right feeling for this kind of effect.
|
||||
|
||||
Compression is done by detecting the volume above a chosen level
|
||||
@code{threshold} and dividing it by the factor set with @code{ratio}.
|
||||
So if you set the threshold to -12dB and your signal reaches -6dB a ratio
|
||||
of 2:1 will result in a signal at -9dB. Because an exact manipulation of
|
||||
the signal would cause distortion of the waveform the reduction can be
|
||||
levelled over the time. This is done by setting "Attack" and "Release".
|
||||
@code{attack} determines how long the signal has to rise above the threshold
|
||||
before any reduction will occur and @code{release} sets the time the signal
|
||||
has to fall below the threshold to reduce the reduction again. Shorter signals
|
||||
than the chosen attack time will be left untouched.
|
||||
The overall reduction of the signal can be made up afterwards with the
|
||||
@code{makeup} setting. So compressing the peaks of a signal about 6dB and
|
||||
raising the makeup to this level results in a signal twice as loud than the
|
||||
source. To gain a softer entry in the compression the @code{knee} flattens the
|
||||
hard edge at the threshold in the range of the chosen decibels.
|
||||
|
||||
The filter accepts the following options:
|
||||
|
||||
@table @option
|
||||
@item threshold
|
||||
If a signal of second stream rises above this level it will affect the gain
|
||||
reduction of the first stream.
|
||||
By default it is 0.125. Range is between 0.00097563 and 1.
|
||||
|
||||
@item ratio
|
||||
Set a ratio by which the signal is reduced. 1:2 means that if the level
|
||||
rose 4dB above the threshold, it will be only 2dB above after the reduction.
|
||||
Default is 2. Range is between 1 and 20.
|
||||
|
||||
@item attack
|
||||
Amount of milliseconds the signal has to rise above the threshold before gain
|
||||
reduction starts. Default is 20. Range is between 0.01 and 2000.
|
||||
|
||||
@item release
|
||||
Amount of milliseconds the signal has to fall below the threshold before
|
||||
reduction is decreased again. Default is 250. Range is between 0.01 and 9000.
|
||||
|
||||
@item makeup
|
||||
Set the amount by how much signal will be amplified after processing.
|
||||
Default is 2. Range is from 1 and 64.
|
||||
|
||||
@item knee
|
||||
Curve the sharp knee around the threshold to enter gain reduction more softly.
|
||||
Default is 2.82843. Range is between 1 and 8.
|
||||
|
||||
@item link
|
||||
Choose if the @code{average} level between all channels of input stream
|
||||
or the louder(@code{maximum}) channel of input stream affects the
|
||||
reduction. Default is @code{average}.
|
||||
|
||||
@item detection
|
||||
Should the exact signal be taken in case of @code{peak} or an RMS one in case
|
||||
of @code{rms}. Default is @code{rms} which is mostly smoother.
|
||||
|
||||
@item mix
|
||||
How much to use compressed signal in output. Default is 1.
|
||||
Range is between 0 and 1.
|
||||
@end table
|
||||
|
||||
@section acrossfade
|
||||
|
||||
Apply cross fade from one input audio stream to another input audio stream.
|
||||
|
@ -23,6 +23,7 @@ OBJS = allfilters.o \
|
||||
transform.o \
|
||||
video.o \
|
||||
|
||||
OBJS-$(CONFIG_ACOMPRESSOR_FILTER) += af_sidechaincompress.o
|
||||
OBJS-$(CONFIG_ACROSSFADE_FILTER) += af_afade.o
|
||||
OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o
|
||||
OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sidechain compressor filter
|
||||
* Audio (Sidechain) Compressor filter
|
||||
*/
|
||||
|
||||
#include "libavutil/avassert.h"
|
||||
@ -61,7 +61,7 @@ typedef struct SidechainCompressContext {
|
||||
#define A AV_OPT_FLAG_AUDIO_PARAM
|
||||
#define F AV_OPT_FLAG_FILTERING_PARAM
|
||||
|
||||
static const AVOption sidechaincompress_options[] = {
|
||||
static const AVOption options[] = {
|
||||
{ "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0.000976563, 1, A|F },
|
||||
{ "ratio", "set ratio", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 20, A|F },
|
||||
{ "attack", "set attack", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 0.01, 2000, A|F },
|
||||
@ -78,6 +78,7 @@ static const AVOption sidechaincompress_options[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
#define sidechaincompress_options options
|
||||
AVFILTER_DEFINE_CLASS(sidechaincompress);
|
||||
|
||||
static av_cold int init(AVFilterContext *ctx)
|
||||
@ -126,33 +127,24 @@ static double output_gain(double lin_slope, double ratio, double thres,
|
||||
return exp(gain - slope);
|
||||
}
|
||||
|
||||
static int filter_frame(AVFilterLink *link, AVFrame *frame)
|
||||
static int compressor_config_output(AVFilterLink *outlink)
|
||||
{
|
||||
AVFilterContext *ctx = link->dst;
|
||||
AVFilterContext *ctx = outlink->src;
|
||||
SidechainCompressContext *s = ctx->priv;
|
||||
AVFilterLink *sclink = ctx->inputs[1];
|
||||
AVFilterLink *outlink = ctx->outputs[0];
|
||||
|
||||
s->attack_coeff = FFMIN(1., 1. / (s->attack * outlink->sample_rate / 4000.));
|
||||
s->release_coeff = FFMIN(1., 1. / (s->release * outlink->sample_rate / 4000.));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void compressor(SidechainCompressContext *s,
|
||||
double *sample, const double *scsrc, int nb_samples,
|
||||
AVFilterLink *inlink, AVFilterLink *sclink)
|
||||
{
|
||||
const double makeup = s->makeup;
|
||||
const double mix = s->mix;
|
||||
const double *scsrc;
|
||||
double *sample;
|
||||
int nb_samples;
|
||||
int ret, i, c;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (link == ctx->inputs[i])
|
||||
break;
|
||||
av_assert0(i < 2 && !s->input_frame[i]);
|
||||
s->input_frame[i] = frame;
|
||||
|
||||
if (!s->input_frame[0] || !s->input_frame[1])
|
||||
return 0;
|
||||
|
||||
nb_samples = FFMIN(s->input_frame[0]->nb_samples,
|
||||
s->input_frame[1]->nb_samples);
|
||||
|
||||
sample = (double *)s->input_frame[0]->data[0];
|
||||
scsrc = (const double *)s->input_frame[1]->data[0];
|
||||
int i, c;
|
||||
|
||||
for (i = 0; i < nb_samples; i++) {
|
||||
double abs_sample, gain = 1.0;
|
||||
@ -179,13 +171,42 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame)
|
||||
s->knee_start, s->knee_stop,
|
||||
s->compressed_knee_stop, s->detection);
|
||||
|
||||
for (c = 0; c < outlink->channels; c++)
|
||||
for (c = 0; c < inlink->channels; c++)
|
||||
sample[c] *= (gain * makeup * mix + (1. - mix));
|
||||
|
||||
sample += outlink->channels;
|
||||
sample += inlink->channels;
|
||||
scsrc += sclink->channels;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_SIDECHAINCOMPRESS_FILTER
|
||||
static int filter_frame(AVFilterLink *link, AVFrame *frame)
|
||||
{
|
||||
AVFilterContext *ctx = link->dst;
|
||||
SidechainCompressContext *s = ctx->priv;
|
||||
AVFilterLink *outlink = ctx->outputs[0];
|
||||
const double *scsrc;
|
||||
double *sample;
|
||||
int nb_samples;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
if (link == ctx->inputs[i])
|
||||
break;
|
||||
av_assert0(i < 2 && !s->input_frame[i]);
|
||||
s->input_frame[i] = frame;
|
||||
|
||||
if (!s->input_frame[0] || !s->input_frame[1])
|
||||
return 0;
|
||||
|
||||
nb_samples = FFMIN(s->input_frame[0]->nb_samples,
|
||||
s->input_frame[1]->nb_samples);
|
||||
|
||||
sample = (double *)s->input_frame[0]->data[0];
|
||||
scsrc = (const double *)s->input_frame[1]->data[0];
|
||||
|
||||
compressor(s, sample, scsrc, nb_samples,
|
||||
ctx->inputs[0], ctx->inputs[1]);
|
||||
ret = ff_filter_frame(outlink, s->input_frame[0]);
|
||||
|
||||
s->input_frame[0] = NULL;
|
||||
@ -253,7 +274,6 @@ static int query_formats(AVFilterContext *ctx)
|
||||
static int config_output(AVFilterLink *outlink)
|
||||
{
|
||||
AVFilterContext *ctx = outlink->src;
|
||||
SidechainCompressContext *s = ctx->priv;
|
||||
|
||||
if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
|
||||
av_log(ctx, AV_LOG_ERROR,
|
||||
@ -268,8 +288,7 @@ static int config_output(AVFilterLink *outlink)
|
||||
outlink->channel_layout = ctx->inputs[0]->channel_layout;
|
||||
outlink->channels = ctx->inputs[0]->channels;
|
||||
|
||||
s->attack_coeff = FFMIN(1., 1. / (s->attack * outlink->sample_rate / 4000.));
|
||||
s->release_coeff = FFMIN(1., 1. / (s->release * outlink->sample_rate / 4000.));
|
||||
compressor_config_output(outlink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -310,3 +329,83 @@ AVFilter ff_af_sidechaincompress = {
|
||||
.inputs = sidechaincompress_inputs,
|
||||
.outputs = sidechaincompress_outputs,
|
||||
};
|
||||
#endif /* CONFIG_SIDECHAINCOMPRESS_FILTER */
|
||||
|
||||
#if CONFIG_ACOMPRESSOR_FILTER
|
||||
static int acompressor_filter_frame(AVFilterLink *inlink, AVFrame *frame)
|
||||
{
|
||||
AVFilterContext *ctx = inlink->dst;
|
||||
SidechainCompressContext *s = ctx->priv;
|
||||
AVFilterLink *outlink = ctx->outputs[0];
|
||||
double *sample;
|
||||
|
||||
sample = (double *)frame->data[0];
|
||||
compressor(s, sample, sample, frame->nb_samples,
|
||||
inlink, inlink);
|
||||
|
||||
return ff_filter_frame(outlink, frame);
|
||||
}
|
||||
|
||||
static int acompressor_query_formats(AVFilterContext *ctx)
|
||||
{
|
||||
AVFilterFormats *formats;
|
||||
AVFilterChannelLayouts *layouts;
|
||||
static const enum AVSampleFormat sample_fmts[] = {
|
||||
AV_SAMPLE_FMT_DBL,
|
||||
AV_SAMPLE_FMT_NONE
|
||||
};
|
||||
int ret;
|
||||
|
||||
layouts = ff_all_channel_counts();
|
||||
if (!layouts)
|
||||
return AVERROR(ENOMEM);
|
||||
ret = ff_set_common_channel_layouts(ctx, layouts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
formats = ff_make_format_list(sample_fmts);
|
||||
if (!formats)
|
||||
return AVERROR(ENOMEM);
|
||||
ret = ff_set_common_formats(ctx, formats);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
formats = ff_all_samplerates();
|
||||
if (!formats)
|
||||
return AVERROR(ENOMEM);
|
||||
return ff_set_common_samplerates(ctx, formats);
|
||||
}
|
||||
|
||||
#define acompressor_options options
|
||||
AVFILTER_DEFINE_CLASS(acompressor);
|
||||
|
||||
static const AVFilterPad acompressor_inputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_AUDIO,
|
||||
.filter_frame = acompressor_filter_frame,
|
||||
.needs_writable = 1,
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static const AVFilterPad acompressor_outputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_AUDIO,
|
||||
.config_props = compressor_config_output,
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
AVFilter ff_af_acompressor = {
|
||||
.name = "acompressor",
|
||||
.description = NULL_IF_CONFIG_SMALL("Audio compressor."),
|
||||
.priv_size = sizeof(SidechainCompressContext),
|
||||
.priv_class = &acompressor_class,
|
||||
.init = init,
|
||||
.query_formats = acompressor_query_formats,
|
||||
.inputs = acompressor_inputs,
|
||||
.outputs = acompressor_outputs,
|
||||
};
|
||||
#endif /* CONFIG_ACOMPRESSOR_FILTER */
|
||||
|
@ -45,6 +45,7 @@ void avfilter_register_all(void)
|
||||
return;
|
||||
initialized = 1;
|
||||
|
||||
REGISTER_FILTER(ACOMPRESSOR, acompressor, af);
|
||||
REGISTER_FILTER(ACROSSFADE, acrossfade, af);
|
||||
REGISTER_FILTER(ADELAY, adelay, af);
|
||||
REGISTER_FILTER(AECHO, aecho, af);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "libavutil/version.h"
|
||||
|
||||
#define LIBAVFILTER_VERSION_MAJOR 6
|
||||
#define LIBAVFILTER_VERSION_MINOR 16
|
||||
#define LIBAVFILTER_VERSION_MINOR 17
|
||||
#define LIBAVFILTER_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
|
Loading…
Reference in New Issue
Block a user