[FFmpeg-devel] [PATCH] avfilter: add anoisesrc

Ganesh Ajjanagadde gajjanag at mit.edu
Wed Nov 4 20:30:52 CET 2015


On Wed, Nov 4, 2015 at 2:18 PM, Kyle Swanson <k at ylo.ph> wrote:
> Hi,
>
> On Wed, Nov 4, 2015 at 12:30 PM, Nicolas George <george at nsup.org> wrote:
>> Thanks for the updated patch, see comments below.
>>
>> Le quartidi 14 brumaire, an CCXXIV, Kyle Swanson a écrit :
>>> Signed-off-by: Kyle Swanson <k at ylo.ph>
>>> ---
>>>  Changelog                    |   1 +
>>>  doc/filters.texi             |  36 +++++++
>>>  libavfilter/Makefile         |   1 +
>>>  libavfilter/allfilters.c     |   1 +
>>>  libavfilter/asrc_anoisesrc.c | 222 +++++++++++++++++++++++++++++++++++++++++++
>>>  libavfilter/version.h        |   4 +-
>>>  6 files changed, 263 insertions(+), 2 deletions(-)
>>>  create mode 100644 libavfilter/asrc_anoisesrc.c
>>>
>>> diff --git a/Changelog b/Changelog
>>> index 91955da..ca477de 100644
>>> --- a/Changelog
>>> +++ b/Changelog
>>> @@ -30,6 +30,7 @@ version <next>:
>>>  - innoHeim/Rsupport Screen Capture Codec decoder
>>>  - ADPCM AICA decoder
>>>  - Interplay ACM demuxer and audio decoder
>>> +- anoisesrc audio source
>>>
>>>
>>>  version 2.8:
>>> diff --git a/doc/filters.texi b/doc/filters.texi
>>> index 15ea77a..620d787 100644
>>> --- a/doc/filters.texi
>>> +++ b/doc/filters.texi
>>> @@ -3193,6 +3193,42 @@ ffplay -f lavfi flite=text='No more be grieved for which that thou hast done.'
>>>  For more information about libflite, check:
>>>  @url{http://www.speech.cs.cmu.edu/flite/}
>>>
>>> + at section anoisesrc
>>> +
>>> +Generate a noise audio signal.
>>> +
>>> +The filter accepts the following options:
>>> +
>>> + at table @option
>>> +
>>> + at item color, colour, c
>>> +Specify the color of noise. Available noise colors are white, pink, and brown. Default color is white.
>>> +
>>> + at item sample_rate, r
>>> +Specify the sample rate. Default value is 48000 Hz.
>>> +
>>> + at item duration, d
>>> +Specify the duration of the generated audio stream. Not specifying this option results in noise with an infinite length.
>>> +
>>> + at item amplitude, a
>>> +Specify the amplitude (0.0 - 1.0) of the generated audio stream. Default value is 1.0.
>>> +
>>> + at item seed, s
>>> +Specify a value used to seed the PRNG. Default value is 0.
>>> +
>>> + at end table
>>> +
>>> + at subsection Examples
>>> +
>>> + at itemize
>>> +
>>> + at item
>>> +Generate 60 seconds of pink noise, with a 44.1 kHz sampling rate and an amplitude of 0.5:
>>> + at example
>>> +anoisesrc=d=60:c=pink:r=44100:a=0.5
>>> + at end example
>>> + at end itemize
>>> +
>>>  @section sine
>>>
>>>  Generate an audio signal made of a sine wave with amplitude 1/8.
>>> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
>>> index 1b23085..5f60e15 100644
>>> --- a/libavfilter/Makefile
>>> +++ b/libavfilter/Makefile
>>> @@ -93,6 +93,7 @@ OBJS-$(CONFIG_VOLUMEDETECT_FILTER)           += af_volumedetect.o
>>>  OBJS-$(CONFIG_AEVALSRC_FILTER)               += aeval.o
>>>  OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o
>>
>>>  OBJS-$(CONFIG_FLITE_FILTER)                  += asrc_flite.o
>>> +OBJS-$(CONFIG_ANOISESRC_FILTER)              += asrc_anoisesrc.o
>>>  OBJS-$(CONFIG_SINE_FILTER)                   += asrc_sine.o
>>
>> Alphabetic order after renaming the filter.
>
> Yep. Will fix this.
>
>>
>>>
>>>  OBJS-$(CONFIG_ANULLSINK_FILTER)              += asink_anullsink.o
>>> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>>> index a538b81..e716174 100644
>>> --- a/libavfilter/allfilters.c
>>> +++ b/libavfilter/allfilters.c
>>> @@ -115,6 +115,7 @@ void avfilter_register_all(void)
>>>      REGISTER_FILTER(AEVALSRC,       aevalsrc,       asrc);
>>>      REGISTER_FILTER(ANULLSRC,       anullsrc,       asrc);
>>>      REGISTER_FILTER(FLITE,          flite,          asrc);
>>> +    REGISTER_FILTER(ANOISESRC,      anoisesrc,      asrc);
>>>      REGISTER_FILTER(SINE,           sine,           asrc);
>>>
>>>      REGISTER_FILTER(ANULLSINK,      anullsink,      asink);
>>> diff --git a/libavfilter/asrc_anoisesrc.c b/libavfilter/asrc_anoisesrc.c
>>> new file mode 100644
>>> index 0000000..d008d67
>>> --- /dev/null
>>> +++ b/libavfilter/asrc_anoisesrc.c
>>> @@ -0,0 +1,222 @@
>>> +/*
>>> + * Copyright (c) 2015 Kyle Swanson <k at ylo.ph>.
>>> + *
>>> + * 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"
>>> +#include "libavutil/lfg.h"
>>> +
>>> +typedef struct {
>>> +    const AVClass *class;
>>> +    int sample_rate;
>>> +    double amplitude;
>>> +    int64_t dur;
>>> +    char *color;
>>> +    int seed;
>>> +
>>> +    int infinite;
>>> +    double (*filter)(double white, double *buf);
>>> +    double* buf;
>>> +    int buf_size;
>>> +    AVLFG c;
>>> +} ANoiseSrcContext;
>>> +
>>> +#define OFFSET(x) offsetof(ANoiseSrcContext, x)
>>> +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
>>> +
>>> +static const AVOption anoisesrc_options[] = {
>>> +    { "sample_rate",  "set sample rate",  OFFSET(sample_rate),  AV_OPT_TYPE_INT,       {.i64 = 48000},    15,        INT_MAX,   FLAGS },
>>> +    { "r",            "set sample rate",  OFFSET(sample_rate),  AV_OPT_TYPE_INT,       {.i64 = 48000},    15,        INT_MAX,   FLAGS },
>>> +    { "amplitude",    "set amplitude",    OFFSET(amplitude),    AV_OPT_TYPE_DOUBLE,    {.dbl = 1.},       0.,        1.,        FLAGS },
>>> +    { "a",            "set amplitude",    OFFSET(amplitude),    AV_OPT_TYPE_DOUBLE,    {.dbl = 1.},       0.,        1.,        FLAGS },
>>> +    { "duration",     "set duration",     OFFSET(dur),          AV_OPT_TYPE_DURATION,  {.i64 = 0},       0,         INT64_MAX, FLAGS },
>>> +    { "d",            "set duration",     OFFSET(dur),          AV_OPT_TYPE_DURATION,  {.i64 = 0},       0,         INT64_MAX, FLAGS },
>>> +    { "color",        "set noise color",  OFFSET(color),        AV_OPT_TYPE_STRING,    {.str = "white"},  CHAR_MIN,  CHAR_MAX,  FLAGS },
>>> +    { "colour",       "set noise color",  OFFSET(color),        AV_OPT_TYPE_STRING,    {.str = "white"},  CHAR_MIN,  CHAR_MAX,  FLAGS },
>>> +    { "c",            "set noise color",  OFFSET(color),        AV_OPT_TYPE_STRING,    {.str = "white"},  CHAR_MIN,  CHAR_MAX,  FLAGS },
>>> +    { "seed",         "set random seed",  OFFSET(seed),         AV_OPT_TYPE_INT,       {.i64 = 0},        0,         UINT_MAX,   FLAGS },
>>> +    { "s",            "set random seed",  OFFSET(seed),         AV_OPT_TYPE_INT,       {.i64 = 0},        0,         UINT_MAX,   FLAGS },
>>> +    {NULL}
>>> +};
>>> +
>>> +AVFILTER_DEFINE_CLASS(anoisesrc);
>>> +
>>> +static av_cold int query_formats(AVFilterContext *ctx)
>>> +{
>>> +    ANoiseSrcContext *s = ctx->priv;
>>> +    static const int64_t chlayouts[] = { AV_CH_LAYOUT_MONO, -1 };
>>> +    int sample_rates[] = { s->sample_rate, -1 };
>>
>>> +    static const enum AVSampleFormat sample_fmts[] = {
>>> +        AV_SAMPLE_FMT_DBL,
>>> +        AV_SAMPLE_FMT_NONE
>>> +    };
>>
>> I already commented on that: please avoid floating-point computations unless
>> they are absolutely necessary.
>>
>
> I can change this, but most filters I've seen have used floating point
> sample formats. Anyone else have any opinions on this?
>
>>> +
>>> +    AVFilterFormats *formats;
>>> +    AVFilterChannelLayouts *layouts;
>>> +    int ret;
>>> +
>>> +    formats = ff_make_format_list(sample_fmts);
>>> +    if (!formats)
>>> +        return AVERROR(ENOMEM);
>>> +    ret = ff_set_common_formats (ctx, formats);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    layouts = avfilter_make_format64_list(chlayouts);
>>> +    if (!layouts)
>>> +        return AVERROR(ENOMEM);
>>> +    ret = ff_set_common_channel_layouts(ctx, layouts);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    formats = ff_make_format_list(sample_rates);
>>> +    if (!formats)
>>> +        return AVERROR(ENOMEM);
>>> +    return ff_set_common_samplerates(ctx, formats);
>>> +}
>>> +
>>> +static double white_filter(double white, double *buf) {
>>> +    return white;
>>> +};
>>> +
>>> +static double pink_filter(double white, double *buf) {
>>> +    double pink;
>>> +
>>> +    /* http://www.musicdsp.org/files/pink.txt */
>>> +    buf[0] = 0.99886 * buf[0] + white * 0.0555179;
>>> +    buf[1] = 0.99332 * buf[1] + white * 0.0750759;
>>> +    buf[2] = 0.96900 * buf[2] + white * 0.1538520;
>>> +    buf[3] = 0.86650 * buf[3] + white * 0.3104856;
>>> +    buf[4] = 0.55000 * buf[4] + white * 0.5329522;
>>> +    buf[5] = -0.7616 * buf[5] - white * 0.0168980;
>>> +    pink = buf[0] + buf[1] + buf[2] + buf[3] + buf[4] + buf[5] + buf[6] + white * 0.5362;
>>> +    buf[6] = white * 0.115926;
>>> +    return pink * 0.11;
>>> +}
>>> +
>>> +static double brown_filter(double white, double *buf) {
>>> +    double brown;
>>> +
>>> +    brown = ((0.02 * white) + buf[0]) / 1.02;
>>> +    buf[0] = brown;
>>> +    return brown * 3.5;
>>> +}
>>> +
>>> +static av_cold int config_props(AVFilterLink *outlink)
>>> +{
>>> +    AVFilterContext *ctx = outlink->src;
>>> +    ANoiseSrcContext *s = ctx->priv;
>>> +    if (s->dur == 0) {
>>> +        s->infinite = 1;
>>> +    } else {
>>> +        s->dur = av_rescale(s->dur, s->sample_rate, AV_TIME_BASE);
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static av_cold int init(AVFilterContext *ctx) {
>>> +    ANoiseSrcContext *s = ctx->priv;
>>> +
>>> +    av_lfg_init(&s->c, s->seed);
>>> +
>>
>>> +    if (!strcmp(s->color, "pink")) {
>>> +        s->filter = pink_filter;
>>> +        s->buf_size = 7;
>>> +    } else if(!strcmp(s->color, "brown")) {
>>> +        s->filter = brown_filter;
>>> +        s->buf_size = 1;
>>> +    } else if(!strcmp(s->color, "white")) {
>>> +        s->filter = white_filter;
>>> +        s->buf_size = 0;
>>> +    } else {
>>> +        av_log(ctx, AV_LOG_ERROR, "Invalid noise color: %s\n", s->color);
>>> +        return AVERROR_OPTION_NOT_FOUND;
>>> +    }
>>
>> Better use AV_OPT_TYPE_FLAG for that.
>>
>>> +
>>> +    if (s->buf_size > 0) {
>>> +        s->buf = av_malloc_array(s->buf_size, sizeof(double));
>>
>> Unless I am mistaken, buf_size will be at most 7. I do not think allocating
>> it dynamically is worth it, just allocate it directly in the structure.
>>
>
> This makes it easier for someone to add different flavors of filtered
> noise later on, and define their own sample buffer. I understand this
> is a tiny buffer, but why allocate too much memory if we won't need
> it?

I am with Nicolas on this one. Any audio noise source will be of a
form generated by a simple linear filter over white noise, at least
for the foreseeable future of this filter. It will be bounded by a
number < 50 or so at max, and such an array is never worth the
headache of malloc and free.

The memory penalty is minimal. In fact, underneath malloc often aligns
stuff so that even though your buffer (as accessible to you) has say
23 bytes, malloc will keep 32 bytes underneath.

In any case, if you really need a malloc'ed buffer at some point, it
may be added. Simple solutions should be favored when possible.

>
>>> +        if (!s->buf)
>>> +            return AVERROR(ENOMEM);
>>> +        for (int i = 0; i < s->buf_size; i++)
>>> +            s->buf[i] = 0;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int request_frame(AVFilterLink *outlink) {
>>> +    AVFilterContext *ctx = outlink->src;
>>> +    ANoiseSrcContext *s = ctx->priv;
>>> +    AVFrame *frame;
>>> +    int nb_samples, i;
>>> +    double *dst;
>>> +
>>> +    if (!s->infinite && s->dur <= 0) {
>>> +        return AVERROR_EOF;
>>> +    } else if (!s->infinite && s->dur < 1024) {
>>> +        nb_samples = s->dur;
>>> +    } else {
>>> +        nb_samples = 1024;
>>> +    }
>>> +
>>> +    if (!(frame = ff_get_audio_buffer(outlink, nb_samples)))
>>> +        return AVERROR(ENOMEM);
>>> +
>>> +    dst = (double *)frame->data[0];
>>> +    for (i = 0; i < nb_samples; i++) {
>>> +        double white;
>>> +        white = s->amplitude * ((2 * ((double) av_lfg_get(&s->c) / 0xffffffff)) - 1);
>>> +        dst[i] = s->filter(white, s->buf);
>>> +    }
>>> +
>>> +    s->dur -= nb_samples;
>>> +    return ff_filter_frame(outlink, frame);
>>> +}
>>> +
>>> +static av_cold void uninit(AVFilterContext *ctx) {
>>> +    ANoiseSrcContext *s = ctx->priv;
>>> +    if (s->buf_size > 0)
>>> +        av_freep(&s->buf);
>>> +}
>>> +
>>> +static const AVFilterPad anoisesrc_outputs[] = {
>>> +    {
>>> +        .name          = "default",
>>> +        .type          = AVMEDIA_TYPE_AUDIO,
>>> +        .request_frame = request_frame,
>>> +        .config_props  = config_props,
>>> +    },
>>> +    { NULL }
>>> +};
>>> +
>>> +AVFilter ff_asrc_anoisesrc = {
>>> +    .name          = "anoisesrc",
>>> +    .description   = NULL_IF_CONFIG_SMALL("Generate a noise audio signal."),
>>> +    .init          = init,
>>> +    .uninit        = uninit,
>>> +    .query_formats = query_formats,
>>> +    .priv_size     = sizeof(ANoiseSrcContext),
>>> +    .inputs        = NULL,
>>> +    .outputs       = anoisesrc_outputs,
>>> +    .priv_class    = &anoisesrc_class,
>>> +};
>>> diff --git a/libavfilter/version.h b/libavfilter/version.h
>>> index c3ecf91..ed3b642 100644
>>> --- a/libavfilter/version.h
>>> +++ b/libavfilter/version.h
>>> @@ -30,8 +30,8 @@
>>>  #include "libavutil/version.h"
>>>
>>>  #define LIBAVFILTER_VERSION_MAJOR   6
>>> -#define LIBAVFILTER_VERSION_MINOR  14
>>> -#define LIBAVFILTER_VERSION_MICRO 101
>>> +#define LIBAVFILTER_VERSION_MINOR  15
>>> +#define LIBAVFILTER_VERSION_MICRO 100
>>>
>>>  #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
>>>                                                 LIBAVFILTER_VERSION_MINOR, \
>>
>> Regards,
>>
>> --
>>   Nicolas George
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> Thanks for your comments. I'll wait to see if anyone else has anything
> to add, and I'll send an updated patch.
>
> Kyle
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list