From 93d709a6c04af3749cd12ed98791bceb05f3f626 Mon Sep 17 00:00:00 2001
From: Sanchit Sinha <sanchit15083@iiitd.ac.in>
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 <sanchit15083@iiitd.ac.in>
---
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
@@ -33,6 +33,7 @@ version <next>:
- tlut2 video filter
- floodfill video filter
- pseudocolor video filter
+- Ambisonic Decoder
version 3.3:
- CrystalHD decoder moved to new decode API
@@ -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
new file mode 100644
@@ -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 <math.h>
+#include <stdio.h>
+#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;i<s->nb_channels;i++)
+ {
+ input_arr[i]=(float*)in[i];
+ }
+
+ /*forward filter*/
+ if(d1) {
+ for(i=0;i<s->nb_channels;i++) {
+ for(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(i=0;i<s->nb_channels;i++) {
+ for(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)
+{
+ int i,j,k;
+ float sum;
+ for(j=0;j<samples;j++) {
+ sum=0;
+ for(k=0;k<s->nb_channels;k++) {
+ for(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
@@ -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