mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
lavfi: add earwax audio filter, ported from Sox
Signed-off-by: Stefano Sabatini <stefasab@gmail.com>
This commit is contained in:
parent
618ac71354
commit
fa4f06ab5e
@ -74,6 +74,7 @@ easier to use. The changes are:
|
||||
- replacement Indeo 3 decoder
|
||||
- new ffmpeg option: -map_channel
|
||||
- volume audio filter added
|
||||
- earwax audio filter added
|
||||
|
||||
|
||||
version 0.8:
|
||||
|
@ -224,6 +224,17 @@ expressed in the form "[@var{c0} @var{c1} @var{c2} @var{c3} @var{c4} @var{c5}
|
||||
@var{c6} @var{c7}]"
|
||||
@end table
|
||||
|
||||
@section earwax
|
||||
|
||||
Make audio easier to listen to on headphones.
|
||||
|
||||
This filter adds `cues' to 44.1kHz stereo (i.e. audio CD format) audio
|
||||
so that when listened to on headphones the stereo image is moved from
|
||||
inside your head (standard for headphones) to outside and in front of
|
||||
the listener (standard for speakers).
|
||||
|
||||
Ported from SoX.
|
||||
|
||||
@section volume
|
||||
|
||||
Adjust the input audio volume.
|
||||
|
@ -28,6 +28,7 @@ OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o
|
||||
OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o
|
||||
OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o
|
||||
OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o
|
||||
OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o
|
||||
OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o
|
||||
|
||||
OBJS-$(CONFIG_ABUFFER_FILTER) += asrc_abuffer.o
|
||||
|
161
libavfilter/af_earwax.c
Normal file
161
libavfilter/af_earwax.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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/audioconvert.h"
|
||||
#include "avfilter.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)
|
||||
{
|
||||
AVFilterFormats *formats = NULL;
|
||||
avfilter_add_format(&formats, AV_SAMPLE_FMT_S16);
|
||||
avfilter_set_common_sample_formats(ctx, formats);
|
||||
formats = NULL;
|
||||
avfilter_add_format(&formats, AV_CH_LAYOUT_STEREO);
|
||||
avfilter_set_common_channel_layouts(ctx, formats);
|
||||
formats = NULL;
|
||||
avfilter_add_format(&formats, AVFILTER_PACKED);
|
||||
avfilter_set_common_packing_formats(ctx, formats);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_input(AVFilterLink *inlink)
|
||||
{
|
||||
if (inlink->sample_rate != 44100) {
|
||||
av_log(inlink->dst, AV_LOG_ERROR,
|
||||
"The earwax filter only works for 44.1kHz audio. Insert "
|
||||
"a resample filter before this\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
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 = 32;
|
||||
for (j = 0; j < NUMTAPS; j++)
|
||||
sample += in[j] * filt[j];
|
||||
*out = sample >> 6;
|
||||
out++;
|
||||
in++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
|
||||
{
|
||||
AVFilterLink *outlink = inlink->dst->outputs[0];
|
||||
int16_t *taps, *endin, *in, *out;
|
||||
AVFilterBufferRef *outsamples =
|
||||
avfilter_get_audio_buffer(inlink, AV_PERM_WRITE,
|
||||
insamples->audio->nb_samples);
|
||||
taps = ((EarwaxContext *)inlink->dst->priv)->taps;
|
||||
out = (int16_t *)outsamples->data[0];
|
||||
in = (int16_t *)insamples ->data[0];
|
||||
|
||||
// copy part of new input and process with saved input
|
||||
memcpy(taps+NUMTAPS, in, NUMTAPS * sizeof(*taps));
|
||||
out = scalarproduct(taps, taps + NUMTAPS, out);
|
||||
|
||||
// process current input
|
||||
endin = in + insamples->audio->nb_samples * 2 - NUMTAPS;
|
||||
out = scalarproduct(in, endin, out);
|
||||
|
||||
// save part of input for next round
|
||||
memcpy(taps, endin, NUMTAPS * sizeof(*taps));
|
||||
|
||||
avfilter_filter_samples(outlink, outsamples);
|
||||
avfilter_unref_buffer(insamples);
|
||||
}
|
||||
|
||||
AVFilter avfilter_af_earwax = {
|
||||
.name = "earwax",
|
||||
.description = NULL_IF_CONFIG_SMALL("Widen the stereo image."),
|
||||
.query_formats = query_formats,
|
||||
.priv_size = sizeof(EarwaxContext),
|
||||
.inputs = (AVFilterPad[]) {{ .name = "default",
|
||||
.type = AVMEDIA_TYPE_AUDIO,
|
||||
.filter_samples = filter_samples,
|
||||
.config_props = config_input,
|
||||
.min_perms = AV_PERM_READ, },
|
||||
{ .name = NULL}},
|
||||
|
||||
.outputs = (AVFilterPad[]) {{ .name = "default",
|
||||
.type = AVMEDIA_TYPE_AUDIO, },
|
||||
{ .name = NULL}},
|
||||
};
|
@ -39,6 +39,7 @@ void avfilter_register_all(void)
|
||||
REGISTER_FILTER (ANULL, anull, af);
|
||||
REGISTER_FILTER (ARESAMPLE, aresample, af);
|
||||
REGISTER_FILTER (ASHOWINFO, ashowinfo, af);
|
||||
REGISTER_FILTER (EARWAX, earwax, af);
|
||||
REGISTER_FILTER (VOLUME, volume, af);
|
||||
|
||||
REGISTER_FILTER (ABUFFER, abuffer, asrc);
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "libavutil/rational.h"
|
||||
|
||||
#define LIBAVFILTER_VERSION_MAJOR 2
|
||||
#define LIBAVFILTER_VERSION_MINOR 46
|
||||
#define LIBAVFILTER_VERSION_MINOR 47
|
||||
#define LIBAVFILTER_VERSION_MICRO 0
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
|
Loading…
Reference in New Issue
Block a user