[FFmpeg-devel] [PATCH] avfilter: add audio pulsator filter

Ganesh Ajjanagadde gajjanag at mit.edu
Wed Dec 2 02:49:06 CET 2015


On Tue, Dec 1, 2015 at 7:04 AM, Paul B Mahol <onemda at gmail.com> wrote:
> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> ---
>  doc/filters.texi           |  57 +++++++++
>  libavfilter/Makefile       |   1 +
>  libavfilter/af_apulsator.c | 279 +++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c   |   1 +
>  4 files changed, 338 insertions(+)
>  create mode 100644 libavfilter/af_apulsator.c
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index fc71a99..a4afcac 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -1030,6 +1030,63 @@ It accepts the following values:
>  @end table
>  @end table
>
> + at section apulsator
> +
> +Audio pulsator is something between an autopanner and a tremolo.
> +But it can produce funny stereo effects as well. Pulsator changes the volume
> +of the left and right channel based on a LFO (low frequency oscillator) with
> +different waveforms and shifted phases.
> +This filter have the ability to define an offset between left and right
> +channel. An offset of 0 means that both LFO shapes match each other.
> +The left and right channel are altered equally - a conventional tremolo.
> +An offset of 50% means that the shape of the right channel is exactly shifted
> +in phase (or moved backwards about half of the frequency) - pulsator acts as
> +an autopanner. At 1 both curves match again. Every setting in between moves the
> +phase shift gapless between all stages and produces some "bypassing" sounds with
> +sine and triangle waveforms. The more you set the offset near 1 (starting from
> +the 0.5) the faster the signal passes from the left to the right speaker.
> +
> +The filter accepts the following options:
> +
> + at table @option
> + at item level_in
> +Set input gain. By default it is 1. Range is [0.015625 - 64].
> +
> + at item level_out
> +Set output gain. By default it is 1. Range is [0.015625 - 64].
> +
> + at item mode
> +Set waveform shape the LFO will use. Can be one of: sine, triangle, square,
> +sawup or sawdown. Default is sine.
> +
> + at item amount
> +Set modulation. Define how much of original signal is affected by the LFO.
> +
> + at item offset_l
> +Set left channel offset. Default is 0. Allowed range is [0 - 1].
> +
> + at item offset_r
> +Set right channel offset. Default is 0.5. Allowed range is [0 - 1].
> +
> + at item width
> +Set pulse width.
> +
> + at item timing
> +Set possible timing mode. Can be one of: bpm, ms or hz. Default is hz.
> +
> + at item bpm
> +Set bpm. Default is 120. Allowed range is [30 - 300]. Only used if timing
> +is set to bpm.
> +
> + at item ms
> +Set ms. Default is 500. Allowed range is [10 - 2000]. Only used if timing
> +is set to ms.
> +
> + at item hz
> +Set frequency in Hz. Default is 2. Allowed range is [0.01 - 100]. Only used
> +if timing is set to hz.
> + at end table
> +
>  @anchor{aresample}
>  @section aresample
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e31bdaa..b6c0d7b 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -40,6 +40,7 @@ OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o
>  OBJS-$(CONFIG_APAD_FILTER)                   += af_apad.o
>  OBJS-$(CONFIG_APERMS_FILTER)                 += f_perms.o
>  OBJS-$(CONFIG_APHASER_FILTER)                += af_aphaser.o generate_wave_table.o
> +OBJS-$(CONFIG_APULSATOR_FILTER)              += af_apulsator.o
>  OBJS-$(CONFIG_AREALTIME_FILTER)              += f_realtime.o
>  OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o
>  OBJS-$(CONFIG_AREVERSE_FILTER)               += f_reverse.o
> diff --git a/libavfilter/af_apulsator.c b/libavfilter/af_apulsator.c
> new file mode 100644
> index 0000000..9100eff
> --- /dev/null
> +++ b/libavfilter/af_apulsator.c
> @@ -0,0 +1,279 @@
> +/*
> + * Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
> + *
> + * 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"
> +#include "audio.h"
> +
> +enum PulsatorModes { SINE, TRIANGLE, SQUARE, SAWUP, SAWDOWN, NB_MODES };
> +enum PulsatorTimings { UNIT_BPM, UNIT_MS, UNIT_HZ, NB_TIMINGS };
> +
> +typedef struct SimpleLFO {
> +    double phase;
> +    double freq;
> +    double offset;
> +    double amount;
> +    double pwidth;
> +    int mode;
> +    int srate;
> +} SimpleLFO;
> +
> +typedef struct AudioPulsatorContext {
> +    const AVClass *class;
> +    int mode;
> +    double level_in;
> +    double level_out;
> +    double amount;
> +    double offset_l;
> +    double offset_r;
> +    double pwidth;
> +    double bpm;
> +    double hz;
> +    int ms;
> +    int timing;
> +
> +    SimpleLFO lfoL, lfoR;
> +} AudioPulsatorContext;
> +
> +#define OFFSET(x) offsetof(AudioPulsatorContext, x)
> +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
> +
> +static const AVOption apulsator_options[] = {
> +    { "level_in",   "set input gain", OFFSET(level_in),  AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, FLAGS, },
> +    { "level_out", "set output gain", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, FLAGS, },
> +    { "mode",             "set mode", OFFSET(mode),      AV_OPT_TYPE_INT,    {.i64=SINE}, SINE,   NB_MODES-1, FLAGS, "mode" },
> +    {   "sine",                 NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=SINE},    0,            0, FLAGS, "mode" },
> +    {   "triangle",             NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=TRIANGLE},0,            0, FLAGS, "mode" },
> +    {   "square",               NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=SQUARE},  0,            0, FLAGS, "mode" },
> +    {   "sawup",                NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=SAWUP},   0,            0, FLAGS, "mode" },
> +    {   "sawdown",              NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=SAWDOWN}, 0,            0, FLAGS, "mode" },
> +    { "amount",     "set modulation", OFFSET(amount),    AV_OPT_TYPE_DOUBLE, {.dbl=1},       0,            1, FLAGS },
> +    { "offset_l",     "set offset L", OFFSET(offset_l),  AV_OPT_TYPE_DOUBLE, {.dbl=0},       0,            1, FLAGS },
> +    { "offset_r",     "set offset R", OFFSET(offset_r),  AV_OPT_TYPE_DOUBLE, {.dbl=.5},      0,            1, FLAGS },
> +    { "width",     "set pulse width", OFFSET(pwidth),    AV_OPT_TYPE_DOUBLE, {.dbl=1},       0,            2, FLAGS },
> +    { "timing",         "set timing", OFFSET(timing),    AV_OPT_TYPE_INT,    {.i64=2},       0, NB_TIMINGS-1, FLAGS, "timing" },
> +    {   "bpm",                  NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=UNIT_BPM},  0,          0, FLAGS, "timing" },
> +    {   "ms",                   NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=UNIT_MS},   0,          0, FLAGS, "timing" },
> +    {   "hz",                   NULL, 0,                 AV_OPT_TYPE_CONST,  {.i64=UNIT_HZ},   0,          0, FLAGS, "timing" },
> +    { "bpm",               "set BPM", OFFSET(bpm),       AV_OPT_TYPE_DOUBLE, {.dbl=120},    30,          300, FLAGS },
> +    { "ms",                 "set ms", OFFSET(ms),        AV_OPT_TYPE_INT,    {.i64=500},    10,         2000, FLAGS },
> +    { "hz",          "set frequency", OFFSET(hz),        AV_OPT_TYPE_DOUBLE, {.dbl=2},    0.01,          100, FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(apulsator);
> +
> +static void lfo_advance(SimpleLFO *lfo, unsigned count)
> +{
> +    lfo->phase = fabs(lfo->phase + count * lfo->freq / lfo->srate);
> +    if (lfo->phase >= 1)
> +        lfo->phase = fmod(lfo->phase, 1);
> +}
> +
> +static double lfo_get_value(SimpleLFO *lfo)
> +{
> +    double phs = FFMIN(100, lfo->phase / FFMIN(1.99, FFMAX(0.01, lfo->pwidth)) + lfo->offset);
> +    double val;
> +
> +    if (phs > 1)
> +        phs = fmod(phs, 1.);
> +
> +    switch (lfo->mode) {
> +    case SINE:
> +        val = sin(phs * 2 * M_PI);
> +        break;
> +    case TRIANGLE:
> +        if (phs > 0.75)
> +            val = (phs - 0.75) * 4 - 1;
> +        else if (phs > 0.5)
> +            val = (phs - 0.5) * 4 * -1;
> +        else if (phs > 0.25)
> +            val = 1 - (phs - 0.25) * 4;
> +        else
> +            val = phs * 4;

Why didn't you remove the useless branch and replace with a simple
-4*phs + 2 for 0.25 to 0.75 case?

I guess you did not remove the branch because you believed (but have
not substantiated) that some notion of "bit-exactness" (highly dubious
since floating point is not reproducible exactly across all platforms)
with respect to some reference is violated. This "reference" is copied
over from some other source code with no link whatsoever in the commit
message or for the reviewer; only a simple copyright notice, making it
impossible for a reviewer to do a proper review.

[...]


More information about the ffmpeg-devel mailing list