Merge remote-tracking branch 'qatar/master'
authorMichael Niedermayer <michaelni@gmx.at>
Tue, 29 Oct 2013 10:50:56 +0000 (11:50 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Tue, 29 Oct 2013 10:58:11 +0000 (11:58 +0100)
* qatar/master:
  lavfi: do not export the filters from shared objects

Conflicts:
libavfilter/af_amix.c
libavfilter/af_anull.c
libavfilter/asrc_anullsrc.c
libavfilter/f_select.c
libavfilter/f_settb.c
libavfilter/split.c
libavfilter/src_movie.c
libavfilter/vf_aspect.c
libavfilter/vf_blackframe.c
libavfilter/vf_colorbalance.c
libavfilter/vf_copy.c
libavfilter/vf_crop.c
libavfilter/vf_cropdetect.c
libavfilter/vf_drawbox.c
libavfilter/vf_format.c
libavfilter/vf_framestep.c
libavfilter/vf_frei0r.c
libavfilter/vf_hflip.c
libavfilter/vf_libopencv.c
libavfilter/vf_lut.c
libavfilter/vf_null.c
libavfilter/vf_overlay.c
libavfilter/vf_scale.c
libavfilter/vf_transpose.c
libavfilter/vf_unsharp.c
libavfilter/vf_vflip.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
145 files changed:
1  2 
libavfilter/af_aconvert.c
libavfilter/af_adelay.c
libavfilter/af_aecho.c
libavfilter/af_afade.c
libavfilter/af_aformat.c
libavfilter/af_amerge.c
libavfilter/af_amix.c
libavfilter/af_anull.c
libavfilter/af_apad.c
libavfilter/af_aphaser.c
libavfilter/af_aresample.c
libavfilter/af_asetnsamples.c
libavfilter/af_asetrate.c
libavfilter/af_ashowinfo.c
libavfilter/af_astats.c
libavfilter/af_astreamsync.c
libavfilter/af_asyncts.c
libavfilter/af_atempo.c
libavfilter/af_biquads.c
libavfilter/af_channelmap.c
libavfilter/af_channelsplit.c
libavfilter/af_compand.c
libavfilter/af_earwax.c
libavfilter/af_join.c
libavfilter/af_ladspa.c
libavfilter/af_pan.c
libavfilter/af_replaygain.c
libavfilter/af_resample.c
libavfilter/af_silencedetect.c
libavfilter/af_volume.c
libavfilter/af_volumedetect.c
libavfilter/allfilters.c
libavfilter/asink_anullsink.c
libavfilter/asrc_aevalsrc.c
libavfilter/asrc_anullsrc.c
libavfilter/asrc_flite.c
libavfilter/asrc_sine.c
libavfilter/avf_avectorscope.c
libavfilter/avf_concat.c
libavfilter/avf_showspectrum.c
libavfilter/avf_showwaves.c
libavfilter/buffersink.c
libavfilter/buffersrc.c
libavfilter/f_ebur128.c
libavfilter/f_interleave.c
libavfilter/f_perms.c
libavfilter/f_select.c
libavfilter/f_sendcmd.c
libavfilter/f_settb.c
libavfilter/f_zmq.c
libavfilter/fifo.c
libavfilter/setpts.c
libavfilter/split.c
libavfilter/src_movie.c
libavfilter/trim.c
libavfilter/vf_alphamerge.c
libavfilter/vf_aspect.c
libavfilter/vf_bbox.c
libavfilter/vf_blackdetect.c
libavfilter/vf_blackframe.c
libavfilter/vf_blend.c
libavfilter/vf_boxblur.c
libavfilter/vf_colorbalance.c
libavfilter/vf_colorchannelmixer.c
libavfilter/vf_colormatrix.c
libavfilter/vf_copy.c
libavfilter/vf_crop.c
libavfilter/vf_cropdetect.c
libavfilter/vf_curves.c
libavfilter/vf_dctdnoiz.c
libavfilter/vf_decimate.c
libavfilter/vf_delogo.c
libavfilter/vf_deshake.c
libavfilter/vf_drawbox.c
libavfilter/vf_drawtext.c
libavfilter/vf_edgedetect.c
libavfilter/vf_extractplanes.c
libavfilter/vf_fade.c
libavfilter/vf_field.c
libavfilter/vf_fieldmatch.c
libavfilter/vf_fieldorder.c
libavfilter/vf_format.c
libavfilter/vf_fps.c
libavfilter/vf_framestep.c
libavfilter/vf_frei0r.c
libavfilter/vf_geq.c
libavfilter/vf_gradfun.c
libavfilter/vf_hflip.c
libavfilter/vf_histeq.c
libavfilter/vf_histogram.c
libavfilter/vf_hqdn3d.c
libavfilter/vf_hue.c
libavfilter/vf_idet.c
libavfilter/vf_il.c
libavfilter/vf_interlace.c
libavfilter/vf_kerndeint.c
libavfilter/vf_libopencv.c
libavfilter/vf_lut.c
libavfilter/vf_lut3d.c
libavfilter/vf_mcdeint.c
libavfilter/vf_mergeplanes.c
libavfilter/vf_mp.c
libavfilter/vf_mpdecimate.c
libavfilter/vf_noise.c
libavfilter/vf_null.c
libavfilter/vf_overlay.c
libavfilter/vf_owdenoise.c
libavfilter/vf_pad.c
libavfilter/vf_perspective.c
libavfilter/vf_phase.c
libavfilter/vf_pixdesctest.c
libavfilter/vf_pp.c
libavfilter/vf_psnr.c
libavfilter/vf_pullup.c
libavfilter/vf_removelogo.c
libavfilter/vf_rotate.c
libavfilter/vf_sab.c
libavfilter/vf_scale.c
libavfilter/vf_separatefields.c
libavfilter/vf_setfield.c
libavfilter/vf_showinfo.c
libavfilter/vf_smartblur.c
libavfilter/vf_spp.c
libavfilter/vf_stereo3d.c
libavfilter/vf_subtitles.c
libavfilter/vf_super2xsai.c
libavfilter/vf_swapuv.c
libavfilter/vf_telecine.c
libavfilter/vf_thumbnail.c
libavfilter/vf_tile.c
libavfilter/vf_tinterlace.c
libavfilter/vf_transpose.c
libavfilter/vf_unsharp.c
libavfilter/vf_vflip.c
libavfilter/vf_vidstabdetect.c
libavfilter/vf_vidstabtransform.c
libavfilter/vf_vignette.c
libavfilter/vf_w3fdif.c
libavfilter/vf_yadif.c
libavfilter/vsink_nullsink.c
libavfilter/vsrc_cellauto.c
libavfilter/vsrc_life.c
libavfilter/vsrc_mandelbrot.c
libavfilter/vsrc_mptestsrc.c
libavfilter/vsrc_testsrc.c

index a198d2b,0000000..10497aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,196 @@@
- AVFilter avfilter_af_aconvert = {
 +/*
 + * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram <smeenaks@ucsd.edu>
 + * Copyright (c) 2011 Stefano Sabatini
 + * Copyright (c) 2011 Mina Nagy Zaki
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * sample format and channel layout conversion audio filter
 + */
 +
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "libswresample/swresample.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass       *class;
 +    enum AVSampleFormat  out_sample_fmt;
 +    int64_t              out_chlayout;
 +    struct SwrContext *swr;
 +    char *format_str;
 +    char *channel_layout_str;
 +} AConvertContext;
 +
 +#define OFFSET(x) offsetof(AConvertContext, x)
 +#define A AV_OPT_FLAG_AUDIO_PARAM
 +#define F AV_OPT_FLAG_FILTERING_PARAM
 +static const AVOption aconvert_options[] = {
 +    { "sample_fmt",     "", OFFSET(format_str),         AV_OPT_TYPE_STRING, .flags = A|F },
 +    { "channel_layout", "", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A|F },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(aconvert);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AConvertContext *aconvert = ctx->priv;
 +    int ret = 0;
 +
 +    av_log(ctx, AV_LOG_WARNING, "This filter is deprecated, use aformat instead\n");
 +
 +    aconvert->out_sample_fmt  = AV_SAMPLE_FMT_NONE;
 +    aconvert->out_chlayout    = 0;
 +
 +    if (aconvert->format_str && strcmp(aconvert->format_str, "auto") &&
 +        (ret = ff_parse_sample_format(&aconvert->out_sample_fmt, aconvert->format_str, ctx)) < 0)
 +        return ret;
 +    if (aconvert->channel_layout_str && strcmp(aconvert->channel_layout_str, "auto"))
 +        return ff_parse_channel_layout(&aconvert->out_chlayout, aconvert->channel_layout_str, ctx);
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AConvertContext *aconvert = ctx->priv;
 +    swr_free(&aconvert->swr);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats = NULL;
 +    AConvertContext *aconvert = ctx->priv;
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    AVFilterChannelLayouts *layouts;
 +
 +    ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
 +                         &inlink->out_formats);
 +    if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) {
 +        formats = NULL;
 +        ff_add_format(&formats, aconvert->out_sample_fmt);
 +        ff_formats_ref(formats, &outlink->in_formats);
 +    } else
 +        ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
 +                             &outlink->in_formats);
 +
 +    ff_channel_layouts_ref(ff_all_channel_layouts(),
 +                         &inlink->out_channel_layouts);
 +    if (aconvert->out_chlayout != 0) {
 +        layouts = NULL;
 +        ff_add_channel_layout(&layouts, aconvert->out_chlayout);
 +        ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +    } else
 +        ff_channel_layouts_ref(ff_all_channel_layouts(),
 +                             &outlink->in_channel_layouts);
 +
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    int ret;
 +    AVFilterContext *ctx = outlink->src;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    AConvertContext *aconvert = ctx->priv;
 +    char buf1[64], buf2[64];
 +
 +    /* if not specified in args, use the format and layout of the output */
 +    if (aconvert->out_sample_fmt == AV_SAMPLE_FMT_NONE)
 +        aconvert->out_sample_fmt = outlink->format;
 +    if (aconvert->out_chlayout   == 0)
 +        aconvert->out_chlayout   = outlink->channel_layout;
 +
 +    aconvert->swr = swr_alloc_set_opts(aconvert->swr,
 +                                       aconvert->out_chlayout, aconvert->out_sample_fmt, inlink->sample_rate,
 +                                       inlink->channel_layout, inlink->format,           inlink->sample_rate,
 +                                       0, ctx);
 +    if (!aconvert->swr)
 +        return AVERROR(ENOMEM);
 +    ret = swr_init(aconvert->swr);
 +    if (ret < 0)
 +        return ret;
 +
 +    av_get_channel_layout_string(buf1, sizeof(buf1),
 +                                 -1, inlink ->channel_layout);
 +    av_get_channel_layout_string(buf2, sizeof(buf2),
 +                                 -1, outlink->channel_layout);
 +    av_log(ctx, AV_LOG_VERBOSE,
 +           "fmt:%s cl:%s -> fmt:%s cl:%s\n",
 +           av_get_sample_fmt_name(inlink ->format), buf1,
 +           av_get_sample_fmt_name(outlink->format), buf2);
 +
 +    return 0;
 +}
 +
 +static int  filter_frame(AVFilterLink *inlink, AVFrame *insamplesref)
 +{
 +    AConvertContext *aconvert = inlink->dst->priv;
 +    const int n = insamplesref->nb_samples;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFrame *outsamplesref = ff_get_audio_buffer(outlink, n);
 +    int ret;
 +
 +    if (!outsamplesref)
 +        return AVERROR(ENOMEM);
 +    swr_convert(aconvert->swr, outsamplesref->extended_data, n,
 +                        (void *)insamplesref->extended_data, n);
 +
 +    av_frame_copy_props(outsamplesref, insamplesref);
 +    av_frame_set_channels(outsamplesref, outlink->channels);
 +    outsamplesref->channel_layout = outlink->channel_layout;
 +
 +    ret = ff_filter_frame(outlink, outsamplesref);
 +    av_frame_free(&insamplesref);
 +    return ret;
 +}
 +
 +static const AVFilterPad aconvert_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad aconvert_outputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_output,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_aconvert = {
 +    .name          = "aconvert",
 +    .description   = NULL_IF_CONFIG_SMALL("Convert the input audio to sample_fmt:channel_layout."),
 +    .priv_size     = sizeof(AConvertContext),
 +    .priv_class    = &aconvert_class,
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .inputs        = aconvert_inputs,
 +    .outputs       = aconvert_outputs,
 +};
index d51264f,0000000..363b84d
mode 100644,000000..100644
--- /dev/null
@@@ -1,283 -1,0 +1,283 @@@
- AVFilter avfilter_af_adelay = {
 +/*
 + * Copyright (c) 2013 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/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct ChanDelay {
 +    int delay;
 +    unsigned delay_index;
 +    unsigned index;
 +    uint8_t *samples;
 +} ChanDelay;
 +
 +typedef struct AudioDelayContext {
 +    const AVClass *class;
 +    char *delays;
 +    ChanDelay *chandelay;
 +    int nb_delays;
 +    int block_align;
 +    unsigned max_delay;
 +    int64_t next_pts;
 +
 +    void (*delay_channel)(ChanDelay *d, int nb_samples,
 +                          const uint8_t *src, uint8_t *dst);
 +} AudioDelayContext;
 +
 +#define OFFSET(x) offsetof(AudioDelayContext, x)
 +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption adelay_options[] = {
 +    { "delays", "set list of delays for each channel", OFFSET(delays), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(adelay);
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterChannelLayouts *layouts;
 +    AVFilterFormats *formats;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
 +        AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +#define DELAY(name, type, fill)                                           \
 +static void delay_channel_## name ##p(ChanDelay *d, int nb_samples,       \
 +                                      const uint8_t *ssrc, uint8_t *ddst) \
 +{                                                                         \
 +    const type *src = (type *)ssrc;                                       \
 +    type *dst = (type *)ddst;                                             \
 +    type *samples = (type *)d->samples;                                   \
 +                                                                          \
 +    while (nb_samples) {                                                  \
 +        if (d->delay_index < d->delay) {                                  \
 +            const int len = FFMIN(nb_samples, d->delay - d->delay_index); \
 +                                                                          \
 +            memcpy(&samples[d->delay_index], src, len * sizeof(type));    \
 +            memset(dst, fill, len * sizeof(type));                        \
 +            d->delay_index += len;                                        \
 +            src += len;                                                   \
 +            dst += len;                                                   \
 +            nb_samples -= len;                                            \
 +        } else {                                                          \
 +            *dst = samples[d->index];                                     \
 +            samples[d->index] = *src;                                     \
 +            nb_samples--;                                                 \
 +            d->index++;                                                   \
 +            src++, dst++;                                                 \
 +            d->index = d->index >= d->delay ? 0 : d->index;               \
 +        }                                                                 \
 +    }                                                                     \
 +}
 +
 +DELAY(u8,  uint8_t, 0x80)
 +DELAY(s16, int16_t, 0)
 +DELAY(s32, int32_t, 0)
 +DELAY(flt, float,   0)
 +DELAY(dbl, double,  0)
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AudioDelayContext *s = ctx->priv;
 +    char *p, *arg, *saveptr = NULL;
 +    int i;
 +
 +    s->chandelay = av_calloc(inlink->channels, sizeof(*s->chandelay));
 +    if (!s->chandelay)
 +        return AVERROR(ENOMEM);
 +    s->nb_delays = inlink->channels;
 +    s->block_align = av_get_bytes_per_sample(inlink->format);
 +
 +    p = s->delays;
 +    for (i = 0; i < s->nb_delays; i++) {
 +        ChanDelay *d = &s->chandelay[i];
 +        float delay;
 +
 +        if (!(arg = av_strtok(p, "|", &saveptr)))
 +            break;
 +
 +        p = NULL;
 +        sscanf(arg, "%f", &delay);
 +
 +        d->delay = delay * inlink->sample_rate / 1000.0;
 +        if (d->delay < 0) {
 +            av_log(ctx, AV_LOG_ERROR, "Delay must be non negative number.\n");
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    for (i = 0; i < s->nb_delays; i++) {
 +        ChanDelay *d = &s->chandelay[i];
 +
 +        if (!d->delay)
 +            continue;
 +
 +        d->samples = av_malloc_array(d->delay, s->block_align);
 +        if (!d->samples)
 +            return AVERROR(ENOMEM);
 +
 +        s->max_delay = FFMAX(s->max_delay, d->delay);
 +    }
 +
 +    if (!s->max_delay) {
 +        av_log(ctx, AV_LOG_ERROR, "At least one delay >0 must be specified.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    switch (inlink->format) {
 +    case AV_SAMPLE_FMT_U8P : s->delay_channel = delay_channel_u8p ; break;
 +    case AV_SAMPLE_FMT_S16P: s->delay_channel = delay_channel_s16p; break;
 +    case AV_SAMPLE_FMT_S32P: s->delay_channel = delay_channel_s32p; break;
 +    case AV_SAMPLE_FMT_FLTP: s->delay_channel = delay_channel_fltp; break;
 +    case AV_SAMPLE_FMT_DBLP: s->delay_channel = delay_channel_dblp; break;
 +    }
 +
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AudioDelayContext *s = ctx->priv;
 +    AVFrame *out_frame;
 +    int i;
 +
 +    if (ctx->is_disabled || !s->delays)
 +        return ff_filter_frame(ctx->outputs[0], frame);
 +
 +    out_frame = ff_get_audio_buffer(inlink, frame->nb_samples);
 +    if (!out_frame)
 +        return AVERROR(ENOMEM);
 +    av_frame_copy_props(out_frame, frame);
 +
 +    for (i = 0; i < s->nb_delays; i++) {
 +        ChanDelay *d = &s->chandelay[i];
 +        const uint8_t *src = frame->extended_data[i];
 +        uint8_t *dst = out_frame->extended_data[i];
 +
 +        if (!d->delay)
 +            memcpy(dst, src, frame->nb_samples * s->block_align);
 +        else
 +            s->delay_channel(d, frame->nb_samples, src, dst);
 +    }
 +
 +    s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
 +    av_frame_free(&frame);
 +    return ff_filter_frame(ctx->outputs[0], out_frame);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AudioDelayContext *s = ctx->priv;
 +    int ret;
 +
 +    ret = ff_request_frame(ctx->inputs[0]);
 +    if (ret == AVERROR_EOF && !ctx->is_disabled && s->max_delay) {
 +        int nb_samples = FFMIN(s->max_delay, 2048);
 +        AVFrame *frame;
 +
 +        frame = ff_get_audio_buffer(outlink, nb_samples);
 +        if (!frame)
 +            return AVERROR(ENOMEM);
 +        s->max_delay -= nb_samples;
 +
 +        av_samples_set_silence(frame->extended_data, 0,
 +                               frame->nb_samples,
 +                               outlink->channels,
 +                               frame->format);
 +
 +        frame->pts = s->next_pts;
 +        if (s->next_pts != AV_NOPTS_VALUE)
 +            s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
 +
 +        ret = filter_frame(ctx->inputs[0], frame);
 +    }
 +
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AudioDelayContext *s = ctx->priv;
 +    int i;
 +
 +    for (i = 0; i < s->nb_delays; i++)
 +        av_free(s->chandelay[i].samples);
 +    av_freep(&s->chandelay);
 +}
 +
 +static const AVFilterPad adelay_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_input,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad adelay_outputs[] = {
 +    {
 +        .name          = "default",
 +        .request_frame = request_frame,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_adelay = {
 +    .name          = "adelay",
 +    .description   = NULL_IF_CONFIG_SMALL("Delay one or more audio channels."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AudioDelayContext),
 +    .priv_class    = &adelay_class,
 +    .uninit        = uninit,
 +    .inputs        = adelay_inputs,
 +    .outputs       = adelay_outputs,
 +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
 +};
index 0c3ba2f,0000000..9c86f8d
mode 100644,000000..100644
--- /dev/null
@@@ -1,359 -1,0 +1,359 @@@
- AVFilter avfilter_af_aecho = {
 +/*
 + * Copyright (c) 2013 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/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct AudioEchoContext {
 +    const AVClass *class;
 +    float in_gain, out_gain;
 +    char *delays, *decays;
 +    float *delay, *decay;
 +    int nb_echoes;
 +    int delay_index;
 +    uint8_t **delayptrs;
 +    int max_samples, fade_out;
 +    int *samples;
 +    int64_t next_pts;
 +
 +    void (*echo_samples)(struct AudioEchoContext *ctx, uint8_t **delayptrs,
 +                         uint8_t * const *src, uint8_t **dst,
 +                         int nb_samples, int channels);
 +} AudioEchoContext;
 +
 +#define OFFSET(x) offsetof(AudioEchoContext, x)
 +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption aecho_options[] = {
 +    { "in_gain",  "set signal input gain",  OFFSET(in_gain),  AV_OPT_TYPE_FLOAT,  {.dbl=0.6}, 0, 1, A },
 +    { "out_gain", "set signal output gain", OFFSET(out_gain), AV_OPT_TYPE_FLOAT,  {.dbl=0.3}, 0, 1, A },
 +    { "delays",   "set list of signal delays", OFFSET(delays), AV_OPT_TYPE_STRING, {.str="1000"}, 0, 0, A },
 +    { "decays",   "set list of signal decays", OFFSET(decays), AV_OPT_TYPE_STRING, {.str="0.5"}, 0, 0, A },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(aecho);
 +
 +static void count_items(char *item_str, int *nb_items)
 +{
 +    char *p;
 +
 +    *nb_items = 1;
 +    for (p = item_str; *p; p++) {
 +        if (*p == '|')
 +            (*nb_items)++;
 +    }
 +
 +}
 +
 +static void fill_items(char *item_str, int *nb_items, float *items)
 +{
 +    char *p, *saveptr = NULL;
 +    int i, new_nb_items = 0;
 +
 +    p = item_str;
 +    for (i = 0; i < *nb_items; i++) {
 +        char *tstr = av_strtok(p, "|", &saveptr);
 +        p = NULL;
 +        new_nb_items += sscanf(tstr, "%f", &items[i]) == 1;
 +    }
 +
 +    *nb_items = new_nb_items;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AudioEchoContext *s = ctx->priv;
 +
 +    av_freep(&s->delay);
 +    av_freep(&s->decay);
 +    av_freep(&s->samples);
 +
 +    if (s->delayptrs)
 +        av_freep(&s->delayptrs[0]);
 +    av_freep(&s->delayptrs);
 +}
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AudioEchoContext *s = ctx->priv;
 +    int nb_delays, nb_decays, i;
 +
 +    if (!s->delays || !s->decays) {
 +        av_log(ctx, AV_LOG_ERROR, "Missing delays and/or decays.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    count_items(s->delays, &nb_delays);
 +    count_items(s->decays, &nb_decays);
 +
 +    s->delay = av_realloc_f(s->delay, nb_delays, sizeof(*s->delay));
 +    s->decay = av_realloc_f(s->decay, nb_decays, sizeof(*s->decay));
 +    if (!s->delay || !s->decay)
 +        return AVERROR(ENOMEM);
 +
 +    fill_items(s->delays, &nb_delays, s->delay);
 +    fill_items(s->decays, &nb_decays, s->decay);
 +
 +    if (nb_delays != nb_decays) {
 +        av_log(ctx, AV_LOG_ERROR, "Number of delays %d differs from number of decays %d.\n", nb_delays, nb_decays);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    s->nb_echoes = nb_delays;
 +    if (!s->nb_echoes) {
 +        av_log(ctx, AV_LOG_ERROR, "At least one decay & delay must be set.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    s->samples = av_realloc_f(s->samples, nb_delays, sizeof(*s->samples));
 +    if (!s->samples)
 +        return AVERROR(ENOMEM);
 +
 +    for (i = 0; i < nb_delays; i++) {
 +        if (s->delay[i] <= 0 || s->delay[i] > 90000) {
 +            av_log(ctx, AV_LOG_ERROR, "delay[%d]: %f is out of allowed range: (0, 90000]\n", i, s->delay[i]);
 +            return AVERROR(EINVAL);
 +        }
 +        if (s->decay[i] <= 0 || s->decay[i] > 1) {
 +            av_log(ctx, AV_LOG_ERROR, "decay[%d]: %f is out of allowed range: (0, 1]\n", i, s->decay[i]);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    s->next_pts = AV_NOPTS_VALUE;
 +
 +    av_log(ctx, AV_LOG_DEBUG, "nb_echoes:%d\n", s->nb_echoes);
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterChannelLayouts *layouts;
 +    AVFilterFormats *formats;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
 +        AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
 +
 +#define ECHO(name, type, min, max)                                          \
 +static void echo_samples_## name ##p(AudioEchoContext *ctx,                 \
 +                                     uint8_t **delayptrs,                   \
 +                                     uint8_t * const *src, uint8_t **dst,   \
 +                                     int nb_samples, int channels)          \
 +{                                                                           \
 +    const double out_gain = ctx->out_gain;                                  \
 +    const double in_gain = ctx->in_gain;                                    \
 +    const int nb_echoes = ctx->nb_echoes;                                   \
 +    const int max_samples = ctx->max_samples;                               \
 +    int i, j, chan, av_uninit(index);                                       \
 +                                                                            \
 +    av_assert1(channels > 0); /* would corrupt delay_index */               \
 +                                                                            \
 +    for (chan = 0; chan < channels; chan++) {                               \
 +        const type *s = (type *)src[chan];                                  \
 +        type *d = (type *)dst[chan];                                        \
 +        type *dbuf = (type *)delayptrs[chan];                               \
 +                                                                            \
 +        index = ctx->delay_index;                                           \
 +        for (i = 0; i < nb_samples; i++, s++, d++) {                        \
 +            double out, in;                                                 \
 +                                                                            \
 +            in = *s;                                                        \
 +            out = in * in_gain;                                             \
 +            for (j = 0; j < nb_echoes; j++) {                               \
 +                int ix = index + max_samples - ctx->samples[j];             \
 +                ix = MOD(ix, max_samples);                                  \
 +                out += dbuf[ix] * ctx->decay[j];                            \
 +            }                                                               \
 +            out *= out_gain;                                                \
 +                                                                            \
 +            *d = av_clipd(out, min, max);                                   \
 +            dbuf[index] = in;                                               \
 +                                                                            \
 +            index = MOD(index + 1, max_samples);                            \
 +        }                                                                   \
 +    }                                                                       \
 +    ctx->delay_index = index;                                               \
 +}
 +
 +ECHO(dbl, double,  -1.0,      1.0      )
 +ECHO(flt, float,   -1.0,      1.0      )
 +ECHO(s16, int16_t, INT16_MIN, INT16_MAX)
 +ECHO(s32, int32_t, INT32_MIN, INT32_MAX)
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AudioEchoContext *s = ctx->priv;
 +    float volume = 1.0;
 +    int i;
 +
 +    for (i = 0; i < s->nb_echoes; i++) {
 +        s->samples[i] = s->delay[i] * outlink->sample_rate / 1000.0;
 +        s->max_samples = FFMAX(s->max_samples, s->samples[i]);
 +        volume += s->decay[i];
 +    }
 +
 +    if (s->max_samples <= 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Nothing to echo - missing delay samples.\n");
 +        return AVERROR(EINVAL);
 +    }
 +    s->fade_out = s->max_samples;
 +
 +    if (volume * s->in_gain * s->out_gain > 1.0)
 +        av_log(ctx, AV_LOG_WARNING,
 +               "out_gain %f can cause saturation of output\n", s->out_gain);
 +
 +    switch (outlink->format) {
 +    case AV_SAMPLE_FMT_DBLP: s->echo_samples = echo_samples_dblp; break;
 +    case AV_SAMPLE_FMT_FLTP: s->echo_samples = echo_samples_fltp; break;
 +    case AV_SAMPLE_FMT_S16P: s->echo_samples = echo_samples_s16p; break;
 +    case AV_SAMPLE_FMT_S32P: s->echo_samples = echo_samples_s32p; break;
 +    }
 +
 +
 +    if (s->delayptrs)
 +        av_freep(&s->delayptrs[0]);
 +    av_freep(&s->delayptrs);
 +
 +    return av_samples_alloc_array_and_samples(&s->delayptrs, NULL,
 +                                              outlink->channels,
 +                                              s->max_samples,
 +                                              outlink->format, 0);
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AudioEchoContext *s = ctx->priv;
 +    AVFrame *out_frame;
 +
 +    if (av_frame_is_writable(frame)) {
 +        out_frame = frame;
 +    } else {
 +        out_frame = ff_get_audio_buffer(inlink, frame->nb_samples);
 +        if (!out_frame)
 +            return AVERROR(ENOMEM);
 +        av_frame_copy_props(out_frame, frame);
 +    }
 +
 +    s->echo_samples(s, s->delayptrs, frame->extended_data, out_frame->extended_data,
 +                    frame->nb_samples, inlink->channels);
 +
 +    if (frame != out_frame)
 +        av_frame_free(&frame);
 +
 +    s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
 +    return ff_filter_frame(ctx->outputs[0], out_frame);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AudioEchoContext *s = ctx->priv;
 +    int ret;
 +
 +    ret = ff_request_frame(ctx->inputs[0]);
 +
 +    if (ret == AVERROR_EOF && !ctx->is_disabled && s->fade_out) {
 +        int nb_samples = FFMIN(s->fade_out, 2048);
 +        AVFrame *frame;
 +
 +        frame = ff_get_audio_buffer(outlink, nb_samples);
 +        if (!frame)
 +            return AVERROR(ENOMEM);
 +        s->fade_out -= nb_samples;
 +
 +        av_samples_set_silence(frame->extended_data, 0,
 +                               frame->nb_samples,
 +                               outlink->channels,
 +                               frame->format);
 +
 +        s->echo_samples(s, s->delayptrs, frame->extended_data, frame->extended_data,
 +                        frame->nb_samples, outlink->channels);
 +
 +        frame->pts = s->next_pts;
 +        if (s->next_pts != AV_NOPTS_VALUE)
 +            s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
 +
 +        return ff_filter_frame(outlink, frame);
 +    }
 +
 +    return ret;
 +}
 +
 +static const AVFilterPad aecho_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad aecho_outputs[] = {
 +    {
 +        .name          = "default",
 +        .request_frame = request_frame,
 +        .config_props  = config_output,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_aecho = {
 +    .name          = "aecho",
 +    .description   = NULL_IF_CONFIG_SMALL("Add echoing to the audio."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AudioEchoContext),
 +    .priv_class    = &aecho_class,
 +    .init          = init,
 +    .uninit        = uninit,
 +    .inputs        = aecho_inputs,
 +    .outputs       = aecho_outputs,
 +};
index 2ffb962,0000000..fbf9802
mode 100644,000000..100644
--- /dev/null
@@@ -1,300 -1,0 +1,300 @@@
- AVFilter avfilter_af_afade = {
 +/*
 + * Copyright (c) 2013 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
 + */
 +
 +/**
 + * @file
 + * fade audio filter
 + */
 +
 +#include "libavutil/opt.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int type;
 +    int curve;
 +    int nb_samples;
 +    int64_t start_sample;
 +    int64_t duration;
 +    int64_t start_time;
 +
 +    void (*fade_samples)(uint8_t **dst, uint8_t * const *src,
 +                         int nb_samples, int channels, int direction,
 +                         int64_t start, int range, int curve);
 +} AudioFadeContext;
 +
 +enum CurveType { TRI, QSIN, ESIN, HSIN, LOG, PAR, QUA, CUB, SQU, CBR };
 +
 +#define OFFSET(x) offsetof(AudioFadeContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption afade_options[] = {
 +    { "type",         "set the fade direction",                      OFFSET(type),         AV_OPT_TYPE_INT,    {.i64 = 0    }, 0, 1, FLAGS, "type" },
 +    { "t",            "set the fade direction",                      OFFSET(type),         AV_OPT_TYPE_INT,    {.i64 = 0    }, 0, 1, FLAGS, "type" },
 +    { "in",           "fade-in",                                     0,                    AV_OPT_TYPE_CONST,  {.i64 = 0    }, 0, 0, FLAGS, "type" },
 +    { "out",          "fade-out",                                    0,                    AV_OPT_TYPE_CONST,  {.i64 = 1    }, 0, 0, FLAGS, "type" },
 +    { "start_sample", "set number of first sample to start fading",  OFFSET(start_sample), AV_OPT_TYPE_INT64,  {.i64 = 0    }, 0, INT64_MAX, FLAGS },
 +    { "ss",           "set number of first sample to start fading",  OFFSET(start_sample), AV_OPT_TYPE_INT64,  {.i64 = 0    }, 0, INT64_MAX, FLAGS },
 +    { "nb_samples",   "set number of samples for fade duration",     OFFSET(nb_samples),   AV_OPT_TYPE_INT,    {.i64 = 44100}, 1, INT32_MAX, FLAGS },
 +    { "ns",           "set number of samples for fade duration",     OFFSET(nb_samples),   AV_OPT_TYPE_INT,    {.i64 = 44100}, 1, INT32_MAX, FLAGS },
 +    { "start_time",   "set time to start fading",                    OFFSET(start_time),   AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
 +    { "st",           "set time to start fading",                    OFFSET(start_time),   AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
 +    { "duration",     "set fade duration",                           OFFSET(duration),     AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
 +    { "d",            "set fade duration",                           OFFSET(duration),     AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
 +    { "curve",        "set fade curve type",                         OFFSET(curve),        AV_OPT_TYPE_INT,    {.i64 = TRI  }, TRI, CBR, FLAGS, "curve" },
 +    { "c",            "set fade curve type",                         OFFSET(curve),        AV_OPT_TYPE_INT,    {.i64 = TRI  }, TRI, CBR, FLAGS, "curve" },
 +    { "tri",          "linear slope",                                0,                    AV_OPT_TYPE_CONST,  {.i64 = TRI  }, 0, 0, FLAGS, "curve" },
 +    { "qsin",         "quarter of sine wave",                        0,                    AV_OPT_TYPE_CONST,  {.i64 = QSIN }, 0, 0, FLAGS, "curve" },
 +    { "esin",         "exponential sine wave",                       0,                    AV_OPT_TYPE_CONST,  {.i64 = ESIN }, 0, 0, FLAGS, "curve" },
 +    { "hsin",         "half of sine wave",                           0,                    AV_OPT_TYPE_CONST,  {.i64 = HSIN }, 0, 0, FLAGS, "curve" },
 +    { "log",          "logarithmic",                                 0,                    AV_OPT_TYPE_CONST,  {.i64 = LOG  }, 0, 0, FLAGS, "curve" },
 +    { "par",          "inverted parabola",                           0,                    AV_OPT_TYPE_CONST,  {.i64 = PAR  }, 0, 0, FLAGS, "curve" },
 +    { "qua",          "quadratic",                                   0,                    AV_OPT_TYPE_CONST,  {.i64 = QUA  }, 0, 0, FLAGS, "curve" },
 +    { "cub",          "cubic",                                       0,                    AV_OPT_TYPE_CONST,  {.i64 = CUB  }, 0, 0, FLAGS, "curve" },
 +    { "squ",          "square root",                                 0,                    AV_OPT_TYPE_CONST,  {.i64 = SQU  }, 0, 0, FLAGS, "curve" },
 +    { "cbr",          "cubic root",                                  0,                    AV_OPT_TYPE_CONST,  {.i64 = CBR  }, 0, 0, FLAGS, "curve" },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(afade);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AudioFadeContext *s = ctx->priv;
 +
 +    if (INT64_MAX - s->nb_samples < s->start_sample)
 +        return AVERROR(EINVAL);
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P,
 +        AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P,
 +        AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
 +        AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static double fade_gain(int curve, int64_t index, int range)
 +{
 +    double gain;
 +
 +    gain = FFMAX(0.0, FFMIN(1.0, 1.0 * index / range));
 +
 +    switch (curve) {
 +    case QSIN:
 +        gain = sin(gain * M_PI / 2.0);
 +        break;
 +    case ESIN:
 +        gain = 1.0 - cos(M_PI / 4.0 * (pow(2.0*gain - 1, 3) + 1));
 +        break;
 +    case HSIN:
 +        gain = (1.0 - cos(gain * M_PI)) / 2.0;
 +        break;
 +    case LOG:
 +        gain = pow(0.1, (1 - gain) * 5.0);
 +        break;
 +    case PAR:
 +        gain = (1 - (1 - gain) * (1 - gain));
 +        break;
 +    case QUA:
 +        gain *= gain;
 +        break;
 +    case CUB:
 +        gain = gain * gain * gain;
 +        break;
 +    case SQU:
 +        gain = sqrt(gain);
 +        break;
 +    case CBR:
 +        gain = cbrt(gain);
 +        break;
 +    }
 +
 +    return gain;
 +}
 +
 +#define FADE_PLANAR(name, type)                                             \
 +static void fade_samples_## name ##p(uint8_t **dst, uint8_t * const *src,   \
 +                                     int nb_samples, int channels, int dir, \
 +                                     int64_t start, int range, int curve)   \
 +{                                                                           \
 +    int i, c;                                                               \
 +                                                                            \
 +    for (i = 0; i < nb_samples; i++) {                                      \
 +        double gain = fade_gain(curve, start + i * dir, range);             \
 +        for (c = 0; c < channels; c++) {                                    \
 +            type *d = (type *)dst[c];                                       \
 +            const type *s = (type *)src[c];                                 \
 +                                                                            \
 +            d[i] = s[i] * gain;                                             \
 +        }                                                                   \
 +    }                                                                       \
 +}
 +
 +#define FADE(name, type)                                                    \
 +static void fade_samples_## name (uint8_t **dst, uint8_t * const *src,      \
 +                                  int nb_samples, int channels, int dir,    \
 +                                  int64_t start, int range, int curve)      \
 +{                                                                           \
 +    type *d = (type *)dst[0];                                               \
 +    const type *s = (type *)src[0];                                         \
 +    int i, c, k = 0;                                                        \
 +                                                                            \
 +    for (i = 0; i < nb_samples; i++) {                                      \
 +        double gain = fade_gain(curve, start + i * dir, range);             \
 +        for (c = 0; c < channels; c++, k++)                                 \
 +            d[k] = s[k] * gain;                                             \
 +    }                                                                       \
 +}
 +
 +FADE_PLANAR(dbl, double)
 +FADE_PLANAR(flt, float)
 +FADE_PLANAR(s16, int16_t)
 +FADE_PLANAR(s32, int32_t)
 +
 +FADE(dbl, double)
 +FADE(flt, float)
 +FADE(s16, int16_t)
 +FADE(s32, int32_t)
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AudioFadeContext *s  = ctx->priv;
 +
 +    switch (inlink->format) {
 +    case AV_SAMPLE_FMT_DBL:  s->fade_samples = fade_samples_dbl;  break;
 +    case AV_SAMPLE_FMT_DBLP: s->fade_samples = fade_samples_dblp; break;
 +    case AV_SAMPLE_FMT_FLT:  s->fade_samples = fade_samples_flt;  break;
 +    case AV_SAMPLE_FMT_FLTP: s->fade_samples = fade_samples_fltp; break;
 +    case AV_SAMPLE_FMT_S16:  s->fade_samples = fade_samples_s16;  break;
 +    case AV_SAMPLE_FMT_S16P: s->fade_samples = fade_samples_s16p; break;
 +    case AV_SAMPLE_FMT_S32:  s->fade_samples = fade_samples_s32;  break;
 +    case AV_SAMPLE_FMT_S32P: s->fade_samples = fade_samples_s32p; break;
 +    }
 +
 +    if (s->duration)
 +        s->nb_samples = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE);
 +    if (s->start_time)
 +        s->start_sample = av_rescale(s->start_time, inlink->sample_rate, AV_TIME_BASE);
 +
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 +{
 +    AudioFadeContext *s     = inlink->dst->priv;
 +    AVFilterLink *outlink   = inlink->dst->outputs[0];
 +    int nb_samples          = buf->nb_samples;
 +    AVFrame *out_buf;
 +    int64_t cur_sample = av_rescale_q(buf->pts, (AVRational){1, outlink->sample_rate}, outlink->time_base);
 +
 +    if ((!s->type && (s->start_sample + s->nb_samples < cur_sample)) ||
 +        ( s->type && (cur_sample + s->nb_samples < s->start_sample)))
 +        return ff_filter_frame(outlink, buf);
 +
 +    if (av_frame_is_writable(buf)) {
 +        out_buf = buf;
 +    } else {
 +        out_buf = ff_get_audio_buffer(inlink, nb_samples);
 +        if (!out_buf)
 +            return AVERROR(ENOMEM);
 +        av_frame_copy_props(out_buf, buf);
 +    }
 +
 +    if ((!s->type && (cur_sample + nb_samples < s->start_sample)) ||
 +        ( s->type && (s->start_sample + s->nb_samples < cur_sample))) {
 +        av_samples_set_silence(out_buf->extended_data, 0, nb_samples,
 +                               av_frame_get_channels(out_buf), out_buf->format);
 +    } else {
 +        int64_t start;
 +
 +        if (!s->type)
 +            start = cur_sample - s->start_sample;
 +        else
 +            start = s->start_sample + s->nb_samples - cur_sample;
 +
 +        s->fade_samples(out_buf->extended_data, buf->extended_data,
 +                        nb_samples, av_frame_get_channels(buf),
 +                        s->type ? -1 : 1, start,
 +                        s->nb_samples, s->curve);
 +    }
 +
 +    if (buf != out_buf)
 +        av_frame_free(&buf);
 +
 +    return ff_filter_frame(outlink, out_buf);
 +}
 +
 +static const AVFilterPad avfilter_af_afade_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +        .config_props = config_input,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad avfilter_af_afade_outputs[] = {
 +    {
 +        .name = "default",
 +        .type = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_afade = {
 +    .name          = "afade",
 +    .description   = NULL_IF_CONFIG_SMALL("Fade in/out input audio."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AudioFadeContext),
 +    .init          = init,
 +    .inputs        = avfilter_af_afade_inputs,
 +    .outputs       = avfilter_af_afade_outputs,
 +    .priv_class    = &afade_class,
 +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
 +};
Simple merge
index 47e7ee6,0000000..82b694b
mode 100644,000000..100644
--- /dev/null
@@@ -1,350 -1,0 +1,350 @@@
- AVFilter avfilter_af_amerge = {
 +/*
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Audio merging filter
 + */
 +
 +#include "libavutil/avstring.h"
 +#include "libavutil/bprint.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "libswresample/swresample.h" // only for SWR_CH_MAX
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "bufferqueue.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int nb_inputs;
 +    int route[SWR_CH_MAX]; /**< channels routing, see copy_samples */
 +    int bps;
 +    struct amerge_input {
 +        struct FFBufQueue queue;
 +        int nb_ch;         /**< number of channels for the input */
 +        int nb_samples;
 +        int pos;
 +    } *in;
 +} AMergeContext;
 +
 +#define OFFSET(x) offsetof(AMergeContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption amerge_options[] = {
 +    { "inputs", "specify the number of inputs", OFFSET(nb_inputs),
 +      AV_OPT_TYPE_INT, { .i64 = 2 }, 2, SWR_CH_MAX, FLAGS },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(amerge);
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int i;
 +
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        if (am->in)
 +            ff_bufqueue_discard_all(&am->in[i].queue);
 +        if (ctx->input_pads)
 +            av_freep(&ctx->input_pads[i].name);
 +    }
 +    av_freep(&am->in);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int64_t inlayout[SWR_CH_MAX], outlayout = 0;
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    int i, overlap = 0, nb_ch = 0;
 +
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        if (!ctx->inputs[i]->in_channel_layouts ||
 +            !ctx->inputs[i]->in_channel_layouts->nb_channel_layouts) {
 +            av_log(ctx, AV_LOG_WARNING,
 +                   "No channel layout for input %d\n", i + 1);
 +            return AVERROR(EAGAIN);
 +        }
 +        inlayout[i] = ctx->inputs[i]->in_channel_layouts->channel_layouts[0];
 +        if (ctx->inputs[i]->in_channel_layouts->nb_channel_layouts > 1) {
 +            char buf[256];
 +            av_get_channel_layout_string(buf, sizeof(buf), 0, inlayout[i]);
 +            av_log(ctx, AV_LOG_INFO, "Using \"%s\" for input %d\n", buf, i + 1);
 +        }
 +        am->in[i].nb_ch = av_get_channel_layout_nb_channels(inlayout[i]);
 +        if (outlayout & inlayout[i])
 +            overlap++;
 +        outlayout |= inlayout[i];
 +        nb_ch += am->in[i].nb_ch;
 +    }
 +    if (nb_ch > SWR_CH_MAX) {
 +        av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX);
 +        return AVERROR(EINVAL);
 +    }
 +    if (overlap) {
 +        av_log(ctx, AV_LOG_WARNING,
 +               "Input channel layouts overlap: "
 +               "output layout will be determined by the number of distinct input channels\n");
 +        for (i = 0; i < nb_ch; i++)
 +            am->route[i] = i;
 +        outlayout = av_get_default_channel_layout(nb_ch);
 +        if (!outlayout)
 +            outlayout = ((int64_t)1 << nb_ch) - 1;
 +    } else {
 +        int *route[SWR_CH_MAX];
 +        int c, out_ch_number = 0;
 +
 +        route[0] = am->route;
 +        for (i = 1; i < am->nb_inputs; i++)
 +            route[i] = route[i - 1] + am->in[i - 1].nb_ch;
 +        for (c = 0; c < 64; c++)
 +            for (i = 0; i < am->nb_inputs; i++)
 +                if ((inlayout[i] >> c) & 1)
 +                    *(route[i]++) = out_ch_number++;
 +    }
 +    formats = ff_make_format_list(ff_packed_sample_fmts_array);
 +    ff_set_common_formats(ctx, formats);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        layouts = NULL;
 +        ff_add_channel_layout(&layouts, inlayout[i]);
 +        ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
 +    }
 +    layouts = NULL;
 +    ff_add_channel_layout(&layouts, outlayout);
 +    ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts);
 +    ff_set_common_samplerates(ctx, ff_all_samplerates());
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AMergeContext *am = ctx->priv;
 +    AVBPrint bp;
 +    int i;
 +
 +    for (i = 1; i < am->nb_inputs; i++) {
 +        if (ctx->inputs[i]->sample_rate != ctx->inputs[0]->sample_rate) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Inputs must have the same sample rate "
 +                   "%d for in%d vs %d\n",
 +                   ctx->inputs[i]->sample_rate, i, ctx->inputs[0]->sample_rate);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +    am->bps = av_get_bytes_per_sample(ctx->outputs[0]->format);
 +    outlink->sample_rate = ctx->inputs[0]->sample_rate;
 +    outlink->time_base   = ctx->inputs[0]->time_base;
 +
 +    av_bprint_init(&bp, 0, 1);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        av_bprintf(&bp, "%sin%d:", i ? " + " : "", i);
 +        av_bprint_channel_layout(&bp, -1, ctx->inputs[i]->channel_layout);
 +    }
 +    av_bprintf(&bp, " -> out:");
 +    av_bprint_channel_layout(&bp, -1, ctx->outputs[0]->channel_layout);
 +    av_log(ctx, AV_LOG_VERBOSE, "%s\n", bp.str);
 +
 +    return 0;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AMergeContext *am = ctx->priv;
 +    int i, ret;
 +
 +    for (i = 0; i < am->nb_inputs; i++)
 +        if (!am->in[i].nb_samples)
 +            if ((ret = ff_request_frame(ctx->inputs[i])) < 0)
 +                return ret;
 +    return 0;
 +}
 +
 +/**
 + * Copy samples from several input streams to one output stream.
 + * @param nb_inputs number of inputs
 + * @param in        inputs; used only for the nb_ch field;
 + * @param route     routing values;
 + *                  input channel i goes to output channel route[i];
 + *                  i <  in[0].nb_ch are the channels from the first output;
 + *                  i >= in[0].nb_ch are the channels from the second output
 + * @param ins       pointer to the samples of each inputs, in packed format;
 + *                  will be left at the end of the copied samples
 + * @param outs      pointer to the samples of the output, in packet format;
 + *                  must point to a buffer big enough;
 + *                  will be left at the end of the copied samples
 + * @param ns        number of samples to copy
 + * @param bps       bytes per sample
 + */
 +static inline void copy_samples(int nb_inputs, struct amerge_input in[],
 +                                int *route, uint8_t *ins[],
 +                                uint8_t **outs, int ns, int bps)
 +{
 +    int *route_cur;
 +    int i, c, nb_ch = 0;
 +
 +    for (i = 0; i < nb_inputs; i++)
 +        nb_ch += in[i].nb_ch;
 +    while (ns--) {
 +        route_cur = route;
 +        for (i = 0; i < nb_inputs; i++) {
 +            for (c = 0; c < in[i].nb_ch; c++) {
 +                memcpy((*outs) + bps * *(route_cur++), ins[i], bps);
 +                ins[i] += bps;
 +            }
 +        }
 +        *outs += nb_ch * bps;
 +    }
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AMergeContext *am = ctx->priv;
 +    AVFilterLink *const outlink = ctx->outputs[0];
 +    int input_number;
 +    int nb_samples, ns, i;
 +    AVFrame *outbuf, *inbuf[SWR_CH_MAX];
 +    uint8_t *ins[SWR_CH_MAX], *outs;
 +
 +    for (input_number = 0; input_number < am->nb_inputs; input_number++)
 +        if (inlink == ctx->inputs[input_number])
 +            break;
 +    av_assert1(input_number < am->nb_inputs);
 +    if (ff_bufqueue_is_full(&am->in[input_number].queue)) {
 +        av_frame_free(&insamples);
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_bufqueue_add(ctx, &am->in[input_number].queue, av_frame_clone(insamples));
 +    am->in[input_number].nb_samples += insamples->nb_samples;
 +    av_frame_free(&insamples);
 +    nb_samples = am->in[0].nb_samples;
 +    for (i = 1; i < am->nb_inputs; i++)
 +        nb_samples = FFMIN(nb_samples, am->in[i].nb_samples);
 +    if (!nb_samples)
 +        return 0;
 +
 +    outbuf = ff_get_audio_buffer(ctx->outputs[0], nb_samples);
 +    if (!outbuf)
 +        return AVERROR(ENOMEM);
 +    outs = outbuf->data[0];
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
 +        ins[i] = inbuf[i]->data[0] +
 +                 am->in[i].pos * am->in[i].nb_ch * am->bps;
 +    }
 +    av_frame_copy_props(outbuf, inbuf[0]);
 +    outbuf->pts = inbuf[0]->pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE :
 +                  inbuf[0]->pts +
 +                  av_rescale_q(am->in[0].pos,
 +                               (AVRational){ 1, ctx->inputs[0]->sample_rate },
 +                               ctx->outputs[0]->time_base);
 +
 +    outbuf->nb_samples     = nb_samples;
 +    outbuf->channel_layout = outlink->channel_layout;
 +    av_frame_set_channels(outbuf, outlink->channels);
 +
 +    while (nb_samples) {
 +        ns = nb_samples;
 +        for (i = 0; i < am->nb_inputs; i++)
 +            ns = FFMIN(ns, inbuf[i]->nb_samples - am->in[i].pos);
 +        /* Unroll the most common sample formats: speed +~350% for the loop,
 +           +~13% overall (including two common decoders) */
 +        switch (am->bps) {
 +            case 1:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 1);
 +                break;
 +            case 2:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 2);
 +                break;
 +            case 4:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 4);
 +                break;
 +            default:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, am->bps);
 +                break;
 +        }
 +
 +        nb_samples -= ns;
 +        for (i = 0; i < am->nb_inputs; i++) {
 +            am->in[i].nb_samples -= ns;
 +            am->in[i].pos += ns;
 +            if (am->in[i].pos == inbuf[i]->nb_samples) {
 +                am->in[i].pos = 0;
 +                av_frame_free(&inbuf[i]);
 +                ff_bufqueue_get(&am->in[i].queue);
 +                inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
 +                ins[i] = inbuf[i] ? inbuf[i]->data[0] : NULL;
 +            }
 +        }
 +    }
 +    return ff_filter_frame(ctx->outputs[0], outbuf);
 +}
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int i;
 +
 +    am->in = av_calloc(am->nb_inputs, sizeof(*am->in));
 +    if (!am->in)
 +        return AVERROR(ENOMEM);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        char *name = av_asprintf("in%d", i);
 +        AVFilterPad pad = {
 +            .name             = name,
 +            .type             = AVMEDIA_TYPE_AUDIO,
 +            .filter_frame     = filter_frame,
 +        };
 +        if (!name)
 +            return AVERROR(ENOMEM);
 +        ff_insert_inpad(ctx, i, &pad);
 +    }
 +    return 0;
 +}
 +
 +static const AVFilterPad amerge_outputs[] = {
 +    {
 +        .name          = "default",
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +        .config_props  = config_output,
 +        .request_frame = request_frame,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_amerge = {
 +    .name          = "amerge",
 +    .description   = NULL_IF_CONFIG_SMALL("Merge two or more audio streams into "
 +                                          "a single multi-channel stream."),
 +    .priv_size     = sizeof(AMergeContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .inputs        = NULL,
 +    .outputs       = amerge_outputs,
 +    .priv_class    = &amerge_class,
 +    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
 +};
@@@ -546,11 -551,12 +546,11 @@@ static const AVFilterPad avfilter_af_am
      { NULL }
  };
  
- AVFilter avfilter_af_amix = {
+ AVFilter ff_af_amix = {
 -    .name          = "amix",
 -    .description   = NULL_IF_CONFIG_SMALL("Audio mixing."),
 -    .priv_size     = sizeof(MixContext),
 -    .priv_class    = &amix_class,
 -
 +    .name           = "amix",
 +    .description    = NULL_IF_CONFIG_SMALL("Audio mixing."),
 +    .priv_size      = sizeof(MixContext),
 +    .priv_class     = &amix_class,
      .init           = init,
      .uninit         = uninit,
      .query_formats  = query_formats,
@@@ -43,10 -43,13 +43,10 @@@ static const AVFilterPad avfilter_af_an
      { NULL }
  };
  
- AVFilter avfilter_af_anull = {
+ AVFilter ff_af_anull = {
 -    .name      = "anull",
 -    .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
 -
 -    .priv_size = 0,
 -
 -    .inputs    = avfilter_af_anull_inputs,
 -
 -    .outputs   = avfilter_af_anull_outputs,
 +    .name          = "anull",
 +    .description   = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
 +    .query_formats = ff_query_formats_all,
 +    .inputs        = avfilter_af_anull_inputs,
 +    .outputs       = avfilter_af_anull_outputs,
  };
index f1c3a78,0000000..88a3a77
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,156 @@@
- AVFilter avfilter_af_apad = {
 +/*
 + * Copyright (c) 2012 Michael Niedermayer
 + *
 + * 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
 + *
 + */
 +
 +/**
 + * @file
 + * audio pad filter.
 + *
 + * Based on af_aresample.c
 + */
 +
 +#include "libavutil/avstring.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "libavutil/avassert.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int64_t next_pts;
 +
 +    int packet_size;
 +    int64_t pad_len;
 +    int64_t whole_len;
 +} APadContext;
 +
 +#define OFFSET(x) offsetof(APadContext, x)
 +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption apad_options[] = {
 +    { "packet_size", "set silence packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, { .i64 = 4096 }, 0, INT_MAX, A },
 +    { "pad_len",     "number of samples of silence to add",          OFFSET(pad_len),   AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, A },
 +    { "whole_len",   "target number of samples in the audio stream", OFFSET(whole_len), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, A },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(apad);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    APadContext *apad = ctx->priv;
 +
 +    apad->next_pts = AV_NOPTS_VALUE;
 +    if (apad->whole_len && apad->pad_len) {
 +        av_log(ctx, AV_LOG_ERROR, "Both whole and pad length are set, this is not possible\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    APadContext *apad = ctx->priv;
 +
 +    if (apad->whole_len)
 +        apad->whole_len -= frame->nb_samples;
 +
 +    apad->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
 +    return ff_filter_frame(ctx->outputs[0], frame);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    APadContext *apad = ctx->priv;
 +    int ret;
 +
 +    ret = ff_request_frame(ctx->inputs[0]);
 +
 +    if (ret == AVERROR_EOF && !ctx->is_disabled) {
 +        int n_out = apad->packet_size;
 +        AVFrame *outsamplesref;
 +
 +        if (apad->whole_len > 0) {
 +            apad->pad_len = apad->whole_len;
 +            apad->whole_len = 0;
 +        }
 +        if (apad->pad_len > 0) {
 +            n_out = FFMIN(n_out, apad->pad_len);
 +            apad->pad_len -= n_out;
 +        }
 +
 +        if(!n_out)
 +            return AVERROR_EOF;
 +
 +        outsamplesref = ff_get_audio_buffer(outlink, n_out);
 +        if (!outsamplesref)
 +            return AVERROR(ENOMEM);
 +
 +        av_assert0(outsamplesref->sample_rate == outlink->sample_rate);
 +        av_assert0(outsamplesref->nb_samples  == n_out);
 +
 +        av_samples_set_silence(outsamplesref->extended_data, 0,
 +                               n_out,
 +                               av_frame_get_channels(outsamplesref),
 +                               outsamplesref->format);
 +
 +        outsamplesref->pts = apad->next_pts;
 +        if (apad->next_pts != AV_NOPTS_VALUE)
 +            apad->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base);
 +
 +        return ff_filter_frame(outlink, outsamplesref);
 +    }
 +    return ret;
 +}
 +
 +static const AVFilterPad apad_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad apad_outputs[] = {
 +    {
 +        .name          = "default",
 +        .request_frame = request_frame,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_apad = {
 +    .name          = "apad",
 +    .description   = NULL_IF_CONFIG_SMALL("Pad audio with silence."),
 +    .init          = init,
 +    .priv_size     = sizeof(APadContext),
 +    .inputs        = apad_inputs,
 +    .outputs       = apad_outputs,
 +    .priv_class    = &apad_class,
 +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
 +};
index 5ac99f3,0000000..0fc45ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,358 -1,0 +1,358 @@@
- AVFilter avfilter_af_aphaser = {
 +/*
 + * Copyright (c) 2013 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
 + */
 +
 +/**
 + * @file
 + * phaser audio filter
 + */
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/opt.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +enum WaveType {
 +    WAVE_SIN,
 +    WAVE_TRI,
 +    WAVE_NB,
 +};
 +
 +typedef struct AudioPhaserContext {
 +    const AVClass *class;
 +    double in_gain, out_gain;
 +    double delay;
 +    double decay;
 +    double speed;
 +
 +    enum WaveType type;
 +
 +    int delay_buffer_length;
 +    double *delay_buffer;
 +
 +    int modulation_buffer_length;
 +    int32_t *modulation_buffer;
 +
 +    int delay_pos, modulation_pos;
 +
 +    void (*phaser)(struct AudioPhaserContext *p,
 +                   uint8_t * const *src, uint8_t **dst,
 +                   int nb_samples, int channels);
 +} AudioPhaserContext;
 +
 +#define OFFSET(x) offsetof(AudioPhaserContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption aphaser_options[] = {
 +    { "in_gain",  "set input gain",            OFFSET(in_gain),  AV_OPT_TYPE_DOUBLE, {.dbl=.4},  0,  1,   FLAGS },
 +    { "out_gain", "set output gain",           OFFSET(out_gain), AV_OPT_TYPE_DOUBLE, {.dbl=.74}, 0,  1e9, FLAGS },
 +    { "delay",    "set delay in milliseconds", OFFSET(delay),    AV_OPT_TYPE_DOUBLE, {.dbl=3.},  0,  5,   FLAGS },
 +    { "decay",    "set decay",                 OFFSET(decay),    AV_OPT_TYPE_DOUBLE, {.dbl=.4},  0, .99,  FLAGS },
 +    { "speed",    "set modulation speed",      OFFSET(speed),    AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .1,  2,   FLAGS },
 +    { "type",     "set modulation type",       OFFSET(type),     AV_OPT_TYPE_INT,    {.i64=WAVE_TRI}, 0, WAVE_NB-1, FLAGS, "type" },
 +    { "triangular",  NULL, 0, AV_OPT_TYPE_CONST,  {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" },
 +    { "t",           NULL, 0, AV_OPT_TYPE_CONST,  {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" },
 +    { "sinusoidal",  NULL, 0, AV_OPT_TYPE_CONST,  {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" },
 +    { "s",           NULL, 0, AV_OPT_TYPE_CONST,  {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(aphaser);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AudioPhaserContext *p = ctx->priv;
 +
 +    if (p->in_gain > (1 - p->decay * p->decay))
 +        av_log(ctx, AV_LOG_WARNING, "in_gain may cause clipping\n");
 +    if (p->in_gain / (1 - p->decay) > 1 / p->out_gain)
 +        av_log(ctx, AV_LOG_WARNING, "out_gain may cause clipping\n");
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
 +        AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P,
 +        AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static void generate_wave_table(enum WaveType wave_type, enum AVSampleFormat sample_fmt,
 +                                void *table, int table_size,
 +                                double min, double max, double phase)
 +{
 +    uint32_t i, phase_offset = phase / M_PI / 2 * table_size + 0.5;
 +
 +    for (i = 0; i < table_size; i++) {
 +        uint32_t point = (i + phase_offset) % table_size;
 +        double d;
 +
 +        switch (wave_type) {
 +        case WAVE_SIN:
 +            d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2;
 +            break;
 +        case WAVE_TRI:
 +            d = (double)point * 2 / table_size;
 +            switch (4 * point / table_size) {
 +            case 0: d = d + 0.5; break;
 +            case 1:
 +            case 2: d = 1.5 - d; break;
 +            case 3: d = d - 1.5; break;
 +            }
 +            break;
 +        default:
 +            av_assert0(0);
 +        }
 +
 +        d  = d * (max - min) + min;
 +        switch (sample_fmt) {
 +        case AV_SAMPLE_FMT_FLT: {
 +            float *fp = (float *)table;
 +            *fp++ = (float)d;
 +            table = fp;
 +            continue; }
 +        case AV_SAMPLE_FMT_DBL: {
 +            double *dp = (double *)table;
 +            *dp++ = d;
 +            table = dp;
 +            continue; }
 +        }
 +
 +        d += d < 0 ? -0.5 : 0.5;
 +        switch (sample_fmt) {
 +        case AV_SAMPLE_FMT_S16: {
 +            int16_t *sp = table;
 +            *sp++ = (int16_t)d;
 +            table = sp;
 +            continue; }
 +        case AV_SAMPLE_FMT_S32: {
 +            int32_t *ip = table;
 +            *ip++ = (int32_t)d;
 +            table = ip;
 +            continue; }
 +        default:
 +            av_assert0(0);
 +        }
 +    }
 +}
 +
 +#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
 +
 +#define PHASER_PLANAR(name, type)                                      \
 +static void phaser_## name ##p(AudioPhaserContext *p,                  \
 +                               uint8_t * const *src, uint8_t **dst,    \
 +                               int nb_samples, int channels)           \
 +{                                                                      \
 +    int i, c, delay_pos, modulation_pos;                               \
 +                                                                       \
 +    av_assert0(channels > 0);                                          \
 +    for (c = 0; c < channels; c++) {                                   \
 +        type *s = (type *)src[c];                                      \
 +        type *d = (type *)dst[c];                                      \
 +        double *buffer = p->delay_buffer +                             \
 +                         c * p->delay_buffer_length;                   \
 +                                                                       \
 +        delay_pos      = p->delay_pos;                                 \
 +        modulation_pos = p->modulation_pos;                            \
 +                                                                       \
 +        for (i = 0; i < nb_samples; i++, s++, d++) {                   \
 +            double v = *s * p->in_gain + buffer[                       \
 +                       MOD(delay_pos + p->modulation_buffer[           \
 +                       modulation_pos],                                \
 +                       p->delay_buffer_length)] * p->decay;            \
 +                                                                       \
 +            modulation_pos = MOD(modulation_pos + 1,                   \
 +                             p->modulation_buffer_length);             \
 +            delay_pos = MOD(delay_pos + 1, p->delay_buffer_length);    \
 +            buffer[delay_pos] = v;                                     \
 +                                                                       \
 +            *d = v * p->out_gain;                                      \
 +        }                                                              \
 +    }                                                                  \
 +                                                                       \
 +    p->delay_pos      = delay_pos;                                     \
 +    p->modulation_pos = modulation_pos;                                \
 +}
 +
 +#define PHASER(name, type)                                              \
 +static void phaser_## name (AudioPhaserContext *p,                      \
 +                            uint8_t * const *src, uint8_t **dst,        \
 +                            int nb_samples, int channels)               \
 +{                                                                       \
 +    int i, c, delay_pos, modulation_pos;                                \
 +    type *s = (type *)src[0];                                           \
 +    type *d = (type *)dst[0];                                           \
 +    double *buffer = p->delay_buffer;                                   \
 +                                                                        \
 +    delay_pos      = p->delay_pos;                                      \
 +    modulation_pos = p->modulation_pos;                                 \
 +                                                                        \
 +    for (i = 0; i < nb_samples; i++) {                                  \
 +        int pos = MOD(delay_pos + p->modulation_buffer[modulation_pos], \
 +                   p->delay_buffer_length) * channels;                  \
 +        int npos;                                                       \
 +                                                                        \
 +        delay_pos = MOD(delay_pos + 1, p->delay_buffer_length);         \
 +        npos = delay_pos * channels;                                    \
 +        for (c = 0; c < channels; c++, s++, d++) {                      \
 +            double v = *s * p->in_gain + buffer[pos + c] * p->decay;    \
 +                                                                        \
 +            buffer[npos + c] = v;                                       \
 +                                                                        \
 +            *d = v * p->out_gain;                                       \
 +        }                                                               \
 +                                                                        \
 +        modulation_pos = MOD(modulation_pos + 1,                        \
 +                         p->modulation_buffer_length);                  \
 +    }                                                                   \
 +                                                                        \
 +    p->delay_pos      = delay_pos;                                      \
 +    p->modulation_pos = modulation_pos;                                 \
 +}
 +
 +PHASER_PLANAR(dbl, double)
 +PHASER_PLANAR(flt, float)
 +PHASER_PLANAR(s16, int16_t)
 +PHASER_PLANAR(s32, int32_t)
 +
 +PHASER(dbl, double)
 +PHASER(flt, float)
 +PHASER(s16, int16_t)
 +PHASER(s32, int32_t)
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AudioPhaserContext *p = outlink->src->priv;
 +    AVFilterLink *inlink = outlink->src->inputs[0];
 +
 +    p->delay_buffer_length = p->delay * 0.001 * inlink->sample_rate + 0.5;
 +    p->delay_buffer = av_calloc(p->delay_buffer_length, sizeof(*p->delay_buffer) * inlink->channels);
 +    p->modulation_buffer_length = inlink->sample_rate / p->speed + 0.5;
 +    p->modulation_buffer = av_malloc(p->modulation_buffer_length * sizeof(*p->modulation_buffer));
 +
 +    if (!p->modulation_buffer || !p->delay_buffer)
 +        return AVERROR(ENOMEM);
 +
 +    generate_wave_table(p->type, AV_SAMPLE_FMT_S32,
 +                        p->modulation_buffer, p->modulation_buffer_length,
 +                        1., p->delay_buffer_length, M_PI / 2.0);
 +
 +    p->delay_pos = p->modulation_pos = 0;
 +
 +    switch (inlink->format) {
 +    case AV_SAMPLE_FMT_DBL:  p->phaser = phaser_dbl;  break;
 +    case AV_SAMPLE_FMT_DBLP: p->phaser = phaser_dblp; break;
 +    case AV_SAMPLE_FMT_FLT:  p->phaser = phaser_flt;  break;
 +    case AV_SAMPLE_FMT_FLTP: p->phaser = phaser_fltp; break;
 +    case AV_SAMPLE_FMT_S16:  p->phaser = phaser_s16;  break;
 +    case AV_SAMPLE_FMT_S16P: p->phaser = phaser_s16p; break;
 +    case AV_SAMPLE_FMT_S32:  p->phaser = phaser_s32;  break;
 +    case AV_SAMPLE_FMT_S32P: p->phaser = phaser_s32p; break;
 +    default: av_assert0(0);
 +    }
 +
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *inbuf)
 +{
 +    AudioPhaserContext *p = inlink->dst->priv;
 +    AVFilterLink *outlink = inlink->dst->outputs[0];
 +    AVFrame *outbuf;
 +
 +    if (av_frame_is_writable(inbuf)) {
 +        outbuf = inbuf;
 +    } else {
 +        outbuf = ff_get_audio_buffer(inlink, inbuf->nb_samples);
 +        if (!outbuf)
 +            return AVERROR(ENOMEM);
 +        av_frame_copy_props(outbuf, inbuf);
 +    }
 +
 +    p->phaser(p, inbuf->extended_data, outbuf->extended_data,
 +              outbuf->nb_samples, av_frame_get_channels(outbuf));
 +
 +    if (inbuf != outbuf)
 +        av_frame_free(&inbuf);
 +
 +    return ff_filter_frame(outlink, outbuf);
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AudioPhaserContext *p = ctx->priv;
 +
 +    av_freep(&p->delay_buffer);
 +    av_freep(&p->modulation_buffer);
 +}
 +
 +static const AVFilterPad aphaser_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad aphaser_outputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_output,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_aphaser = {
 +    .name          = "aphaser",
 +    .description   = NULL_IF_CONFIG_SMALL("Add a phasing effect to the audio."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AudioPhaserContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .inputs        = aphaser_inputs,
 +    .outputs       = aphaser_outputs,
 +    .priv_class    = &aphaser_class,
 +};
index 7b6ab68,0000000..86e7b74
mode 100644,000000..100644
--- /dev/null
@@@ -1,309 -1,0 +1,309 @@@
- AVFilter avfilter_af_aresample = {
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + * Copyright (c) 2011 Mina Nagy Zaki
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * resampling audio filter
 + */
 +
 +#include "libavutil/avstring.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "libavutil/avassert.h"
 +#include "libswresample/swresample.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int sample_rate_arg;
 +    double ratio;
 +    struct SwrContext *swr;
 +    int64_t next_pts;
 +    int req_fullfilled;
 +} AResampleContext;
 +
 +static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    int ret = 0;
 +
 +    aresample->next_pts = AV_NOPTS_VALUE;
 +    aresample->swr = swr_alloc();
 +    if (!aresample->swr) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +
 +    if (opts) {
 +        AVDictionaryEntry *e = NULL;
 +
 +        while ((e = av_dict_get(*opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
 +            if ((ret = av_opt_set(aresample->swr, e->key, e->value, 0)) < 0)
 +                goto end;
 +        }
 +        av_dict_free(opts);
 +    }
 +    if (aresample->sample_rate_arg > 0)
 +        av_opt_set_int(aresample->swr, "osr", aresample->sample_rate_arg, 0);
 +end:
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    swr_free(&aresample->swr);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    int out_rate                   = av_get_int(aresample->swr, "osr", NULL);
 +    uint64_t out_layout            = av_get_int(aresample->swr, "ocl", NULL);
 +    enum AVSampleFormat out_format = av_get_int(aresample->swr, "osf", NULL);
 +
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +
 +    AVFilterFormats        *in_formats      = ff_all_formats(AVMEDIA_TYPE_AUDIO);
 +    AVFilterFormats        *out_formats;
 +    AVFilterFormats        *in_samplerates  = ff_all_samplerates();
 +    AVFilterFormats        *out_samplerates;
 +    AVFilterChannelLayouts *in_layouts      = ff_all_channel_counts();
 +    AVFilterChannelLayouts *out_layouts;
 +
 +    ff_formats_ref  (in_formats,      &inlink->out_formats);
 +    ff_formats_ref  (in_samplerates,  &inlink->out_samplerates);
 +    ff_channel_layouts_ref(in_layouts,      &inlink->out_channel_layouts);
 +
 +    if(out_rate > 0) {
 +        out_samplerates = ff_make_format_list((int[]){ out_rate, -1 });
 +    } else {
 +        out_samplerates = ff_all_samplerates();
 +    }
 +    ff_formats_ref(out_samplerates, &outlink->in_samplerates);
 +
 +    if(out_format != AV_SAMPLE_FMT_NONE) {
 +        out_formats = ff_make_format_list((int[]){ out_format, -1 });
 +    } else
 +        out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO);
 +    ff_formats_ref(out_formats, &outlink->in_formats);
 +
 +    if(out_layout) {
 +        out_layouts = avfilter_make_format64_list((int64_t[]){ out_layout, -1 });
 +    } else
 +        out_layouts = ff_all_channel_counts();
 +    ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts);
 +
 +    return 0;
 +}
 +
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    int ret;
 +    AVFilterContext *ctx = outlink->src;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    AResampleContext *aresample = ctx->priv;
 +    int out_rate;
 +    uint64_t out_layout;
 +    enum AVSampleFormat out_format;
 +    char inchl_buf[128], outchl_buf[128];
 +
 +    aresample->swr = swr_alloc_set_opts(aresample->swr,
 +                                        outlink->channel_layout, outlink->format, outlink->sample_rate,
 +                                        inlink->channel_layout, inlink->format, inlink->sample_rate,
 +                                        0, ctx);
 +    if (!aresample->swr)
 +        return AVERROR(ENOMEM);
 +    if (!inlink->channel_layout)
 +        av_opt_set_int(aresample->swr, "ich", inlink->channels, 0);
 +    if (!outlink->channel_layout)
 +        av_opt_set_int(aresample->swr, "och", outlink->channels, 0);
 +
 +    ret = swr_init(aresample->swr);
 +    if (ret < 0)
 +        return ret;
 +
 +    out_rate   = av_get_int(aresample->swr, "osr", NULL);
 +    out_layout = av_get_int(aresample->swr, "ocl", NULL);
 +    out_format = av_get_int(aresample->swr, "osf", NULL);
 +    outlink->time_base = (AVRational) {1, out_rate};
 +
 +    av_assert0(outlink->sample_rate == out_rate);
 +    av_assert0(outlink->channel_layout == out_layout || !outlink->channel_layout);
 +    av_assert0(outlink->format == out_format);
 +
 +    aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate;
 +
 +    av_get_channel_layout_string(inchl_buf,  sizeof(inchl_buf),  inlink ->channels, inlink ->channel_layout);
 +    av_get_channel_layout_string(outchl_buf, sizeof(outchl_buf), outlink->channels, outlink->channel_layout);
 +
 +    av_log(ctx, AV_LOG_VERBOSE, "ch:%d chl:%s fmt:%s r:%dHz -> ch:%d chl:%s fmt:%s r:%dHz\n",
 +           inlink ->channels, inchl_buf,  av_get_sample_fmt_name(inlink->format),  inlink->sample_rate,
 +           outlink->channels, outchl_buf, av_get_sample_fmt_name(outlink->format), outlink->sample_rate);
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref)
 +{
 +    AResampleContext *aresample = inlink->dst->priv;
 +    const int n_in  = insamplesref->nb_samples;
 +    int n_out       = n_in * aresample->ratio * 2 + 256;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFrame *outsamplesref = ff_get_audio_buffer(outlink, n_out);
 +    int ret;
 +
 +    if(!outsamplesref)
 +        return AVERROR(ENOMEM);
 +
 +    av_frame_copy_props(outsamplesref, insamplesref);
 +    outsamplesref->format                = outlink->format;
 +    av_frame_set_channels(outsamplesref, outlink->channels);
 +    outsamplesref->channel_layout        = outlink->channel_layout;
 +    outsamplesref->sample_rate           = outlink->sample_rate;
 +
 +    if(insamplesref->pts != AV_NOPTS_VALUE) {
 +        int64_t inpts = av_rescale(insamplesref->pts, inlink->time_base.num * (int64_t)outlink->sample_rate * inlink->sample_rate, inlink->time_base.den);
 +        int64_t outpts= swr_next_pts(aresample->swr, inpts);
 +        aresample->next_pts =
 +        outsamplesref->pts  = ROUNDED_DIV(outpts, inlink->sample_rate);
 +    } else {
 +        outsamplesref->pts  = AV_NOPTS_VALUE;
 +    }
 +    n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out,
 +                                 (void *)insamplesref->extended_data, n_in);
 +    if (n_out <= 0) {
 +        av_frame_free(&outsamplesref);
 +        av_frame_free(&insamplesref);
 +        return 0;
 +    }
 +
 +    outsamplesref->nb_samples  = n_out;
 +
 +    ret = ff_filter_frame(outlink, outsamplesref);
 +    aresample->req_fullfilled= 1;
 +    av_frame_free(&insamplesref);
 +    return ret;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AResampleContext *aresample = ctx->priv;
 +    AVFilterLink *const inlink = outlink->src->inputs[0];
 +    int ret;
 +
 +    aresample->req_fullfilled = 0;
 +    do{
 +        ret = ff_request_frame(ctx->inputs[0]);
 +    }while(!aresample->req_fullfilled && ret>=0);
 +
 +    if (ret == AVERROR_EOF) {
 +        AVFrame *outsamplesref;
 +        int n_out = 4096;
 +
 +        outsamplesref = ff_get_audio_buffer(outlink, n_out);
 +        if (!outsamplesref)
 +            return AVERROR(ENOMEM);
 +        n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out, 0, 0);
 +        if (n_out <= 0) {
 +            av_frame_free(&outsamplesref);
 +            return (n_out == 0) ? AVERROR_EOF : n_out;
 +        }
 +
 +        outsamplesref->sample_rate = outlink->sample_rate;
 +        outsamplesref->nb_samples  = n_out;
 +#if 0
 +        outsamplesref->pts = aresample->next_pts;
 +        if(aresample->next_pts != AV_NOPTS_VALUE)
 +            aresample->next_pts += av_rescale_q(n_out, (AVRational){1 ,outlink->sample_rate}, outlink->time_base);
 +#else
 +        outsamplesref->pts = swr_next_pts(aresample->swr, INT64_MIN);
 +        outsamplesref->pts = ROUNDED_DIV(outsamplesref->pts, inlink->sample_rate);
 +#endif
 +
 +        return ff_filter_frame(outlink, outsamplesref);
 +    }
 +    return ret;
 +}
 +
 +static const AVClass *resample_child_class_next(const AVClass *prev)
 +{
 +    return prev ? NULL : swr_get_class();
 +}
 +
 +static void *resample_child_next(void *obj, void *prev)
 +{
 +    AResampleContext *s = obj;
 +    return prev ? NULL : s->swr;
 +}
 +
 +#define OFFSET(x) offsetof(AResampleContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption options[] = {
 +    {"sample_rate", NULL, OFFSET(sample_rate_arg), AV_OPT_TYPE_INT, {.i64=0},  0,        INT_MAX, FLAGS },
 +    {NULL}
 +};
 +
 +static const AVClass aresample_class = {
 +    .class_name       = "aresample",
 +    .item_name        = av_default_item_name,
 +    .option           = options,
 +    .version          = LIBAVUTIL_VERSION_INT,
 +    .child_class_next = resample_child_class_next,
 +    .child_next       = resample_child_next,
 +};
 +
 +static const AVFilterPad aresample_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad aresample_outputs[] = {
 +    {
 +        .name          = "default",
 +        .config_props  = config_output,
 +        .request_frame = request_frame,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_aresample = {
 +    .name          = "aresample",
 +    .description   = NULL_IF_CONFIG_SMALL("Resample audio data."),
 +    .init_dict     = init_dict,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AResampleContext),
 +    .priv_class    = &aresample_class,
 +    .inputs        = aresample_inputs,
 +    .outputs       = aresample_outputs,
 +};
index bca8fa6,0000000..fbcf275
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,196 @@@
- AVFilter avfilter_af_asetnsamples = {
 +/*
 + * Copyright (c) 2012 Andrey Utkin
 + * Copyright (c) 2012 Stefano Sabatini
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Filter that changes number of samples on single output operation
 + */
 +
 +#include "libavutil/audio_fifo.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +#include "formats.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int nb_out_samples;  ///< how many samples to output
 +    AVAudioFifo *fifo;   ///< samples are queued here
 +    int64_t next_out_pts;
 +    int pad;
 +} ASNSContext;
 +
 +#define OFFSET(x) offsetof(ASNSContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption asetnsamples_options[] = {
 +    { "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
 +    { "n",              "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
 +    { "pad", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
 +    { "p",   "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(asetnsamples);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    ASNSContext *asns = ctx->priv;
 +
 +    asns->next_out_pts = AV_NOPTS_VALUE;
 +    av_log(ctx, AV_LOG_VERBOSE, "nb_out_samples:%d pad:%d\n", asns->nb_out_samples, asns->pad);
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    ASNSContext *asns = ctx->priv;
 +    av_audio_fifo_free(asns->fifo);
 +}
 +
 +static int config_props_output(AVFilterLink *outlink)
 +{
 +    ASNSContext *asns = outlink->src->priv;
 +
 +    asns->fifo = av_audio_fifo_alloc(outlink->format, outlink->channels, asns->nb_out_samples);
 +    if (!asns->fifo)
 +        return AVERROR(ENOMEM);
 +    outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
 +
 +    return 0;
 +}
 +
 +static int push_samples(AVFilterLink *outlink)
 +{
 +    ASNSContext *asns = outlink->src->priv;
 +    AVFrame *outsamples = NULL;
 +    int ret, nb_out_samples, nb_pad_samples;
 +
 +    if (asns->pad) {
 +        nb_out_samples = av_audio_fifo_size(asns->fifo) ? asns->nb_out_samples : 0;
 +        nb_pad_samples = nb_out_samples - FFMIN(nb_out_samples, av_audio_fifo_size(asns->fifo));
 +    } else {
 +        nb_out_samples = FFMIN(asns->nb_out_samples, av_audio_fifo_size(asns->fifo));
 +        nb_pad_samples = 0;
 +    }
 +
 +    if (!nb_out_samples)
 +        return 0;
 +
 +    outsamples = ff_get_audio_buffer(outlink, nb_out_samples);
 +    if (!outsamples)
 +        return AVERROR(ENOMEM);
 +
 +    av_audio_fifo_read(asns->fifo,
 +                       (void **)outsamples->extended_data, nb_out_samples);
 +
 +    if (nb_pad_samples)
 +        av_samples_set_silence(outsamples->extended_data, nb_out_samples - nb_pad_samples,
 +                               nb_pad_samples, outlink->channels,
 +                               outlink->format);
 +    outsamples->nb_samples     = nb_out_samples;
 +    outsamples->channel_layout = outlink->channel_layout;
 +    outsamples->sample_rate    = outlink->sample_rate;
 +    outsamples->pts = asns->next_out_pts;
 +
 +    if (asns->next_out_pts != AV_NOPTS_VALUE)
 +        asns->next_out_pts += nb_out_samples;
 +
 +    ret = ff_filter_frame(outlink, outsamples);
 +    if (ret < 0)
 +        return ret;
 +    return nb_out_samples;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    ASNSContext *asns = ctx->priv;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    int ret;
 +    int nb_samples = insamples->nb_samples;
 +
 +    if (av_audio_fifo_space(asns->fifo) < nb_samples) {
 +        av_log(ctx, AV_LOG_DEBUG, "No space for %d samples, stretching audio fifo\n", nb_samples);
 +        ret = av_audio_fifo_realloc(asns->fifo, av_audio_fifo_size(asns->fifo) + nb_samples);
 +        if (ret < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Stretching audio fifo failed, discarded %d samples\n", nb_samples);
 +            return -1;
 +        }
 +    }
 +    av_audio_fifo_write(asns->fifo, (void **)insamples->extended_data, nb_samples);
 +    if (asns->next_out_pts == AV_NOPTS_VALUE)
 +        asns->next_out_pts = insamples->pts;
 +    av_frame_free(&insamples);
 +
 +    while (av_audio_fifo_size(asns->fifo) >= asns->nb_out_samples)
 +        push_samples(outlink);
 +    return 0;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterLink *inlink = outlink->src->inputs[0];
 +    int ret;
 +
 +    ret = ff_request_frame(inlink);
 +    if (ret == AVERROR_EOF) {
 +        ret = push_samples(outlink);
 +        return ret < 0 ? ret : ret > 0 ? 0 : AVERROR_EOF;
 +    }
 +
 +    return ret;
 +}
 +
 +static const AVFilterPad asetnsamples_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad asetnsamples_outputs[] = {
 +    {
 +        .name          = "default",
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +        .request_frame = request_frame,
 +        .config_props  = config_props_output,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_asetnsamples = {
 +    .name        = "asetnsamples",
 +    .description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."),
 +    .priv_size   = sizeof(ASNSContext),
 +    .priv_class  = &asetnsamples_class,
 +    .init        = init,
 +    .uninit      = uninit,
 +    .inputs      = asetnsamples_inputs,
 +    .outputs     = asetnsamples_outputs,
 +};
index e031b89,0000000..0d06915
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,119 @@@
- AVFilter avfilter_af_asetrate = {
 +/*
 + * Copyright (c) 2013 Nicolas George
 + *
 + * 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 "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int sample_rate;
 +    int rescale_pts;
 +} ASetRateContext;
 +
 +#define CONTEXT ASetRateContext
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +#define OPT_GENERIC(name, field, def, min, max, descr, type, deffield, ...) \
 +    { name, descr, offsetof(CONTEXT, field), AV_OPT_TYPE_ ## type,          \
 +      { .deffield = def }, min, max, FLAGS, __VA_ARGS__ }
 +
 +#define OPT_INT(name, field, def, min, max, descr, ...) \
 +    OPT_GENERIC(name, field, def, min, max, descr, INT, i64, __VA_ARGS__)
 +
 +static const AVOption asetrate_options[] = {
 +    OPT_INT("sample_rate", sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
 +    OPT_INT("r",           sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
 +    {NULL},
 +};
 +
 +AVFILTER_DEFINE_CLASS(asetrate);
 +
 +static av_cold int query_formats(AVFilterContext *ctx)
 +{
 +    ASetRateContext *sr = ctx->priv;
 +    int sample_rates[] = { sr->sample_rate, -1 };
 +
 +    ff_formats_ref(ff_make_format_list(sample_rates),
 +                   &ctx->outputs[0]->in_samplerates);
 +    return 0;
 +}
 +
 +static av_cold int config_props(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    ASetRateContext *sr = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    AVRational intb = ctx->inputs[0]->time_base;
 +    int inrate = inlink->sample_rate;
 +
 +    if (intb.num == 1 && intb.den == inrate) {
 +        outlink->time_base.num = 1;
 +        outlink->time_base.den = outlink->sample_rate;
 +    } else {
 +        outlink->time_base = intb;
 +        sr->rescale_pts = 1;
 +        if (av_q2d(intb) > 1.0 / FFMAX(inrate, outlink->sample_rate))
 +            av_log(ctx, AV_LOG_WARNING, "Time base is inaccurate\n");
 +    }
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    ASetRateContext *sr = ctx->priv;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +
 +    frame->sample_rate = outlink->sample_rate;
 +    if (sr->rescale_pts)
 +        frame->pts = av_rescale(frame->pts, inlink->sample_rate,
 +                                           outlink->sample_rate);
 +    return ff_filter_frame(outlink, frame);
 +}
 +
 +static const AVFilterPad asetrate_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad asetrate_outputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_props,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_asetrate = {
 +    .name          = "asetrate",
 +    .description   = NULL_IF_CONFIG_SMALL("Change the sample rate without "
 +                                          "altering the data."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(ASetRateContext),
 +    .inputs        = asetrate_inputs,
 +    .outputs       = asetrate_outputs,
 +    .priv_class    = &asetrate_class,
 +};
@@@ -113,10 -124,10 +113,10 @@@ static const AVFilterPad outputs[] = 
          .name = "default",
          .type = AVMEDIA_TYPE_AUDIO,
      },
 -    { NULL },
 +    { NULL }
  };
  
- AVFilter avfilter_af_ashowinfo = {
+ AVFilter ff_af_ashowinfo = {
      .name        = "ashowinfo",
      .description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."),
      .priv_size   = sizeof(AShowInfoContext),
index f6a2045,0000000..60ccd73
mode 100644,000000..100644
--- /dev/null
@@@ -1,274 -1,0 +1,274 @@@
- AVFilter avfilter_af_astats = {
 +/*
 + * Copyright (c) 2009 Rob Sykes <robs@users.sourceforge.net>
 + * Copyright (c) 2013 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 <float.h>
 +
 +#include "libavutil/opt.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct ChannelStats {
 +    double last;
 +    double sigma_x, sigma_x2;
 +    double avg_sigma_x2, min_sigma_x2, max_sigma_x2;
 +    double min, max;
 +    double min_run, max_run;
 +    double min_runs, max_runs;
 +    uint64_t min_count, max_count;
 +    uint64_t nb_samples;
 +} ChannelStats;
 +
 +typedef struct {
 +    const AVClass *class;
 +    ChannelStats *chstats;
 +    int nb_channels;
 +    uint64_t tc_samples;
 +    double time_constant;
 +    double mult;
 +} AudioStatsContext;
 +
 +#define OFFSET(x) offsetof(AudioStatsContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption astats_options[] = {
 +    { "length", "set the window length", OFFSET(time_constant), AV_OPT_TYPE_DOUBLE, {.dbl=.05}, .01, 10, FLAGS },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(astats);
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AudioStatsContext *s = outlink->src->priv;
 +    int c;
 +
 +    s->chstats = av_calloc(sizeof(*s->chstats), outlink->channels);
 +    if (!s->chstats)
 +        return AVERROR(ENOMEM);
 +    s->nb_channels = outlink->channels;
 +    s->mult = exp((-1 / s->time_constant / outlink->sample_rate));
 +    s->tc_samples = 5 * s->time_constant * outlink->sample_rate + .5;
 +
 +    for (c = 0; c < s->nb_channels; c++) {
 +        ChannelStats *p = &s->chstats[c];
 +
 +        p->min = p->min_sigma_x2 = DBL_MAX;
 +        p->max = p->max_sigma_x2 = DBL_MIN;
 +    }
 +
 +    return 0;
 +}
 +
 +static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d)
 +{
 +    if (d < p->min) {
 +        p->min = d;
 +        p->min_run = 1;
 +        p->min_runs = 0;
 +        p->min_count = 1;
 +    } else if (d == p->min) {
 +        p->min_count++;
 +        p->min_run = d == p->last ? p->min_run + 1 : 1;
 +    } else if (p->last == p->min) {
 +        p->min_runs += p->min_run * p->min_run;
 +    }
 +
 +    if (d > p->max) {
 +        p->max = d;
 +        p->max_run = 1;
 +        p->max_runs = 0;
 +        p->max_count = 1;
 +    } else if (d == p->max) {
 +        p->max_count++;
 +        p->max_run = d == p->last ? p->max_run + 1 : 1;
 +    } else if (p->last == p->max) {
 +        p->max_runs += p->max_run * p->max_run;
 +    }
 +
 +    p->sigma_x += d;
 +    p->sigma_x2 += d * d;
 +    p->avg_sigma_x2 = p->avg_sigma_x2 * s->mult + (1.0 - s->mult) * d * d;
 +    p->last = d;
 +
 +    if (p->nb_samples >= s->tc_samples) {
 +        p->max_sigma_x2 = FFMAX(p->max_sigma_x2, p->avg_sigma_x2);
 +        p->min_sigma_x2 = FFMIN(p->min_sigma_x2, p->avg_sigma_x2);
 +    }
 +    p->nb_samples++;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 +{
 +    AudioStatsContext *s = inlink->dst->priv;
 +    const int channels = s->nb_channels;
 +    const double *src;
 +    int i, c;
 +
 +    switch (inlink->format) {
 +    case AV_SAMPLE_FMT_DBLP:
 +        for (c = 0; c < channels; c++) {
 +            ChannelStats *p = &s->chstats[c];
 +            src = (const double *)buf->extended_data[c];
 +
 +            for (i = 0; i < buf->nb_samples; i++, src++)
 +                update_stat(s, p, *src);
 +        }
 +        break;
 +    case AV_SAMPLE_FMT_DBL:
 +        src = (const double *)buf->extended_data[0];
 +
 +        for (i = 0; i < buf->nb_samples; i++) {
 +            for (c = 0; c < channels; c++, src++)
 +                update_stat(s, &s->chstats[c], *src);
 +        }
 +        break;
 +    }
 +
 +    return ff_filter_frame(inlink->dst->outputs[0], buf);
 +}
 +
 +#define LINEAR_TO_DB(x) (log10(x) * 20)
 +
 +static void print_stats(AVFilterContext *ctx)
 +{
 +    AudioStatsContext *s = ctx->priv;
 +    uint64_t min_count = 0, max_count = 0, nb_samples = 0;
 +    double min_runs = 0, max_runs = 0,
 +           min = DBL_MAX, max = DBL_MIN,
 +           max_sigma_x = 0,
 +           sigma_x = 0,
 +           sigma_x2 = 0,
 +           min_sigma_x2 = DBL_MAX,
 +           max_sigma_x2 = DBL_MIN;
 +    int c;
 +
 +    for (c = 0; c < s->nb_channels; c++) {
 +        ChannelStats *p = &s->chstats[c];
 +
 +        if (p->nb_samples < s->tc_samples)
 +            p->min_sigma_x2 = p->max_sigma_x2 = p->sigma_x2 / p->nb_samples;
 +
 +        min = FFMIN(min, p->min);
 +        max = FFMAX(max, p->max);
 +        min_sigma_x2 = FFMIN(min_sigma_x2, p->min_sigma_x2);
 +        max_sigma_x2 = FFMAX(max_sigma_x2, p->max_sigma_x2);
 +        sigma_x += p->sigma_x;
 +        sigma_x2 += p->sigma_x2;
 +        min_count += p->min_count;
 +        max_count += p->max_count;
 +        min_runs += p->min_runs;
 +        max_runs += p->max_runs;
 +        nb_samples += p->nb_samples;
 +        if (fabs(p->sigma_x) > fabs(max_sigma_x))
 +            max_sigma_x = p->sigma_x;
 +
 +        av_log(ctx, AV_LOG_INFO, "Channel: %d\n", c + 1);
 +        av_log(ctx, AV_LOG_INFO, "DC offset: %f\n", p->sigma_x / p->nb_samples);
 +        av_log(ctx, AV_LOG_INFO, "Min level: %f\n", p->min);
 +        av_log(ctx, AV_LOG_INFO, "Max level: %f\n", p->max);
 +        av_log(ctx, AV_LOG_INFO, "Peak level dB: %f\n", LINEAR_TO_DB(FFMAX(-p->min, p->max)));
 +        av_log(ctx, AV_LOG_INFO, "RMS level dB: %f\n", LINEAR_TO_DB(sqrt(p->sigma_x2 / p->nb_samples)));
 +        av_log(ctx, AV_LOG_INFO, "RMS peak dB: %f\n", LINEAR_TO_DB(sqrt(p->max_sigma_x2)));
 +        if (p->min_sigma_x2 != 1)
 +            av_log(ctx, AV_LOG_INFO, "RMS trough dB: %f\n",LINEAR_TO_DB(sqrt(p->min_sigma_x2)));
 +        av_log(ctx, AV_LOG_INFO, "Crest factor: %f\n", p->sigma_x2 ? FFMAX(-p->min, p->max) / sqrt(p->sigma_x2 / p->nb_samples) : 1);
 +        av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count)));
 +        av_log(ctx, AV_LOG_INFO, "Peak count: %"PRId64"\n", p->min_count + p->max_count);
 +    }
 +
 +    av_log(ctx, AV_LOG_INFO, "Overall\n");
 +    av_log(ctx, AV_LOG_INFO, "DC offset: %f\n", max_sigma_x / (nb_samples / s->nb_channels));
 +    av_log(ctx, AV_LOG_INFO, "Min level: %f\n", min);
 +    av_log(ctx, AV_LOG_INFO, "Max level: %f\n", max);
 +    av_log(ctx, AV_LOG_INFO, "Peak level dB: %f\n", LINEAR_TO_DB(FFMAX(-min, max)));
 +    av_log(ctx, AV_LOG_INFO, "RMS level dB: %f\n", LINEAR_TO_DB(sqrt(sigma_x2 / nb_samples)));
 +    av_log(ctx, AV_LOG_INFO, "RMS peak dB: %f\n", LINEAR_TO_DB(sqrt(max_sigma_x2)));
 +    if (min_sigma_x2 != 1)
 +        av_log(ctx, AV_LOG_INFO, "RMS trough dB: %f\n", LINEAR_TO_DB(sqrt(min_sigma_x2)));
 +    av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count)));
 +    av_log(ctx, AV_LOG_INFO, "Peak count: %f\n", (min_count + max_count) / (double)s->nb_channels);
 +    av_log(ctx, AV_LOG_INFO, "Number of samples: %"PRId64"\n", nb_samples / s->nb_channels);
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AudioStatsContext *s = ctx->priv;
 +
 +    print_stats(ctx);
 +    av_freep(&s->chstats);
 +}
 +
 +static const AVFilterPad astats_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad astats_outputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_output,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_astats = {
 +    .name          = "astats",
 +    .description   = NULL_IF_CONFIG_SMALL("Show time domain statistics about audio frames."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AudioStatsContext),
 +    .priv_class    = &astats_class,
 +    .uninit        = uninit,
 +    .inputs        = astats_inputs,
 +    .outputs       = astats_outputs,
 +};
index 1ea3a8a,0000000..becfe34
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,240 @@@
- AVFilter avfilter_af_astreamsync = {
 +/*
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Stream (de)synchronization filter
 + */
 +
 +#include "libavutil/eval.h"
 +#include "libavutil/opt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +#define QUEUE_SIZE 16
 +
 +static const char * const var_names[] = {
 +    "b1", "b2",
 +    "s1", "s2",
 +    "t1", "t2",
 +    NULL
 +};
 +
 +enum var_name {
 +    VAR_B1, VAR_B2,
 +    VAR_S1, VAR_S2,
 +    VAR_T1, VAR_T2,
 +    VAR_NB
 +};
 +
 +typedef struct {
 +    const AVClass *class;
 +    AVExpr *expr;
 +    char *expr_str;
 +    double var_values[VAR_NB];
 +    struct buf_queue {
 +        AVFrame *buf[QUEUE_SIZE];
 +        unsigned tail, nb;
 +        /* buf[tail] is the oldest,
 +           buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
 +    } queue[2];
 +    int req[2];
 +    int next_out;
 +    int eof; /* bitmask, one bit for each stream */
 +} AStreamSyncContext;
 +
 +#define OFFSET(x) offsetof(AStreamSyncContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +static const AVOption astreamsync_options[] = {
 +    { "expr", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
 +    { "e",    "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(astreamsync);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    int r, i;
 +
 +    r = av_expr_parse(&as->expr, as->expr_str, var_names,
 +                      NULL, NULL, NULL, NULL, 0, ctx);
 +    if (r < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", as->expr_str);
 +        return r;
 +    }
 +    for (i = 0; i < 42; i++)
 +        av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    int i;
 +    AVFilterFormats *formats, *rates;
 +    AVFilterChannelLayouts *layouts;
 +
 +    for (i = 0; i < 2; i++) {
 +        formats = ctx->inputs[i]->in_formats;
 +        ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
 +        ff_formats_ref(formats, &ctx->outputs[i]->in_formats);
 +        rates = ff_all_samplerates();
 +        ff_formats_ref(rates, &ctx->inputs[i]->out_samplerates);
 +        ff_formats_ref(rates, &ctx->outputs[i]->in_samplerates);
 +        layouts = ctx->inputs[i]->in_channel_layouts;
 +        ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
 +        ff_channel_layouts_ref(layouts, &ctx->outputs[i]->in_channel_layouts);
 +    }
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    int id = outlink == ctx->outputs[1];
 +
 +    outlink->sample_rate = ctx->inputs[id]->sample_rate;
 +    outlink->time_base   = ctx->inputs[id]->time_base;
 +    return 0;
 +}
 +
 +static int send_out(AVFilterContext *ctx, int out_id)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    struct buf_queue *queue = &as->queue[out_id];
 +    AVFrame *buf = queue->buf[queue->tail];
 +    int ret;
 +
 +    queue->buf[queue->tail] = NULL;
 +    as->var_values[VAR_B1 + out_id]++;
 +    as->var_values[VAR_S1 + out_id] += buf->nb_samples;
 +    if (buf->pts != AV_NOPTS_VALUE)
 +        as->var_values[VAR_T1 + out_id] =
 +            av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
 +    as->var_values[VAR_T1 + out_id] += buf->nb_samples /
 +                                   (double)ctx->inputs[out_id]->sample_rate;
 +    ret = ff_filter_frame(ctx->outputs[out_id], buf);
 +    queue->nb--;
 +    queue->tail = (queue->tail + 1) % QUEUE_SIZE;
 +    if (as->req[out_id])
 +        as->req[out_id]--;
 +    return ret;
 +}
 +
 +static void send_next(AVFilterContext *ctx)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    int i;
 +
 +    while (1) {
 +        if (!as->queue[as->next_out].nb)
 +            break;
 +        send_out(ctx, as->next_out);
 +        if (!as->eof)
 +            as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
 +    }
 +    for (i = 0; i < 2; i++)
 +        if (as->queue[i].nb == QUEUE_SIZE)
 +            send_out(ctx, i);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AStreamSyncContext *as = ctx->priv;
 +    int id = outlink == ctx->outputs[1];
 +
 +    as->req[id]++;
 +    while (as->req[id] && !(as->eof & (1 << id))) {
 +        if (as->queue[as->next_out].nb) {
 +            send_next(ctx);
 +        } else {
 +            as->eof |= 1 << as->next_out;
 +            ff_request_frame(ctx->inputs[as->next_out]);
 +            if (as->eof & (1 << as->next_out))
 +                as->next_out = !as->next_out;
 +        }
 +    }
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AStreamSyncContext *as = ctx->priv;
 +    int id = inlink == ctx->inputs[1];
 +
 +    as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
 +        insamples;
 +    as->eof &= ~(1 << id);
 +    send_next(ctx);
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +
 +    av_expr_free(as->expr);
 +    as->expr = NULL;
 +}
 +
 +static const AVFilterPad astreamsync_inputs[] = {
 +    {
 +        .name         = "in1",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },{
 +        .name         = "in2",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad astreamsync_outputs[] = {
 +    {
 +        .name          = "out1",
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +        .config_props  = config_output,
 +        .request_frame = request_frame,
 +    },{
 +        .name          = "out2",
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +        .config_props  = config_output,
 +        .request_frame = request_frame,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_astreamsync = {
 +    .name          = "astreamsync",
 +    .description   = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
 +                                          "in a configurable order."),
 +    .priv_size     = sizeof(AStreamSyncContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .inputs        = astreamsync_inputs,
 +    .outputs       = astreamsync_outputs,
 +    .priv_class    = &astreamsync_class,
 +};
@@@ -309,13 -313,16 +309,13 @@@ static const AVFilterPad avfilter_af_as
      { NULL }
  };
  
- AVFilter avfilter_af_asyncts = {
+ AVFilter ff_af_asyncts = {
      .name        = "asyncts",
      .description = NULL_IF_CONFIG_SMALL("Sync audio data to timestamps"),
 -
      .init        = init,
      .uninit      = uninit,
 -
      .priv_size   = sizeof(ASyncContext),
 -    .priv_class  = &async_class,
 -
 +    .priv_class  = &asyncts_class,
      .inputs      = avfilter_af_asyncts_inputs,
      .outputs     = avfilter_af_asyncts_outputs,
  };
index ad12786,0000000..c474d6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1196 -1,0 +1,1196 @@@
- AVFilter avfilter_af_atempo = {
 +/*
 + * Copyright (c) 2012 Pavel Koshevoy <pkoshevoy at gmail dot com>
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * tempo scaling audio filter -- an implementation of WSOLA algorithm
 + *
 + * Based on MIT licensed yaeAudioTempoFilter.h and yaeAudioFragment.h
 + * from Apprentice Video player by Pavel Koshevoy.
 + * https://sourceforge.net/projects/apprenticevideo/
 + *
 + * An explanation of SOLA algorithm is available at
 + * http://www.surina.net/article/time-and-pitch-scaling.html
 + *
 + * WSOLA is very similar to SOLA, only one major difference exists between
 + * these algorithms.  SOLA shifts audio fragments along the output stream,
 + * where as WSOLA shifts audio fragments along the input stream.
 + *
 + * The advantage of WSOLA algorithm is that the overlap region size is
 + * always the same, therefore the blending function is constant and
 + * can be precomputed.
 + */
 +
 +#include <float.h>
 +#include "libavcodec/avfft.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/eval.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +/**
 + * A fragment of audio waveform
 + */
 +typedef struct {
 +    // index of the first sample of this fragment in the overall waveform;
 +    // 0: input sample position
 +    // 1: output sample position
 +    int64_t position[2];
 +
 +    // original packed multi-channel samples:
 +    uint8_t *data;
 +
 +    // number of samples in this fragment:
 +    int nsamples;
 +
 +    // rDFT transform of the down-mixed mono fragment, used for
 +    // fast waveform alignment via correlation in frequency domain:
 +    FFTSample *xdat;
 +} AudioFragment;
 +
 +/**
 + * Filter state machine states
 + */
 +typedef enum {
 +    YAE_LOAD_FRAGMENT,
 +    YAE_ADJUST_POSITION,
 +    YAE_RELOAD_FRAGMENT,
 +    YAE_OUTPUT_OVERLAP_ADD,
 +    YAE_FLUSH_OUTPUT,
 +} FilterState;
 +
 +/**
 + * Filter state machine
 + */
 +typedef struct {
 +    const AVClass *class;
 +
 +    // ring-buffer of input samples, necessary because some times
 +    // input fragment position may be adjusted backwards:
 +    uint8_t *buffer;
 +
 +    // ring-buffer maximum capacity, expressed in sample rate time base:
 +    int ring;
 +
 +    // ring-buffer house keeping:
 +    int size;
 +    int head;
 +    int tail;
 +
 +    // 0: input sample position corresponding to the ring buffer tail
 +    // 1: output sample position
 +    int64_t position[2];
 +
 +    // sample format:
 +    enum AVSampleFormat format;
 +
 +    // number of channels:
 +    int channels;
 +
 +    // row of bytes to skip from one sample to next, across multple channels;
 +    // stride = (number-of-channels * bits-per-sample-per-channel) / 8
 +    int stride;
 +
 +    // fragment window size, power-of-two integer:
 +    int window;
 +
 +    // Hann window coefficients, for feathering
 +    // (blending) the overlapping fragment region:
 +    float *hann;
 +
 +    // tempo scaling factor:
 +    double tempo;
 +
 +    // a snapshot of previous fragment input and output position values
 +    // captured when the tempo scale factor was set most recently:
 +    int64_t origin[2];
 +
 +    // current/previous fragment ring-buffer:
 +    AudioFragment frag[2];
 +
 +    // current fragment index:
 +    uint64_t nfrag;
 +
 +    // current state:
 +    FilterState state;
 +
 +    // for fast correlation calculation in frequency domain:
 +    RDFTContext *real_to_complex;
 +    RDFTContext *complex_to_real;
 +    FFTSample *correlation;
 +
 +    // for managing AVFilterPad.request_frame and AVFilterPad.filter_frame
 +    AVFrame *dst_buffer;
 +    uint8_t *dst;
 +    uint8_t *dst_end;
 +    uint64_t nsamples_in;
 +    uint64_t nsamples_out;
 +} ATempoContext;
 +
 +#define OFFSET(x) offsetof(ATempoContext, x)
 +
 +static const AVOption atempo_options[] = {
 +    { "tempo", "set tempo scale factor",
 +      OFFSET(tempo), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0.5, 2.0,
 +      AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(atempo);
 +
 +inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
 +{
 +    return &atempo->frag[atempo->nfrag % 2];
 +}
 +
 +inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
 +{
 +    return &atempo->frag[(atempo->nfrag + 1) % 2];
 +}
 +
 +/**
 + * Reset filter to initial state, do not deallocate existing local buffers.
 + */
 +static void yae_clear(ATempoContext *atempo)
 +{
 +    atempo->size = 0;
 +    atempo->head = 0;
 +    atempo->tail = 0;
 +
 +    atempo->nfrag = 0;
 +    atempo->state = YAE_LOAD_FRAGMENT;
 +
 +    atempo->position[0] = 0;
 +    atempo->position[1] = 0;
 +
 +    atempo->origin[0] = 0;
 +    atempo->origin[1] = 0;
 +
 +    atempo->frag[0].position[0] = 0;
 +    atempo->frag[0].position[1] = 0;
 +    atempo->frag[0].nsamples    = 0;
 +
 +    atempo->frag[1].position[0] = 0;
 +    atempo->frag[1].position[1] = 0;
 +    atempo->frag[1].nsamples    = 0;
 +
 +    // shift left position of 1st fragment by half a window
 +    // so that no re-normalization would be required for
 +    // the left half of the 1st fragment:
 +    atempo->frag[0].position[0] = -(int64_t)(atempo->window / 2);
 +    atempo->frag[0].position[1] = -(int64_t)(atempo->window / 2);
 +
 +    av_frame_free(&atempo->dst_buffer);
 +    atempo->dst     = NULL;
 +    atempo->dst_end = NULL;
 +
 +    atempo->nsamples_in       = 0;
 +    atempo->nsamples_out      = 0;
 +}
 +
 +/**
 + * Reset filter to initial state and deallocate all buffers.
 + */
 +static void yae_release_buffers(ATempoContext *atempo)
 +{
 +    yae_clear(atempo);
 +
 +    av_freep(&atempo->frag[0].data);
 +    av_freep(&atempo->frag[1].data);
 +    av_freep(&atempo->frag[0].xdat);
 +    av_freep(&atempo->frag[1].xdat);
 +
 +    av_freep(&atempo->buffer);
 +    av_freep(&atempo->hann);
 +    av_freep(&atempo->correlation);
 +
 +    av_rdft_end(atempo->real_to_complex);
 +    atempo->real_to_complex = NULL;
 +
 +    av_rdft_end(atempo->complex_to_real);
 +    atempo->complex_to_real = NULL;
 +}
 +
 +/* av_realloc is not aligned enough; fortunately, the data does not need to
 + * be preserved */
 +#define RE_MALLOC_OR_FAIL(field, field_size)                    \
 +    do {                                                        \
 +        av_freep(&field);                                       \
 +        field = av_malloc(field_size);                          \
 +        if (!field) {                                           \
 +            yae_release_buffers(atempo);                        \
 +            return AVERROR(ENOMEM);                             \
 +        }                                                       \
 +    } while (0)
 +
 +/**
 + * Prepare filter for processing audio data of given format,
 + * sample rate and number of channels.
 + */
 +static int yae_reset(ATempoContext *atempo,
 +                     enum AVSampleFormat format,
 +                     int sample_rate,
 +                     int channels)
 +{
 +    const int sample_size = av_get_bytes_per_sample(format);
 +    uint32_t nlevels  = 0;
 +    uint32_t pot;
 +    int i;
 +
 +    atempo->format   = format;
 +    atempo->channels = channels;
 +    atempo->stride   = sample_size * channels;
 +
 +    // pick a segment window size:
 +    atempo->window = sample_rate / 24;
 +
 +    // adjust window size to be a power-of-two integer:
 +    nlevels = av_log2(atempo->window);
 +    pot = 1 << nlevels;
 +    av_assert0(pot <= atempo->window);
 +
 +    if (pot < atempo->window) {
 +        atempo->window = pot * 2;
 +        nlevels++;
 +    }
 +
 +    // initialize audio fragment buffers:
 +    RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window * atempo->stride);
 +    RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window * atempo->stride);
 +    RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, atempo->window * sizeof(FFTComplex));
 +    RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, atempo->window * sizeof(FFTComplex));
 +
 +    // initialize rDFT contexts:
 +    av_rdft_end(atempo->real_to_complex);
 +    atempo->real_to_complex = NULL;
 +
 +    av_rdft_end(atempo->complex_to_real);
 +    atempo->complex_to_real = NULL;
 +
 +    atempo->real_to_complex = av_rdft_init(nlevels + 1, DFT_R2C);
 +    if (!atempo->real_to_complex) {
 +        yae_release_buffers(atempo);
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    atempo->complex_to_real = av_rdft_init(nlevels + 1, IDFT_C2R);
 +    if (!atempo->complex_to_real) {
 +        yae_release_buffers(atempo);
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(FFTComplex));
 +
 +    atempo->ring = atempo->window * 3;
 +    RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring * atempo->stride);
 +
 +    // initialize the Hann window function:
 +    RE_MALLOC_OR_FAIL(atempo->hann, atempo->window * sizeof(float));
 +
 +    for (i = 0; i < atempo->window; i++) {
 +        double t = (double)i / (double)(atempo->window - 1);
 +        double h = 0.5 * (1.0 - cos(2.0 * M_PI * t));
 +        atempo->hann[i] = (float)h;
 +    }
 +
 +    yae_clear(atempo);
 +    return 0;
 +}
 +
 +static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
 +{
 +    const AudioFragment *prev;
 +    ATempoContext *atempo = ctx->priv;
 +    char   *tail = NULL;
 +    double tempo = av_strtod(arg_tempo, &tail);
 +
 +    if (tail && *tail) {
 +        av_log(ctx, AV_LOG_ERROR, "Invalid tempo value '%s'\n", arg_tempo);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (tempo < 0.5 || tempo > 2.0) {
 +        av_log(ctx, AV_LOG_ERROR, "Tempo value %f exceeds [0.5, 2.0] range\n",
 +               tempo);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    prev = yae_prev_frag(atempo);
 +    atempo->origin[0] = prev->position[0] + atempo->window / 2;
 +    atempo->origin[1] = prev->position[1] + atempo->window / 2;
 +    atempo->tempo = tempo;
 +    return 0;
 +}
 +
 +/**
 + * A helper macro for initializing complex data buffer with scalar data
 + * of a given type.
 + */
 +#define yae_init_xdat(scalar_type, scalar_max)                          \
 +    do {                                                                \
 +        const uint8_t *src_end = src +                                  \
 +            frag->nsamples * atempo->channels * sizeof(scalar_type);    \
 +                                                                        \
 +        FFTSample *xdat = frag->xdat;                                   \
 +        scalar_type tmp;                                                \
 +                                                                        \
 +        if (atempo->channels == 1) {                                    \
 +            for (; src < src_end; xdat++) {                             \
 +                tmp = *(const scalar_type *)src;                        \
 +                src += sizeof(scalar_type);                             \
 +                                                                        \
 +                *xdat = (FFTSample)tmp;                                 \
 +            }                                                           \
 +        } else {                                                        \
 +            FFTSample s, max, ti, si;                                   \
 +            int i;                                                      \
 +                                                                        \
 +            for (; src < src_end; xdat++) {                             \
 +                tmp = *(const scalar_type *)src;                        \
 +                src += sizeof(scalar_type);                             \
 +                                                                        \
 +                max = (FFTSample)tmp;                                   \
 +                s = FFMIN((FFTSample)scalar_max,                        \
 +                          (FFTSample)fabsf(max));                       \
 +                                                                        \
 +                for (i = 1; i < atempo->channels; i++) {                \
 +                    tmp = *(const scalar_type *)src;                    \
 +                    src += sizeof(scalar_type);                         \
 +                                                                        \
 +                    ti = (FFTSample)tmp;                                \
 +                    si = FFMIN((FFTSample)scalar_max,                   \
 +                               (FFTSample)fabsf(ti));                   \
 +                                                                        \
 +                    if (s < si) {                                       \
 +                        s   = si;                                       \
 +                        max = ti;                                       \
 +                    }                                                   \
 +                }                                                       \
 +                                                                        \
 +                *xdat = max;                                            \
 +            }                                                           \
 +        }                                                               \
 +    } while (0)
 +
 +/**
 + * Initialize complex data buffer of a given audio fragment
 + * with down-mixed mono data of appropriate scalar type.
 + */
 +static void yae_downmix(ATempoContext *atempo, AudioFragment *frag)
 +{
 +    // shortcuts:
 +    const uint8_t *src = frag->data;
 +
 +    // init complex data buffer used for FFT and Correlation:
 +    memset(frag->xdat, 0, sizeof(FFTComplex) * atempo->window);
 +
 +    if (atempo->format == AV_SAMPLE_FMT_U8) {
 +        yae_init_xdat(uint8_t, 127);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S16) {
 +        yae_init_xdat(int16_t, 32767);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S32) {
 +        yae_init_xdat(int, 2147483647);
 +    } else if (atempo->format == AV_SAMPLE_FMT_FLT) {
 +        yae_init_xdat(float, 1);
 +    } else if (atempo->format == AV_SAMPLE_FMT_DBL) {
 +        yae_init_xdat(double, 1);
 +    }
 +}
 +
 +/**
 + * Populate the internal data buffer on as-needed basis.
 + *
 + * @return
 + *   0 if requested data was already available or was successfully loaded,
 + *   AVERROR(EAGAIN) if more input data is required.
 + */
 +static int yae_load_data(ATempoContext *atempo,
 +                         const uint8_t **src_ref,
 +                         const uint8_t *src_end,
 +                         int64_t stop_here)
 +{
 +    // shortcut:
 +    const uint8_t *src = *src_ref;
 +    const int read_size = stop_here - atempo->position[0];
 +
 +    if (stop_here <= atempo->position[0]) {
 +        return 0;
 +    }
 +
 +    // samples are not expected to be skipped:
 +    av_assert0(read_size <= atempo->ring);
 +
 +    while (atempo->position[0] < stop_here && src < src_end) {
 +        int src_samples = (src_end - src) / atempo->stride;
 +
 +        // load data piece-wise, in order to avoid complicating the logic:
 +        int nsamples = FFMIN(read_size, src_samples);
 +        int na;
 +        int nb;
 +
 +        nsamples = FFMIN(nsamples, atempo->ring);
 +        na = FFMIN(nsamples, atempo->ring - atempo->tail);
 +        nb = FFMIN(nsamples - na, atempo->ring);
 +
 +        if (na) {
 +            uint8_t *a = atempo->buffer + atempo->tail * atempo->stride;
 +            memcpy(a, src, na * atempo->stride);
 +
 +            src += na * atempo->stride;
 +            atempo->position[0] += na;
 +
 +            atempo->size = FFMIN(atempo->size + na, atempo->ring);
 +            atempo->tail = (atempo->tail + na) % atempo->ring;
 +            atempo->head =
 +                atempo->size < atempo->ring ?
 +                atempo->tail - atempo->size :
 +                atempo->tail;
 +        }
 +
 +        if (nb) {
 +            uint8_t *b = atempo->buffer;
 +            memcpy(b, src, nb * atempo->stride);
 +
 +            src += nb * atempo->stride;
 +            atempo->position[0] += nb;
 +
 +            atempo->size = FFMIN(atempo->size + nb, atempo->ring);
 +            atempo->tail = (atempo->tail + nb) % atempo->ring;
 +            atempo->head =
 +                atempo->size < atempo->ring ?
 +                atempo->tail - atempo->size :
 +                atempo->tail;
 +        }
 +    }
 +
 +    // pass back the updated source buffer pointer:
 +    *src_ref = src;
 +
 +    // sanity check:
 +    av_assert0(atempo->position[0] <= stop_here);
 +
 +    return atempo->position[0] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
 +/**
 + * Populate current audio fragment data buffer.
 + *
 + * @return
 + *   0 when the fragment is ready,
 + *   AVERROR(EAGAIN) if more input data is required.
 + */
 +static int yae_load_frag(ATempoContext *atempo,
 +                         const uint8_t **src_ref,
 +                         const uint8_t *src_end)
 +{
 +    // shortcuts:
 +    AudioFragment *frag = yae_curr_frag(atempo);
 +    uint8_t *dst;
 +    int64_t missing, start, zeros;
 +    uint32_t nsamples;
 +    const uint8_t *a, *b;
 +    int i0, i1, n0, n1, na, nb;
 +
 +    int64_t stop_here = frag->position[0] + atempo->window;
 +    if (src_ref && yae_load_data(atempo, src_ref, src_end, stop_here) != 0) {
 +        return AVERROR(EAGAIN);
 +    }
 +
 +    // calculate the number of samples we don't have:
 +    missing =
 +        stop_here > atempo->position[0] ?
 +        stop_here - atempo->position[0] : 0;
 +
 +    nsamples =
 +        missing < (int64_t)atempo->window ?
 +        (uint32_t)(atempo->window - missing) : 0;
 +
 +    // setup the output buffer:
 +    frag->nsamples = nsamples;
 +    dst = frag->data;
 +
 +    start = atempo->position[0] - atempo->size;
 +    zeros = 0;
 +
 +    if (frag->position[0] < start) {
 +        // what we don't have we substitute with zeros:
 +        zeros = FFMIN(start - frag->position[0], (int64_t)nsamples);
 +        av_assert0(zeros != nsamples);
 +
 +        memset(dst, 0, zeros * atempo->stride);
 +        dst += zeros * atempo->stride;
 +    }
 +
 +    if (zeros == nsamples) {
 +        return 0;
 +    }
 +
 +    // get the remaining data from the ring buffer:
 +    na = (atempo->head < atempo->tail ?
 +          atempo->tail - atempo->head :
 +          atempo->ring - atempo->head);
 +
 +    nb = atempo->head < atempo->tail ? 0 : atempo->tail;
 +
 +    // sanity check:
 +    av_assert0(nsamples <= zeros + na + nb);
 +
 +    a = atempo->buffer + atempo->head * atempo->stride;
 +    b = atempo->buffer;
 +
 +    i0 = frag->position[0] + zeros - start;
 +    i1 = i0 < na ? 0 : i0 - na;
 +
 +    n0 = i0 < na ? FFMIN(na - i0, (int)(nsamples - zeros)) : 0;
 +    n1 = nsamples - zeros - n0;
 +
 +    if (n0) {
 +        memcpy(dst, a + i0 * atempo->stride, n0 * atempo->stride);
 +        dst += n0 * atempo->stride;
 +    }
 +
 +    if (n1) {
 +        memcpy(dst, b + i1 * atempo->stride, n1 * atempo->stride);
 +    }
 +
 +    return 0;
 +}
 +
 +/**
 + * Prepare for loading next audio fragment.
 + */
 +static void yae_advance_to_next_frag(ATempoContext *atempo)
 +{
 +    const double fragment_step = atempo->tempo * (double)(atempo->window / 2);
 +
 +    const AudioFragment *prev;
 +    AudioFragment       *frag;
 +
 +    atempo->nfrag++;
 +    prev = yae_prev_frag(atempo);
 +    frag = yae_curr_frag(atempo);
 +
 +    frag->position[0] = prev->position[0] + (int64_t)fragment_step;
 +    frag->position[1] = prev->position[1] + atempo->window / 2;
 +    frag->nsamples    = 0;
 +}
 +
 +/**
 + * Calculate cross-correlation via rDFT.
 + *
 + * Multiply two vectors of complex numbers (result of real_to_complex rDFT)
 + * and transform back via complex_to_real rDFT.
 + */
 +static void yae_xcorr_via_rdft(FFTSample *xcorr,
 +                               RDFTContext *complex_to_real,
 +                               const FFTComplex *xa,
 +                               const FFTComplex *xb,
 +                               const int window)
 +{
 +    FFTComplex *xc = (FFTComplex *)xcorr;
 +    int i;
 +
 +    // NOTE: first element requires special care -- Given Y = rDFT(X),
 +    // Im(Y[0]) and Im(Y[N/2]) are always zero, therefore av_rdft_calc
 +    // stores Re(Y[N/2]) in place of Im(Y[0]).
 +
 +    xc->re = xa->re * xb->re;
 +    xc->im = xa->im * xb->im;
 +    xa++;
 +    xb++;
 +    xc++;
 +
 +    for (i = 1; i < window; i++, xa++, xb++, xc++) {
 +        xc->re = (xa->re * xb->re + xa->im * xb->im);
 +        xc->im = (xa->im * xb->re - xa->re * xb->im);
 +    }
 +
 +    // apply inverse rDFT:
 +    av_rdft_calc(complex_to_real, xcorr);
 +}
 +
 +/**
 + * Calculate alignment offset for given fragment
 + * relative to the previous fragment.
 + *
 + * @return alignment offset of current fragment relative to previous.
 + */
 +static int yae_align(AudioFragment *frag,
 +                     const AudioFragment *prev,
 +                     const int window,
 +                     const int delta_max,
 +                     const int drift,
 +                     FFTSample *correlation,
 +                     RDFTContext *complex_to_real)
 +{
 +    int       best_offset = -drift;
 +    FFTSample best_metric = -FLT_MAX;
 +    FFTSample *xcorr;
 +
 +    int i0;
 +    int i1;
 +    int i;
 +
 +    yae_xcorr_via_rdft(correlation,
 +                       complex_to_real,
 +                       (const FFTComplex *)prev->xdat,
 +                       (const FFTComplex *)frag->xdat,
 +                       window);
 +
 +    // identify search window boundaries:
 +    i0 = FFMAX(window / 2 - delta_max - drift, 0);
 +    i0 = FFMIN(i0, window);
 +
 +    i1 = FFMIN(window / 2 + delta_max - drift, window - window / 16);
 +    i1 = FFMAX(i1, 0);
 +
 +    // identify cross-correlation peaks within search window:
 +    xcorr = correlation + i0;
 +
 +    for (i = i0; i < i1; i++, xcorr++) {
 +        FFTSample metric = *xcorr;
 +
 +        // normalize:
 +        FFTSample drifti = (FFTSample)(drift + i);
 +        metric *= drifti * (FFTSample)(i - i0) * (FFTSample)(i1 - i);
 +
 +        if (metric > best_metric) {
 +            best_metric = metric;
 +            best_offset = i - window / 2;
 +        }
 +    }
 +
 +    return best_offset;
 +}
 +
 +/**
 + * Adjust current fragment position for better alignment
 + * with previous fragment.
 + *
 + * @return alignment correction.
 + */
 +static int yae_adjust_position(ATempoContext *atempo)
 +{
 +    const AudioFragment *prev = yae_prev_frag(atempo);
 +    AudioFragment       *frag = yae_curr_frag(atempo);
 +
 +    const double prev_output_position =
 +        (double)(prev->position[1] - atempo->origin[1] + atempo->window / 2);
 +
 +    const double ideal_output_position =
 +        (double)(prev->position[0] - atempo->origin[0] + atempo->window / 2) /
 +        atempo->tempo;
 +
 +    const int drift = (int)(prev_output_position - ideal_output_position);
 +
 +    const int delta_max  = atempo->window / 2;
 +    const int correction = yae_align(frag,
 +                                     prev,
 +                                     atempo->window,
 +                                     delta_max,
 +                                     drift,
 +                                     atempo->correlation,
 +                                     atempo->complex_to_real);
 +
 +    if (correction) {
 +        // adjust fragment position:
 +        frag->position[0] -= correction;
 +
 +        // clear so that the fragment can be reloaded:
 +        frag->nsamples = 0;
 +    }
 +
 +    return correction;
 +}
 +
 +/**
 + * A helper macro for blending the overlap region of previous
 + * and current audio fragment.
 + */
 +#define yae_blend(scalar_type)                                          \
 +    do {                                                                \
 +        const scalar_type *aaa = (const scalar_type *)a;                \
 +        const scalar_type *bbb = (const scalar_type *)b;                \
 +                                                                        \
 +        scalar_type *out     = (scalar_type *)dst;                      \
 +        scalar_type *out_end = (scalar_type *)dst_end;                  \
 +        int64_t i;                                                      \
 +                                                                        \
 +        for (i = 0; i < overlap && out < out_end;                       \
 +             i++, atempo->position[1]++, wa++, wb++) {                  \
 +            float w0 = *wa;                                             \
 +            float w1 = *wb;                                             \
 +            int j;                                                      \
 +                                                                        \
 +            for (j = 0; j < atempo->channels;                           \
 +                 j++, aaa++, bbb++, out++) {                            \
 +                float t0 = (float)*aaa;                                 \
 +                float t1 = (float)*bbb;                                 \
 +                                                                        \
 +                *out =                                                  \
 +                    frag->position[0] + i < 0 ?                         \
 +                    *aaa :                                              \
 +                    (scalar_type)(t0 * w0 + t1 * w1);                   \
 +            }                                                           \
 +        }                                                               \
 +        dst = (uint8_t *)out;                                           \
 +    } while (0)
 +
 +/**
 + * Blend the overlap region of previous and current audio fragment
 + * and output the results to the given destination buffer.
 + *
 + * @return
 + *   0 if the overlap region was completely stored in the dst buffer,
 + *   AVERROR(EAGAIN) if more destination buffer space is required.
 + */
 +static int yae_overlap_add(ATempoContext *atempo,
 +                           uint8_t **dst_ref,
 +                           uint8_t *dst_end)
 +{
 +    // shortcuts:
 +    const AudioFragment *prev = yae_prev_frag(atempo);
 +    const AudioFragment *frag = yae_curr_frag(atempo);
 +
 +    const int64_t start_here = FFMAX(atempo->position[1],
 +                                     frag->position[1]);
 +
 +    const int64_t stop_here = FFMIN(prev->position[1] + prev->nsamples,
 +                                    frag->position[1] + frag->nsamples);
 +
 +    const int64_t overlap = stop_here - start_here;
 +
 +    const int64_t ia = start_here - prev->position[1];
 +    const int64_t ib = start_here - frag->position[1];
 +
 +    const float *wa = atempo->hann + ia;
 +    const float *wb = atempo->hann + ib;
 +
 +    const uint8_t *a = prev->data + ia * atempo->stride;
 +    const uint8_t *b = frag->data + ib * atempo->stride;
 +
 +    uint8_t *dst = *dst_ref;
 +
 +    av_assert0(start_here <= stop_here &&
 +               frag->position[1] <= start_here &&
 +               overlap <= frag->nsamples);
 +
 +    if (atempo->format == AV_SAMPLE_FMT_U8) {
 +        yae_blend(uint8_t);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S16) {
 +        yae_blend(int16_t);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S32) {
 +        yae_blend(int);
 +    } else if (atempo->format == AV_SAMPLE_FMT_FLT) {
 +        yae_blend(float);
 +    } else if (atempo->format == AV_SAMPLE_FMT_DBL) {
 +        yae_blend(double);
 +    }
 +
 +    // pass-back the updated destination buffer pointer:
 +    *dst_ref = dst;
 +
 +    return atempo->position[1] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
 +/**
 + * Feed as much data to the filter as it is able to consume
 + * and receive as much processed data in the destination buffer
 + * as it is able to produce or store.
 + */
 +static void
 +yae_apply(ATempoContext *atempo,
 +          const uint8_t **src_ref,
 +          const uint8_t *src_end,
 +          uint8_t **dst_ref,
 +          uint8_t *dst_end)
 +{
 +    while (1) {
 +        if (atempo->state == YAE_LOAD_FRAGMENT) {
 +            // load additional data for the current fragment:
 +            if (yae_load_frag(atempo, src_ref, src_end) != 0) {
 +                break;
 +            }
 +
 +            // down-mix to mono:
 +            yae_downmix(atempo, yae_curr_frag(atempo));
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat);
 +
 +            // must load the second fragment before alignment can start:
 +            if (!atempo->nfrag) {
 +                yae_advance_to_next_frag(atempo);
 +                continue;
 +            }
 +
 +            atempo->state = YAE_ADJUST_POSITION;
 +        }
 +
 +        if (atempo->state == YAE_ADJUST_POSITION) {
 +            // adjust position for better alignment:
 +            if (yae_adjust_position(atempo)) {
 +                // reload the fragment at the corrected position, so that the
 +                // Hann window blending would not require normalization:
 +                atempo->state = YAE_RELOAD_FRAGMENT;
 +            } else {
 +                atempo->state = YAE_OUTPUT_OVERLAP_ADD;
 +            }
 +        }
 +
 +        if (atempo->state == YAE_RELOAD_FRAGMENT) {
 +            // load additional data if necessary due to position adjustment:
 +            if (yae_load_frag(atempo, src_ref, src_end) != 0) {
 +                break;
 +            }
 +
 +            // down-mix to mono:
 +            yae_downmix(atempo, yae_curr_frag(atempo));
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat);
 +
 +            atempo->state = YAE_OUTPUT_OVERLAP_ADD;
 +        }
 +
 +        if (atempo->state == YAE_OUTPUT_OVERLAP_ADD) {
 +            // overlap-add and output the result:
 +            if (yae_overlap_add(atempo, dst_ref, dst_end) != 0) {
 +                break;
 +            }
 +
 +            // advance to the next fragment, repeat:
 +            yae_advance_to_next_frag(atempo);
 +            atempo->state = YAE_LOAD_FRAGMENT;
 +        }
 +    }
 +}
 +
 +/**
 + * Flush any buffered data from the filter.
 + *
 + * @return
 + *   0 if all data was completely stored in the dst buffer,
 + *   AVERROR(EAGAIN) if more destination buffer space is required.
 + */
 +static int yae_flush(ATempoContext *atempo,
 +                     uint8_t **dst_ref,
 +                     uint8_t *dst_end)
 +{
 +    AudioFragment *frag = yae_curr_frag(atempo);
 +    int64_t overlap_end;
 +    int64_t start_here;
 +    int64_t stop_here;
 +    int64_t offset;
 +
 +    const uint8_t *src;
 +    uint8_t *dst;
 +
 +    int src_size;
 +    int dst_size;
 +    int nbytes;
 +
 +    atempo->state = YAE_FLUSH_OUTPUT;
 +
 +    if (atempo->position[0] == frag->position[0] + frag->nsamples &&
 +        atempo->position[1] == frag->position[1] + frag->nsamples) {
 +        // the current fragment is already flushed:
 +        return 0;
 +    }
 +
 +    if (frag->position[0] + frag->nsamples < atempo->position[0]) {
 +        // finish loading the current (possibly partial) fragment:
 +        yae_load_frag(atempo, NULL, NULL);
 +
 +        if (atempo->nfrag) {
 +            // down-mix to mono:
 +            yae_downmix(atempo, frag);
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, frag->xdat);
 +
 +            // align current fragment to previous fragment:
 +            if (yae_adjust_position(atempo)) {
 +                // reload the current fragment due to adjusted position:
 +                yae_load_frag(atempo, NULL, NULL);
 +            }
 +        }
 +    }
 +
 +    // flush the overlap region:
 +    overlap_end = frag->position[1] + FFMIN(atempo->window / 2,
 +                                            frag->nsamples);
 +
 +    while (atempo->position[1] < overlap_end) {
 +        if (yae_overlap_add(atempo, dst_ref, dst_end) != 0) {
 +            return AVERROR(EAGAIN);
 +        }
 +    }
 +
 +    // flush the remaininder of the current fragment:
 +    start_here = FFMAX(atempo->position[1], overlap_end);
 +    stop_here  = frag->position[1] + frag->nsamples;
 +    offset     = start_here - frag->position[1];
 +    av_assert0(start_here <= stop_here && frag->position[1] <= start_here);
 +
 +    src = frag->data + offset * atempo->stride;
 +    dst = (uint8_t *)*dst_ref;
 +
 +    src_size = (int)(stop_here - start_here) * atempo->stride;
 +    dst_size = dst_end - dst;
 +    nbytes = FFMIN(src_size, dst_size);
 +
 +    memcpy(dst, src, nbytes);
 +    dst += nbytes;
 +
 +    atempo->position[1] += (nbytes / atempo->stride);
 +
 +    // pass-back the updated destination buffer pointer:
 +    *dst_ref = (uint8_t *)dst;
 +
 +    return atempo->position[1] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    ATempoContext *atempo = ctx->priv;
 +    atempo->format = AV_SAMPLE_FMT_NONE;
 +    atempo->state  = YAE_LOAD_FRAGMENT;
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    ATempoContext *atempo = ctx->priv;
 +    yae_release_buffers(atempo);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterChannelLayouts *layouts = NULL;
 +    AVFilterFormats        *formats = NULL;
 +
 +    // WSOLA necessitates an internal sliding window ring buffer
 +    // for incoming audio stream.
 +    //
 +    // Planar sample formats are too cumbersome to store in a ring buffer,
 +    // therefore planar sample formats are not supported.
 +    //
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_U8,
 +        AV_SAMPLE_FMT_S16,
 +        AV_SAMPLE_FMT_S32,
 +        AV_SAMPLE_FMT_FLT,
 +        AV_SAMPLE_FMT_DBL,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static int config_props(AVFilterLink *inlink)
 +{
 +    AVFilterContext  *ctx = inlink->dst;
 +    ATempoContext *atempo = ctx->priv;
 +
 +    enum AVSampleFormat format = inlink->format;
 +    int sample_rate = (int)inlink->sample_rate;
 +    int channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
 +
 +    ctx->outputs[0]->flags |= FF_LINK_FLAG_REQUEST_LOOP;
 +
 +    return yae_reset(atempo, format, sample_rate, channels);
 +}
 +
 +static int push_samples(ATempoContext *atempo,
 +                        AVFilterLink *outlink,
 +                        int n_out)
 +{
 +    int ret;
 +
 +    atempo->dst_buffer->sample_rate = outlink->sample_rate;
 +    atempo->dst_buffer->nb_samples  = n_out;
 +
 +    // adjust the PTS:
 +    atempo->dst_buffer->pts =
 +        av_rescale_q(atempo->nsamples_out,
 +                     (AVRational){ 1, outlink->sample_rate },
 +                     outlink->time_base);
 +
 +    ret = ff_filter_frame(outlink, atempo->dst_buffer);
 +    if (ret < 0)
 +        return ret;
 +    atempo->dst_buffer = NULL;
 +    atempo->dst        = NULL;
 +    atempo->dst_end    = NULL;
 +
 +    atempo->nsamples_out += n_out;
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *src_buffer)
 +{
 +    AVFilterContext  *ctx = inlink->dst;
 +    ATempoContext *atempo = ctx->priv;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +
 +    int ret = 0;
 +    int n_in = src_buffer->nb_samples;
 +    int n_out = (int)(0.5 + ((double)n_in) / atempo->tempo);
 +
 +    const uint8_t *src = src_buffer->data[0];
 +    const uint8_t *src_end = src + n_in * atempo->stride;
 +
 +    while (src < src_end) {
 +        if (!atempo->dst_buffer) {
 +            atempo->dst_buffer = ff_get_audio_buffer(outlink, n_out);
 +            if (!atempo->dst_buffer)
 +                return AVERROR(ENOMEM);
 +            av_frame_copy_props(atempo->dst_buffer, src_buffer);
 +
 +            atempo->dst = atempo->dst_buffer->data[0];
 +            atempo->dst_end = atempo->dst + n_out * atempo->stride;
 +        }
 +
 +        yae_apply(atempo, &src, src_end, &atempo->dst, atempo->dst_end);
 +
 +        if (atempo->dst == atempo->dst_end) {
 +            int n_samples = ((atempo->dst - atempo->dst_buffer->data[0]) /
 +                             atempo->stride);
 +            ret = push_samples(atempo, outlink, n_samples);
 +            if (ret < 0)
 +                goto end;
 +        }
 +    }
 +
 +    atempo->nsamples_in += n_in;
 +end:
 +    av_frame_free(&src_buffer);
 +    return ret;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext  *ctx = outlink->src;
 +    ATempoContext *atempo = ctx->priv;
 +    int ret;
 +
 +    ret = ff_request_frame(ctx->inputs[0]);
 +
 +    if (ret == AVERROR_EOF) {
 +        // flush the filter:
 +        int n_max = atempo->ring;
 +        int n_out;
 +        int err = AVERROR(EAGAIN);
 +
 +        while (err == AVERROR(EAGAIN)) {
 +            if (!atempo->dst_buffer) {
 +                atempo->dst_buffer = ff_get_audio_buffer(outlink, n_max);
 +                if (!atempo->dst_buffer)
 +                    return AVERROR(ENOMEM);
 +
 +                atempo->dst = atempo->dst_buffer->data[0];
 +                atempo->dst_end = atempo->dst + n_max * atempo->stride;
 +            }
 +
 +            err = yae_flush(atempo, &atempo->dst, atempo->dst_end);
 +
 +            n_out = ((atempo->dst - atempo->dst_buffer->data[0]) /
 +                     atempo->stride);
 +
 +            if (n_out) {
 +                ret = push_samples(atempo, outlink, n_out);
 +            }
 +        }
 +
 +        av_frame_free(&atempo->dst_buffer);
 +        atempo->dst     = NULL;
 +        atempo->dst_end = NULL;
 +
 +        return AVERROR_EOF;
 +    }
 +
 +    return ret;
 +}
 +
 +static int process_command(AVFilterContext *ctx,
 +                           const char *cmd,
 +                           const char *arg,
 +                           char *res,
 +                           int res_len,
 +                           int flags)
 +{
 +    return !strcmp(cmd, "tempo") ? yae_set_tempo(ctx, arg) : AVERROR(ENOSYS);
 +}
 +
 +static const AVFilterPad atempo_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +        .config_props = config_props,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad atempo_outputs[] = {
 +    {
 +        .name          = "default",
 +        .request_frame = request_frame,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_atempo = {
 +    .name            = "atempo",
 +    .description     = NULL_IF_CONFIG_SMALL("Adjust audio tempo."),
 +    .init            = init,
 +    .uninit          = uninit,
 +    .query_formats   = query_formats,
 +    .process_command = process_command,
 +    .priv_size       = sizeof(ATempoContext),
 +    .priv_class      = &atempo_class,
 +    .inputs          = atempo_inputs,
 +    .outputs         = atempo_outputs,
 +};
index 7d7f9f3,0000000..5bafad1
mode 100644,000000..100644
--- /dev/null
@@@ -1,620 -1,0 +1,620 @@@
- AVFilter avfilter_af_##name_ = {                         \
 +/*
 + * Copyright (c) 2013 Paul B Mahol
 + * Copyright (c) 2006-2008 Rob Sykes <robs@users.sourceforge.net>
 + *
 + * 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
 + */
 +
 +/*
 + * 2-pole filters designed by Robert Bristow-Johnson <rbj@audioimagination.com>
 + *   see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
 + *
 + * 1-pole filters based on code (c) 2000 Chris Bagwell <cbagwell@sprynet.com>
 + *   Algorithms: Recursive single pole low/high pass filter
 + *   Reference: The Scientist and Engineer's Guide to Digital Signal Processing
 + *
 + *   low-pass: output[N] = input[N] * A + output[N-1] * B
 + *     X = exp(-2.0 * pi * Fc)
 + *     A = 1 - X
 + *     B = X
 + *     Fc = cutoff freq / sample rate
 + *
 + *     Mimics an RC low-pass filter:
 + *
 + *     ---/\/\/\/\----------->
 + *                   |
 + *                  --- C
 + *                  ---
 + *                   |
 + *                   |
 + *                   V
 + *
 + *   high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
 + *     X  = exp(-2.0 * pi * Fc)
 + *     A0 = (1 + X) / 2
 + *     A1 = -(1 + X) / 2
 + *     B1 = X
 + *     Fc = cutoff freq / sample rate
 + *
 + *     Mimics an RC high-pass filter:
 + *
 + *         || C
 + *     ----||--------->
 + *         ||    |
 + *               <
 + *               > R
 + *               <
 + *               |
 + *               V
 + */
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/opt.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +enum FilterType {
 +    biquad,
 +    equalizer,
 +    bass,
 +    treble,
 +    band,
 +    bandpass,
 +    bandreject,
 +    allpass,
 +    highpass,
 +    lowpass,
 +};
 +
 +enum WidthType {
 +    NONE,
 +    HERTZ,
 +    OCTAVE,
 +    QFACTOR,
 +    SLOPE,
 +};
 +
 +typedef struct ChanCache {
 +    double i1, i2;
 +    double o1, o2;
 +} ChanCache;
 +
 +typedef struct {
 +    const AVClass *class;
 +
 +    enum FilterType filter_type;
 +    enum WidthType width_type;
 +    int poles;
 +    int csg;
 +
 +    double gain;
 +    double frequency;
 +    double width;
 +
 +    double a0, a1, a2;
 +    double b0, b1, b2;
 +
 +    ChanCache *cache;
 +
 +    void (*filter)(const void *ibuf, void *obuf, int len,
 +                   double *i1, double *i2, double *o1, double *o2,
 +                   double b0, double b1, double b2, double a1, double a2);
 +} BiquadsContext;
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    BiquadsContext *p = ctx->priv;
 +
 +    if (p->filter_type != biquad) {
 +        if (p->frequency <= 0 || p->width <= 0) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid frequency %f and/or width %f <= 0\n",
 +                   p->frequency, p->width);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_S16P,
 +        AV_SAMPLE_FMT_S32P,
 +        AV_SAMPLE_FMT_FLTP,
 +        AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +#define BIQUAD_FILTER(name, type, min, max)                                   \
 +static void biquad_## name (const void *input, void *output, int len,         \
 +                            double *in1, double *in2,                         \
 +                            double *out1, double *out2,                       \
 +                            double b0, double b1, double b2,                  \
 +                            double a1, double a2)                             \
 +{                                                                             \
 +    const type *ibuf = input;                                                 \
 +    type *obuf = output;                                                      \
 +    double i1 = *in1;                                                         \
 +    double i2 = *in2;                                                         \
 +    double o1 = *out1;                                                        \
 +    double o2 = *out2;                                                        \
 +    int i;                                                                    \
 +    a1 = -a1;                                                                 \
 +    a2 = -a2;                                                                 \
 +                                                                              \
 +    for (i = 0; i+1 < len; i++) {                                             \
 +        o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1;            \
 +        i2 = ibuf[i];                                                         \
 +        if (o2 < min) {                                                       \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = min;                                                    \
 +        } else if (o2 > max) {                                                \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = max;                                                    \
 +        } else {                                                              \
 +            obuf[i] = o2;                                                     \
 +        }                                                                     \
 +        i++;                                                                  \
 +        o1 = i1 * b2 + i2 * b1 + ibuf[i] * b0 + o1 * a2 + o2 * a1;            \
 +        i1 = ibuf[i];                                                         \
 +        if (o1 < min) {                                                       \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = min;                                                    \
 +        } else if (o1 > max) {                                                \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = max;                                                    \
 +        } else {                                                              \
 +            obuf[i] = o1;                                                     \
 +        }                                                                     \
 +    }                                                                         \
 +    if (i < len) {                                                            \
 +        double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2;     \
 +        i2 = i1;                                                              \
 +        i1 = ibuf[i];                                                         \
 +        o2 = o1;                                                              \
 +        o1 = o0;                                                              \
 +        if (o0 < min) {                                                       \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = min;                                                    \
 +        } else if (o0 > max) {                                                \
 +            av_log(NULL, AV_LOG_WARNING, "clipping\n");                       \
 +            obuf[i] = max;                                                    \
 +        } else {                                                              \
 +            obuf[i] = o0;                                                     \
 +        }                                                                     \
 +    }                                                                         \
 +    *in1  = i1;                                                               \
 +    *in2  = i2;                                                               \
 +    *out1 = o1;                                                               \
 +    *out2 = o2;                                                               \
 +}
 +
 +BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX)
 +BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX)
 +BIQUAD_FILTER(flt, float,   -1., 1.)
 +BIQUAD_FILTER(dbl, double,  -1., 1.)
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx    = outlink->src;
 +    BiquadsContext *p       = ctx->priv;
 +    AVFilterLink *inlink    = ctx->inputs[0];
 +    double A = exp(p->gain / 40 * log(10.));
 +    double w0 = 2 * M_PI * p->frequency / inlink->sample_rate;
 +    double alpha;
 +
 +    if (w0 > M_PI) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "Invalid frequency %f. Frequency must be less than half the sample-rate %d.\n",
 +               p->frequency, inlink->sample_rate);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    switch (p->width_type) {
 +    case NONE:
 +        alpha = 0.0;
 +        break;
 +    case HERTZ:
 +        alpha = sin(w0) / (2 * p->frequency / p->width);
 +        break;
 +    case OCTAVE:
 +        alpha = sin(w0) * sinh(log(2.) / 2 * p->width * w0 / sin(w0));
 +        break;
 +    case QFACTOR:
 +        alpha = sin(w0) / (2 * p->width);
 +        break;
 +    case SLOPE:
 +        alpha = sin(w0) / 2 * sqrt((A + 1 / A) * (1 / p->width - 1) + 2);
 +        break;
 +    default:
 +        av_assert0(0);
 +    }
 +
 +    switch (p->filter_type) {
 +    case biquad:
 +        break;
 +    case equalizer:
 +        p->a0 =   1 + alpha / A;
 +        p->a1 =  -2 * cos(w0);
 +        p->a2 =   1 - alpha / A;
 +        p->b0 =   1 + alpha * A;
 +        p->b1 =  -2 * cos(w0);
 +        p->b2 =   1 - alpha * A;
 +        break;
 +    case bass:
 +        p->a0 =          (A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha;
 +        p->a1 =    -2 * ((A - 1) + (A + 1) * cos(w0));
 +        p->a2 =          (A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha;
 +        p->b0 =     A * ((A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha);
 +        p->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0));
 +        p->b2 =     A * ((A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha);
 +        break;
 +    case treble:
 +        p->a0 =          (A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha;
 +        p->a1 =     2 * ((A - 1) - (A + 1) * cos(w0));
 +        p->a2 =          (A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha;
 +        p->b0 =     A * ((A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha);
 +        p->b1 =-2 * A * ((A - 1) + (A + 1) * cos(w0));
 +        p->b2 =     A * ((A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha);
 +        break;
 +    case bandpass:
 +        if (p->csg) {
 +            p->a0 =  1 + alpha;
 +            p->a1 = -2 * cos(w0);
 +            p->a2 =  1 - alpha;
 +            p->b0 =  sin(w0) / 2;
 +            p->b1 =  0;
 +            p->b2 = -sin(w0) / 2;
 +        } else {
 +            p->a0 =  1 + alpha;
 +            p->a1 = -2 * cos(w0);
 +            p->a2 =  1 - alpha;
 +            p->b0 =  alpha;
 +            p->b1 =  0;
 +            p->b2 = -alpha;
 +        }
 +        break;
 +    case bandreject:
 +        p->a0 =  1 + alpha;
 +        p->a1 = -2 * cos(w0);
 +        p->a2 =  1 - alpha;
 +        p->b0 =  1;
 +        p->b1 = -2 * cos(w0);
 +        p->b2 =  1;
 +        break;
 +    case lowpass:
 +        if (p->poles == 1) {
 +            p->a0 = 1;
 +            p->a1 = -exp(-w0);
 +            p->a2 = 0;
 +            p->b0 = 1 + p->a1;
 +            p->b1 = 0;
 +            p->b2 = 0;
 +        } else {
 +            p->a0 =  1 + alpha;
 +            p->a1 = -2 * cos(w0);
 +            p->a2 =  1 - alpha;
 +            p->b0 = (1 - cos(w0)) / 2;
 +            p->b1 =  1 - cos(w0);
 +            p->b2 = (1 - cos(w0)) / 2;
 +        }
 +        break;
 +    case highpass:
 +        if (p->poles == 1) {
 +            p->a0 = 1;
 +            p->a1 = -exp(-w0);
 +            p->a2 = 0;
 +            p->b0 = (1 - p->a1) / 2;
 +            p->b1 = -p->b0;
 +            p->b2 = 0;
 +        } else {
 +            p->a0 =   1 + alpha;
 +            p->a1 =  -2 * cos(w0);
 +            p->a2 =   1 - alpha;
 +            p->b0 =  (1 + cos(w0)) / 2;
 +            p->b1 = -(1 + cos(w0));
 +            p->b2 =  (1 + cos(w0)) / 2;
 +        }
 +        break;
 +    case allpass:
 +        p->a0 =  1 + alpha;
 +        p->a1 = -2 * cos(w0);
 +        p->a2 =  1 - alpha;
 +        p->b0 =  1 - alpha;
 +        p->b1 = -2 * cos(w0);
 +        p->b2 =  1 + alpha;
 +        break;
 +    default:
 +        av_assert0(0);
 +    }
 +
 +    p->a1 /= p->a0;
 +    p->a2 /= p->a0;
 +    p->b0 /= p->a0;
 +    p->b1 /= p->a0;
 +    p->b2 /= p->a0;
 +
 +    p->cache = av_realloc_f(p->cache, sizeof(ChanCache), inlink->channels);
 +    if (!p->cache)
 +        return AVERROR(ENOMEM);
 +    memset(p->cache, 0, sizeof(ChanCache) * inlink->channels);
 +
 +    switch (inlink->format) {
 +    case AV_SAMPLE_FMT_S16P: p->filter = biquad_s16; break;
 +    case AV_SAMPLE_FMT_S32P: p->filter = biquad_s32; break;
 +    case AV_SAMPLE_FMT_FLTP: p->filter = biquad_flt; break;
 +    case AV_SAMPLE_FMT_DBLP: p->filter = biquad_dbl; break;
 +    default: av_assert0(0);
 +    }
 +
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 +{
 +    BiquadsContext *p       = inlink->dst->priv;
 +    AVFilterLink *outlink   = inlink->dst->outputs[0];
 +    AVFrame *out_buf;
 +    int nb_samples = buf->nb_samples;
 +    int ch;
 +
 +    if (av_frame_is_writable(buf)) {
 +        out_buf = buf;
 +    } else {
 +        out_buf = ff_get_audio_buffer(inlink, nb_samples);
 +        if (!out_buf)
 +            return AVERROR(ENOMEM);
 +        av_frame_copy_props(out_buf, buf);
 +    }
 +
 +    for (ch = 0; ch < av_frame_get_channels(buf); ch++)
 +        p->filter(buf->extended_data[ch],
 +                  out_buf->extended_data[ch], nb_samples,
 +                  &p->cache[ch].i1, &p->cache[ch].i2,
 +                  &p->cache[ch].o1, &p->cache[ch].o2,
 +                  p->b0, p->b1, p->b2, p->a1, p->a2);
 +
 +    if (buf != out_buf)
 +        av_frame_free(&buf);
 +
 +    return ff_filter_frame(outlink, out_buf);
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    BiquadsContext *p = ctx->priv;
 +
 +    av_freep(&p->cache);
 +}
 +
 +static const AVFilterPad inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad outputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props = config_output,
 +    },
 +    { NULL }
 +};
 +
 +#define OFFSET(x) offsetof(BiquadsContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +#define DEFINE_BIQUAD_FILTER(name_, description_)                       \
 +AVFILTER_DEFINE_CLASS(name_);                                           \
 +static av_cold int name_##_init(AVFilterContext *ctx) \
 +{                                                                       \
 +    BiquadsContext *p = ctx->priv;                                      \
 +    p->class = &name_##_class;                                          \
 +    p->filter_type = name_;                                             \
 +    return init(ctx);                                             \
 +}                                                                       \
 +                                                         \
++AVFilter ff_af_##name_ = {                         \
 +    .name          = #name_,                             \
 +    .description   = NULL_IF_CONFIG_SMALL(description_), \
 +    .priv_size     = sizeof(BiquadsContext),             \
 +    .init          = name_##_init,                       \
 +    .uninit        = uninit,                             \
 +    .query_formats = query_formats,                      \
 +    .inputs        = inputs,                             \
 +    .outputs       = outputs,                            \
 +    .priv_class    = &name_##_class,                     \
 +}
 +
 +#if CONFIG_EQUALIZER_FILTER
 +static const AVOption equalizer_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 999, FLAGS},
 +    {"w",     "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 999, FLAGS},
 +    {"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {"g",    "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(equalizer, "Apply two-pole peaking equalization (EQ) filter.");
 +#endif  /* CONFIG_EQUALIZER_FILTER */
 +#if CONFIG_BASS_FILTER
 +static const AVOption bass_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
 +    {"w",     "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
 +    {"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {"g",    "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(bass, "Boost or cut lower frequencies.");
 +#endif  /* CONFIG_BASS_FILTER */
 +#if CONFIG_TREBLE_FILTER
 +static const AVOption treble_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
 +    {"w",     "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
 +    {"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {"g",    "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(treble, "Boost or cut upper frequencies.");
 +#endif  /* CONFIG_TREBLE_FILTER */
 +#if CONFIG_BANDPASS_FILTER
 +static const AVOption bandpass_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
 +    {"w",     "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
 +    {"csg",   "use constant skirt gain", OFFSET(csg), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(bandpass, "Apply a two-pole Butterworth band-pass filter.");
 +#endif  /* CONFIG_BANDPASS_FILTER */
 +#if CONFIG_BANDREJECT_FILTER
 +static const AVOption bandreject_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
 +    {"w",     "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(bandreject, "Apply a two-pole Butterworth band-reject filter.");
 +#endif  /* CONFIG_BANDREJECT_FILTER */
 +#if CONFIG_LOWPASS_FILTER
 +static const AVOption lowpass_options[] = {
 +    {"frequency", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=500}, 0, 999999, FLAGS},
 +    {"f",         "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=500}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
 +    {"w",     "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
 +    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
 +    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(lowpass, "Apply a low-pass filter with 3dB point frequency.");
 +#endif  /* CONFIG_LOWPASS_FILTER */
 +#if CONFIG_HIGHPASS_FILTER
 +static const AVOption highpass_options[] = {
 +    {"frequency", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"f",         "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
 +    {"w",     "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
 +    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
 +    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(highpass, "Apply a high-pass filter with 3dB point frequency.");
 +#endif  /* CONFIG_HIGHPASS_FILTER */
 +#if CONFIG_ALLPASS_FILTER
 +static const AVOption allpass_options[] = {
 +    {"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"f",         "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
 +    {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=HERTZ}, HERTZ, SLOPE, FLAGS, "width_type"},
 +    {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
 +    {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
 +    {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
 +    {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
 +    {"width", "set filter-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=707.1}, 0, 99999, FLAGS},
 +    {"w",     "set filter-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=707.1}, 0, 99999, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(allpass, "Apply a two-pole all-pass filter.");
 +#endif  /* CONFIG_ALLPASS_FILTER */
 +#if CONFIG_BIQUAD_FILTER
 +static const AVOption biquad_options[] = {
 +    {"a0", NULL, OFFSET(a0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {"a1", NULL, OFFSET(a1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {"a2", NULL, OFFSET(a2), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {"b0", NULL, OFFSET(b0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {"b1", NULL, OFFSET(b1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {"b2", NULL, OFFSET(b2), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
 +    {NULL}
 +};
 +
 +DEFINE_BIQUAD_FILTER(biquad, "Apply a biquad IIR filter with the given coefficients.");
 +#endif  /* CONFIG_BIQUAD_FILTER */
Simple merge
@@@ -136,14 -139,17 +136,14 @@@ static const AVFilterPad avfilter_af_ch
      { NULL }
  };
  
- AVFilter avfilter_af_channelsplit = {
+ AVFilter ff_af_channelsplit = {
      .name           = "channelsplit",
 -    .description    = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams"),
 +    .description    = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."),
      .priv_size      = sizeof(ChannelSplitContext),
      .priv_class     = &channelsplit_class,
 -
      .init           = init,
      .query_formats  = query_formats,
 -
 -    .inputs  = avfilter_af_channelsplit_inputs,
 -    .outputs = NULL,
 -
 -    .flags   = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
 +    .inputs         = avfilter_af_channelsplit_inputs,
 +    .outputs        = NULL,
 +    .flags          = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
  };
index 586ac2f,0000000..ec1e962
mode 100644,000000..100644
--- /dev/null
@@@ -1,518 -1,0 +1,518 @@@
- AVFilter avfilter_af_compand = {
 +/*
 + * Copyright (c) 1999 Chris Bagwell
 + * Copyright (c) 1999 Nick Bailey
 + * Copyright (c) 2007 Rob Sykes <robs@users.sourceforge.net>
 + * Copyright (c) 2013 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/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct ChanParam {
 +    double attack;
 +    double decay;
 +    double volume;
 +} ChanParam;
 +
 +typedef struct CompandSegment {
 +    double x, y;
 +    double a, b;
 +} CompandSegment;
 +
 +typedef struct CompandContext {
 +    const AVClass *class;
 +    char *attacks, *decays, *points;
 +    CompandSegment *segments;
 +    ChanParam *channels;
 +    double in_min_lin;
 +    double out_min_lin;
 +    double curve_dB;
 +    double gain_dB;
 +    double initial_volume;
 +    double delay;
 +    uint8_t **delayptrs;
 +    int delay_samples;
 +    int delay_count;
 +    int delay_index;
 +    int64_t pts;
 +
 +    int (*compand)(AVFilterContext *ctx, AVFrame *frame);
 +} CompandContext;
 +
 +#define OFFSET(x) offsetof(CompandContext, x)
 +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 +
 +static const AVOption compand_options[] = {
 +    { "attacks", "set time over which increase of volume is determined", OFFSET(attacks), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
 +    { "decays", "set time over which decrease of volume is determined", OFFSET(decays), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
 +    { "points", "set points of transfer function", OFFSET(points), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
 +    { "soft-knee", "set soft-knee", OFFSET(curve_dB), AV_OPT_TYPE_DOUBLE, {.dbl=0.01}, 0.01, 900, A },
 +    { "gain", "set output gain", OFFSET(gain_dB), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, A },
 +    { "volume", "set initial volume", OFFSET(initial_volume), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 0, A },
 +    { "delay", "set delay for samples before sending them to volume adjuster", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 20, A },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(compand);
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    CompandContext *s = ctx->priv;
 +
 +    if (!s->attacks || !s->decays || !s->points) {
 +        av_log(ctx, AV_LOG_ERROR, "Missing attacks and/or decays and/or points.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    CompandContext *s = ctx->priv;
 +
 +    av_freep(&s->channels);
 +    av_freep(&s->segments);
 +    if (s->delayptrs)
 +        av_freep(&s->delayptrs[0]);
 +    av_freep(&s->delayptrs);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterChannelLayouts *layouts;
 +    AVFilterFormats *formats;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_DBLP,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static void count_items(char *item_str, int *nb_items)
 +{
 +    char *p;
 +
 +    *nb_items = 1;
 +    for (p = item_str; *p; p++) {
 +        if (*p == ' ')
 +            (*nb_items)++;
 +    }
 +
 +}
 +
 +static void update_volume(ChanParam *cp, double in)
 +{
 +    double delta = in - cp->volume;
 +
 +    if (delta > 0.0)
 +        cp->volume += delta * cp->attack;
 +    else
 +        cp->volume += delta * cp->decay;
 +}
 +
 +static double get_volume(CompandContext *s, double in_lin)
 +{
 +    CompandSegment *cs;
 +    double in_log, out_log;
 +    int i;
 +
 +    if (in_lin < s->in_min_lin)
 +        return s->out_min_lin;
 +
 +    in_log = log(in_lin);
 +
 +    for (i = 1;; i++)
 +        if (in_log <= s->segments[i + 1].x)
 +            break;
 +
 +    cs = &s->segments[i];
 +    in_log -= cs->x;
 +    out_log = cs->y + in_log * (cs->a * in_log + cs->b);
 +
 +    return exp(out_log);
 +}
 +
 +static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame)
 +{
 +    CompandContext *s = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    const int channels = inlink->channels;
 +    const int nb_samples = frame->nb_samples;
 +    AVFrame *out_frame;
 +    int chan, i;
 +
 +    if (av_frame_is_writable(frame)) {
 +        out_frame = frame;
 +    } else {
 +        out_frame = ff_get_audio_buffer(inlink, nb_samples);
 +        if (!out_frame)
 +            return AVERROR(ENOMEM);
 +        av_frame_copy_props(out_frame, frame);
 +    }
 +
 +    for (chan = 0; chan < channels; chan++) {
 +        const double *src = (double *)frame->extended_data[chan];
 +        double *dst = (double *)out_frame->extended_data[chan];
 +        ChanParam *cp = &s->channels[chan];
 +
 +        for (i = 0; i < nb_samples; i++) {
 +            update_volume(cp, fabs(src[i]));
 +
 +            dst[i] = av_clipd(src[i] * get_volume(s, cp->volume), -1, 1);
 +        }
 +    }
 +
 +    if (frame != out_frame)
 +        av_frame_free(&frame);
 +
 +    return ff_filter_frame(ctx->outputs[0], out_frame);
 +}
 +
 +#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
 +
 +static int compand_delay(AVFilterContext *ctx, AVFrame *frame)
 +{
 +    CompandContext *s = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    const int channels = inlink->channels;
 +    const int nb_samples = frame->nb_samples;
 +    int chan, i, av_uninit(dindex), oindex, av_uninit(count);
 +    AVFrame *out_frame = NULL;
 +
 +    av_assert1(channels > 0); /* would corrupt delay_count and delay_index */
 +
 +    for (chan = 0; chan < channels; chan++) {
 +        const double *src = (double *)frame->extended_data[chan];
 +        double *dbuf = (double *)s->delayptrs[chan];
 +        ChanParam *cp = &s->channels[chan];
 +        double *dst;
 +
 +        count  = s->delay_count;
 +        dindex = s->delay_index;
 +        for (i = 0, oindex = 0; i < nb_samples; i++) {
 +            const double in = src[i];
 +            update_volume(cp, fabs(in));
 +
 +            if (count >= s->delay_samples) {
 +                if (!out_frame) {
 +                    out_frame = ff_get_audio_buffer(inlink, nb_samples - i);
 +                    if (!out_frame)
 +                        return AVERROR(ENOMEM);
 +                    av_frame_copy_props(out_frame, frame);
 +                    out_frame->pts = s->pts;
 +                    s->pts += av_rescale_q(nb_samples - i, (AVRational){1, inlink->sample_rate}, inlink->time_base);
 +                }
 +
 +                dst = (double *)out_frame->extended_data[chan];
 +                dst[oindex++] = av_clipd(dbuf[dindex] * get_volume(s, cp->volume), -1, 1);
 +            } else {
 +                count++;
 +            }
 +
 +            dbuf[dindex] = in;
 +            dindex = MOD(dindex + 1, s->delay_samples);
 +        }
 +    }
 +
 +    s->delay_count = count;
 +    s->delay_index = dindex;
 +
 +    av_frame_free(&frame);
 +    return out_frame ? ff_filter_frame(ctx->outputs[0], out_frame) : 0;
 +}
 +
 +static int compand_drain(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    CompandContext *s = ctx->priv;
 +    const int channels = outlink->channels;
 +    int chan, i, dindex;
 +    AVFrame *frame = NULL;
 +
 +    frame = ff_get_audio_buffer(outlink, FFMIN(2048, s->delay_count));
 +    if (!frame)
 +        return AVERROR(ENOMEM);
 +    frame->pts = s->pts;
 +    s->pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
 +
 +    for (chan = 0; chan < channels; chan++) {
 +        double *dbuf = (double *)s->delayptrs[chan];
 +        double *dst = (double *)frame->extended_data[chan];
 +        ChanParam *cp = &s->channels[chan];
 +
 +        dindex = s->delay_index;
 +        for (i = 0; i < frame->nb_samples; i++) {
 +            dst[i] = av_clipd(dbuf[dindex] * get_volume(s, cp->volume), -1, 1);
 +            dindex = MOD(dindex + 1, s->delay_samples);
 +        }
 +    }
 +    s->delay_count -= frame->nb_samples;
 +    s->delay_index = dindex;
 +
 +    return ff_filter_frame(outlink, frame);
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    CompandContext *s = ctx->priv;
 +    const int sample_rate = outlink->sample_rate;
 +    double radius = s->curve_dB * M_LN10 / 20;
 +    int nb_attacks, nb_decays, nb_points;
 +    char *p, *saveptr = NULL;
 +    int new_nb_items, num;
 +    int i;
 +
 +    count_items(s->attacks, &nb_attacks);
 +    count_items(s->decays, &nb_decays);
 +    count_items(s->points, &nb_points);
 +
 +    if ((nb_attacks > outlink->channels) || (nb_decays > outlink->channels)) {
 +        av_log(ctx, AV_LOG_ERROR, "Number of attacks/decays bigger than number of channels.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    uninit(ctx);
 +
 +    s->channels = av_mallocz_array(outlink->channels, sizeof(*s->channels));
 +    s->segments = av_mallocz_array((nb_points + 4) * 2, sizeof(*s->segments));
 +
 +    if (!s->channels || !s->segments)
 +        return AVERROR(ENOMEM);
 +
 +    p = s->attacks;
 +    for (i = 0, new_nb_items = 0; i < nb_attacks; i++) {
 +        char *tstr = av_strtok(p, " ", &saveptr);
 +        p = NULL;
 +        new_nb_items += sscanf(tstr, "%lf", &s->channels[i].attack) == 1;
 +        if (s->channels[i].attack < 0)
 +            return AVERROR(EINVAL);
 +    }
 +    nb_attacks = new_nb_items;
 +
 +    p = s->decays;
 +    for (i = 0, new_nb_items = 0; i < nb_decays; i++) {
 +        char *tstr = av_strtok(p, " ", &saveptr);
 +        p = NULL;
 +        new_nb_items += sscanf(tstr, "%lf", &s->channels[i].decay) == 1;
 +        if (s->channels[i].decay < 0)
 +            return AVERROR(EINVAL);
 +    }
 +    nb_decays = new_nb_items;
 +
 +    if (nb_attacks != nb_decays) {
 +        av_log(ctx, AV_LOG_ERROR, "Number of attacks %d differs from number of decays %d.\n", nb_attacks, nb_decays);
 +        return AVERROR(EINVAL);
 +    }
 +
 +#define S(x) s->segments[2 * ((x) + 1)]
 +    p = s->points;
 +    for (i = 0, new_nb_items = 0; i < nb_points; i++) {
 +        char *tstr = av_strtok(p, " ", &saveptr);
 +        p = NULL;
 +        if (sscanf(tstr, "%lf/%lf", &S(i).x, &S(i).y) != 2) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid and/or missing input/output value.\n");
 +            return AVERROR(EINVAL);
 +        }
 +        if (i && S(i - 1).x > S(i).x) {
 +            av_log(ctx, AV_LOG_ERROR, "Transfer function input values must be increasing.\n");
 +            return AVERROR(EINVAL);
 +        }
 +        S(i).y -= S(i).x;
 +        av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y);
 +        new_nb_items++;
 +    }
 +    num = new_nb_items;
 +
 +    /* Add 0,0 if necessary */
 +    if (num == 0 || S(num - 1).x)
 +        num++;
 +
 +#undef S
 +#define S(x) s->segments[2 * (x)]
 +    /* Add a tail off segment at the start */
 +    S(0).x = S(1).x - 2 * s->curve_dB;
 +    S(0).y = S(1).y;
 +    num++;
 +
 +    /* Join adjacent colinear segments */
 +    for (i = 2; i < num; i++) {
 +        double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x);
 +        double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x);
 +        int j;
 +
 +        if (fabs(g1 - g2))
 +            continue;
 +        num--;
 +        for (j = --i; j < num; j++)
 +            S(j) = S(j + 1);
 +    }
 +
 +    for (i = 0; !i || s->segments[i - 2].x; i += 2) {
 +        s->segments[i].y += s->gain_dB;
 +        s->segments[i].x *= M_LN10 / 20;
 +        s->segments[i].y *= M_LN10 / 20;
 +    }
 +
 +#define L(x) s->segments[i - (x)]
 +    for (i = 4; s->segments[i - 2].x; i += 2) {
 +        double x, y, cx, cy, in1, in2, out1, out2, theta, len, r;
 +
 +        L(4).a = 0;
 +        L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x);
 +
 +        L(2).a = 0;
 +        L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x);
 +
 +        theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x);
 +        len = sqrt(pow(L(2).x - L(4).x, 2.) + pow(L(2).y - L(4).y, 2.));
 +        r = FFMIN(radius, len);
 +        L(3).x = L(2).x - r * cos(theta);
 +        L(3).y = L(2).y - r * sin(theta);
 +
 +        theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x);
 +        len = sqrt(pow(L(0).x - L(2).x, 2.) + pow(L(0).y - L(2).y, 2.));
 +        r = FFMIN(radius, len / 2);
 +        x = L(2).x + r * cos(theta);
 +        y = L(2).y + r * sin(theta);
 +
 +        cx = (L(3).x + L(2).x + x) / 3;
 +        cy = (L(3).y + L(2).y + y) / 3;
 +
 +        L(2).x = x;
 +        L(2).y = y;
 +
 +        in1 = cx - L(3).x;
 +        out1 = cy - L(3).y;
 +        in2 = L(2).x - L(3).x;
 +        out2 = L(2).y - L(3).y;
 +        L(3).a = (out2 / in2 - out1 / in1) / (in2-in1);
 +        L(3).b = out1 / in1 - L(3).a * in1;
 +    }
 +    L(3).x = 0;
 +    L(3).y = L(2).y;
 +
 +    s->in_min_lin  = exp(s->segments[1].x);
 +    s->out_min_lin = exp(s->segments[1].y);
 +
 +    for (i = 0; i < outlink->channels; i++) {
 +        ChanParam *cp = &s->channels[i];
 +
 +        if (cp->attack > 1.0 / sample_rate)
 +            cp->attack = 1.0 - exp(-1.0 / (sample_rate * cp->attack));
 +        else
 +            cp->attack = 1.0;
 +        if (cp->decay > 1.0 / sample_rate)
 +            cp->decay = 1.0 - exp(-1.0 / (sample_rate * cp->decay));
 +        else
 +            cp->decay = 1.0;
 +        cp->volume = pow(10.0, s->initial_volume / 20);
 +    }
 +
 +    s->delay_samples = s->delay * sample_rate;
 +    if (s->delay_samples > 0) {
 +        int ret;
 +        if ((ret = av_samples_alloc_array_and_samples(&s->delayptrs, NULL,
 +                                                      outlink->channels,
 +                                                      s->delay_samples,
 +                                                      outlink->format, 0)) < 0)
 +            return ret;
 +        s->compand = compand_delay;
 +        outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
 +    } else {
 +        s->compand = compand_nodelay;
 +    }
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    CompandContext *s = ctx->priv;
 +
 +    return s->compand(ctx, frame);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    CompandContext *s = ctx->priv;
 +    int ret;
 +
 +    ret = ff_request_frame(ctx->inputs[0]);
 +
 +    if (ret == AVERROR_EOF && !ctx->is_disabled && s->delay_count)
 +        ret = compand_drain(outlink);
 +
 +    return ret;
 +}
 +
 +static const AVFilterPad compand_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad compand_outputs[] = {
 +    {
 +        .name          = "default",
 +        .request_frame = request_frame,
 +        .config_props  = config_output,
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_compand = {
 +    .name          = "compand",
 +    .description   = NULL_IF_CONFIG_SMALL("Compress or expand audio dynamic range."),
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(CompandContext),
 +    .priv_class    = &compand_class,
 +    .init          = init,
 +    .uninit        = uninit,
 +    .inputs        = compand_inputs,
 +    .outputs       = compand_outputs,
 +};
index 3db4659,0000000..c310997
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,172 @@@
- AVFilter avfilter_af_earwax = {
 +/*
 + * Copyright (c) 2011 Mina Nagy Zaki
 + * Copyright (c) 2000 Edward Beingessner And Sundry Contributors.
 + * This source code is freely redistributable and may be used for any purpose.
 + * This copyright notice must be maintained.  Edward Beingessner And Sundry
 + * Contributors are not responsible for the consequences of using this
 + * software.
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Stereo Widening Effect. Adds audio cues to move stereo image in
 + * front of the listener. Adapted from the libsox earwax effect.
 + */
 +
 +#include "libavutil/channel_layout.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "formats.h"
 +
 +#define NUMTAPS 64
 +
 +static const int8_t filt[NUMTAPS] = {
 +/* 30°  330° */
 +    4,   -6,     /* 32 tap stereo FIR filter. */
 +    4,  -11,     /* One side filters as if the */
 +   -1,   -5,     /* signal was from 30 degrees */
 +    3,    3,     /* from the ear, the other as */
 +   -2,    5,     /* if 330 degrees. */
 +   -5,    0,
 +    9,    1,
 +    6,    3,     /*                         Input                         */
 +   -4,   -1,     /*                   Left         Right                  */
 +   -5,   -3,     /*                __________   __________                */
 +   -2,   -5,     /*               |          | |          |               */
 +   -7,    1,     /*           .---|  Hh,0(f) | |  Hh,0(f) |---.           */
 +    6,   -7,     /*          /    |__________| |__________|    \          */
 +   30,  -29,     /*         /                \ /                \         */
 +   12,   -3,     /*        /                  X                  \        */
 +  -11,    4,     /*       /                  / \                  \       */
 +   -3,    7,     /*  ____V_____   __________V   V__________   _____V____  */
 +  -20,   23,     /* |          | |          |   |          | |          | */
 +    2,    0,     /* | Hh,30(f) | | Hh,330(f)|   | Hh,330(f)| | Hh,30(f) | */
 +    1,   -6,     /* |__________| |__________|   |__________| |__________| */
 +  -14,   -5,     /*      \     ___      /           \      ___     /      */
 +   15,  -18,     /*       \   /   \    /    _____    \    /   \   /       */
 +    6,    7,     /*        `->| + |<--'    /     \    `-->| + |<-'        */
 +   15,  -10,     /*           \___/      _/       \_      \___/           */
 +  -14,   22,     /*               \     / \       / \     /               */
 +   -7,   -2,     /*                `--->| |       | |<---'                */
 +   -4,    9,     /*                     \_/       \_/                     */
 +    6,  -12,     /*                                                       */
 +    6,   -6,     /*                       Headphones                      */
 +    0,  -11,
 +    0,   -5,
 +    4,    0};
 +
 +typedef struct {
 +    int16_t taps[NUMTAPS * 2];
 +} EarwaxContext;
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    static const int sample_rates[] = { 44100, -1 };
 +
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layout = NULL;
 +
 +    ff_add_format(&formats, AV_SAMPLE_FMT_S16);
 +    ff_set_common_formats(ctx, formats);
 +    ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO);
 +    ff_set_common_channel_layouts(ctx, layout);
 +    ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
 +
 +    return 0;
 +}
 +
 +//FIXME: replace with DSPContext.scalarproduct_int16
 +static inline int16_t *scalarproduct(const int16_t *in, const int16_t *endin, int16_t *out)
 +{
 +    int32_t sample;
 +    int16_t j;
 +
 +    while (in < endin) {
 +        sample = 0;
 +        for (j = 0; j < NUMTAPS; j++)
 +            sample += in[j] * filt[j];
 +        *out = av_clip_int16(sample >> 6);
 +        out++;
 +        in++;
 +    }
 +
 +    return out;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
 +{
 +    AVFilterLink *outlink = inlink->dst->outputs[0];
 +    int16_t *taps, *endin, *in, *out;
 +    AVFrame *outsamples = ff_get_audio_buffer(inlink, insamples->nb_samples);
 +    int len;
 +
 +    if (!outsamples) {
 +        av_frame_free(&insamples);
 +        return AVERROR(ENOMEM);
 +    }
 +    av_frame_copy_props(outsamples, insamples);
 +
 +    taps  = ((EarwaxContext *)inlink->dst->priv)->taps;
 +    out   = (int16_t *)outsamples->data[0];
 +    in    = (int16_t *)insamples ->data[0];
 +
 +    len = FFMIN(NUMTAPS, 2*insamples->nb_samples);
 +    // copy part of new input and process with saved input
 +    memcpy(taps+NUMTAPS, in, len * sizeof(*taps));
 +    out   = scalarproduct(taps, taps + len, out);
 +
 +    // process current input
 +    if (2*insamples->nb_samples >= NUMTAPS ){
 +        endin = in + insamples->nb_samples * 2 - NUMTAPS;
 +        scalarproduct(in, endin, out);
 +
 +        // save part of input for next round
 +        memcpy(taps, endin, NUMTAPS * sizeof(*taps));
 +    } else
 +        memmove(taps, taps + 2*insamples->nb_samples, NUMTAPS * sizeof(*taps));
 +
 +    av_frame_free(&insamples);
 +    return ff_filter_frame(outlink, outsamples);
 +}
 +
 +static const AVFilterPad earwax_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .filter_frame = filter_frame,
 +    },
 +    { NULL }
 +};
 +
 +static const AVFilterPad earwax_outputs[] = {
 +    {
 +        .name = "default",
 +        .type = AVMEDIA_TYPE_AUDIO,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_earwax = {
 +    .name           = "earwax",
 +    .description    = NULL_IF_CONFIG_SMALL("Widen the stereo image."),
 +    .query_formats  = query_formats,
 +    .priv_size      = sizeof(EarwaxContext),
 +    .inputs         = earwax_inputs,
 +    .outputs        = earwax_outputs,
 +};
@@@ -501,12 -509,13 +501,12 @@@ static const AVFilterPad avfilter_af_jo
      { NULL }
  };
  
- AVFilter avfilter_af_join = {
+ AVFilter ff_af_join = {
      .name           = "join",
      .description    = NULL_IF_CONFIG_SMALL("Join multiple audio streams into "
 -                                           "multi-channel output"),
 +                                           "multi-channel output."),
      .priv_size      = sizeof(JoinContext),
      .priv_class     = &join_class,
 -
      .init           = join_init,
      .uninit         = join_uninit,
      .query_formats  = join_query_formats,
index 7578a38,0000000..82c3d70
mode 100644,000000..100644
--- /dev/null
@@@ -1,703 -1,0 +1,703 @@@
- AVFilter avfilter_af_ladspa = {
 +/*
 + * Copyright (c) 2013 Paul B Mahol
 + * Copyright (c) 2011 Mina Nagy Zaki
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * LADSPA wrapper
 + */
 +
 +#include <dlfcn.h>
 +#include <ladspa.h>
 +#include "libavutil/avstring.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct LADSPAContext {
 +    const AVClass *class;
 +    char *dl_name;
 +    char *plugin;
 +    char *options;
 +    void *dl_handle;
 +
 +    unsigned long nb_inputs;
 +    unsigned long *ipmap;      /* map input number to port number */
 +
 +    unsigned long nb_inputcontrols;
 +    unsigned long *icmap;      /* map input control number to port number */
 +    LADSPA_Data *ictlv;        /* input controls values */
 +
 +    unsigned long nb_outputs;
 +    unsigned long *opmap;      /* map output number to port number */
 +
 +    unsigned long nb_outputcontrols;
 +    unsigned long *ocmap;      /* map output control number to port number */
 +    LADSPA_Data *octlv;        /* output controls values */
 +
 +    const LADSPA_Descriptor *desc;
 +    int *ctl_needs_value;
 +    int nb_handles;
 +    LADSPA_Handle *handles;
 +
 +    int sample_rate;
 +    int nb_samples;
 +    int64_t pts;
 +    int64_t duration;
 +} LADSPAContext;
 +
 +#define OFFSET(x) offsetof(LADSPAContext, x)
 +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
 +static const AVOption ladspa_options[] = {
 +    { "file", "set library name or full path", OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "f",    "set library name or full path", OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "plugin", "set plugin name", OFFSET(plugin), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "p",      "set plugin name", OFFSET(plugin), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "controls", "set plugin options", OFFSET(options), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "c",        "set plugin options", OFFSET(options), AV_OPT_TYPE_STRING, .flags = FLAGS },
 +    { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT32_MAX, FLAGS },
 +    { "s",           "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT32_MAX, FLAGS },
 +    { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
 +    { "n",          "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
 +    { "duration", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS },
 +    { "d",        "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(ladspa);
 +
 +static void print_ctl_info(AVFilterContext *ctx, int level,
 +                           LADSPAContext *s, int ctl, unsigned long *map,
 +                           LADSPA_Data *values, int print)
 +{
 +    const LADSPA_PortRangeHint *h = s->desc->PortRangeHints + map[ctl];
 +
 +    av_log(ctx, level, "c%i: %s [", ctl, s->desc->PortNames[map[ctl]]);
 +
 +    if (LADSPA_IS_HINT_TOGGLED(h->HintDescriptor)) {
 +        av_log(ctx, level, "toggled (1 or 0)");
 +
 +        if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
 +            av_log(ctx, level, " (default %i)", (int)values[ctl]);
 +    } else {
 +        if (LADSPA_IS_HINT_INTEGER(h->HintDescriptor)) {
 +            av_log(ctx, level, "<int>");
 +
 +            if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor))
 +                av_log(ctx, level, ", min: %i", (int)h->LowerBound);
 +
 +            if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor))
 +                av_log(ctx, level, ", max: %i", (int)h->UpperBound);
 +
 +            if (print)
 +                av_log(ctx, level, " (value %d)", (int)values[ctl]);
 +            else if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
 +                av_log(ctx, level, " (default %d)", (int)values[ctl]);
 +        } else {
 +            av_log(ctx, level, "<float>");
 +
 +            if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor))
 +                av_log(ctx, level, ", min: %f", h->LowerBound);
 +
 +            if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor))
 +                av_log(ctx, level, ", max: %f", h->UpperBound);
 +
 +            if (print)
 +                av_log(ctx, level, " (value %f)", values[ctl]);
 +            else if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
 +                av_log(ctx, level, " (default %f)", values[ctl]);
 +        }
 +
 +        if (LADSPA_IS_HINT_SAMPLE_RATE(h->HintDescriptor))
 +            av_log(ctx, level, ", multiple of sample rate");
 +
 +        if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
 +            av_log(ctx, level, ", logarithmic scale");
 +    }
 +
 +    av_log(ctx, level, "]\n");
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    LADSPAContext *s = ctx->priv;
 +    AVFrame *out;
 +    int i, h;
 +
 +    if (!s->nb_outputs ||
 +        (av_frame_is_writable(in) && s->nb_inputs == s->nb_outputs &&
 +        !(s->desc->Properties & LADSPA_PROPERTY_INPLACE_BROKEN))) {
 +        out = in;
 +    } else {
 +        out = ff_get_audio_buffer(ctx->outputs[0], in->nb_samples);
 +        if (!out) {
 +            av_frame_free(&in);
 +            return AVERROR(ENOMEM);
 +        }
 +        av_frame_copy_props(out, in);
 +    }
 +
 +    for (h = 0; h < s->nb_handles; h++) {
 +        for (i = 0; i < s->nb_inputs; i++) {
 +            s->desc->connect_port(s->handles[h], s->ipmap[i],
 +                                  (LADSPA_Data*)in->extended_data[i]);
 +        }
 +
 +        for (i = 0; i < s->nb_outputs; i++) {
 +            s->desc->connect_port(s->handles[h], s->opmap[i],
 +                                  (LADSPA_Data*)out->extended_data[i]);
 +        }
 +
 +        s->desc->run(s->handles[h], in->nb_samples);
 +    }
 +
 +    for (i = 0; i < s->nb_outputcontrols; i++)
 +        print_ctl_info(ctx, AV_LOG_VERBOSE, s, i, s->ocmap, s->octlv, 1);
 +
 +    if (out != in)
 +        av_frame_free(&in);
 +
 +    return ff_filter_frame(ctx->outputs[0], out);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    LADSPAContext *s = ctx->priv;
 +    AVFrame *out;
 +    int64_t t;
 +    int i;
 +
 +    if (ctx->nb_inputs)
 +        return ff_request_frame(ctx->inputs[0]);
 +
 +    t = av_rescale(s->pts, AV_TIME_BASE, s->sample_rate);
 +    if (s->duration >= 0 && t >= s->duration)
 +        return AVERROR_EOF;
 +
 +    out = ff_get_audio_buffer(outlink, s->nb_samples);
 +    if (!out)
 +        return AVERROR(ENOMEM);
 +
 +    for (i = 0; i < s->nb_outputs; i++)
 +        s->desc->connect_port(s->handles[0], s->opmap[i],
 +                (LADSPA_Data*)out->extended_data[i]);
 +
 +    s->desc->run(s->handles[0], s->nb_samples);
 +
 +    for (i = 0; i < s->nb_outputcontrols; i++)
 +        print_ctl_info(ctx, AV_LOG_INFO, s, i, s->ocmap, s->octlv, 1);
 +
 +    out->sample_rate = s->sample_rate;
 +    out->pts         = s->pts;
 +    s->pts          += s->nb_samples;
 +
 +    return ff_filter_frame(outlink, out);
 +}
 +
 +static void set_default_ctl_value(LADSPAContext *s, int ctl,
 +                                  unsigned long *map, LADSPA_Data *values)
 +{
 +    const LADSPA_PortRangeHint *h = s->desc->PortRangeHints + map[ctl];
 +    const LADSPA_Data lower = h->LowerBound;
 +    const LADSPA_Data upper = h->UpperBound;
 +
 +    if (LADSPA_IS_HINT_DEFAULT_MINIMUM(h->HintDescriptor)) {
 +        values[ctl] = lower;
 +    } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(h->HintDescriptor)) {
 +        values[ctl] = upper;
 +    } else if (LADSPA_IS_HINT_DEFAULT_0(h->HintDescriptor)) {
 +        values[ctl] = 0.0;
 +    } else if (LADSPA_IS_HINT_DEFAULT_1(h->HintDescriptor)) {
 +        values[ctl] = 1.0;
 +    } else if (LADSPA_IS_HINT_DEFAULT_100(h->HintDescriptor)) {
 +        values[ctl] = 100.0;
 +    } else if (LADSPA_IS_HINT_DEFAULT_440(h->HintDescriptor)) {
 +        values[ctl] = 440.0;
 +    } else if (LADSPA_IS_HINT_DEFAULT_LOW(h->HintDescriptor)) {
 +        if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
 +            values[ctl] = exp(log(lower) * 0.75 + log(upper) * 0.25);
 +        else
 +            values[ctl] = lower * 0.75 + upper * 0.25;
 +    } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(h->HintDescriptor)) {
 +        if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
 +            values[ctl] = exp(log(lower) * 0.5 + log(upper) * 0.5);
 +        else
 +            values[ctl] = lower * 0.5 + upper * 0.5;
 +    } else if (LADSPA_IS_HINT_DEFAULT_HIGH(h->HintDescriptor)) {
 +        if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
 +            values[ctl] = exp(log(lower) * 0.25 + log(upper) * 0.75);
 +        else
 +            values[ctl] = lower * 0.25 + upper * 0.75;
 +    }
 +}
 +
 +static int connect_ports(AVFilterContext *ctx, AVFilterLink *link)
 +{
 +    LADSPAContext *s = ctx->priv;
 +    int i, j;
 +
 +    s->nb_handles = s->nb_inputs == 1 && s->nb_outputs == 1 ? link->channels : 1;
 +    s->handles    = av_calloc(s->nb_handles, sizeof(*s->handles));
 +    if (!s->handles)
 +        return AVERROR(ENOMEM);
 +
 +    for (i = 0; i < s->nb_handles; i++) {
 +        s->handles[i] = s->desc->instantiate(s->desc, link->sample_rate);
 +        if (!s->handles[i]) {
 +            av_log(ctx, AV_LOG_ERROR, "Could not instantiate plugin.\n");
 +            return AVERROR_EXTERNAL;
 +        }
 +
 +        // Connect the input control ports
 +        for (j = 0; j < s->nb_inputcontrols; j++)
 +            s->desc->connect_port(s->handles[i], s->icmap[j], s->ictlv + j);
 +
 +        // Connect the output control ports
 +        for (j = 0; j < s->nb_outputcontrols; j++)
 +            s->desc->connect_port(s->handles[i], s->ocmap[j], &s->octlv[j]);
 +
 +        if (s->desc->activate)
 +            s->desc->activate(s->handles[i]);
 +    }
 +
 +    av_log(ctx, AV_LOG_DEBUG, "handles: %d\n", s->nb_handles);
 +
 +    return 0;
 +}
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +
 +    return connect_ports(ctx, inlink);
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    int ret;
 +
 +    if (ctx->nb_inputs) {
 +        AVFilterLink *inlink = ctx->inputs[0];
 +
 +        outlink->format      = inlink->format;
 +        outlink->sample_rate = inlink->sample_rate;
 +
 +        ret = 0;
 +    } else {
 +        LADSPAContext *s = ctx->priv;
 +
 +        outlink->sample_rate = s->sample_rate;
 +        outlink->time_base   = (AVRational){1, s->sample_rate};
 +
 +        ret = connect_ports(ctx, outlink);
 +    }
 +
 +    return ret;
 +}
 +
 +static void count_ports(const LADSPA_Descriptor *desc,
 +                        unsigned long *nb_inputs, unsigned long *nb_outputs)
 +{
 +    LADSPA_PortDescriptor pd;
 +    int i;
 +
 +    for (i = 0; i < desc->PortCount; i++) {
 +        pd = desc->PortDescriptors[i];
 +
 +        if (LADSPA_IS_PORT_AUDIO(pd)) {
 +            if (LADSPA_IS_PORT_INPUT(pd)) {
 +                (*nb_inputs)++;
 +            } else if (LADSPA_IS_PORT_OUTPUT(pd)) {
 +                (*nb_outputs)++;
 +            }
 +        }
 +    }
 +}
 +
 +static void *try_load(const char *dir, const char *soname)
 +{
 +    char *path = av_asprintf("%s/%s.so", dir, soname);
 +    void *ret = NULL;
 +
 +    if (path) {
 +        ret = dlopen(path, RTLD_LOCAL|RTLD_NOW);
 +        av_free(path);
 +    }
 +
 +    return ret;
 +}
 +
 +static int set_control(AVFilterContext *ctx, unsigned long port, LADSPA_Data value)
 +{
 +    LADSPAContext *s = ctx->priv;
 +    const char *label = s->desc->Label;
 +    LADSPA_PortRangeHint *h = (LADSPA_PortRangeHint *)s->desc->PortRangeHints +
 +                              s->icmap[port];
 +
 +    if (port >= s->nb_inputcontrols) {
 +        av_log(ctx, AV_LOG_ERROR, "Control c%ld is out of range [0 - %lu].\n",
 +               port, s->nb_inputcontrols);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor) &&
 +            value < h->LowerBound) {
 +        av_log(ctx, AV_LOG_ERROR,
 +                "%s: input control c%ld is below lower boundary of %0.4f.\n",
 +                label, port, h->LowerBound);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor) &&
 +            value > h->UpperBound) {
 +        av_log(ctx, AV_LOG_ERROR,
 +                "%s: input control c%ld is above upper boundary of %0.4f.\n",
 +                label, port, h->UpperBound);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    s->ictlv[port] = value;
 +
 +    return 0;
 +}
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    LADSPAContext *s = ctx->priv;
 +    LADSPA_Descriptor_Function descriptor_fn;
 +    const LADSPA_Descriptor *desc;
 +    LADSPA_PortDescriptor pd;
 +    AVFilterPad pad = { NULL };
 +    char *p, *arg, *saveptr = NULL;
 +    unsigned long nb_ports;
 +    int i;
 +
 +    if (!s->dl_name) {
 +        av_log(ctx, AV_LOG_ERROR, "No plugin name provided\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (s->dl_name[0] == '/' || s->dl_name[0] == '.') {
 +        // argument is a path
 +        s->dl_handle = dlopen(s->dl_name, RTLD_LOCAL|RTLD_NOW);
 +    } else {
 +        // argument is a shared object name
 +        char *paths = av_strdup(getenv("LADSPA_PATH"));
 +        const char *separator = ":";
 +
 +        if (paths) {
 +            p = paths;
 +            while ((arg = av_strtok(p, separator, &saveptr)) && !s->dl_handle) {
 +                s->dl_handle = try_load(arg, s->dl_name);
 +                p = NULL;
 +            }
 +        }
 +
 +        av_free(paths);
 +        if (!s->dl_handle && (paths = av_asprintf("%s/.ladspa/lib", getenv("HOME")))) {
 +            s->dl_handle = try_load(paths, s->dl_name);
 +            av_free(paths);
 +        }
 +
 +        if (!s->dl_handle)
 +            s->dl_handle = try_load("/usr/local/lib/ladspa", s->dl_name);
 +
 +        if (!s->dl_handle)
 +            s->dl_handle = try_load("/usr/lib/ladspa", s->dl_name);
 +    }
 +    if (!s->dl_handle) {
 +        av_log(ctx, AV_LOG_ERROR, "Failed to load '%s'\n", s->dl_name);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    descriptor_fn = dlsym(s->dl_handle, "ladspa_descriptor");
 +    if (!descriptor_fn) {
 +        av_log(ctx, AV_LOG_ERROR, "Could not find ladspa_descriptor: %s\n", dlerror());
 +        return AVERROR(EINVAL);
 +    }
 +
 +    // Find the requested plugin, or list plugins
 +    if (!s->plugin) {
 +        av_log(ctx, AV_LOG_INFO, "The '%s' library contains the following plugins:\n", s->dl_name);
 +        av_log(ctx, AV_LOG_INFO, "I = Input Channels\n");
 +        av_log(ctx, AV_LOG_INFO, "O = Output Channels\n");
 +        av_log(ctx, AV_LOG_INFO, "I:O %-25s %s\n", "Plugin", "Description");
 +        av_log(ctx, AV_LOG_INFO, "\n");
 +        for (i = 0; desc = descriptor_fn(i); i++) {
 +            unsigned long inputs = 0, outputs = 0;
 +
 +            count_ports(desc, &inputs, &outputs);
 +            av_log(ctx, AV_LOG_INFO, "%lu:%lu %-25s %s\n", inputs, outputs, desc->Label,
 +                                     av_x_if_null(desc->Name, "?"));
 +            av_log(ctx, AV_LOG_VERBOSE, "Maker: %s\n", av_x_if_null(desc->Maker, "?"));
 +            av_log(ctx, AV_LOG_VERBOSE, "Copyright: %s\n", av_x_if_null(desc->Copyright, "?"));
 +        }
 +        return AVERROR_EXIT;
 +    } else {
 +        for (i = 0;; i++) {
 +            desc = descriptor_fn(i);
 +            if (!desc) {
 +                av_log(ctx, AV_LOG_ERROR, "Could not find plugin: %s\n", s->plugin);
 +                return AVERROR(EINVAL);
 +            }
 +
 +            if (desc->Label && !strcmp(desc->Label, s->plugin))
 +                break;
 +        }
 +    }
 +
 +    s->desc  = desc;
 +    nb_ports = desc->PortCount;
 +
 +    s->ipmap = av_calloc(nb_ports, sizeof(*s->ipmap));
 +    s->opmap = av_calloc(nb_ports, sizeof(*s->opmap));
 +    s->icmap = av_calloc(nb_ports, sizeof(*s->icmap));
 +    s->ocmap = av_calloc(nb_ports, sizeof(*s->ocmap));
 +    s->ictlv = av_calloc(nb_ports, sizeof(*s->ictlv));
 +    s->octlv = av_calloc(nb_ports, sizeof(*s->octlv));
 +    s->ctl_needs_value = av_calloc(nb_ports, sizeof(*s->ctl_needs_value));
 +    if (!s->ipmap || !s->opmap || !s->icmap ||
 +        !s->ocmap || !s->ictlv || !s->octlv || !s->ctl_needs_value)
 +        return AVERROR(ENOMEM);
 +
 +    for (i = 0; i < nb_ports; i++) {
 +        pd = desc->PortDescriptors[i];
 +
 +        if (LADSPA_IS_PORT_AUDIO(pd)) {
 +            if (LADSPA_IS_PORT_INPUT(pd)) {
 +                s->ipmap[s->nb_inputs] = i;
 +                s->nb_inputs++;
 +            } else if (LADSPA_IS_PORT_OUTPUT(pd)) {
 +                s->opmap[s->nb_outputs] = i;
 +                s->nb_outputs++;
 +            }
 +        } else if (LADSPA_IS_PORT_CONTROL(pd)) {
 +            if (LADSPA_IS_PORT_INPUT(pd)) {
 +                s->icmap[s->nb_inputcontrols] = i;
 +
 +                if (LADSPA_IS_HINT_HAS_DEFAULT(desc->PortRangeHints[i].HintDescriptor))
 +                    set_default_ctl_value(s, s->nb_inputcontrols, s->icmap, s->ictlv);
 +                else
 +                    s->ctl_needs_value[s->nb_inputcontrols] = 1;
 +
 +                s->nb_inputcontrols++;
 +            } else if (LADSPA_IS_PORT_OUTPUT(pd)) {
 +                s->ocmap[s->nb_outputcontrols] = i;
 +                s->nb_outputcontrols++;
 +            }
 +        }
 +    }
 +
 +    // List Control Ports if "help" is specified
 +    if (s->options && !strcmp(s->options, "help")) {
 +        if (!s->nb_inputcontrols) {
 +            av_log(ctx, AV_LOG_INFO,
 +                   "The '%s' plugin does not have any input controls.\n",
 +                   desc->Label);
 +        } else {
 +            av_log(ctx, AV_LOG_INFO,
 +                   "The '%s' plugin has the following input controls:\n",
 +                   desc->Label);
 +            for (i = 0; i < s->nb_inputcontrols; i++)
 +                print_ctl_info(ctx, AV_LOG_INFO, s, i, s->icmap, s->ictlv, 0);
 +        }
 +        return AVERROR_EXIT;
 +    }
 +
 +    // Parse control parameters
 +    p = s->options;
 +    while (s->options) {
 +        LADSPA_Data val;
 +        int ret;
 +
 +        if (!(arg = av_strtok(p, "|", &saveptr)))
 +            break;
 +        p = NULL;
 +
 +        if (sscanf(arg, "c%d=%f", &i, &val) != 2) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid syntax.\n");
 +            return AVERROR(EINVAL);
 +        }
 +
 +        if ((ret = set_control(ctx, i, val)) < 0)
 +            return ret;
 +        s->ctl_needs_value[i] = 0;
 +    }
 +
 +    // Check if any controls are not set
 +    for (i = 0; i < s->nb_inputcontrols; i++) {
 +        if (s->ctl_needs_value[i]) {
 +            av_log(ctx, AV_LOG_ERROR, "Control c%d must be set.\n", i);
 +            print_ctl_info(ctx, AV_LOG_ERROR, s, i, s->icmap, s->ictlv, 0);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    pad.type = AVMEDIA_TYPE_AUDIO;
 +
 +    if (s->nb_inputs) {
 +        pad.name = av_asprintf("in0:%s%lu", desc->Label, s->nb_inputs);
 +        if (!pad.name)
 +            return AVERROR(ENOMEM);
 +
 +        pad.filter_frame = filter_frame;
 +        pad.config_props = config_input;
 +        if (ff_insert_inpad(ctx, ctx->nb_inputs, &pad) < 0) {
 +            av_freep(&pad.name);
 +            return AVERROR(ENOMEM);
 +        }
 +    }
 +
 +    av_log(ctx, AV_LOG_DEBUG, "ports: %lu\n", nb_ports);
 +    av_log(ctx, AV_LOG_DEBUG, "inputs: %lu outputs: %lu\n",
 +                              s->nb_inputs, s->nb_outputs);
 +    av_log(ctx, AV_LOG_DEBUG, "input controls: %lu output controls: %lu\n",
 +                              s->nb_inputcontrols, s->nb_outputcontrols);
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    LADSPAContext *s = ctx->priv;
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    static const enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    if (s->nb_inputs) {
 +        formats = ff_all_samplerates();
 +        if (!formats)
 +            return AVERROR(ENOMEM);
 +
 +        ff_set_common_samplerates(ctx, formats);
 +    } else {
 +        int sample_rates[] = { s->sample_rate, -1 };
 +
 +        ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
 +    }
 +
 +    if (s->nb_inputs == 1 && s->nb_outputs == 1) {
 +        // We will instantiate multiple LADSPA_Handle, one over each channel
 +        layouts = ff_all_channel_layouts();
 +        if (!layouts)
 +            return AVERROR(ENOMEM);
 +
 +        ff_set_common_channel_layouts(ctx, layouts);
 +    } else {
 +        AVFilterLink *outlink = ctx->outputs[0];
 +
 +        if (s->nb_inputs >= 1) {
 +            AVFilterLink *inlink = ctx->inputs[0];
 +            int64_t inlayout = FF_COUNT2LAYOUT(s->nb_inputs);
 +
 +            layouts = NULL;
 +            ff_add_channel_layout(&layouts, inlayout);
 +            ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
 +
 +            if (!s->nb_outputs)
 +                ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +        }
 +
 +        if (s->nb_outputs >= 1) {
 +            int64_t outlayout = FF_COUNT2LAYOUT(s->nb_outputs);
 +
 +            layouts = NULL;
 +            ff_add_channel_layout(&layouts, outlayout);
 +            ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    LADSPAContext *s = ctx->priv;
 +    int i;
 +
 +    for (i = 0; i < s->nb_handles; i++) {
 +        if (s->desc->deactivate)
 +            s->desc->deactivate(s->handles[i]);
 +        if (s->desc->cleanup)
 +            s->desc->cleanup(s->handles[i]);
 +    }
 +
 +    if (s->dl_handle)
 +        dlclose(s->dl_handle);
 +
 +    av_freep(&s->ipmap);
 +    av_freep(&s->opmap);
 +    av_freep(&s->icmap);
 +    av_freep(&s->ocmap);
 +    av_freep(&s->ictlv);
 +    av_freep(&s->octlv);
 +    av_freep(&s->handles);
 +    av_freep(&s->ctl_needs_value);
 +
 +    if (ctx->nb_inputs)
 +        av_freep(&ctx->input_pads[0].name);
 +}
 +
 +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
 +                           char *res, int res_len, int flags)
 +{
 +    LADSPA_Data value;
 +    unsigned long port;
 +
 +    if (sscanf(cmd, "c%ld", &port) + sscanf(args, "%f", &value) != 2)
 +        return AVERROR(EINVAL);
 +
 +    return set_control(ctx, port, value);
 +}
 +
 +static const AVFilterPad ladspa_outputs[] = {
 +    {
 +        .name          = "default",
 +        .type          = AVMEDIA_TYPE_AUDIO,
 +        .config_props  = config_output,
 +        .request_frame = request_frame,
 +    },
 +    { NULL }
 +};
 +
++AVFilter ff_af_ladspa = {
 +    .name          = "ladspa",
 +    .description   = NULL_IF_CONFIG_SMALL("Apply LADSPA effect."),
 +    .priv_size     = sizeof(LADSPAContext),
 +    .priv_class    = &ladspa_class,
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .process_command = process_command,
 +    .inputs        = 0,
 +    .outputs       = ladspa_outputs,
 +    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
 +};
index 9cee9d9,0000000..1a01664
mode 100644,000000..100644
--- /dev/null
@@@ -1,422 -1,0 +1,422 @@@
- AVFilter avfilter_af_pan = {
 +/*
 + * Copyright (c) 2002 Anders Johansson <ajh@atri.curtin.edu.au>
 + * Copyright (c) 2011 Clément Bœsch <u pkh me>
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Audio panning filter (channels mixing)
 + * Original code written by Anders Johansson for MPlayer,
 + * reimplemented for FFmpeg.
 + */
 +
 +#include <stdio.h>
 +#include "libavutil/avstring.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/opt.h"
 +#include "libswresample/swresample.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "formats.h"
 +#include "internal.h"
 +
 +#define MAX_CHANNELS 63
 +
 +typedef struct PanContext {
 +    const AVClass *class;
 +    char *args;
 +    int64_t out_channel_layout;
 +    double gain[MAX_CHANNELS][MAX_CHANNELS];
 +    int64_t need_renorm;
 +    int need_renumber;
 +    int nb_input_channels;
 +    int nb_output_channels;
 +
 +    int pure_gains;
 +    /* channel mapping specific */
 +    int channel_map[SWR_CH_MAX];
 +    struct SwrContext *swr;
 +} PanContext;
 +
 +static void skip_spaces(char **arg)
 +{
 +    int len = 0;
 +
 +    sscanf(*arg, " %n", &len);
 +    *arg += len;
 +}
 +
 +static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
 +{
 +    char buf[8];
 +    int len, i, channel_id = 0;
 +    int64_t layout, layout0;
 +
 +    skip_spaces(arg);
 +    /* try to parse a channel name, e.g. "FL" */
 +    if (sscanf(*arg, "%7[A-Z]%n", buf, &len)) {
 +        layout0 = layout = av_get_channel_layout(buf);
 +        /* channel_id <- first set bit in layout */
 +        for (i = 32; i > 0; i >>= 1) {
 +            if (layout >= (int64_t)1 << i) {
 +                channel_id += i;
 +                layout >>= i;
 +            }
 +        }
 +        /* reject layouts that are not a single channel */
 +        if (channel_id >= MAX_CHANNELS || layout0 != (int64_t)1 << channel_id)
 +            return AVERROR(EINVAL);
 +        *rchannel = channel_id;
 +        *rnamed = 1;
 +        *arg += len;
 +        return 0;
 +    }
 +    /* try to parse a channel number, e.g. "c2" */
 +    if (sscanf(*arg, "c%d%n", &channel_id, &len) &&
 +        channel_id >= 0 && channel_id < MAX_CHANNELS) {
 +        *rchannel = channel_id;
 +        *rnamed = 0;
 +        *arg += len;
 +        return 0;
 +    }
 +    return AVERROR(EINVAL);
 +}
 +
 +static av_cold int init(AVFilterContext *ctx)
 +{
 +    PanContext *const pan = ctx->priv;
 +    char *arg, *arg0, *tokenizer, *args = av_strdup(pan->args);
 +    int out_ch_id, in_ch_id, len, named, ret;
 +    int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels
 +    double gain;
 +
 +    if (!pan->args) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "pan filter needs a channel layout and a set "
 +               "of channels definitions as parameter\n");
 +        return AVERROR(EINVAL);
 +    }
 +    if (!args)
 +        return AVERROR(ENOMEM);
 +    arg = av_strtok(args, "|", &tokenizer);
 +    ret = ff_parse_channel_layout(&pan->out_channel_layout, arg, ctx);
 +    if (ret < 0)
 +        goto fail;
 +    pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channel_layout);
 +
 +    /* parse channel specifications */
 +    while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) {
 +        /* channel name */
 +        if (parse_channel_name(&arg, &out_ch_id, &named)) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Expected out channel name, got \"%.8s\"\n", arg);
 +            ret = AVERROR(EINVAL);
 +            goto fail;
 +        }
 +        if (named) {
 +            if (!((pan->out_channel_layout >> out_ch_id) & 1)) {
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Channel \"%.8s\" does not exist in the chosen layout\n", arg0);
 +                ret = AVERROR(EINVAL);
 +                goto fail;
 +            }
 +            /* get the channel number in the output channel layout:
 +             * out_channel_layout & ((1 << out_ch_id) - 1) are all the
 +             * channels that come before out_ch_id,
 +             * so their count is the index of out_ch_id */
 +            out_ch_id = av_get_channel_layout_nb_channels(pan->out_channel_layout & (((int64_t)1 << out_ch_id) - 1));
 +        }
 +        if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Invalid out channel name \"%.8s\"\n", arg0);
 +            ret = AVERROR(EINVAL);
 +            goto fail;
 +        }
 +        skip_spaces(&arg);
 +        if (*arg == '=') {
 +            arg++;
 +        } else if (*arg == '<') {
 +            pan->need_renorm |= (int64_t)1 << out_ch_id;
 +            arg++;
 +        } else {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Syntax error after channel name in \"%.8s\"\n", arg0);
 +            ret = AVERROR(EINVAL);
 +            goto fail;
 +        }
 +        /* gains */
 +        while (1) {
 +            gain = 1;
 +            if (sscanf(arg, "%lf%n *%n", &gain, &len, &len))
 +                arg += len;
 +            if (parse_channel_name(&arg, &in_ch_id, &named)){
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Expected in channel name, got \"%.8s\"\n", arg);
 +                 ret = AVERROR(EINVAL);
 +                 goto fail;
 +            }
 +            nb_in_channels[named]++;
 +            if (nb_in_channels[!named]) {
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Can not mix named and numbered channels\n");
 +                ret = AVERROR(EINVAL);
 +                goto fail;
 +            }
 +            pan->gain[out_ch_id][in_ch_id] = gain;
 +            skip_spaces(&arg);
 +            if (!*arg)
 +                break;
 +            if (*arg != '+') {
 +                av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg);
 +                ret = AVERROR(EINVAL);
 +                goto fail;
 +            }
 +            arg++;
 +        }
 +    }
 +    pan->need_renumber = !!nb_in_channels[1];
 +
 +    ret = 0;
 +fail:
 +    av_free(args);
 +    return ret;
 +}
 +
 +static int are_gains_pure(const PanContext *pan)
 +{
 +    int i, j;
 +
 +    for (i = 0; i < MAX_CHANNELS; i++) {
 +        int nb_gain = 0;
 +
 +        for (j = 0; j < MAX_CHANNELS; j++) {
 +            double gain = pan->gain[i][j];
 +
 +            /* channel mapping is effective only if 0% or 100% of a channel is
 +             * selected... */
 +            if (gain != 0. && gain != 1.)
 +                return 0;
 +            /* ...and if the output channel is only composed of one input */
 +            if (gain && nb_gain++)
 +                return 0;
 +        }
 +    }
 +    return 1;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    PanContext *pan = ctx->priv;
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layouts;
 +
 +    pan->pure_gains = are_gains_pure(pan);
 +    /* libswr supports any sample and packing formats */
 +    ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO));
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    // inlink supports any channel layout
 +    layouts = ff_all_channel_layouts();
 +    ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
 +
 +    // outlink supports only requested output channel layout
 +    layouts = NULL;
 +    ff_add_channel_layout(&layouts, pan->out_channel_layout);
 +    ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +    return 0;
 +}
 +
 +static int config_props(AVFilterLink *link)
 +{
 +    AVFilterContext *ctx = link->dst;
 +    PanContext *pan = ctx->priv;
 +    char buf[1024], *cur;
 +    int i, j, k, r;
 +    double t;
 +
 +    pan->nb_input_channels = av_get_channel_layout_nb_channels(link->channel_layout);
 +    if (pan->need_renumber) {
 +        // input channels were given by their name: renumber them
 +        for (i = j = 0; i < MAX_CHANNELS; i++) {
 +            if ((link->channel_layout >> i) & 1) {
 +                for (k = 0; k < pan->nb_output_channels; k++)
 +                    pan->gain[k][j] = pan->gain[k][i];
 +                j++;
 +            }
 +        }
 +    }
 +
 +    // sanity check; can't be done in query_formats since the inlink
 +    // channel layout is unknown at that time
 +    if (pan->nb_input_channels > SWR_CH_MAX ||
 +        pan->nb_output_channels > SWR_CH_MAX) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "libswresample support a maximum of %d channels. "
 +               "Feel free to ask for a higher limit.\n", SWR_CH_MAX);
 +        return AVERROR_PATCHWELCOME;
 +    }
 +
 +    // init libswresample context
 +    pan->swr = swr_alloc_set_opts(pan->swr,
 +                                  pan->out_channel_layout, link->format, link->sample_rate,
 +                                  link->channel_layout,    link->format, link->sample_rate,
 +                                  0, ctx);
 +    if (!pan->swr)
 +        return AVERROR(ENOMEM);
 +
 +    // gains are pure, init the channel mapping
 +    if (pan->pure_gains) {
 +
 +        // get channel map from the pure gains
 +        for (i = 0; i < pan->nb_output_channels; i++) {
 +            int ch_id = -1;
 +            for (j = 0; j < pan->nb_input_channels; j++) {
 +                if (pan->gain[i][j]) {
 +                    ch_id = j;
 +                    break;
 +                }
 +            }
 +            pan->channel_map[i] = ch_id;
 +        }
 +
 +        av_opt_set_int(pan->swr, "icl", pan->out_channel_layout, 0);
 +        av_opt_set_int(pan->swr, "uch", pan->nb_output_channels, 0);
 +        swr_set_channel_mapping(pan->swr, pan->channel_map);
 +    } else {
 +        // renormalize
 +        for (i = 0; i < pan->nb_output_channels; i++) {
 +            if (!((pan->need_renorm >> i) & 1))
 +                continue;
 +            t = 0;
 +            for (j = 0; j < pan->nb_input_channels; j++)
 +                t += pan->gain[i][j];
 +            if (t > -1E-5 && t < 1E-5) {
 +                // t is almost 0 but not exactly, this is probably a mistake
 +                if (t)
 +                    av_log(ctx, AV_LOG_WARNING,
 +                           "Degenerate coefficients while renormalizing\n");
 +                continue;
 +            }
 +            for (j = 0; j < pan->nb_input_channels; j++)
 +                pan->gain[i][j] /= t;
 +        }
 +        av_opt_set_int(pan->swr, "icl", link->channel_layout, 0);
 +        av_opt_set_int(pan->swr, "ocl", pan->out_channel_layout, 0);
 +        swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]);
 +    }
 +
 +    r = swr_init(pan->swr);
 +    if (r < 0)
 +        return r;
 +
 +    // summary
 +    for (i = 0; i < pan->nb_output_channels; i++) {
 +        cur = buf;
 +        for (j = 0; j < pan->nb_input_channels; j++) {
 +            r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d",
 +                         j ? " + " : "", pan->gain[i][j], j);
 +            cur += FFMIN(buf + sizeof(buf) - cur, r);
 +        }
 +        av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf);
 +    }
 +    // add channel mapping summary if possible
 +    if (pan->pure_gains) {
 +        av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:");
 +        for (i = 0; i < pan->nb_output_channels; i++)
 +            if (pan->channel_map[i] < 0)
 +                av_log(ctx, AV_LOG_INFO, " M");
 +            else
 +                av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]);
 +        av_log(ctx, AV_LOG_INFO, "\n");
 +        return 0;
 +    }
 +    return 0;
 +}
 +
 +static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
 +{
 +    int ret;
 +    int n = insamples->nb_samples;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFrame *outsamples = ff_get_audio_buffer(outlink, n);
 +    PanContext *pan = inlink->dst->priv;
 +
 +    if (!outsamples)
 +        return AVERROR(ENOMEM);
 +    swr_convert(pan->swr, outsamples->data, n, (void *)insamples->data, n);
 +    av_frame_copy_props(outsamples, insamples);
 +    outsamples->channel_layout = outlink->channel_layout;
 +    av_frame_set_channels(outsamples, outlink->channels);
 +
 +    ret = ff_filter_frame(outlink, outsamples);
 +    av_frame_free(&insamples);
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    PanContext *pan = ctx->priv;
 +    swr_free(&pan->swr);
 +}
 +
 +#define OFFSET(x) offsetof(PanContext, x)
 +
 +static const AVOption pan_options[] = {
 +    { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM },
 +    { NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(pan);
 +
 +static const AVFilterPad pan_inputs[] = {
 +    {
 +        .name         = "default",
 +        .type         = AVMEDIA_TYPE_AUDIO,
 +        .config_props =