avfilter: add compensation delay line filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Paul B Mahol 2015-11-24 11:14:36 +01:00
parent dad354f38d
commit 3f895dcb0d
6 changed files with 250 additions and 1 deletions

View File

@ -34,6 +34,7 @@ version <next>:
- realtime filter
- anoisesrc audio filter source
- IVR demuxer
- compensationdelay filter
version 2.8:

View File

@ -1628,6 +1628,54 @@ compand=.1|.1:.1|.1:-45.1/-45.1|-45/-900|0/-900:.01:45:-90:.1
@end example
@end itemize
@section compensationdelay
Compensation Delay Line is a metric based delay to compensate differing
positions of microphones or speakers.
For example, you have recorded guitar with two microphones placed in
different location. Because the front of sound wave has fixed speed in
normal conditions, the phasing of microphones can vary and depends on
their location and interposition. The best sound mix can be achieved when
these microphones are in phase (synchronized). Note that distance of
~30 cm between microphones makes one microphone to capture signal in
antiphase to another microphone. That makes the final mix sounding moody.
This filter helps to solve phasing problems by adding different delays
to each microphone track and make them synchronized.
The best result can be reached when you take one track as base and
synchronize other tracks one by one with it.
Remember that synchronization/delay tolerance depends on sample rate, too.
Higher sample rates will give more tolerance.
It accepts the following parameters:
@table @option
@item mm
Set millimeters distance. This is compensation distance for fine tuning.
Default is 0.
@item cm
Set cm distance. This is compensation distance for tightening distance setup.
Default is 0.
@item m
Set meters distance. This is compensation distance for hard distance setup.
Default is 0.
@item dry
Set dry amount. Amount of unprocessed (dry) signal.
Default is 0.
@item wet
Set wet amount. Amount of processed (wet) signal.
Default is 1.
@item temp
Set temperature degree in Celsius. This is the temperature of the environment.
Default is 20.
@end table
@section dcshift
Apply a DC shift to the audio.

View File

@ -64,6 +64,7 @@ OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o
OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o
OBJS-$(CONFIG_CHORUS_FILTER) += af_chorus.o generate_wave_table.o
OBJS-$(CONFIG_COMPAND_FILTER) += af_compand.o
OBJS-$(CONFIG_COMPENSATIONDELAY_FILTER) += af_compensationdelay.o
OBJS-$(CONFIG_DCSHIFT_FILTER) += af_dcshift.o
OBJS-$(CONFIG_DYNAUDNORM_FILTER) += af_dynaudnorm.o
OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen, Vladimir Sadovnikov and others
* Copyright (c) 2015 Paul B Mahol
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct CompensationDelayContext {
const AVClass *class;
int distance_mm;
int distance_cm;
int distance_m;
double dry, wet;
int temp;
unsigned delay;
unsigned w_ptr;
unsigned buf_size;
AVFrame *delay_frame;
} CompensationDelayContext;
#define OFFSET(x) offsetof(CompensationDelayContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption compensationdelay_options[] = {
{ "mm", "set mm distance", OFFSET(distance_mm), AV_OPT_TYPE_INT, {.i64=0}, 0, 10, A },
{ "cm", "set cm distance", OFFSET(distance_cm), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, A },
{ "m", "set meter distance", OFFSET(distance_m), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, A },
{ "dry", "set dry amount", OFFSET(dry), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, A },
{ "wet", "set wet amount", OFFSET(wet), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, A },
{ "temp", "set temperature °C", OFFSET(temp), AV_OPT_TYPE_INT, {.i64=20}, -50, 50, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(compensationdelay);
// The maximum distance for options
#define COMP_DELAY_MAX_DISTANCE (100.0 * 100.0 + 100.0 * 1.0 + 1.0)
// The actual speed of sound in normal conditions
#define COMP_DELAY_SOUND_SPEED_KM_H(temp) 1.85325 * (643.95 * pow(((temp + 273.15) / 273.15), 0.5))
#define COMP_DELAY_SOUND_SPEED_CM_S(temp) (COMP_DELAY_SOUND_SPEED_KM_H(temp) * (1000.0 * 100.0) /* cm/km */ / (60.0 * 60.0) /* s/h */)
#define COMP_DELAY_SOUND_FRONT_DELAY(temp) (1.0 / COMP_DELAY_SOUND_SPEED_CM_S(temp))
// The maximum delay may be reached by this filter
#define COMP_DELAY_MAX_DELAY (COMP_DELAY_MAX_DISTANCE * COMP_DELAY_SOUND_FRONT_DELAY(50))
static int query_formats(AVFilterContext *ctx)
{
AVFilterChannelLayouts *layouts;
AVFilterFormats *formats;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_DBLP,
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);
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
CompensationDelayContext *s = ctx->priv;
unsigned min_size, new_size = 1;
s->delay = (s->distance_m * 100. + s->distance_cm * 1. + s->distance_mm * .1) *
COMP_DELAY_SOUND_FRONT_DELAY(s->temp) * inlink->sample_rate;
min_size = inlink->sample_rate * COMP_DELAY_MAX_DELAY;
while (new_size < min_size)
new_size <<= 1;
s->delay_frame = av_frame_alloc();
if (!s->delay_frame)
return AVERROR(ENOMEM);
s->buf_size = new_size;
s->delay_frame->format = inlink->format;
s->delay_frame->nb_samples = new_size;
s->delay_frame->channel_layout = inlink->channel_layout;
return av_frame_get_buffer(s->delay_frame, 32);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
CompensationDelayContext *s = ctx->priv;
const unsigned b_mask = s->buf_size - 1;
const unsigned buf_size = s->buf_size;
const unsigned delay = s->delay;
const double dry = s->dry;
const double wet = s->wet;
unsigned r_ptr, w_ptr;
AVFrame *out;
int n, ch;
out = ff_get_audio_buffer(inlink, in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
av_frame_copy_props(out, in);
for (ch = 0; ch < inlink->channels; ch++) {
const double *src = (const double *)in->extended_data[ch];
double *dst = (double *)out->extended_data[ch];
double *buffer = (double *)s->delay_frame->extended_data[ch];
w_ptr = s->w_ptr;
r_ptr = (w_ptr + buf_size - delay) & b_mask;
for (n = 0; n < in->nb_samples; n++) {
const double sample = src[n];
buffer[w_ptr] = sample;
dst[n] = dry * sample + wet * buffer[r_ptr];
w_ptr = (w_ptr + 1) & b_mask;
r_ptr = (r_ptr + 1) & b_mask;
}
}
s->w_ptr = w_ptr;
av_frame_free(&in);
return ff_filter_frame(ctx->outputs[0], out);
}
static av_cold void uninit(AVFilterContext *ctx)
{
CompensationDelayContext *s = ctx->priv;
av_frame_free(&s->delay_frame);
}
static const AVFilterPad compensationdelay_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad compensationdelay_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter ff_af_compensationdelay = {
.name = "compensationdelay",
.description = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."),
.query_formats = query_formats,
.priv_size = sizeof(CompensationDelayContext),
.priv_class = &compensationdelay_class,
.uninit = uninit,
.inputs = compensationdelay_inputs,
.outputs = compensationdelay_outputs,
};

View File

@ -86,6 +86,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(CHANNELSPLIT, channelsplit, af);
REGISTER_FILTER(CHORUS, chorus, af);
REGISTER_FILTER(COMPAND, compand, af);
REGISTER_FILTER(COMPENSATIONDELAY, compensationdelay, af);
REGISTER_FILTER(DCSHIFT, dcshift, af);
REGISTER_FILTER(DYNAUDNORM, dynaudnorm, af);
REGISTER_FILTER(EARWAX, earwax, af);

View File

@ -30,7 +30,7 @@
#include "libavutil/version.h"
#define LIBAVFILTER_VERSION_MAJOR 6
#define LIBAVFILTER_VERSION_MINOR 15
#define LIBAVFILTER_VERSION_MINOR 16
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \