avfilter: add Haas stereo enhancer

Signed-off-by: Paul B Mahol <onemda@gmail.com>
pull/269/head
Paul B Mahol 7 years ago
parent e59da0f7ff
commit cf0eed2525
  1. 1
      Changelog
  2. 64
      doc/filters.texi
  3. 1
      libavfilter/Makefile
  4. 228
      libavfilter/af_haas.c
  5. 1
      libavfilter/allfilters.c
  6. 2
      libavfilter/version.h

@ -43,6 +43,7 @@ version <next>:
- add --disable-autodetect build switch
- drop deprecated qtkit input device (use avfoundation instead)
- despill video filter
- haas audio filter
version 3.3:
- CrystalHD decoder moved to new decode API

@ -2770,6 +2770,70 @@ Set delay-line interpolation, @var{linear} or @var{quadratic}.
Default is @var{linear}.
@end table
@section haas
Apply Haas effect to audio.
Note that this makes most sense to apply on mono signals.
With this filter applied to mono signals it give some directionality and
streches its stereo image.
The filter accepts the following options:
@table @option
@item level_in
Set input level. By default is @var{1}, or 0dB
@item level_out
Set output level. By default is @var{1}, or 0dB.
@item side_gain
Set gain applied to side part of signal. By default is @var{1}.
@item middle_source
Set kind of middle source. Can be one of the following:
@table @samp
@item left
Pick left channel.
@item right
Pick right channel.
@item mid
Pick middle part signal of stereo image.
@item side
Pick side part signal of stereo image.
@end table
@item middle_phase
Change middle phase. By default is disabled.
@item left_delay
Set left channel delay. By default is @var{2.05} milliseconds.
@item left_balance
Set left channel balance. By default is @var{-1}.
@item left_gain
Set left channel gain. By default is @var{1}.
@item left_phase
Change left phase. By default is disabled.
@item right_delay
Set right channel delay. By defaults is @var{2.12} milliseconds.
@item right_balance
Set right channel balance. By default is @var{1}.
@item right_gain
Set right channel gain. By default is @var{1}.
@item right_phase
Change right phase. By default is enabled.
@end table
@section hdcd
Decodes High Definition Compatible Digital (HDCD) data. A 16-bit PCM stream with

@ -91,6 +91,7 @@ OBJS-$(CONFIG_EQUALIZER_FILTER) += af_biquads.o
OBJS-$(CONFIG_EXTRASTEREO_FILTER) += af_extrastereo.o
OBJS-$(CONFIG_FIREQUALIZER_FILTER) += af_firequalizer.o
OBJS-$(CONFIG_FLANGER_FILTER) += af_flanger.o generate_wave_table.o
OBJS-$(CONFIG_HAAS_FILTER) += af_haas.o
OBJS-$(CONFIG_HDCD_FILTER) += af_hdcd.o
OBJS-$(CONFIG_HEADPHONE_FILTER) += af_headphone.o
OBJS-$(CONFIG_HIGHPASS_FILTER) += af_biquads.o

@ -0,0 +1,228 @@
/*
* Copyright (c) 2001-2010 Vladimir Sadovnikov
*
* 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/channel_layout.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "formats.h"
#define MAX_HAAS_DELAY 40
typedef struct HaasContext {
const AVClass *class;
int par_m_source;
double par_delay0;
double par_delay1;
int par_phase0;
int par_phase1;
int par_middle_phase;
double par_side_gain;
double par_gain0;
double par_gain1;
double par_balance0;
double par_balance1;
double level_in;
double level_out;
double *buffer;
size_t buffer_size;
uint32_t write_ptr;
uint32_t delay[2];
double balance_l[2];
double balance_r[2];
double phase0;
double phase1;
} HaasContext;
#define OFFSET(x) offsetof(HaasContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption haas_options[] = {
{ "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A },
{ "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A },
{ "side_gain", "set side gain", OFFSET(par_side_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A },
{ "middle_source", "set middle source", OFFSET(par_m_source), AV_OPT_TYPE_INT, {.i64=2}, 0, 3, A, "source" },
{ "left", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "source" },
{ "right", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "source" },
{ "mid", "L+R", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, "source" },
{ "side", "L-R", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, A, "source" },
{ "middle_phase", "set middle phase", OFFSET(par_middle_phase), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A },
{ "left_delay", "set left delay", OFFSET(par_delay0), AV_OPT_TYPE_DOUBLE, {.dbl=2.05}, 0, MAX_HAAS_DELAY, A },
{ "left_balance", "set left balance", OFFSET(par_balance0), AV_OPT_TYPE_DOUBLE, {.dbl=-1.0}, -1, 1, A },
{ "left_gain", "set left gain", OFFSET(par_gain0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A },
{ "left_phase", "set left phase", OFFSET(par_phase0), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A },
{ "right_delay", "set right delay", OFFSET(par_delay1), AV_OPT_TYPE_DOUBLE, {.dbl=2.12}, 0, MAX_HAAS_DELAY, A },
{ "right_balance", "set right balance", OFFSET(par_balance1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, A },
{ "right_gain", "set right gain", OFFSET(par_gain1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A },
{ "right_phase", "set right phase", OFFSET(par_phase1), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(haas);
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layout = NULL;
int ret;
if ((ret = ff_add_format (&formats, AV_SAMPLE_FMT_DBL )) < 0 ||
(ret = ff_set_common_formats (ctx , formats )) < 0 ||
(ret = ff_add_channel_layout (&layout , AV_CH_LAYOUT_STEREO)) < 0 ||
(ret = ff_set_common_channel_layouts (ctx , layout )) < 0)
return ret;
formats = ff_all_samplerates();
return ff_set_common_samplerates(ctx, formats);
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
HaasContext *s = ctx->priv;
size_t min_buf_size = (size_t)(inlink->sample_rate * MAX_HAAS_DELAY * 0.001);
size_t new_buf_size = 1;
while (new_buf_size < min_buf_size)
new_buf_size <<= 1;
av_freep(&s->buffer);
s->buffer = av_calloc(new_buf_size, sizeof(*s->buffer));
if (!s->buffer)
return AVERROR(ENOMEM);
s->buffer_size = new_buf_size;
s->write_ptr = 0;
s->delay[0] = (uint32_t)(s->par_delay0 * 0.001 * inlink->sample_rate);
s->delay[1] = (uint32_t)(s->par_delay1 * 0.001 * inlink->sample_rate);
s->phase0 = s->par_phase0 ? 1.0 : -1.0;
s->phase1 = s->par_phase1 ? 1.0 : -1.0;
s->balance_l[0] = (s->par_balance0 + 1) / 2 * s->par_gain0 * s->phase0;
s->balance_r[0] = (1.0 - (s->par_balance0 + 1) / 2) * (s->par_gain0) * s->phase0;
s->balance_l[1] = (s->par_balance1 + 1) / 2 * s->par_gain1 * s->phase1;
s->balance_r[1] = (1.0 - (s->par_balance1 + 1) / 2) * (s->par_gain1) * s->phase1;
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
HaasContext *s = ctx->priv;
const double *src = (const double *)in->data[0];
const double level_in = s->level_in;
const double level_out = s->level_out;
const uint32_t mask = s->buffer_size - 1;
double *buffer = s->buffer;
AVFrame *out;
double *dst;
int n;
if (av_frame_is_writable(in)) {
out = in;
} else {
out = ff_get_audio_buffer(inlink, in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
av_frame_copy_props(out, in);
}
dst = (double *)out->data[0];
for (n = 0; n < in->nb_samples; n++, src += 2, dst += 2) {
double mid, side[2], side_l, side_r;
uint32_t s0_ptr, s1_ptr;
switch (s->par_m_source) {
case 0: mid = src[0]; break;
case 1: mid = src[1]; break;
case 2: mid = (src[0] + src[1]) * 0.5; break;
case 3: mid = (src[0] - src[1]) * 0.5; break;
}
mid *= level_in;
buffer[s->write_ptr] = mid;
s0_ptr = (s->write_ptr + s->buffer_size - s->delay[0]) & mask;
s1_ptr = (s->write_ptr + s->buffer_size - s->delay[1]) & mask;
if (s->par_middle_phase)
mid = -mid;
side[0] = buffer[s0_ptr] * s->par_side_gain;
side[1] = buffer[s1_ptr] * s->par_side_gain;
side_l = side[0] * s->balance_l[0] - side[1] * s->balance_l[1];
side_r = side[1] * s->balance_r[1] - side[0] * s->balance_r[0];
dst[0] = (mid + side_l) * level_out;
dst[1] = (mid + side_r) * level_out;
s->write_ptr = (s->write_ptr + 1) & mask;
}
if (out != in)
av_frame_free(&in);
return ff_filter_frame(outlink, out);
}
static av_cold void uninit(AVFilterContext *ctx)
{
HaasContext *s = ctx->priv;
av_freep(&s->buffer);
s->buffer_size = 0;
}
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_input,
},
{ NULL }
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter ff_af_haas = {
.name = "haas",
.description = NULL_IF_CONFIG_SMALL("Apply Haas Stereo Enhancer."),
.query_formats = query_formats,
.priv_size = sizeof(HaasContext),
.priv_class = &haas_class,
.uninit = uninit,
.inputs = inputs,
.outputs = outputs,
};

@ -104,6 +104,7 @@ static void register_all(void)
REGISTER_FILTER(EXTRASTEREO, extrastereo, af);
REGISTER_FILTER(FIREQUALIZER, firequalizer, af);
REGISTER_FILTER(FLANGER, flanger, af);
REGISTER_FILTER(HAAS, haas, af);
REGISTER_FILTER(HDCD, hdcd, af);
REGISTER_FILTER(HEADPHONE, headphone, af);
REGISTER_FILTER(HIGHPASS, highpass, af);

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

Loading…
Cancel
Save