mirror of
https://github.com/nyanmisaka/ffmpeg-rockchip.git
synced 2026-01-24 02:21:11 +01:00
avfilter: add audio compressor filter
Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
parent
3f895dcb0d
commit
1685a781cd
6 changed files with 205 additions and 31 deletions
|
|
@ -35,6 +35,7 @@ version <next>:
|
||||||
- anoisesrc audio filter source
|
- anoisesrc audio filter source
|
||||||
- IVR demuxer
|
- IVR demuxer
|
||||||
- compensationdelay filter
|
- compensationdelay filter
|
||||||
|
- acompressor filter
|
||||||
|
|
||||||
|
|
||||||
version 2.8:
|
version 2.8:
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,78 @@ build.
|
||||||
|
|
||||||
Below is a description of the currently available audio filters.
|
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
|
@section acrossfade
|
||||||
|
|
||||||
Apply cross fade from one input audio stream to another input audio stream.
|
Apply cross fade from one input audio stream to another input audio stream.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ OBJS = allfilters.o \
|
||||||
transform.o \
|
transform.o \
|
||||||
video.o \
|
video.o \
|
||||||
|
|
||||||
|
OBJS-$(CONFIG_ACOMPRESSOR_FILTER) += af_sidechaincompress.o
|
||||||
OBJS-$(CONFIG_ACROSSFADE_FILTER) += af_afade.o
|
OBJS-$(CONFIG_ACROSSFADE_FILTER) += af_afade.o
|
||||||
OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o
|
OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o
|
||||||
OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o
|
OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Sidechain compressor filter
|
* Audio (Sidechain) Compressor filter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "libavutil/avassert.h"
|
#include "libavutil/avassert.h"
|
||||||
|
|
@ -61,7 +61,7 @@ typedef struct SidechainCompressContext {
|
||||||
#define A AV_OPT_FLAG_AUDIO_PARAM
|
#define A AV_OPT_FLAG_AUDIO_PARAM
|
||||||
#define F AV_OPT_FLAG_FILTERING_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 },
|
{ "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 },
|
{ "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 },
|
{ "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 }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define sidechaincompress_options options
|
||||||
AVFILTER_DEFINE_CLASS(sidechaincompress);
|
AVFILTER_DEFINE_CLASS(sidechaincompress);
|
||||||
|
|
||||||
static av_cold int init(AVFilterContext *ctx)
|
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);
|
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;
|
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 makeup = s->makeup;
|
||||||
const double mix = s->mix;
|
const double mix = s->mix;
|
||||||
const double *scsrc;
|
int i, c;
|
||||||
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];
|
|
||||||
|
|
||||||
for (i = 0; i < nb_samples; i++) {
|
for (i = 0; i < nb_samples; i++) {
|
||||||
double abs_sample, gain = 1.0;
|
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->knee_start, s->knee_stop,
|
||||||
s->compressed_knee_stop, s->detection);
|
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[c] *= (gain * makeup * mix + (1. - mix));
|
||||||
|
|
||||||
sample += outlink->channels;
|
sample += inlink->channels;
|
||||||
scsrc += sclink->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]);
|
ret = ff_filter_frame(outlink, s->input_frame[0]);
|
||||||
|
|
||||||
s->input_frame[0] = NULL;
|
s->input_frame[0] = NULL;
|
||||||
|
|
@ -253,7 +274,6 @@ static int query_formats(AVFilterContext *ctx)
|
||||||
static int config_output(AVFilterLink *outlink)
|
static int config_output(AVFilterLink *outlink)
|
||||||
{
|
{
|
||||||
AVFilterContext *ctx = outlink->src;
|
AVFilterContext *ctx = outlink->src;
|
||||||
SidechainCompressContext *s = ctx->priv;
|
|
||||||
|
|
||||||
if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
|
if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
|
||||||
av_log(ctx, AV_LOG_ERROR,
|
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->channel_layout = ctx->inputs[0]->channel_layout;
|
||||||
outlink->channels = ctx->inputs[0]->channels;
|
outlink->channels = ctx->inputs[0]->channels;
|
||||||
|
|
||||||
s->attack_coeff = FFMIN(1., 1. / (s->attack * outlink->sample_rate / 4000.));
|
compressor_config_output(outlink);
|
||||||
s->release_coeff = FFMIN(1., 1. / (s->release * outlink->sample_rate / 4000.));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -310,3 +329,83 @@ AVFilter ff_af_sidechaincompress = {
|
||||||
.inputs = sidechaincompress_inputs,
|
.inputs = sidechaincompress_inputs,
|
||||||
.outputs = sidechaincompress_outputs,
|
.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;
|
return;
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
|
|
||||||
|
REGISTER_FILTER(ACOMPRESSOR, acompressor, af);
|
||||||
REGISTER_FILTER(ACROSSFADE, acrossfade, af);
|
REGISTER_FILTER(ACROSSFADE, acrossfade, af);
|
||||||
REGISTER_FILTER(ADELAY, adelay, af);
|
REGISTER_FILTER(ADELAY, adelay, af);
|
||||||
REGISTER_FILTER(AECHO, aecho, af);
|
REGISTER_FILTER(AECHO, aecho, af);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#include "libavutil/version.h"
|
#include "libavutil/version.h"
|
||||||
|
|
||||||
#define LIBAVFILTER_VERSION_MAJOR 6
|
#define LIBAVFILTER_VERSION_MAJOR 6
|
||||||
#define LIBAVFILTER_VERSION_MINOR 16
|
#define LIBAVFILTER_VERSION_MINOR 17
|
||||||
#define LIBAVFILTER_VERSION_MICRO 100
|
#define LIBAVFILTER_VERSION_MICRO 100
|
||||||
|
|
||||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue