From c802c29bd9d6b141cf0d273d480f00fef97d617f Mon Sep 17 00:00:00 2001
From: Sanchit Sinha <sanchit15083@iiitd.ac.in>
Date: Tue, 15 Aug 2017 23:55:44 +0530
Subject: [PATCH] libavfilter/af_ambisonic.c:Added File for Ambisonic Decoding

Signed-off-by: Sanchit Sinha <sanchit15083@iiitd.ac.in>
---
 Changelog                  |   1 +
 libavfilter/Makefile       |   1 +
 libavfilter/af_ambisonic.c | 732 +++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c   |   1 +
 4 files changed, 735 insertions(+)
 create mode 100644 libavfilter/af_ambisonic.c

diff --git a/Changelog b/Changelog
index c797d68..e09c48b 100644
--- a/Changelog
+++ b/Changelog
@@ -32,6 +32,7 @@ version <next>:
 - unpremultiply video filter
 - tlut2 video filter
 - floodfill video filter
+- Ambisonic Decoder
 
 version 3.3:
 - CrystalHD decoder moved to new decode API
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 06b915f..c1ca5ea 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..3097268
--- /dev/null
+++ b/libavfilter/af_ambisonic.c
@@ -0,0 +1,732 @@
+/*
+ * 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 <math.h>
+#include <stdio.h>
+
+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(2*floor(sqrt(0))+1)},
+            {sqrt(2*floor(sqrt(1))+1)},
+            {sqrt(2*floor(sqrt(2))+1)},
+            {sqrt(2*floor(sqrt(3))+1)},
+        },
+    },
+    [FURMUL]={
+        .matrix={
+            {sqrt(2)},
+            {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 if Near Field Compensation is required/Input distance",OFFSET(e_nf), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 100.0, FLAGS},
+    {"e_ni","Set if Near Field Compensation is required/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)
+{
+    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(int i=0;i<s->nb_channels;i++)
+    {
+        input_arr[i]=(float*)in[i];
+    }
+
+    //forward filter
+    if(d1) {
+        for(int i=0;i<s->nb_channels;i++) {
+            for(int itr=0;itr<s->nb_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(int i=0;i<s->nb_channels;i++) {
+            for(int itr=0;itr<s->nb_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)
+{
+    for(int j=0;j<samples;j++) {
+        float sum=0;
+        for(int k=0;k<s->nb_channels;k++) {
+            for(int i=0;i<s->nb_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;j<nb_channels;j++) {
+        sum+=(decode_matrix[row][j]*vars[j][sample_no]);
+    }
+    return sum;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AmbisonicContext *s = ctx->priv;
+    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;i<s->nb_channels;i++) {
+        vars[i]=(float*)in->extended_data[i];
+    }
+
+    for(i=0;i<ambisonic_matrix[s->lyt].speakers;i++) {
+        c[i]=(float *)out_buf->extended_data[i];
+    }
+
+    for(itr=0;itr<in->nb_samples;itr++) {
+        for(i=0;i<ambisonic_matrix[s->lyt].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;i<ambisonic_matrix[s->lyt].speakers;i++) {
+            c[i][itr]=calc[i];
+        }
+
+        for(i=0;i<ambisonic_matrix[s->lyt].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 e58c0d5..24ba69c 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

