From 1c0210c7981b6a61043d9171f506b435ff5a1f5e Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Tue, 29 Apr 2014 18:53:16 +0200 Subject: lavfi: add Bauer stereo-to-binaural audio filter Signed-off-by: Anton Khirnov --- Changelog | 1 + configure | 4 + doc/filters.texi | 30 +++++++ libavfilter/Makefile | 1 + libavfilter/af_bs2b.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/version.h | 2 +- 7 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 libavfilter/af_bs2b.c diff --git a/Changelog b/Changelog index eddcf1d88b..2dac228957 100644 --- a/Changelog +++ b/Changelog @@ -24,6 +24,7 @@ version : - Silicon Graphics Movie demuxer - On2 AVC (Audio for Video) decoder - support for decoding through DXVA2 in avconv +- libbs2b-based stereo-to-binaural audio filter version 10: diff --git a/configure b/configure index 30f90eb260..47fd690a4f 100755 --- a/configure +++ b/configure @@ -178,6 +178,7 @@ External library support: --enable-bzlib enable bzlib [autodetect] --enable-frei0r enable frei0r video filtering --enable-gnutls enable gnutls [no] + --enable-libbs2b enable bs2b DSP library [no] --enable-libcdio enable audio CD grabbing with libcdio --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 and libraw1394 [no] @@ -1124,6 +1125,7 @@ EXTERNAL_LIBRARY_LIST=" bzlib frei0r gnutls + libbs2b libcdio libdc1394 libfaac @@ -2085,6 +2087,7 @@ unix_protocol_select="network" # filters blackframe_filter_deps="gpl" boxblur_filter_deps="gpl" +bs2b_filter_deps="libbs2b" cropdetect_filter_deps="gpl" delogo_filter_deps="gpl" drawtext_filter_deps="libfreetype" @@ -4027,6 +4030,7 @@ enabled avisynth && { { check_header "avisynth/avisynth_c.h" && check_l die "ERROR: LoadLibrary/dlopen not found, or avisynth header not found"; } enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init +enabled libbs2b && require_pkg_config libbs2b bs2b.h bs2b_open enabled libfaac && require2 libfaac "stdint.h faac.h" faacEncGetVersion -lfaac enabled libfdk_aac && require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac enabled libfontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit diff --git a/doc/filters.texi b/doc/filters.texi index d10a107b64..5f9d1f8388 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -445,6 +445,36 @@ avconv -i INPUT -af atrim=end_sample=1000 @end itemize +@section bs2b +Bauer stereo to binaural transformation, which improves headphone listening of +stereo audio records. + +It accepts the following parameters: +@table @option + +@item profile +Pre-defined crossfeed level. +@table @option + +@item default +Default level (fcut=700, feed=50). + +@item cmoy +Chu Moy circuit (fcut=700, feed=60). + +@item jmeier +Jan Meier circuit (fcut=650, feed=95). + +@end table + +@item fcut +Cut frequency (in Hz). + +@item feed +Feed level (in Hz). + +@end table + @section channelsplit Split each channel from an input audio stream into a separate output stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 9c5c666867..6537a8a21b 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -33,6 +33,7 @@ OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o OBJS-$(CONFIG_ATRIM_FILTER) += trim.o +OBJS-$(CONFIG_BS2B_FILTER) += af_bs2b.o OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o OBJS-$(CONFIG_COMPAND_FILTER) += af_compand.o diff --git a/libavfilter/af_bs2b.c b/libavfilter/af_bs2b.c new file mode 100644 index 0000000000..25e786761f --- /dev/null +++ b/libavfilter/af_bs2b.c @@ -0,0 +1,222 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Bauer stereo-to-binaural filter + */ + +#include + +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct Bs2bContext { + const AVClass *class; + + int profile; + int fcut; + int feed; + + t_bs2bdp bs2bp; + + void (*filter)(t_bs2bdp bs2bdp, uint8_t *sample, int n); +} Bs2bContext; + +#define OFFSET(x) offsetof(Bs2bContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption options[] = { + { "profile", "Apply a pre-defined crossfeed level", + OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, INT_MAX, A, "profile" }, + { "default", "default profile", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, 0, A, "profile" }, + { "cmoy", "Chu Moy circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_CMOY_CLEVEL }, 0, 0, A, "profile" }, + { "jmeier", "Jan Meier circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_JMEIER_CLEVEL }, 0, 0, A, "profile" }, + { "fcut", "Set cut frequency (in Hz)", + OFFSET(fcut), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BS2B_MAXFCUT, A }, + { "feed", "Set feed level (in Hz)", + OFFSET(feed), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BS2B_MAXFEED, A }, + { NULL }, +}; + +static const AVClass bs2b_class = { + .class_name = "bs2b filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static av_cold int init(AVFilterContext *ctx) +{ + Bs2bContext *bs2b = ctx->priv; + + if (!(bs2b->bs2bp = bs2b_open())) + return AVERROR(ENOMEM); + + bs2b_set_level(bs2b->bs2bp, bs2b->profile); + + if (bs2b->fcut) + bs2b_set_level_fcut(bs2b->bs2bp, bs2b->fcut); + + if (bs2b->feed) + bs2b_set_level_feed(bs2b->bs2bp, bs2b->feed); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + Bs2bContext *bs2b = ctx->priv; + + if (bs2b->bs2bp) + bs2b_close(bs2b->bs2bp); +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; + + 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, + }; + + if (ff_add_channel_layout(&layouts, AV_CH_LAYOUT_STEREO) != 0) + 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 filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + int ret; + AVFrame *out_frame; + + Bs2bContext *bs2b = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + + 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(out_frame, frame); + ret = av_frame_copy_props(out_frame, frame); + if (ret < 0) { + av_frame_free(&out_frame); + av_frame_free(&frame); + return ret; + } + } + + bs2b->filter(bs2b->bs2bp, out_frame->extended_data[0], out_frame->nb_samples); + + if (frame != out_frame) + av_frame_free(&frame); + + return ff_filter_frame(outlink, out_frame); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + Bs2bContext *bs2b = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + + int srate = inlink->sample_rate; + + switch (inlink->format) { + case AV_SAMPLE_FMT_U8: + bs2b->filter = bs2b_cross_feed_u8; + break; + case AV_SAMPLE_FMT_S16: + bs2b->filter = bs2b_cross_feed_s16; + break; + case AV_SAMPLE_FMT_S32: + bs2b->filter = bs2b_cross_feed_s32; + break; + case AV_SAMPLE_FMT_FLT: + bs2b->filter = bs2b_cross_feed_f; + break; + case AV_SAMPLE_FMT_DBL: + bs2b->filter = bs2b_cross_feed_d; + break; + default: + return AVERROR_BUG; + } + + if ((srate < BS2B_MINSRATE) || (srate > BS2B_MAXSRATE)) + return AVERROR(ENOSYS); + + bs2b_set_srate(bs2b->bs2bp, srate); + + return 0; +} + +static const AVFilterPad bs2b_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad bs2b_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_af_bs2b = { + .name = "bs2b", + .description = NULL_IF_CONFIG_SMALL("Bauer stereo-to-binaural filter."), + .query_formats = query_formats, + .priv_size = sizeof(Bs2bContext), + .priv_class = &bs2b_class, + .init = init, + .uninit = uninit, + .inputs = bs2b_inputs, + .outputs = bs2b_outputs, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index d7bb47ad5a..67a298d5fc 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -53,6 +53,7 @@ void avfilter_register_all(void) REGISTER_FILTER(ASPLIT, asplit, af); REGISTER_FILTER(ASYNCTS, asyncts, af); REGISTER_FILTER(ATRIM, atrim, af); + REGISTER_FILTER(BS2B, bs2b, af); REGISTER_FILTER(CHANNELMAP, channelmap, af); REGISTER_FILTER(CHANNELSPLIT, channelsplit, af); REGISTER_FILTER(COMPAND, compand, af); diff --git a/libavfilter/version.h b/libavfilter/version.h index 5093e50a09..de64b8b4a9 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 4 -#define LIBAVFILTER_VERSION_MINOR 4 +#define LIBAVFILTER_VERSION_MINOR 5 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ -- cgit v1.2.3