1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

avfilter: add audio pulsator filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Paul B Mahol 2015-11-28 19:50:32 +01:00
parent ca203e9985
commit c4f7b8f0db
6 changed files with 315 additions and 1 deletions

View File

@ -37,6 +37,7 @@ version <next>:
- compensationdelay filter
- acompressor filter
- support encoding 16-bit RLE SGI images
- apulsator filter
version 2.8:

View File

@ -1030,6 +1030,63 @@ It accepts the following values:
@end table
@end table
@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:
@table @option
@item level_in
Set input gain. By default it is 1. Range is [0.015625 - 64].
@item level_out
Set output gain. By default it is 1. Range is [0.015625 - 64].
@item mode
Set waveform shape the LFO will use. Can be one of: sine, triangle, square,
sawup or sawdown. Default is sine.
@item amount
Set modulation. Define how much of original signal is affected by the LFO.
@item offset_l
Set left channel offset. Default is 0. Allowed range is [0 - 1].
@item offset_r
Set right channel offset. Default is 0.5. Allowed range is [0 - 1].
@item width
Set pulse width. Default is 1. Allowed range is [0 - 2].
@item timing
Set possible timing mode. Can be one of: bpm, ms or hz. Default is hz.
@item bpm
Set bpm. Default is 120. Allowed range is [30 - 300]. Only used if timing
is set to bpm.
@item ms
Set ms. Default is 500. Allowed range is [10 - 2000]. Only used if timing
is set to ms.
@item hz
Set frequency in Hz. Default is 2. Allowed range is [0.01 - 100]. Only used
if timing is set to hz.
@end table
@anchor{aresample}
@section aresample

View File

@ -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

254
libavfilter/af_apulsator.c Normal file
View File

@ -0,0 +1,254 @@
/*
* 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.25)
val = -4 * phs + 2;
else
val = phs * 4;
break;
case SQUARE:
val = phs < 0.5 ? -1 : +1;
break;
case SAWUP:
val = phs * 2 - 1;
break;
case SAWDOWN:
val = 1 - phs * 2;
break;
}
return val * lfo->amount;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
AudioPulsatorContext *s = ctx->priv;
const double *src = (const double *)in->data[0];
const int nb_samples = in->nb_samples;
const double level_out = s->level_out;
const double level_in = s->level_in;
const double amount = s->amount;
AVFrame *out;
double *dst;
int n;
if (av_frame_is_writable(in)) {
out = in;
} else {
out = ff_get_audio_buffer(inlink, in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
av_frame_copy_props(out, in);
}
dst = (double *)out->data[0];
for (n = 0; n < nb_samples; n++) {
double outL;
double outR;
double inL = src[0] * level_in;
double inR = src[1] * level_in;
double procL = inL;
double procR = inR;
procL *= lfo_get_value(&s->lfoL) * 0.5 + amount / 2;
procR *= lfo_get_value(&s->lfoR) * 0.5 + amount / 2;
outL = procL + inL * (1 - amount);
outR = procR + inR * (1 - amount);
outL *= level_out;
outR *= level_out;
dst[0] = outL;
dst[1] = outR;
lfo_advance(&s->lfoL, 1);
lfo_advance(&s->lfoR, 1);
dst += 2;
src += 2;
}
if (in != out)
av_frame_free(&in);
return ff_filter_frame(outlink, out);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterChannelLayouts *layout = NULL;
AVFilterFormats *formats = NULL;
int ret;
if ((ret = ff_add_format (&formats, AV_SAMPLE_FMT_DBL )) < 0 ||
(ret = ff_set_common_formats (ctx , formats )) < 0 ||
(ret = ff_add_channel_layout (&layout , AV_CH_LAYOUT_STEREO)) < 0 ||
(ret = ff_set_common_channel_layouts (ctx , layout )) < 0)
return ret;
formats = ff_all_samplerates();
return ff_set_common_samplerates(ctx, formats);
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
AudioPulsatorContext *s = ctx->priv;
double freq;
switch (s->timing) {
case UNIT_BPM: freq = s->bpm / 60; break;
case UNIT_MS: freq = 1 / (s->ms / 1000.); break;
case UNIT_HZ: freq = s->hz; break;
}
s->lfoL.freq = freq;
s->lfoR.freq = freq;
s->lfoL.mode = s->mode;
s->lfoR.mode = s->mode;
s->lfoL.offset = s->offset_l;
s->lfoR.offset = s->offset_r;
s->lfoL.srate = inlink->sample_rate;
s->lfoR.srate = inlink->sample_rate;
s->lfoL.amount = s->amount;
s->lfoR.amount = s->amount;
s->lfoL.pwidth = s->pwidth;
s->lfoR.pwidth = s->pwidth;
return 0;
}
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter ff_af_apulsator = {
.name = "apulsator",
.description = NULL_IF_CONFIG_SMALL("Audio pulsator."),
.priv_size = sizeof(AudioPulsatorContext),
.priv_class = &apulsator_class,
.query_formats = query_formats,
.inputs = inputs,
.outputs = outputs,
};

View File

@ -62,6 +62,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(APAD, apad, af);
REGISTER_FILTER(APERMS, aperms, af);
REGISTER_FILTER(APHASER, aphaser, af);
REGISTER_FILTER(APULSATOR, apulsator, af);
REGISTER_FILTER(AREALTIME, arealtime, af);
REGISTER_FILTER(ARESAMPLE, aresample, af);
REGISTER_FILTER(AREVERSE, areverse, af);

View File

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