From patchwork Sat Aug 19 21:33:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sanchit Sinha X-Patchwork-Id: 4756 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.46.211 with SMTP id u202csp2317321vsu; Sat, 19 Aug 2017 14:39:42 -0700 (PDT) X-Received: by 10.223.141.230 with SMTP id o93mr7696645wrb.110.1503178782612; Sat, 19 Aug 2017 14:39:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1503178782; cv=none; d=google.com; s=arc-20160816; b=rwup8+lOGEa7ppoTV8BIKszZB6xuOGr9ckc/y5A+CzzkCdYF0Is5tqC5QF6G9jXP1D 9l9Fl8LYVA3XO7gr4MZT8NGYuzkzTpifF/ppheStExqciOzVRELe7SxUvsymRn5eSxcI CdCX1CbMonokov6Pgpy03OPL/2Dl8kIdlfkD/p6Uwm0HDsYfSLq+50Qx6W8FugmwK7Ui 5aDFjz+kfFYDqAhMo97dcIEL2n1Fxw65rj/jGwmIaFxqFil4WH9fxSEVwrV9fy5Kd8k9 WZYy3IPV8UMyN9tvX3IrySLcnom+Kl2m7kpB2Unej6RRIzDFhfau3x2XATjg0VxDeheo cERg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:to :message-id:date:from:references:in-reply-to:mime-version :dkim-signature:delivered-to:arc-authentication-results; bh=fEdGEIZaqmDA9eqVU8iI3R5lBJfL0tCllMruu+rrC2s=; b=pIehYFBuoxhUqcmbLJzz2zABoSTOgc0+zMNoMiXb68w289VuQCevZckcfgA7lU1mnU NSErnCue/x8ymc/Bx+KknBb0kEwdWOkaOEXyUCqVqDKNmEI4+3JMjJCdb1Fiz7oggfpl YtUKmGc2fFDxu0Jg+3IdO+j5mqEKal+lLHgnyqoHxgI/zu816RYaoQojKUUfQxzLci6T /JZx97pTBrwJaKGY3kzYEvQXMOCa3/wexqJGFj1xg46z+h+/G1kMvBeIfGiHo4WP077+ lDJY5St3L60ToWKfoUkTDTRBIHrWSCUIND1G50cVqbsxuCbkhi9DQvJ/koYbaXK9XHUe KK9w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@iiitd.ac.in header.s=google header.b=SEEOlbM7; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 56si6971656wrt.177.2017.08.19.14.39.42; Sat, 19 Aug 2017 14:39:42 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@iiitd.ac.in header.s=google header.b=SEEOlbM7; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 657336883D5; Sun, 20 Aug 2017 00:39:32 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f48.google.com (mail-wm0-f48.google.com [74.125.82.48]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 770BA6882C5 for ; Sun, 20 Aug 2017 00:39:25 +0300 (EEST) Received: by mail-wm0-f48.google.com with SMTP id t201so22929951wmt.1 for ; Sat, 19 Aug 2017 14:39:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iiitd.ac.in; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=B4O0cui05F94CrxNcdkO5thhq3b76gk7Z8JPPZzCOAM=; b=SEEOlbM76/ec+wr7steKN5nBNiWIjTRMRfQmfBmsyL3ygyuIsugur6hcAmHAgTw1hh qLCPObBt2LleDHbOMCkVQLdGNx/FYBsUs4eGz0OHMwDwInrFaNtiCpbFEnoj54yt+Xer 0OGsQCsVXvXxJetNWC86IfW6ofPujIa+Z2CSs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=B4O0cui05F94CrxNcdkO5thhq3b76gk7Z8JPPZzCOAM=; b=fVnQOR7kapmUg7fHmH9RtXvjhKgdQBoof95wcHxty17a23perKmMMLxmgczEPPQnHH bouwvsm9xZX/5lsQ16WRJY+7JGXtyQ8tvVhMIRUXyexuARxavdC5f25aLfRtMIaNRXH7 DGjBztDXOnzFe6HlTskMvpIyX8Frg1QXKWsfIblc+HObrDhuJbxUWnVCKTFaJcww2Te3 BgZc87wO0t+7Mc0pjW1kjuhRLJbyNfhvDCjmdkTh5UpBAztptWxOXgkWjdWwct/JRAmN sRSb5TDm+Pj00b8CgXzPEbykP2gHU4a8z1hytPT/43Qg+kzAPNKP/lp0lqsK+LB87OVR aI1g== X-Gm-Message-State: AHYfb5gg48w3XFHpr7X192fINVnXJAaVH6JYQS4+JeicHym8bZNnoLcZ wItk268Nyj2COMTABq/DzhzW0dHshBRB X-Received: by 10.80.170.58 with SMTP id o55mr8334091edc.164.1503178424345; Sat, 19 Aug 2017 14:33:44 -0700 (PDT) MIME-Version: 1.0 Received: by 10.80.147.228 with HTTP; Sat, 19 Aug 2017 14:33:43 -0700 (PDT) In-Reply-To: References: From: Sanchit Sinha Date: Sun, 20 Aug 2017 03:03:43 +0530 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: Re: [FFmpeg-devel] [PATCH] libavfilter/af_ambisonic.c Added File for Ambisonic Filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" On Fri, Aug 18, 2017 at 2:57 PM, Mailtrack Reminder wrote: > ⚠️ Your email to ffmpeg-devel@ffmpeg.org has not been opened yet. Be > reminded in 24H > > or 48H > > (disable > ) > > From 93d709a6c04af3749cd12ed98791bceb05f3f626 Mon Sep 17 00:00:00 2001 From: Sanchit Sinha Date: Sun, 20 Aug 2017 03:02:00 +0530 Subject: [PATCH] libavfilter/af_ambisonic.c:Added File for Ambisonic Decoding Signed-off-by: Sanchit Sinha --- Changelog | 1 + libavfilter/Makefile | 1 + libavfilter/af_ambisonic.c | 736 +++++++++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 4 files changed, 739 insertions(+) create mode 100644 libavfilter/af_ambisonic.c diff --git a/Changelog b/Changelog index 7a6987a..f4fb9df 100644 --- a/Changelog +++ b/Changelog @@ -33,6 +33,7 @@ version : - tlut2 video filter - floodfill video filter - pseudocolor video filter +- Ambisonic Decoder version 3.3: - CrystalHD decoder moved to new decode API diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 1d92dc1..32bf378 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -45,6 +45,7 @@ OBJS-$(CONFIG_AINTERLEAVE_FILTER) += f_interleave.o OBJS-$(CONFIG_ALIMITER_FILTER) += af_alimiter.o OBJS-$(CONFIG_ALLPASS_FILTER) += af_biquads.o OBJS-$(CONFIG_ALOOP_FILTER) += f_loop.o +OBJS-$(CONFIG_AMBISONIC_FILTER) += af_ambisonic.o OBJS-$(CONFIG_AMERGE_FILTER) += af_amerge.o OBJS-$(CONFIG_AMETADATA_FILTER) += f_metadata.o OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o diff --git a/libavfilter/af_ambisonic.c b/libavfilter/af_ambisonic.c new file mode 100644 index 0000000..d18e7e2 --- /dev/null +++ b/libavfilter/af_ambisonic.c @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2017 Sanchit Sinha + * 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/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include +#include +#define SQRT_3 1.732051 + +enum FilterType { + shelf, + nearfield +}; + +enum InputFormat { + N3D , + SN3D , + FURMUL +}; + +enum Rotate { + TILT , + TUMBLE, + YAW +}; + +enum Layouts { + MONO , + STEREO , + TRIANGLE , + SQUARE , + PENTAGON , + HEXAGON , + HEPTAGON , + OCTAGON , + TETRAHEDRON , + OCTAHEDRON , + CUBE , + DODECAHEDRON, + ICOSAHEDRON +}; + +typedef struct Cache { + double i1, i2; + double o1, o2; +} Cache; + +/*Decoding matrix for 1st order files. Similar can be done for 2nd, 3rd etc*/ +static const struct { + int speakers; + float matrix[22][15]; +} ambisonic_matrix[]= { + [MONO]={ + .speakers=1, + .matrix={ + {0.22156, 0, 0, 0}, + }, + }, + [TRIANGLE]={ + .speakers=3, + .matrix={ + {0.17836, 0.32555, 0.18795, 0}, + {0.17836, 0 ,-0.37591, 0}, + {0.17836,-0.32555, 0.18795, 0}, + }, + }, + [SQUARE]={ + .speakers=4, + .matrix={ + {0.39388, 0.18690, 0.18690, 0}, + {0.39388,-0.18690, 0.18690, 0}, + {0.39388,-0.18690,-0.18690, 0}, + {0.39388, 0.18690,-0.18690, 0}, + }, + }, + [PENTAGON]={ + .speakers=5, + .matrix={ + {0.20195, 0 , 0.33420, 0}, + {0.11356, 0.2901 , 0.04186, 0}, + {0.19654,-0.07993,-0.34782, 0}, + {0.19654, 0.07993,-0.34782, 0}, + {0.19654,-0.2901 , 0.04186, 0}, + }, + }, + [HEXAGON]={ + .speakers=6, + .matrix={ + {0.26259, 0 , 0.31326, 0}, + {0.26259, 0.27129, 0.15663, 0}, + {0.26259, 0.27129, -0.15663, 0}, + {0.26259, 0 , -0.31326, 0}, + {0.26259,-0.27129, -0.15663, 0}, + {0.26259,-0.27129, 0.15663, 0}, + }, + }, + [HEPTAGON]={ + .speakers=7, + .matrix={ + {0.22501,-0.0 , 0.26846, 0}, + {0.22507, 0.20989, 0.16741, 0}, + {0.22507, 0.26180, -0.05969, 0}, + {0.22511, 0.11651, -0.24195, 0}, + {0.22511,-0.11651, -0.24195, 0}, + {0.22507,-0.26180, -0.05969, 0}, + {0.22507,-0.20989, 0.16741, 0}, + }, + }, + [OCTAGON]={ + .speakers=8, + .matrix={ + {0.19694, 0.08991, 0.21706, 0}, + {0.19694, 0.21706, 0.08991, 0}, + {0.19694, 0.21706, -0.08991, 0}, + {0.19694, 0.08991, -0.21706, 0}, + {0.19694, -0.08991, -0.21706, 0}, + {0.19694, -0.21706, -0.08991, 0}, + {0.19694, -0.21706, 0.08991, 0}, + {0.19694, -0.08991, 0.21706, 0}, + }, + }, + [OCTAHEDRON]={ + .speakers=6, + .matrix={ + {0.45832, 0.41566, 0.00000, 0.13183}, + {0.95964, 0.41566, 0.00000, -0.36696}, + {0.45832, -0.41566, 0.00000, 0.13183}, + {0.95964, -0.41566, 0.00000, -0.36696}, + {0.35449, 0.00000, 0.00000, 0.23513}, + {0.35449, 0.00000, 0.00000, 0.23513}, + }, + }, + [CUBE]={ + .speakers=8, + .matrix={ + {0.14269, 0.13909, 0.28611, 0.13909}, + {0.14269, 0.13909, -0.28611, 0.13909}, + {0.14269,-0.13909, 0.28611, 0.13909}, + {0.14269,-0.13909, -0.28611, 0.13909}, + {0.14269, 0.13909, 0.28611,-0.13909}, + {0.14269, 0.13909, -0.28611,-0.13909}, + {0.14269,-0.13909, 0.28611,-0.13909}, + {0.14269,-0.13909, -0.28611,-0.13909}, + }, + }, + [ICOSAHEDRON]={ + .speakers=12, + .matrix={ + {0.18245, -1.6727e-34, 2.0067e-17, 2.2478e-01}, + {0.18245, 5.8352e-35, 4.8797e-18, 2.2478e-01}, + {0.21854, -2.5706e-18, 1.7303e-01, 1.5296e-01}, + {0.28727, 3.0478e-01, 1.7303e-01, 1.6203e-02}, + {0.28727, -3.0478e-01, 1.7303e-01, 1.6203e-02}, + {0.39847, 1.8836e-01, 1.7303e-01, -2.0508e-01}, + {0.39847, -1.8836e-01, 1.7303e-01, -2.0508e-01}, + {0.21854, 2.5706e-18, -1.7303e-01, 1.5296e-01}, + {0.28727, 3.0478e-01, -1.7303e-01, 1.6203e-02}, + {0.28727, -3.0478e-01, -1.7303e-01, 1.6203e-02}, + {0.39847, 1.8836e-01, -1.7303e-01, -2.0508e-01}, + {0.39847, -1.8836e-01, -1.7303e-01, -2.0508e-01}, + }, + }, + [DODECAHEDRON]={ + .speakers=20, + .matrix={ + {1.7725e-01, 1.0721e-16, -3.6730e-17, 1.4416e-01}, + {1.7725e-01, 8.4733e-02, 4.8084e-19, 1.1662e-01}, + {1.7725e-01, 1.3710e-01, -3.8973e-18, 4.4547e-02}, + {1.7725e-01, 1.3710e-01, -1.9760e-18, -4.4547e-02}, + {1.7725e-01, 8.4733e-02, 3.0706e-20, -1.1662e-01}, + {1.7725e-01, -1.0415e-16, -1.5089e-19, -1.4416e-01}, + {1.7725e-01, -8.4733e-02, -1.1427e-17, 1.1662e-01}, + {1.7725e-01, -1.3710e-01, -9.4207e-18, 4.4547e-02}, + {1.7725e-01, -1.3710e-01, -5.9922e-18, -4.4547e-02}, + {1.7725e-01, -8.4733e-02, -2.4514e-18, -1.1662e-01}, + {1.7725e-01, 3.5190e-17, 1.9356e-01, 1.1452e-01}, + {1.7725e-01, 1.0891e-01, 1.9356e-01, 3.5389e-02}, + {1.7725e-01, 6.7313e-02, 1.9356e-01, -9.2648e-02}, + {1.7725e-01, -1.0891e-01, 1.9356e-01, 3.5389e-02}, + {1.7725e-01, -6.7313e-02, 1.9356e-01, -9.2648e-02}, + {1.7725e-01, 6.7313e-02, -1.9356e-01, 9.2648e-02}, + {1.7725e-01, 1.0891e-01, -1.9356e-01, -3.5389e-02}, + {1.7725e-01, -4.0103e-17, -1.9356e-01, -1.1452e-01}, + {1.7725e-01, -1.0891e-01, -1.9356e-01, -3.5389e-02}, + {1.7725e-01, -6.7313e-02, -1.9356e-01, 9.2648e-02}, + }, + }, +}; + +/*Matrix for scaling options*/ +static const struct { + float matrix[4][1]; +} scaler_matrix[]= { + [N3D]={ + .matrix={ + {1}, + {1}, + {1}, + {1}, + }, + }, + [SN3D]={ + .matrix={ + {SQRT_3}, + {SQRT_3}, + {0} , + {SQRT_3}, + }, + }, + [FURMUL]={ + .matrix={ + {M_SQRT2}, + {SQRT_3} , + {SQRT_3} , + {SQRT_3} , + }, + }, +}; + +typedef struct AmbisonicContext { + const AVClass *class; + enum FilterType filter_type; + int dimension; /*2D or 3D*/ + enum Layouts lyt; /*Output speaker layout*/ + enum InputFormat s_o; /*Scaling OPtion*/ + int nb_channels; /*No. of channels to consider while decoding*/ + int order; /*Order of ambi file*/ + int enable_shelf; /*Enable shelf filtering or not*/ + double e_nf, e_ni; /*Distances for near field filters*/ + double gain; /*Gain while shelfing*/ + double frequency; /*Freq. of shelf filter(param)*/ + double width; /*Param for shelf filter*/ + double a0, a1, a2; /*Internal param for shelf*/ + double b0, b1, b2; /*Internal param for shelf*/ + double tilt; /*Angle for tilt(x) rotation*/ + double tumble; /*Angle for tumble(y) rotation*/ + double yaw; /*Angle for yaw(z) rotation*/ + Cache *cache; /*Cache area for shelf filter*/ + float angle; /*Angle for rot.*/ + + /*Func pointer for Shelf filter*/ + void (*filter1)(struct AmbisonicContext *s, const void *ibuf, void *obuf, int len, + double *i1, double *i2, double *o1, double *o2, + double b0, double b1, double b2, double a1, double a2, double min, double max, int channelno); + + /*Func pointer for NF filter*/ + void (*filter2)(struct AmbisonicContext *s , + float **in, + float d1, float d2, + float g); + +} AmbisonicContext; + +#define OFFSET(x) offsetof(AmbisonicContext,x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption ambisonic_options[] = { + {"enable_shelf" , "Set if shelf filtering is required" ,OFFSET(enable_shelf), AV_OPT_TYPE_INT , {.i64=1 } , 0 , 1 , FLAGS }, + {"e_s" , "Set if shelf filtering is required" ,OFFSET(enable_shelf), AV_OPT_TYPE_INT , {.i64=1 } , 0 , 1 , FLAGS }, + {"e_nf" , "Set Near Field Compensation(Input distance)" ,OFFSET(e_nf) , AV_OPT_TYPE_DOUBLE, {.dbl=0 } , 0 , 100.0 , FLAGS }, + {"e_ni" , "Set Near Field Compensation(Output distance)" ,OFFSET(e_ni) , AV_OPT_TYPE_DOUBLE, {.dbl=0 } , 0 , 100.0 , FLAGS }, + {"output_layout" , "Enter Layout of output" ,OFFSET(lyt) , AV_OPT_TYPE_INT , {.i64=SQUARE } , MONO, DODECAHEDRON, FLAGS ,"lyt"}, + {"o_l" , "Enter Layout of output" ,OFFSET(lyt) , AV_OPT_TYPE_INT , {.i64=SQUARE } , MONO, DODECAHEDRON, FLAGS ,"lyt"}, + {"scaling_option", "Set the input format (N3D, SN3D, Furse Malham)" ,OFFSET(s_o) , AV_OPT_TYPE_INT , {.i64=N3D } , 0 , 0 , FLAGS ,"scl"}, + {"s_o" , "Set the input format (N3D, SN3D, Furse Malham)" ,OFFSET(s_o) , AV_OPT_TYPE_INT , {.i64=N3D } , N3D , FURMUL , FLAGS ,"scl"}, + {"tilt" , "Set angle for tilt(x-axis)" ,OFFSET(tilt) , AV_OPT_TYPE_DOUBLE, {.dbl=0.0 } , 0.0 , 180.0 , FLAGS }, + {"tumble" , "Set angle for tumble(y-axis)" ,OFFSET(tumble) , AV_OPT_TYPE_DOUBLE, {.dbl=0.0 } , 0.0 , 180.0 , FLAGS }, + {"yaw" , "Set angle for yaw(z-axis)" ,OFFSET(yaw) , AV_OPT_TYPE_DOUBLE, {.dbl=0.0 } , 0.0 , 180.0 , FLAGS }, + {"mono" , "Mono Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=MONO } , 0 , 0 , FLAGS ,"lyt"}, + {"stereo" , "Stereo Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=STEREO } , 0 , 0 , FLAGS ,"lyt"}, + {"triangle" , "Triangle Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=TRIANGLE } , 0 , 0 , FLAGS ,"lyt"}, + {"quad" , "Square Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=SQUARE } , 0 , 0 , FLAGS ,"lyt"}, + {"pentagon" , "Pentagonal Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=PENTAGON } , 0 , 0 , FLAGS ,"lyt"}, + {"hexagon" , "Hexagonal Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=HEXAGON } , 0 , 0 , FLAGS ,"lyt"}, + {"hepatagon" , "Hepatagonal Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=HEPTAGON } , 0 , 0 , FLAGS ,"lyt"}, + {"octagon" , "Octagonal Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=OCTAGON } , 0 , 0 , FLAGS ,"lyt"}, + {"octahedron" , "Octahedron Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=OCTAHEDRON } , 0 , 0 , FLAGS ,"lyt"}, + {"cube" , "Cube Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=CUBE } , 0 , 0 , FLAGS ,"lyt"}, + {"icosahedron" , "Icosahedron Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=ICOSAHEDRON } , 0 , 0 , FLAGS ,"lyt"}, + {"dodecahedron" , "Dodecahedron Speaker Layout" ,0 , AV_OPT_TYPE_CONST , {.i64=DODECAHEDRON} , 0 , 0 , FLAGS ,"lyt"}, + {"n3d" , "N3D Scaling(Normalised)" ,0 , AV_OPT_TYPE_CONST , {.i64=N3D } , 0 , 0 , FLAGS ,"scl"}, + {"sn3d" , "SN3D Scaling(Semi-Normalised)" ,0 , AV_OPT_TYPE_CONST , {.i64=SN3D } , 0 , 0 , FLAGS ,"scl"}, + {"fm" , "Furse Malham Scaling" ,0 , AV_OPT_TYPE_CONST , {.i64=FURMUL } , 0 , 0 , FLAGS ,"scl"}, + {NULL} +}; + +static void shelf_flt( AmbisonicContext *s, + const void *input, void *output, int len, + double *in1, double *in2, + double *out1, double *out2, + double b0, double b1, double b2, + double a1, double a2, double min, double max, int channelno) +{ + + const float *ibuf = input; + float *obuf = output; + double i1 = *in1; + double i2 = *in2; + double o1 = *out1; + double o2 = *out2; + int i; + a1 = -a1; + a2 = -a2; + + switch(channelno){ + case 1: s->gain= 1.75; break; + case 2: s->gain= -1.26; break; + case 3: s->gain= -1.26; break; + default: s->gain= 1; + } + + for (i = 0; i+1 < len; i++) { + o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1; + i2 = ibuf[i]; + if (o2 < min) { + obuf[i] = min; + } else if (o2 > max) { + obuf[i] = max; + } else { + obuf[i] = o2; + } + i++; + o1 = i1 * b2 + i2 * b1 + ibuf[i] * b0 + o1 * a2 + o2 * a1; + i1 = ibuf[i]; + if (o1 < min) { + obuf[i] = min; + } else if (o1 > max) { + obuf[i] = max; + } else { + obuf[i] = o1; + } + } + if (i < len) { + double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; + i2 = i1; + i1 = ibuf[i]; + o2 = o1; + o1 = o0; + if (o0 < min) { + obuf[i] = min; + } else if (o0 > max) { + obuf[i] = max; + } else { + obuf[i] = o0; + } + } + *in1 = i1; + *in2 = i2; + *out1 = o1; + *out2 = o2; +} + +static void nearfield_flt( AmbisonicContext *s, + float **in, + float d1, float d2, + float g) +{ + int i,itr; + float b,g1,c,d, x,y,z=0; + float *input_arr[9]; + + if(d1) d1=340.0f / (d1 * 48e3); + if(d2) d2=340.0f / (d2 * 48e3); + + b = (d1 * 0.5); + g1 = b+1; + g *= g1; + c = (2 * b) / g1; + + b = (d2 * 0.5); + g1 = b+1; + g /= g1; + d = (2 * b) / g1; + + + for(i=0;inb_channels;i++) + { + input_arr[i]=(float*)in[i]; + } + + /*forward filter*/ + if(d1) { + for(i=0;inb_channels;i++) { + for(itr=0;itrnb_channels;itr++) { + x = g * input_arr[i][itr]; + y = x - (d*z) + 1e-30f; + x = y + (c*z); + z += y; + itr += d; + + input_arr[i][itr] = x; + } + } + } else { /*inverse filter*/ + for(i=0;inb_channels;i++) { + for(itr=0;itrnb_channels;itr++) { + x = g * input_arr[i][itr]; + x -= (d*z) + 1e-30f; + z += x; + itr += d; + + input_arr[i][itr] = x; + } + } + } +} + +static void rotate( AmbisonicContext *s, + float **in, float rotate_matrix[9][9], + float angle, + int samples) +{ + int i,j,k; + float sum; + for(j=0;jnb_channels;k++) { + for(i=0;inb_channels;i++) { + sum+=(in[i][j]*rotate_matrix[k][i]); + } + in[k][j]=sum; + } + } +} + + +static void rotate_flt( AmbisonicContext *s, float **in, + float tilt,float tumble,float yaw, + int samples) +{ + /*Rotation matrix values for x,y,z axis*/ + float rotate_matrix_tilt[][9]= {{1, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, + {0, cos(tilt),-sin(tilt) , 0 , 0 , 0 , 0 , 0 , 0 }, + {0, sin(tilt), cos(tilt) , 0 , 0 , 0 , 0 , 0 , 0 }, + {0, 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 }, + {0, 0 , 0 , 0 , cos(tilt), 0 , 0 , -sin(tilt), 0 }, + {0, 0 , 0 , 0 , 0 , cos(2*tilt) , -sqrt(3/4)*sin(2*tilt) , 0 ,-(0.5)*sin(2*tilt) }, + {0, 0 , 0 , 0 , 0 , sqrt(0.75)*sin(2*tilt), (0.25)*(1+3*cos(2*tilt)) , 0 ,-sqrt(3.0/16.0)*(1-cos(2*tilt))}, + {0, 0 , 0 , 0 , sin(tilt), 0 , 0 , cos(tilt) , 0 }, + {0, 0 , 0 , 0 , 0 , (0.5)*sin(2*tilt) , -sqrt(3/16)*(1-cos(2*tilt)) , 0 , (0.25)*(3+cos(2*tilt)) }}; + + float rotate_matrix_tumble[][9]={{1, 0, 0 ,0 ,0 ,0 , 0 ,0 ,0 }, + {0, 1, 0 ,0 ,0 ,0 , 0 ,0 ,0 }, + {0, 0, cos(tumble) ,sin(tumble) ,0 ,0 , 0 ,0 ,0 }, + {0, 0, -sin(tumble),cos(tumble) ,0 ,0 , 0 ,0 ,0 }, + {0, 0, 0 ,0 ,cos(tumble) ,-sin(tumble) , 0 ,0 ,0 }, + {0, 0, 0 ,0 ,sin(tumble) , cos(tumble) , 0 ,0 ,0 }, + {0, 0, 0 ,0 ,0 ,0 , (0.25)*(1+3*cos(2*tumble)) ,sqrt(0.75)*sin(2*tumble) ,sqrt(3.0/16.0)*(1-cos(2*tumble))}, + {0, 0, 0 ,0 ,0 ,0 ,-sqrt(0.75)*sin(2*tumble) ,cos(2*tumble) ,(0.5)*sin(2*tumble) }, + {0, 0, 0 ,0 ,0 ,0 , sqrt(3.0/16.0)*(1-cos(2*tumble)) ,-(0.5)*sin(2*tumble) ,(0.25)*(3+cos(2*tumble)) }}; + + float rotate_matrix_yaw[][9]= {{1, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, + {0, cos(yaw), 0 , sin(yaw) , 0 , 0 , 0 , 0 , 0 }, + {0, 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 }, + {0, -sin(yaw), 0 , cos(yaw) , 0 , 0 , 0 , 0 , 0 }, + {0, 0 , 0 , 0 , cos(2*yaw) , 0 , 0 , 0 , sin(2*yaw)}, + {0, 0 , 0 , 0 , 0 , cos(yaw) , 0 ,sin(yaw) , 0 }, + {0, 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 }, + {0, 0 , 0 , 0 , 0 , -sin(yaw) , 0 , cos(yaw) , 0 }, + {0, 0 , 0 , 0 , -sin(2*yaw) , 0 , 0 , 0 , cos(2*yaw)}}; + + if(tilt) rotate(s,in,rotate_matrix_tilt,tilt,samples); + if(tumble) rotate(s,in,rotate_matrix_tumble,tumble,samples); + if(yaw) rotate(s,in,rotate_matrix_yaw,yaw,samples); +} + +/*Utility function for matrix multiplication*/ +static float multiply(const float decode_matrix[22][15],int row, float *vars[22], + int sample_no, int nb_channels) +{ + float sum=0; + int j; + for(j=0;jpriv; + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; + uint64_t temp; + int ret; + + switch(s->lyt) { + case MONO: temp=AV_CH_LAYOUT_MONO; s->dimension=2; break; + case STEREO: temp=AV_CH_LAYOUT_STEREO; s->dimension=2; break; + case TRIANGLE: temp=AV_CH_LAYOUT_SURROUND; s->dimension=2; break; + case SQUARE: temp=AV_CH_LAYOUT_4POINT0; s->dimension=2; break; + case PENTAGON: temp=AV_CH_LAYOUT_5POINT0; s->dimension=2; break; + case HEXAGON: temp=AV_CH_LAYOUT_6POINT0; s->dimension=2; break; + case HEPTAGON: temp=AV_CH_LAYOUT_7POINT0; s->dimension=2; break; + case OCTAGON: temp=AV_CH_LAYOUT_OCTAGONAL; s->dimension=2; break; + case OCTAHEDRON: temp=AV_CH_LAYOUT_6POINT0; s->dimension=2; break; + case CUBE: temp=AV_CH_LAYOUT_OCTAGONAL; s->dimension=3; break; + case ICOSAHEDRON: temp=AV_CH_LAYOUT_4POINT0; s->dimension=3; break; //Layout not yet available + case DODECAHEDRON: temp=AV_CH_LAYOUT_4POINT0; s->dimension=3; break; //Layout not yet available + default: temp=AV_CH_LAYOUT_4POINT0; s->dimension=2; + } + + /*The order of ambisonic file is calculated by floor(sqrt(no.of input channels))-1.*/ + /*This funct. is not yet in ffmpeg*/ + + s->order=1;//first order ambisonics as of now + + switch(s->dimension) { + case 2: s->nb_channels=2*s->order+1; break; + case 3: s->nb_channels=(s->order+1)*(s->order+1); break; + default: s->nb_channels=2*s->order+1; + } + + ret = ff_add_format(&formats, AV_SAMPLE_FMT_FLTP); + if (ret) + return ret; + ret = ff_set_common_formats(ctx, formats); + if (ret) + return ret; + + ret = ff_add_channel_layout(&layouts, temp); + if (ret) + return ret; + + ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts); + if (ret) + return ret; + + layouts = NULL; + + ret = ff_add_channel_layout(&layouts, AV_CH_LAYOUT_4POINT0); + if (ret) + return ret; + + ret = ff_channel_layouts_ref(layouts, &ctx->inputs[0]->out_channel_layouts); + if (ret) + return ret; + + formats = ff_all_samplerates(); + if(!formats) + return AVERROR(ENOMEM); + return ff_set_common_samplerates(ctx, formats); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AmbisonicContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + double alpha,A,w0; + s->frequency=700; + s->filter_type=shelf; + s->width=10; + A = exp(s->gain / 40 * log(10.)); + w0 = 2 * M_PI * s->frequency / inlink->sample_rate; + alpha=sin(w0) / (2 * s->frequency / s->width); + + switch (s->filter_type) { + case shelf: + s->a0 = (A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha; + s->a1 = -2 * ((A - 1) + (A + 1) * cos(w0)); + s->a2 = (A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha; + s->b0 = A * ((A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha); + s->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0)); + s->b2 = A * ((A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha); + break; + default: + av_assert0(0); + } + s->a1 /= s->a0; + s->a2 /= s->a0; + s->b0 /= s->a0; + s->b1 /= s->a0; + s->b2 /= s->a0; + + s->cache = av_realloc_f(s->cache, sizeof(Cache), inlink->channels); + if (!s->cache) + return AVERROR(ENOMEM); + + memset(s->cache, 0, sizeof(Cache) * inlink->channels); + + s->tilt =(M_PI/180.0f)*s->tilt; + s->tumble=(M_PI/180.0f)*s->tumble; + s->yaw =(M_PI/180.0f)*s->yaw; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AmbisonicContext *s=ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out_buf; + int itr; + float *vars[9]; + float calc[22]={0}; + float *c[22]; + int i; + + out_buf = ff_get_audio_buffer(outlink, in->nb_samples); + if (!out_buf) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out_buf, in); + + s->filter1 = shelf_flt; + + if(s->lyt==MONO) s->enable_shelf=0; /*Shelf filtering not applicable for mono*/ + if(s->enable_shelf) { + // shelf filter + // for w channel gain= 1.75 + s->filter1(s, in->extended_data[0], + out_buf->extended_data[0], in->nb_samples, + &s->cache[0].i1, &s->cache[0].i2, + &s->cache[0].o1, &s->cache[0].o2, + s->b0, s->b1, s->b2, s->a1, s->a2, -1.,1.,1); + // for x & y channel gain= -1.26 + s->filter1(s, in->extended_data[1], + out_buf->extended_data[1], in->nb_samples, + &s->cache[1].i1, &s->cache[1].i2, + &s->cache[1].o1, &s->cache[1].o2, + s->b0, s->b1, s->b2, s->a1, s->a2, -1.,1.,2); + if(s->lyt!=STEREO) + s->filter1(s, in->extended_data[2], + out_buf->extended_data[2], in->nb_samples, + &s->cache[2].i1, &s->cache[2].i2, + &s->cache[2].o1, &s->cache[2].o2, + s->b0, s->b1, s->b2, s->a1, s->a2, -1.,1.,3); + } + + s->filter2 = nearfield_flt; + if(s->e_nf && s->e_ni) { + s->filter2(s,(float **)in->extended_data,s->e_nf,s->e_ni,1.0); + } + + rotate_flt(s,(float **)in->extended_data,s->tilt,s->tumble,s->yaw,in->nb_samples); + + for(i=0;inb_channels;i++) { + vars[i]=(float*)in->extended_data[i]; + } + + for(i=0;ilyt].speakers;i++) { + c[i]=(float *)out_buf->extended_data[i]; + } + + for(itr=0;itrnb_samples;itr++) { + for(i=0;ilyt].speakers;i++) { + if(s->dimension==2) { + calc[i]=multiply(ambisonic_matrix[s->lyt].matrix,i,vars,itr,s->nb_channels); + } else { + switch(s->order) { + case 1: calc[i]=multiply(ambisonic_matrix[s->lyt].matrix,i,vars,itr,s->nb_channels); + break; + /*This will only work once we have marker for more input channels. New vals will then have to be added.*/ + case 2: calc[i]=multiply(ambisonic_matrix[s->lyt].matrix,i,vars,itr,s->nb_channels); + break; + case 3: calc[i]=multiply(ambisonic_matrix[s->lyt].matrix,i,vars,itr,s->nb_channels); + break; + } + } + } + + for(i=0;ilyt].speakers;i++) { + c[i][itr]=calc[i]; + } + + for(i=0;ilyt].speakers;i++) { + c[i][itr]*=scaler_matrix[s->s_o].matrix[0][i]; + } + } + + if (out_buf != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out_buf); + +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AmbisonicContext *s = ctx->priv; + av_freep(&s->cache); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + }, + { NULL } +}; +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(ambisonic); + +AVFilter ff_af_ambisonic = { + .name = "ambisonic", + .description = NULL_IF_CONFIG_SMALL("An ambisonic filter"), + .query_formats = query_formats, + .priv_size = sizeof(AmbisonicContext), + .priv_class = &ambisonic_class, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, +}; \ No newline at end of file diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 8b9b9a4..2217aab 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -58,6 +58,7 @@ static void register_all(void) REGISTER_FILTER(ALIMITER, alimiter, af); REGISTER_FILTER(ALLPASS, allpass, af); REGISTER_FILTER(ALOOP, aloop, af); + REGISTER_FILTER(AMBISONIC, ambisonic, af); REGISTER_FILTER(AMERGE, amerge, af); REGISTER_FILTER(AMETADATA, ametadata, af); REGISTER_FILTER(AMIX, amix, af); -- 2.7.4