[vlc-devel] [PATCH] audio_filter: SOFAlizer new module

Andreas Fuchs andi.fuchs.mail at gmail.com
Mon Sep 23 21:28:57 CEST 2013


I added your comments (Thanks Jean-Baptiste & Ilkka Ollakka) into the code and split the patches to get an extra patch for the UI. The netcdf4 library is used (newest version is 4.3.0). On Debian/Ubuntu it is sufficent to install the package 'libnetcdf-dev' with the netcdf version 4.1.3. Netcdf 4.0 could also work, but I couldn't test it.

SOFAlizer creates a virtual auditory display, i.e., virtual loudspeakers around the user for listening via headphones. The position of the virtual loudspeakers depends on the audio format of the input file (up to 8.1 supported). SOFAlizer filters audio channels with head-related transfer functions (HRTFs). SOFAlizer uses HRTFs stored in SOFA files (www.sofaconventions.org) following the SimpleFreeFieldHRIR Convention. A database of SOFA files can be found at www.sofacoustics.org.
With listener-specific HRTFs, the virtual loudspeakers can be rotated and elevated. With near-field HRTFs, the distance between the loudspeakers and the listener can be varied. SOFAlizer allows to load 5 different SOFA files and easily switch between them. The audio channels can also be presented from one of four pre-defined virtual positions.
SOFAlizer is developed at the Acoustics Research Institute (ARI) of the Austrian Academy of Sciences.
---
 configure.ac                     |   20 +-
 modules/audio_filter/Modules.am  |    4 +-
 modules/audio_filter/sofalizer.c | 1266 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1288 insertions(+), 2 deletions(-)
 create mode 100644 modules/audio_filter/sofalizer.c

diff --git a/configure.ac b/configure.ac
index 417bfdb..0d3afc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -651,7 +651,7 @@ AC_CHECK_FUNC(getopt_long,, [
 AC_SUBST(GNUGETOPT_LIBS)
 
 AC_CHECK_LIB(m,cos,[
-  VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom noise grain scene chorus_flanger freetype swscale postproc equalizer spatializer param_eq samplerate freetype mpc qt4 compressor headphone_channel_mixer normvol audiobargraph_a audiobargraph_v mono colorthres extract ball hotkeys mosaic gaussianblur x262 x26410b hqdn3d anaglyph oldrc ncurses oldmovie glspectrum],[-lm])
+  VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom noise grain scene chorus_flanger freetype swscale postproc equalizer spatializer param_eq samplerate freetype mpc qt4 compressor headphone_channel_mixer normvol audiobargraph_a audiobargraph_v mono colorthres extract ball hotkeys mosaic gaussianblur x262 x26410b hqdn3d anaglyph oldrc ncurses oldmovie glspectrum sofalizer],[-lm])
   LIBM="-lm"
 ], [
   LIBM=""
@@ -4068,6 +4068,24 @@ AS_IF([test "${enable_growl}" != "no"], [
   ]
 )
 
+dnl 
+dnl  NetCDF library for SOFAlizer plugin 
+dnl 
+AC_ARG_ENABLE(sofalizer, 
+  [  --enable-sofalizer           netcdf-binarual support (default disabled)],,
+  [enable_sofalizer=no])
+if test "${enable_sofalizer}" != "no" 
+then 
+   PKG_CHECK_MODULES(NETCDF,netcdf >= 4.1.3, 
+   [ 
+      AC_CHECK_HEADERS(netcdf.h, [], [AC_MSG_ERROR([Missing header file netcdf.h.])] ) 
+      VLC_ADD_PLUGIN([sofalizer]) 
+      VLC_ADD_LDFLAGS([sofalizer],[$NETCDF_LIBS]) 
+      VLC_ADD_CFLAGS([sofalizer], [$NETCDF_CFLAGS]) ],
+   [
+      AC_MSG_ERROR([${NETCDF_PKG_ERRORS}.]) ])
+fi 
+
 dnl
 dnl Libnotify notification plugin
 dnl
diff --git a/modules/audio_filter/Modules.am b/modules/audio_filter/Modules.am
index 89f8783..7345907 100644
--- a/modules/audio_filter/Modules.am
+++ b/modules/audio_filter/Modules.am
@@ -8,6 +8,7 @@ SOURCES_param_eq = param_eq.c
 SOURCES_scaletempo = scaletempo.c
 SOURCES_chorus_flanger = chorus_flanger.c
 SOURCES_stereo_widen = stereo_widen.c
+SOURCES_sofalizer = sofalizer.c
 SOURCES_spatializer = \
 	spatializer/allpass.cpp spatializer/allpass.hpp \
 	spatializer/comb.cpp spatializer/comb.hpp \
@@ -27,7 +28,8 @@ audio_filter_LTLIBRARIES += \
 	libparam_eq_plugin.la \
 	libscaletempo_plugin.la \
 	libspatializer_plugin.la \
-	libstereo_widen_plugin.la
+	libstereo_widen_plugin.la \
+	libsofalizer_plugin.la
 
 # Channel mixers
 SOURCES_trivial_channel_mixer = channel_mixer/trivial.c
diff --git a/modules/audio_filter/sofalizer.c b/modules/audio_filter/sofalizer.c
new file mode 100644
index 0000000..fa263a9
--- /dev/null
+++ b/modules/audio_filter/sofalizer.c
@@ -0,0 +1,1266 @@
+/******************************************************************************
+ * sofalizer.c : SOFAlizer plugin to use SOFA files in vlc
+ *****************************************************************************
+ * Copyright (C) 2013 Andreas Fuchs, ARI
+ *
+ * Authors: Andreas Fuchs <andi.fuchs.mail at gmail.com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 
+*****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_aout.h>
+#include <vlc_filter.h>
+#include <vlc_block.h>
+#include <vlc_modules.h>
+
+#include <math.h>
+#include <vlc_interface.h>
+
+#include <netcdf.h>
+
+#define N_SOFA 5
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+struct nc_sofa_t
+{
+   int i_ncid;
+   int i_n_samples;
+   int i_m_dim;
+   int *p_data_delay;
+   float *p_sp_a;
+   float *p_sp_e;
+   float *p_sp_r;
+   float *p_data_ir;
+};
+
+struct filter_sys_t
+{
+    filter_t *p_parent;
+    vlc_mutex_t lock;
+    bool b_Enable;
+    float *p_speaker_pos;
+
+    /* Control variables */
+    float f_gain;
+    float f_rotation;
+    float f_elevation;
+    float f_radius;
+    int i_azimuth_array[4];
+    int i_elevation_array[4];
+    int i_switch;
+    bool b_mute;
+
+    /* N of Channels to convolute */
+    int i_n_conv;
+    bool b_lfe;
+
+    /* Buffer variables */
+    float *p_ringbuffer_l;
+    float *p_ringbuffer_r;
+    int i_write;
+    int i_buffer_length;
+
+    /* NetCDF variables */
+    struct nc_sofa_t sofa[N_SOFA];
+    int i_i_sofa;  /* Selected Sofa File */
+    int *p_delay_l;
+    int *p_delay_r;
+    float *p_ir_l;
+    float *p_ir_r;
+
+};
+
+struct t_thread_data
+{
+    filter_sys_t *p_sys; 
+    block_t *p_in_buf;     
+    int *p_input_nb;    
+    int *p_delay;
+    int i_write; 
+    int *p_n_clippings;
+    float *p_ringbuffer;
+    float *p_dest;   
+    float *p_ir;
+    float f_gain_lfe;
+};
+
+struct data_findM_t
+{
+    filter_sys_t *p_sys;
+    int i_azim;
+    int i_elev;
+    int *p_m;
+    float f_radius;
+};
+
+static int  Open ( vlc_object_t *p_this );
+static void Close( vlc_object_t * );
+static block_t *DoWork( filter_t *, block_t * );
+
+static int LoadSofa ( filter_t *p_filter, char *filename, int i_i_sofa,  int *p_samplingrate);
+static void CloseSofa( filter_t *p_filter);
+static int LoadIR ( filter_t *p_filter, int i_azim, int i_elev, float f_radius);
+static int GetSpeakerPos ( filter_t *p_filter, float *p_speaker_pos );
+static int MaxDelay ( struct nc_sofa_t *sofa );
+void sofalizer_Convolute ( void *data );
+void sofalizer_FindM ( void *data );
+static int CompensateVolume( filter_t *p_filter);
+
+static int GainCallback( vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+static int RotationCallback( vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+static int ElevationCallback( vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+static int SwitchCallback( vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+static int SelectCallback( vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+static int RadiusCallback(  vlc_object_t *, char const *, vlc_value_t ,
+                          vlc_value_t , void * );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+#define HELP_TEXT N_( "SOFAlizer creates a virtual auditory display, i.e., virtual loudspeakers around the user for listening via headphones. The position of the virtual loudspeakers depends on the audio format of the input file (up to 8.1 supported). SOFAlizer filters audio channels with head-related transfer functions (HRTFs). SOFAlizer uses HRTFs stored in SOFA files (www.sofaconventions.org) following the SimpleFreeFieldHRIR Convention. A database of SOFA files can be found at www.sofacoustics.org.\nWith listener-specific HRTFs, the virtual loudspeakers can be rotated and elevated. With near-field HRTFs, the distance between the loudspeakers and the listener can be varied. SOFAlizer allows to load 5 different SOFA files and easily switch between them. The audio channels can also be presented from one of four pre-defined virtual positions.\nSOFAlizer is developed at the Acoustics Research Institute (ARI) of the Austrian Academy of Sciences." )
+
+#define GAIN_VALUE_TEXT N_( "Gain" )
+#define GAIN_VALUE_LONGTEXT N_( "Sets the volume of the module." )
+
+#define FILE1_NAME_TEXT N_( "SOFA file 1" )
+#define FILE2_NAME_TEXT N_( "SOFA file 2" )
+#define FILE3_NAME_TEXT N_( "SOFA file 3" )
+#define FILE4_NAME_TEXT N_( "SOFA file 4" )
+#define FILE5_NAME_TEXT N_( "SOFA file 5" )
+#define FILE_NAME_LONGTEXT N_( "Load Sofa file. The Sampling Rate of the different files must equal to the Sampling Rate of the first (loaded) file." )
+
+#define SELECT_VALUE_TEXT N_( "Select SOFA file" )
+#define SELECT_VALUE_LONGTEXT N_( "Select the SOFA file to use." )
+#define ROTATION_VALUE_TEXT N_( "Rotation" )
+#define ROTATION_VALUE_LONGTEXT N_( "Rotation angle. >0 -> Right, <0 -> Left. [-180;180]" )
+
+#define ELEVATION_VALUE_TEXT N_( "Elevation" )
+#define ELEVATION_VALUE_LONGTEXT N_( "Elevation angle. [-90;90]" )
+
+#define RADIUS_VALUE_TEXT N_( "Radius")
+#define RADIUS_VALUE_LONGTEXT N_( "Distance of the Loudspeakers as given by the Distance to the Source Position in the SOFA file." )
+
+#define SWITCH_VALUE_TEXT N_( "Switch" )
+#define SWITCH_VALUE_LONGTEXT N_( "Put all Input Channels in Position 1 to 4. Position 0 activates Rotation and Elevation." )
+
+#define POS_AZIMUTH_VALUE_LONGTEXT N_( "Only active for Switch 1-4. One Sound Source at given azimuth angle." )
+#define POS_ELEVATION_VALUE_LONGTEXT N_( "Only active for Switch 1-4. One Sound Source at given elevation angle." )
+
+#define POS1_AZIMUTH_VALUE_TEXT N_( "   Azimuth Position 1 ")
+#define POS1_ELEVATION_VALUE_TEXT N_( "   Elevation Position 1 ")
+#define POS2_AZIMUTH_VALUE_TEXT N_( "   Azimuth Position 2 ")
+#define POS2_ELEVATION_VALUE_TEXT N_( "   Elevation Position 2 ")
+#define POS3_AZIMUTH_VALUE_TEXT N_( "   Azimuth Position 3 ")
+#define POS3_ELEVATION_VALUE_TEXT N_( "   Elevation Position 3 ")
+#define POS4_AZIMUTH_VALUE_TEXT N_( "   Azimuth Position 4 ")
+#define POS4_ELEVATION_VALUE_TEXT N_( "   Elevation Position 4 ")
+
+vlc_module_begin ()
+    set_description( N_("SOFAlizer") )
+    set_shortname( N_("SOFAlizer") )
+    set_capability( "audio filter", 0)
+    set_help( HELP_TEXT )
+    add_loadfile( "sofalizer-filename1", "", FILE1_NAME_TEXT, FILE_NAME_LONGTEXT, false)
+    add_loadfile( "sofalizer-filename2", "", FILE2_NAME_TEXT, FILE_NAME_LONGTEXT, false)
+    add_loadfile( "sofalizer-filename3", "", FILE3_NAME_TEXT, FILE_NAME_LONGTEXT, false)
+    add_loadfile( "sofalizer-filename4", "", FILE4_NAME_TEXT, FILE_NAME_LONGTEXT, false)
+    add_loadfile( "sofalizer-filename5", "", FILE5_NAME_TEXT, FILE_NAME_LONGTEXT, false)
+    add_float( "sofalizer-select", 1 ,  SELECT_VALUE_TEXT, SELECT_VALUE_LONGTEXT, false)
+    add_float( "sofalizer-gain", 0.0, GAIN_VALUE_TEXT, GAIN_VALUE_LONGTEXT, false )
+    add_float( "sofalizer-rotation", 0, ROTATION_VALUE_TEXT, ROTATION_VALUE_LONGTEXT, false )
+    add_float( "sofalizer-elevation", 0, ELEVATION_VALUE_TEXT, ELEVATION_VALUE_LONGTEXT, false )
+    add_float( "sofalizer-radius", 1, RADIUS_VALUE_TEXT, RADIUS_VALUE_LONGTEXT, false )
+    add_float( "sofalizer-switch" , 0 , SWITCH_VALUE_TEXT, SWITCH_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos1-azi", 90 , POS1_AZIMUTH_VALUE_TEXT, POS_AZIMUTH_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos1-ele", 0, POS1_ELEVATION_VALUE_TEXT, POS_ELEVATION_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos2-azi", 180, POS2_AZIMUTH_VALUE_TEXT, POS_AZIMUTH_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos2-ele", 0, POS2_ELEVATION_VALUE_TEXT, POS_ELEVATION_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos3-azi", -90, POS3_AZIMUTH_VALUE_TEXT, POS_AZIMUTH_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos3-ele", 0, POS3_ELEVATION_VALUE_TEXT, POS_ELEVATION_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos4-azi", 0, POS4_AZIMUTH_VALUE_TEXT, POS_AZIMUTH_VALUE_LONGTEXT, false )
+    add_integer( "sofalizer-pos4-ele", 90, POS4_ELEVATION_VALUE_TEXT, POS_ELEVATION_VALUE_LONGTEXT, false )
+    add_shortcut( "sofalizer" )
+    set_category( CAT_AUDIO )
+    set_subcategory( SUBCAT_AUDIO_AFILTER )
+    set_callbacks( Open, Close )
+vlc_module_end ()
+
+
+static int Open( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys = p_filter->p_sys = malloc( sizeof( *p_sys ) );
+    if( unlikely( p_sys == NULL ) )
+        return VLC_ENOMEM;
+    p_sys->p_parent = p_filter;
+    vlc_object_t *p_aout = p_filter->p_parent;
+    intf_thread_t *intf = (intf_thread_t *)p_this; /* Output Interface */
+
+    p_sys->b_Enable = false;
+    char *c_filename[N_SOFA];
+    c_filename[0] = var_CreateGetStringCommand( p_filter, "sofalizer-filename1" );
+    c_filename[1] = var_CreateGetStringCommand( p_filter, "sofalizer-filename2" );
+    c_filename[2] = var_CreateGetStringCommand( p_filter, "sofalizer-filename3" );
+    c_filename[3] = var_CreateGetStringCommand( p_filter, "sofalizer-filename4" );
+    c_filename[4] = var_CreateGetStringCommand( p_filter, "sofalizer-filename5" );
+    p_sys->i_i_sofa     = ( (int) (var_CreateGetFloat ( p_aout, "sofalizer-select" ) )  + 100 - 1 ) % 5  ;
+    p_sys->f_gain       = var_CreateGetFloat( p_aout, "sofalizer-gain" );
+    p_sys->f_rotation   = abs ( ( - (int) var_CreateGetFloat( p_aout, "sofalizer-rotation" ) + 720 ) % 360 );
+    p_sys->f_elevation  = var_CreateGetFloat( p_aout, "sofalizer-elevation" );
+    p_sys->i_switch     = (int)( var_CreateGetFloat ( p_aout, "sofalizer-switch" ) ) ;
+    p_sys->f_radius     = var_CreateGetFloat( p_aout, "sofalizer-radius");
+
+    p_sys->i_azimuth_array[0] = ( var_CreateGetInteger ( p_aout, "sofalizer-pos1-azi" ) + 720 ) % 360 ;
+    p_sys->i_azimuth_array[1] = ( var_CreateGetInteger ( p_aout, "sofalizer-pos2-azi" ) + 720 ) % 360 ;
+    p_sys->i_azimuth_array[2] = ( var_CreateGetInteger ( p_aout, "sofalizer-pos3-azi" ) + 720 ) % 360 ;
+    p_sys->i_azimuth_array[3] = ( var_CreateGetInteger ( p_aout, "sofalizer-pos4-azi" ) + 720 ) % 360 ;
+
+    p_sys->i_elevation_array[0] = var_CreateGetInteger ( p_aout, "sofalizer-pos1-ele" );
+    p_sys->i_elevation_array[1] = var_CreateGetInteger ( p_aout, "sofalizer-pos2-ele" );
+    p_sys->i_elevation_array[2] = var_CreateGetInteger ( p_aout, "sofalizer-pos3-ele" );
+    p_sys->i_elevation_array[3] = var_CreateGetInteger ( p_aout, "sofalizer-pos4-ele" );
+
+    vlc_mutex_init( &p_sys->lock );
+
+    int i_samplingrate = 0;
+    int i_samplingrate_old = 0;
+    int b_found_valid = false;
+    int i = 0;
+    p_sys->b_mute = false ;
+
+    /* Load Sofa files, check for Sampling Rate and valid Selection in the Preferences */
+    for ( i = 0 ; i < N_SOFA ; i++ )
+    {
+        if ( LoadSofa ( p_filter, c_filename[i], i , &i_samplingrate) != VLC_SUCCESS )
+        {
+            msg_Err(p_filter, "Error while loading SOFA file %d: '%s'", i + 1, c_filename[i] );
+        }
+        else
+        {
+            msg_Dbg( p_filter , "File %d: '%s' loaded", i + 1 , c_filename[i] );
+            if ( !b_found_valid )
+            {
+                if ( p_sys->sofa[i].i_ncid )
+                {
+                    i_samplingrate_old = i_samplingrate;
+                    b_found_valid = true;
+                }
+            }
+            if ( p_sys->sofa[i].i_ncid && i_samplingrate != i_samplingrate_old )
+            {
+                msg_Err ( p_filter, " SOFA file %d '%s' with different Sampling Rate. Discarded.", i + 1, c_filename[i] );
+                nc_close( p_sys->sofa[i].i_ncid );
+                free ( p_sys->sofa[i].p_sp_a );
+                free ( p_sys->sofa[i].p_sp_e );
+                p_sys->sofa[i].i_ncid = 0;
+            }
+        }
+    }
+    if ( !p_sys->sofa[p_sys->i_i_sofa].i_ncid )
+    {
+         for ( i = 0 ; i < N_SOFA ; i++)
+         {
+             if ( p_sys->sofa[i].i_ncid )
+             {
+                p_sys->i_i_sofa = i;
+                msg_Err ( intf, "Selected File from Settings invalid. Use File %d", i + 1 );
+                goto go_on;
+             }
+         }
+         msg_Err ( p_filter, "No valid file found." );
+         free( p_sys );
+         CloseSofa( p_filter );
+         return VLC_EGENERIC;
+    }
+
+
+/* if ( p_filter->fmt_in.audio.i_rate != i_samplingrate ) //Check Sampling Rate
+    {
+        msg_Err( p_filter, "Sampling rate of Input und Sofa file doesn't match. Can't load filter" );
+        return  VLC_EGENERIC;
+    }
+*/
+
+    /* Set Filter Settings and calculate Speaker Positions*/
+go_on:   p_filter->fmt_in.audio.i_rate = i_samplingrate_old;
+    p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32 ;
+    p_filter->fmt_out.audio = p_filter->fmt_in.audio;
+
+    p_filter->fmt_out.audio.i_physical_channels = AOUT_CHANS_STEREO; // required for filter output set to stereo
+    p_filter->fmt_out.audio.i_original_channels = AOUT_CHANS_STEREO;
+
+    int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
+    if ( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE )
+    {
+        p_sys->b_lfe = true;
+        p_sys->i_n_conv = i_input_nb - 1 ;
+    }
+    else
+    {
+        p_sys->b_lfe = false;
+        p_sys->i_n_conv = i_input_nb ;
+    }
+    
+    p_sys->p_speaker_pos = malloc( sizeof( float) * p_sys->i_n_conv ) ;
+    if ( unlikely ( !p_sys->p_speaker_pos ) )
+    {
+        CloseSofa( p_filter );        
+        free ( p_sys );        
+        return VLC_ENOMEM;
+    }
+    if ( GetSpeakerPos ( p_filter, p_sys->p_speaker_pos ) != VLC_SUCCESS )
+    {
+        msg_Err (p_filter, "Couldn't get Speaker Positions. Input Channel Configuration not supported. ");
+        free( p_sys->p_speaker_pos );
+        free( p_sys );
+        CloseSofa( p_filter );
+        return VLC_EGENERIC;
+    }
+    
+    /* Find the minimum size (length of impulse response plus maximal Delay) of the Ringbuffer as power of 2.  */
+    int i_n_max = 0;
+    int i_n_current;
+    int i_n_max_ir = 0;
+    for ( i = 0 ; i < N_SOFA ; i++ )
+    {
+        if ( p_sys->sofa[i].i_ncid != 0 )
+        {
+            i_n_current = p_sys->sofa[i].i_n_samples + MaxDelay ( &p_sys->sofa[i] );
+            if ( i_n_current > i_n_max )
+            {
+                i_n_max = i_n_current;
+                i_n_max_ir = p_sys->sofa[i].i_n_samples;
+            }
+        }
+    }
+    i_n_max = pow(2, ceil(log( i_n_max )/ log(2) ) );
+    
+    /* Allocate Memory for the impulse responses, delays and the ringbuffers */
+    float *p_ir_l=malloc( sizeof(float) * i_n_max_ir * p_sys->i_n_conv  ); /* minus LFE .*/
+    if( unlikely( !p_ir_l ) )
+    {
+        free( p_sys->p_speaker_pos);
+        CloseSofa( p_filter );
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+    float *p_ir_r=malloc( sizeof(float) * i_n_max_ir * p_sys->i_n_conv ); /* minus LFE */
+    if( unlikely( !p_ir_r ) )
+    {
+        free( p_sys->p_speaker_pos);
+        free( p_ir_l );
+        CloseSofa( p_filter );
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+
+    int *p_delay_l = malloc ( sizeof( int ) * p_sys->i_n_conv );
+    if( unlikely( !p_delay_l ) )
+    {
+        CloseSofa( p_filter );
+        free( p_sys->p_speaker_pos);
+        free( p_sys );
+        free( p_ir_l );
+        free( p_ir_r );
+        return VLC_ENOMEM;
+    }
+    int *p_delay_r = malloc ( sizeof( int ) * p_sys->i_n_conv );
+    if ( unlikely ( !p_delay_r ) )
+    {
+        CloseSofa( p_filter );
+        free (p_sys->p_speaker_pos );
+        free ( p_ir_l );
+        free ( p_ir_r );
+        free ( p_delay_l );
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+
+    p_sys->p_ir_l = p_ir_l;
+    p_sys->p_ir_r = p_ir_r;
+    p_sys->p_delay_l = p_delay_l;
+    p_sys->p_delay_r = p_delay_r;
+
+    p_sys->i_buffer_length = i_n_max; 
+    p_sys->p_ringbuffer_l = calloc( p_sys->i_buffer_length * i_input_nb, sizeof( float ) );
+    if( unlikely( !p_sys->p_ringbuffer_l ) )
+    {
+        CloseSofa( p_filter );
+        free( p_sys->p_speaker_pos);
+        free( p_ir_l );
+        free( p_ir_r );
+        free( p_delay_l );
+        free( p_delay_r );
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+    p_sys->p_ringbuffer_r = calloc( p_sys->i_buffer_length * i_input_nb, sizeof( float ) );
+    if( unlikely( !p_sys->p_ringbuffer_r ) )
+    {
+        CloseSofa( p_filter );
+        free( p_sys->p_speaker_pos);
+        free( p_ir_l );
+        free( p_ir_r );
+        free( p_delay_l );
+        free( p_delay_r );
+        free( p_sys );
+        free( p_sys->p_ringbuffer_l );
+        return VLC_ENOMEM;
+    }
+    p_sys->i_write = 0;
+
+    CompensateVolume ( p_filter );
+    
+    /* Load the Impulse Responses into p_ir_l and p_ir_r for the required directions */
+    if ( LoadIR ( p_filter, p_sys->f_rotation, p_sys->f_elevation, p_sys->f_radius) != VLC_SUCCESS )
+    {
+        CloseSofa( p_filter );
+        free( p_sys->p_speaker_pos);
+        free( p_ir_l );
+        free( p_ir_r );
+        free( p_delay_l );
+        free( p_delay_r );
+        free( p_sys );
+        free( p_sys->p_ringbuffer_l );
+        free( p_sys->p_ringbuffer_r );
+        return VLC_ENOMEM;
+    }
+    
+    /* Loads of Infos */
+    const char *c_netcdf_v=nc_inq_libvers ();
+    msg_Info(intf, "Samplerate: %d\nKanäle: %d Ph: 0x%x Org: 0x%x\nVersion Netcdf: %s", p_filter->fmt_in.audio.i_rate, i_input_nb, p_filter->fmt_in.audio.i_physical_channels, p_filter->fmt_in.audio.i_original_channels,  c_netcdf_v);
+
+    msg_Info(intf, "Bytes/Frame: %d, Framelänge: %d, Bits/Sample: %d, Blockalign: %d",  p_filter->fmt_in.audio.i_bytes_per_frame,  p_filter->fmt_in.audio.i_frame_length,  p_filter->fmt_in.audio.i_bitspersample,  p_filter->fmt_in.audio.i_blockalign);
+    msg_Info(intf, "Outkanäle: Phys: 0x%x, Kanäle zum Falten: %d, Ringbufferlänge: %d x %d" , p_filter->fmt_out.audio.i_physical_channels, p_sys->i_n_conv, i_input_nb, (int )p_sys->i_buffer_length );
+    
+    p_filter->pf_audio_filter = DoWork;
+    
+    /* Callbacks can call function LoadIR */
+    var_AddCallback( p_aout, "sofalizer-gain", GainCallback, p_sys );
+    var_AddCallback( p_aout, "sofalizer-rotation", RotationCallback, p_sys);
+    var_AddCallback( p_aout, "sofalizer-elevation", ElevationCallback, p_sys );
+    var_AddCallback( p_aout, "sofalizer-switch", SwitchCallback, p_sys );
+    var_AddCallback( p_aout, "sofalizer-select", SelectCallback, p_sys );
+    var_AddCallback( p_aout, "sofalizer-radius", RadiusCallback, p_sys );    
+    p_sys->b_Enable = true;
+    
+    return VLC_SUCCESS;
+}
+
+/* Prepares the data structures for the threads and starts them */
+static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
+{
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    int i_n_clippings_l = 0;
+    int i_n_clippings_r = 0;
+
+    int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
+    int i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
+
+    size_t i_out_size = p_in_buf->i_buffer * i_output_nb / i_input_nb;
+    block_t *p_out_buf = block_Alloc( i_out_size );
+    if ( unlikely( !p_out_buf ) )
+    {
+        msg_Warn( p_filter, "can't get output buffer" );
+        block_Release( p_in_buf );
+        goto out;
+    }
+    p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
+    p_out_buf->i_dts        = p_in_buf->i_dts;
+    p_out_buf->i_pts        = p_in_buf->i_pts;
+    p_out_buf->i_length     = p_in_buf->i_length;
+
+    vlc_thread_t left_thread, right_thread;
+    struct t_thread_data t_data_l;
+    struct t_thread_data t_data_r;
+
+    float f_gain_lfe = exp( (p_sys->f_gain - 3 * i_input_nb - 6) / 20 * log(10)); // -6 dB to get LFE on a similar level
+
+    t_data_l.p_sys = t_data_r.p_sys = p_sys;
+    t_data_l.p_in_buf = t_data_r.p_in_buf = p_in_buf;
+    t_data_l.p_input_nb = t_data_r.p_input_nb = &i_input_nb;
+    t_data_l.f_gain_lfe = t_data_r.f_gain_lfe = f_gain_lfe;
+    t_data_l.p_input_nb = t_data_r.p_input_nb = &i_input_nb;
+    t_data_l.f_gain_lfe = t_data_r.f_gain_lfe = f_gain_lfe;
+    t_data_l.i_write = t_data_r.i_write = p_sys->i_write;
+    t_data_l.p_ringbuffer = p_sys->p_ringbuffer_l;
+    t_data_r.p_ringbuffer = p_sys->p_ringbuffer_r;   
+    t_data_l.p_ir = p_sys->p_ir_l; 
+    t_data_r.p_ir = p_sys->p_ir_r; 
+    t_data_l.p_n_clippings = &i_n_clippings_l;
+    t_data_r.p_n_clippings = &i_n_clippings_r;
+    t_data_l.p_dest = (float *)p_out_buf->p_buffer;
+    t_data_r.p_dest = (float *)p_out_buf->p_buffer + 1;
+    t_data_l.p_delay = p_sys->p_delay_l;
+    t_data_r.p_delay = p_sys->p_delay_r;
+
+    if ( p_sys->b_mute )
+    {
+        memset( (float *)p_out_buf->p_buffer , 0 , sizeof( float ) * i_input_nb * 2 );
+    }
+    else
+    {
+        if( vlc_clone( &left_thread, (void *)&sofalizer_Convolute, (void *)&t_data_l, VLC_THREAD_PRIORITY_HIGHEST ) ) goto out;
+        if( vlc_clone( &right_thread, (void *)&sofalizer_Convolute, (void *)&t_data_r, VLC_THREAD_PRIORITY_HIGHEST ) ) goto out;
+        vlc_join ( left_thread, NULL );
+        vlc_join ( right_thread, NULL );
+        p_sys->i_write = t_data_l.i_write;
+    }
+
+    if ( ( i_n_clippings_l + i_n_clippings_r ) > 0 )
+    {
+        msg_Err(p_filter, "%d of %d Samples in the Outputbuffer clipped. Please reduce gain.", i_n_clippings_l + i_n_clippings_r, p_out_buf->i_nb_samples * 2 );
+    }
+out: block_Release( p_in_buf );
+    return p_out_buf;
+}
+
+/* Writes the samples of the input buffer in the ringbuffer and convolutes with the impulse response */
+void sofalizer_Convolute ( void *p_ptr )
+{
+    struct t_thread_data *t_data;
+    t_data = (struct t_thread_data *)p_ptr;
+    struct filter_sys_t *p_sys = t_data->p_sys;
+    int i_n_samples = p_sys->sofa[p_sys->i_i_sofa].i_n_samples;
+    float *p_src = (float *)t_data->p_in_buf->p_buffer;
+    float *p_temp_ir;
+    float *p_dest = t_data->p_dest;
+    int i_read;
+    int *p_delay = t_data->p_delay;
+    int i_input_nb = *t_data->p_input_nb;
+    int i_buffer_length = p_sys->i_buffer_length; /* #i_input_nb small Ringbuffers with Length i_n_max samples,*/
+    uint32_t i_modulo = (uint32_t) i_buffer_length -1 ; /* -1 for AND instead MODULO */
+    float *p_ringbuffer[i_input_nb];
+    for ( int l = 0 ; l < i_input_nb ; l++ )
+    {
+        p_ringbuffer[l] = t_data->p_ringbuffer + l * i_buffer_length ;
+    }
+    int i_write = t_data->i_write;
+    float *p_ir = t_data->p_ir;
+
+    for ( int i = t_data->p_in_buf->i_nb_samples ; i-- ; )
+    {
+        *( p_dest ) = 0;
+        for ( int l = 0 ; l < i_input_nb ; l++ )
+        {
+            *( p_ringbuffer[l] + i_write ) = *( p_src++);  
+        }
+        p_temp_ir = p_ir;
+        for ( int l = 0 ; l < p_sys->i_n_conv ; l++ )
+        {
+            i_read = ( i_write - *( p_delay + l )- (i_n_samples - 1 )  + i_buffer_length ) & i_modulo ;
+            for ( int j = i_n_samples ; j-- ; )
+            {
+                *( p_dest ) += *( p_ringbuffer[l] + ( ( i_read++ ) & i_modulo ) ) * *( p_temp_ir++ );
+            }
+        }
+        if ( p_sys->b_lfe )
+        {
+            *( p_dest ) += *( p_ringbuffer[p_sys->i_n_conv] + i_write ) * t_data->f_gain_lfe;
+        }
+        if ( *( p_dest ) >= 1 )
+        {
+            *t_data->p_n_clippings = *t_data->p_n_clippings + 1;
+        }
+        p_dest   += 2;
+        i_write  = ( i_write + 1 ) & i_modulo ;
+    }
+    t_data->i_write = i_write;
+    return;
+}
+
+/* Load the Sofa files, check for the most important SOFAconventions and load the whole IR Data, Source-Positions and Delays */
+
+static int LoadSofa ( filter_t *p_filter, char *c_filename, int i_i_sofa , int *p_samplingrate)
+{
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    int i_ncid, i_n_dims, i_n_vars, i_n_gatts, i_n_unlim_dim_id, i_status;
+    unsigned int i_samplingrate;
+    int i_n_samples = 0;
+    int i_m_dim = 0;
+    p_sys->sofa[i_i_sofa].i_ncid = 0;
+    i_status = nc_open( c_filename , NC_NOWRITE, &i_ncid);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Can't find SOFA-file '%s'", c_filename);
+        return VLC_EGENERIC;
+    }
+    nc_inq(i_ncid, &i_n_dims, &i_n_vars, &i_n_gatts, &i_n_unlim_dim_id); /* Get Number of Dimensions, Vars, Globa Attributes and Id of unlimited Dimensions */
+
+    char c_dim_names[i_n_dims][NC_MAX_NAME];   /* Get Dimensions */
+    uint32_t i_dim_length[i_n_dims];
+    int i_m_dim_id = 0;
+    int i_n_dim_id = 0; 
+    for( int ii = 0; ii<i_n_dims; ii++ )
+    {
+        nc_inq_dim( i_ncid, ii, c_dim_names[ii], &i_dim_length[ii] );
+        if ( !strcmp("M", c_dim_names[ii] ) )
+            i_m_dim_id = ii;
+        if ( !strcmp("N", c_dim_names[ii] ) )
+            i_n_dim_id = ii;
+        else { }
+    }
+    i_n_samples = i_dim_length[i_n_dim_id];
+    i_m_dim =  i_dim_length[i_m_dim_id];
+
+    uint32_t i_att_len;
+    i_status = nc_inq_attlen(i_ncid, NC_GLOBAL, "Conventions", &i_att_len);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Can't get Length of Attribute Conventions.");
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    char *psz_conventions;
+    psz_conventions = (char *)malloc( sizeof( char ) * ( i_att_len + 1 ) );
+    nc_get_att_text( i_ncid , NC_GLOBAL, "Conventions", psz_conventions);
+    *( psz_conventions + i_att_len ) = 0;
+    if ( strcmp( "SOFA" , psz_conventions ) )
+    {
+        msg_Err(p_filter, "Not a SOFA file!");
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    nc_inq_attlen (i_ncid, NC_GLOBAL, "SOFAConventions", &i_att_len );
+    char *psz_sofa_conventions;
+    psz_sofa_conventions = (char *)malloc( sizeof ( char ) * ( i_att_len + 1 ) );
+    nc_get_att_text(i_ncid, NC_GLOBAL, "SOFAConventions", psz_sofa_conventions);
+    *( psz_sofa_conventions + i_att_len ) = 0;
+    if ( strcmp( "SimpleFreeFieldHRIR" , psz_sofa_conventions ) )
+    {
+       msg_Err(p_filter, "No SimpleFreeFieldHRIR file!");
+       nc_close(i_ncid);
+       return VLC_EGENERIC;
+   }
+
+    int i_samplingrate_id;
+    i_status = nc_inq_varid( i_ncid, "Data.SamplingRate", &i_samplingrate_id);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read variable Data.SamplingRate ID");
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    i_status = nc_get_var_uint( i_ncid, i_samplingrate_id, &i_samplingrate);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read value of Data.SamplingRate.");
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    *p_samplingrate = i_samplingrate;
+
+    free(psz_sofa_conventions);
+    free(psz_conventions);
+
+    int i_data_ir_id;
+    i_status = nc_inq_varid( i_ncid, "Data.IR", &i_data_ir_id);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read Id of Data.IR." );
+        return VLC_EGENERIC;
+    }
+
+    float *p_data_ir = malloc ( sizeof( float ) * 2 * i_m_dim * i_n_samples );
+    if ( unlikely( !p_data_ir ) )
+    {
+        nc_close(i_ncid);
+        return VLC_ENOMEM;
+    }
+    i_status = nc_get_var_float( i_ncid, i_data_ir_id, p_data_ir );
+    if ( i_status != NC_NOERR )
+    {
+        msg_Err( p_filter, "Couldn't read Data.IR!" );
+        free( p_data_ir );
+        nc_close( i_ncid);
+        return VLC_EGENERIC;
+    }
+
+    int i_sp_id;
+    i_status = nc_inq_varid(i_ncid, "SourcePosition", &i_sp_id);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read ID of SourcePosition");
+        free( p_data_ir );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+
+    float *p_sp_a=(float *)malloc( sizeof(float) * i_m_dim);
+    if( unlikely( !p_sp_a ) )
+    {
+        free( p_data_ir );
+        nc_close(i_ncid);
+        return VLC_ENOMEM;
+    }
+    size_t start_a[]= { 0 , 0 };
+    size_t count_a[] = { i_m_dim , 1 };
+    i_status = nc_get_vara_float (i_ncid, i_sp_id, start_a, count_a, p_sp_a );
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read SourcePosition.");
+        free( p_data_ir );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+
+    float *p_sp_e=(float *)malloc( sizeof(float) * i_m_dim);
+    if( unlikely( !p_sp_e ) )
+    {
+        free( p_data_ir );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_ENOMEM;
+    }
+    size_t start_e[]= { 0 , 1 };
+    size_t count_e[] = { i_m_dim , 1 };
+    i_status = nc_get_vara_float (i_ncid, i_sp_id, start_e, count_e, p_sp_e );
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read SourcePosition.");
+        free( p_data_ir );
+        free( p_sp_e );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+
+    float *p_sp_r=(float *)malloc( sizeof(float) * i_m_dim);
+    if( unlikely( !p_sp_r ) )
+    {
+        free( p_data_ir );
+        free( p_sp_a );
+        free( p_sp_e );
+        nc_close(i_ncid);
+        return VLC_ENOMEM;
+    }
+
+    size_t start_r[]= { 0 , 2 };
+    size_t count_r[] = { i_m_dim , 1 };
+    i_status = nc_get_vara_float (i_ncid, i_sp_id, start_r, count_r, p_sp_r );
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read SourcePosition.");
+        free( p_data_ir );
+        free( p_sp_e );
+        free( p_sp_a );
+        free( p_sp_r );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+
+    int i_data_delay_id;
+    int i_data_delay_dim_id[2];
+    char i_data_delay_dim_name[NC_MAX_NAME];
+
+    int *p_data_delay = calloc ( sizeof( int ) , i_m_dim * 2 );
+    if ( unlikely( !p_data_delay ) )
+    {
+        free( p_data_ir );
+        free( p_sp_e );
+        free( p_sp_r );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_ENOMEM;
+    }
+    i_status = nc_inq_varid(i_ncid, "Data.Delay", &i_data_delay_id);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read Id of Data.Delay." );
+        free( p_data_delay );
+        free( p_data_ir );
+        free( p_sp_r );
+        free( p_sp_e );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    i_status = nc_inq_vardimid ( i_ncid, i_data_delay_id, &i_data_delay_dim_id[0]);
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read Dimension Ids of Data.Delay." );
+        free( p_data_delay );
+        free( p_sp_r );
+        free( p_data_ir );
+        free( p_sp_e );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    i_status = nc_inq_dimname ( i_ncid, i_data_delay_dim_id[0], i_data_delay_dim_name );
+    if (i_status != NC_NOERR)
+    {
+        msg_Err(p_filter, "Couldn't read Dimension Name of Data.Delay." );
+        free( p_data_delay );
+        free( p_data_ir );
+        free( p_sp_r );
+        free( p_sp_e );
+        free( p_sp_a );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    if ( !strncmp ( i_data_delay_dim_name, "I", 1 ) )
+    {
+        msg_Dbg ( p_filter, "DataDelay in Dimension IR");
+        int i_Delay[2];
+        i_status = nc_get_var_int( i_ncid, i_data_delay_id, &i_Delay[0] );
+        if ( i_status != NC_NOERR )
+        {
+            free( p_sp_r );
+            free( p_sp_e );
+            free( p_sp_a );
+            free( p_data_ir );
+            free( p_data_delay );
+            nc_close(i_ncid);
+            return VLC_EGENERIC;
+        }
+        int *p_data_delay_R = p_data_delay + i_m_dim;
+        for ( int i = 0 ; i < i_m_dim ; i++ )
+        {
+            *( p_data_delay + i ) = i_Delay[0];
+            *( p_data_delay_R + i ) = i_Delay[1];
+        }
+    }
+    else if ( strncmp ( i_data_delay_dim_name, "M", 1 ) )
+    {
+        msg_Err ( p_filter, "DataDelay not in the required Dimensions IR or MR.");
+        free( p_data_delay );
+        free( p_data_ir );
+        free( p_sp_e );
+        free( p_sp_a );
+        free( p_sp_r );
+        nc_close(i_ncid);
+        return VLC_EGENERIC;
+    }
+    else if ( !strncmp ( i_data_delay_dim_name, "M", 1 ) )
+    {
+        msg_Dbg( p_filter, "DataDelay in Dimension MR");
+        i_status = nc_get_var_int( i_ncid, i_data_delay_id, p_data_delay );
+        if (i_status != NC_NOERR)
+        {
+            msg_Err(p_filter, "Couldn't read Data.Delay");
+            free( p_sp_e );
+            free( p_sp_a );
+            free( p_data_ir );
+            free( p_data_delay );
+            free( p_sp_r );
+            nc_close(i_ncid);
+            return VLC_EGENERIC;
+        }
+    }
+    vlc_mutex_lock( &p_sys->lock );
+    p_sys->sofa[i_i_sofa].i_m_dim = i_m_dim;
+    p_sys->sofa[i_i_sofa].i_n_samples = i_n_samples;
+    p_sys->sofa[i_i_sofa].p_sp_a = p_sp_a;
+    p_sys->sofa[i_i_sofa].p_sp_e = p_sp_e;
+    p_sys->sofa[i_i_sofa].i_ncid = i_ncid;
+    p_sys->sofa[i_i_sofa].p_data_delay = p_data_delay;
+    p_sys->sofa[i_i_sofa].p_data_ir = p_data_ir;
+    p_sys->sofa[i_i_sofa].p_sp_r = p_sp_r;
+    vlc_mutex_unlock( &p_sys->lock );
+    nc_close(i_ncid);
+    return VLC_SUCCESS;
+}
+
+
+/* Load the impulse responses (reversed) for directions in p_ir_l and p_ir_r and put the gain on them. Find the correct impulse response with FindM threads.*/
+static int LoadIR ( filter_t *p_filter, int i_azim, int i_elev, float f_radius)
+{
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    vlc_thread_t thread_find_m[p_sys->i_n_conv];
+    struct data_findM_t data_find_m[p_sys->i_n_conv];
+    int i_n_samples = p_sys->sofa[p_sys->i_i_sofa].i_n_samples;
+    int i_n_conv = p_sys->i_n_conv;
+    int i_delay_l[i_n_conv];
+    int i_delay_r[i_n_conv];
+    int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
+    float f_gain_lin = exp( (p_sys->f_gain - 3 * i_input_nb) / 20 * log(10)); /* -3dB per channel */
+
+    float p_ir_l[p_sys->i_n_conv][p_sys->sofa[p_sys->i_i_sofa].i_n_samples];
+    float p_ir_r[p_sys->i_n_conv][p_sys->sofa[p_sys->i_i_sofa].i_n_samples];
+
+    int i_m[p_sys->i_n_conv];
+    if ( p_sys->i_switch )
+    {
+        i_azim = p_sys->i_azimuth_array[p_sys->i_switch - 1];
+        i_elev = p_sys->i_elevation_array[p_sys->i_switch -1];
+    }
+    for ( int i = 0 ; i < p_sys->i_n_conv ; i++ )
+    {
+        data_find_m[i].p_sys = p_sys;
+        data_find_m[i].i_azim = (int)(p_sys->p_speaker_pos[i] + i_azim ) % 360;
+        data_find_m[i].i_elev = i_elev;
+        data_find_m[i].f_radius = f_radius;
+        data_find_m[i].p_m = &i_m[i];
+        if ( vlc_clone ( &thread_find_m[i] , (void *)&sofalizer_FindM, (void *)&data_find_m[i],  VLC_THREAD_PRIORITY_LOW ) ) {}
+    }
+    for ( int i = 0 ; i < p_sys->i_n_conv ; i++ )
+    {
+        vlc_join( thread_find_m[i] , NULL );
+        for ( int j = 0 ; j < i_n_samples ; j++ )
+        {
+            p_ir_l[i][j] = *( p_sys->sofa[p_sys->i_i_sofa].p_data_ir + 2 * i_m[i] * i_n_samples + i_n_samples - 1 - j ) * f_gain_lin;
+            p_ir_r[i][j] = *( p_sys->sofa[p_sys->i_i_sofa].p_data_ir + 2 * i_m[i] * i_n_samples + i_n_samples - 1 - j  + i_n_samples ) * f_gain_lin;
+
+        }
+        msg_Dbg( p_filter, "Index: %d, Azimuth: %f, Elevation: %f, Radius: %f of SOFA file.", i_m[i], *(p_sys->sofa[p_sys->i_i_sofa].p_sp_a + i_m[i]), *(p_sys->sofa[p_sys->i_i_sofa].p_sp_e + i_m[i]), *(p_sys->sofa[p_sys->i_i_sofa].p_sp_r + i_m[i]) );
+
+        i_delay_l[i] = *( p_sys->sofa[p_sys->i_i_sofa].p_data_delay + 2 * i_m[i] );
+        i_delay_r[i] = *( p_sys->sofa[p_sys->i_i_sofa].p_data_delay + 2 * i_m[i] + 1);
+    }
+
+    vlc_mutex_lock( &p_sys->lock );
+    memcpy ( p_sys->p_ir_l , &p_ir_l[0][0] , sizeof( float ) * p_sys->i_n_conv * p_sys->sofa[p_sys->i_i_sofa].i_n_samples );
+    memcpy ( p_sys->p_ir_r , &p_ir_r[0][0] , sizeof( float ) * p_sys->i_n_conv * p_sys->sofa[p_sys->i_i_sofa].i_n_samples );
+    memcpy ( p_sys->p_delay_l , &i_delay_l[0] , sizeof( int ) * p_sys->i_n_conv );
+    memcpy ( p_sys->p_delay_r , &i_delay_r[0] , sizeof( int ) * p_sys->i_n_conv );
+    vlc_mutex_unlock( &p_sys->lock );
+    return VLC_SUCCESS;
+}
+
+void sofalizer_FindM ( void *p_ptr)
+{
+    struct data_findM_t *t_data;
+    t_data = (struct data_findM_t *)p_ptr;
+    struct filter_sys_t *p_sys = t_data->p_sys;
+    /*filter_t *p_filter = p_sys->p_parent;*/
+    int i_azim = t_data->i_azim;
+    int i_elev = t_data->i_elev;
+    float f_radius = t_data->f_radius;
+    int *p_m = t_data->p_m;
+    float *p_sp_a = p_sys->sofa[p_sys->i_i_sofa].p_sp_a;
+    float *p_sp_e = p_sys->sofa[p_sys->i_i_sofa].p_sp_e;
+    float *p_sp_r = p_sys->sofa[p_sys->i_i_sofa].p_sp_r;
+    int i_m_dim = p_sys->sofa[p_sys->i_i_sofa].i_m_dim;
+    int i_i_best = 0;
+    float f_delta = 1000;
+    float f_current;
+    for ( int i = 0; i < i_m_dim ; i++ )
+    {
+        f_current = fabs ( *(p_sp_a++) - i_azim ) + fabs( *(p_sp_e++) - i_elev ) +  fabs( *(p_sp_r++) - f_radius );
+        if ( f_current <= f_delta )
+        {
+                f_delta = f_current;
+                i_i_best = i;
+        }
+    }
+    *p_m = i_i_best;
+    return;
+}
+
+/* Find the Maximum Delay in the Sofa File */
+static int MaxDelay ( struct nc_sofa_t *sofa )
+{
+    int i_max = 0;
+    for ( int  i ; i < ( sofa->i_m_dim * 2 ) ; i++ )
+    {
+        if ( *( sofa->p_data_delay + i ) > i_max )
+            i_max = *( sofa->p_data_delay + i) ;
+    }
+    return i_max;
+}
+
+/* Compensate the Volume of the Sofa file. The Energy of the IR closest to ( 0°, 0°, 1m ) to the left ear is calculated and used for compensation.*/
+static int CompensateVolume( filter_t *p_filter)
+{
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    float f_energy = 0;
+    vlc_thread_t thread_find_m;
+    struct data_findM_t data_find_m;
+    int i_m;
+    int i_i_sofa_backup = p_sys->i_i_sofa;
+    float *p_ir;
+    float f_compensate;
+    for ( int i = 0 ; i < N_SOFA ; i++ )
+    {
+        if( p_sys->sofa[i].i_ncid )
+        {
+            struct nc_sofa_t *p_sofa = &p_sys->sofa[i];
+            p_sys->i_i_sofa = i;
+            data_find_m.p_sys = p_sys;
+            data_find_m.i_azim = 0;
+            data_find_m.i_elev = 0;
+            data_find_m.f_radius = 1;
+            data_find_m.p_m = &i_m;
+            if ( vlc_clone( &thread_find_m, (void *)&sofalizer_FindM, (void *)&data_find_m, VLC_THREAD_PRIORITY_LOW ) ) {} ;
+            vlc_join( thread_find_m , NULL );
+            p_ir = p_sofa->p_data_ir + 2 * i_m * p_sofa->i_n_samples;
+            for ( int j = 0 ; j < p_sofa->i_n_samples ; j ++ )
+            {
+                f_energy += *( p_ir + j ) * *(p_ir + j );
+            }
+            f_compensate = 256 / ( p_sofa->i_n_samples * sqrt( f_energy ) );
+            msg_Dbg( p_filter, "Compensate-factor: %f", f_compensate );
+            p_ir = p_sofa->p_data_ir;
+            for ( int j = 0 ; j < ( p_sofa->i_n_samples * p_sofa->i_m_dim * 2 ) ; j++ )
+            {
+                *( p_ir + j ) *= f_compensate;
+            }
+        }
+    }
+    p_sys->i_i_sofa = i_i_sofa_backup;
+    return VLC_SUCCESS;
+}
+
+static int GetSpeakerPos ( filter_t *p_filter, float *p_speaker_pos )
+{
+    uint16_t i_physical_channels = p_filter->fmt_in.audio.i_physical_channels;
+    if ( i_physical_channels == AOUT_CHAN_CENTER )
+    {
+        memcpy( p_speaker_pos , (float[1]){ 0 } , 1 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_STEREO || i_physical_channels == AOUT_CHANS_2_1 )
+    {
+        memcpy( p_speaker_pos , (float[2]){ 30 , 330 }, 2 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_3_0 || i_physical_channels == AOUT_CHANS_3_1 )
+    {
+        memcpy( p_speaker_pos , (float[3]){ 30 , 330 , 0 }, 3 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_4_0 || i_physical_channels == AOUT_CHANS_4_1 )
+    {
+        memcpy( p_speaker_pos , (float[4]){ 30 , 330 , 120 , 240 }, 4 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_5_0 || i_physical_channels == AOUT_CHANS_5_1 )
+    {
+        memcpy( p_speaker_pos , (float[5]){ 30 , 330 , 120 , 240 , 0 } , 5 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_5_0_MIDDLE || i_physical_channels == ( AOUT_CHANS_5_0_MIDDLE | AOUT_CHAN_LFE ) )
+    {
+        memcpy( p_speaker_pos , (float[5]){ 30 , 330 , 120 , 240 , 0 } , 5 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_6_0 )
+    {
+        memcpy( p_speaker_pos , (float[6]){ 30 , 330 , 90 , 270 , 150 , 210 }, 6 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_7_0 || i_physical_channels == AOUT_CHANS_7_1 )
+    {
+        memcpy( p_speaker_pos , (float[7]){ 30 , 330 , 90 , 270 , 150 , 210 , 0 }, 7 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    else if ( i_physical_channels == AOUT_CHANS_8_1 )
+    {
+        memcpy( p_speaker_pos , (float[8]){ 30 , 330 , 90 , 270 , 150 , 210 , 180 , 0 }, 8 * sizeof (float ) ) ;
+        return VLC_SUCCESS;
+    }
+    return VLC_EGENERIC;
+}
+
+/* Callbacks */
+
+static int GainCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->f_gain = newval.f_float;
+        vlc_mutex_unlock( &p_sys->lock );
+        LoadIR( p_filter, p_sys->f_rotation, p_sys->f_elevation, p_sys->f_radius );
+        msg_Dbg( p_filter, "New Gain-value: %f", newval.f_float );
+   }
+   return VLC_SUCCESS;
+}
+
+static int RotationCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data)
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    float f_temp= (int) (- newval.f_float + 720 ) % 360  ;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->f_rotation = f_temp ;
+        vlc_mutex_unlock( &p_sys->lock );
+        LoadIR( p_filter, f_temp, p_sys->f_elevation, p_sys->f_radius ) ;
+        msg_Dbg( p_filter, "New azimuth-value: %f", f_temp  );
+    }
+    return VLC_SUCCESS;
+}
+
+static int ElevationCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->f_elevation = newval.f_float ;
+        vlc_mutex_unlock( &p_sys->lock ) ;
+        LoadIR( p_filter, p_sys->f_rotation, newval.f_float, p_sys->f_radius );
+        msg_Dbg( p_filter, "New elvation-value: %f", newval.f_float );
+    }
+    return VLC_SUCCESS;
+}
+
+static int RadiusCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->f_radius = newval.f_float ;
+        vlc_mutex_unlock( &p_sys->lock ) ;
+        LoadIR( p_filter, p_sys->f_rotation, p_sys->f_elevation,  newval.f_float );
+        msg_Dbg( p_filter, "New radius-value: %f", newval.f_float );
+    }
+    return VLC_SUCCESS;
+}
+
+
+static int SwitchCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->i_switch = (int) newval.f_float ;
+        if ( p_sys->i_switch )
+        {
+            for ( int i = 0 ; i < p_sys->i_n_conv ; i++ ) *(p_sys->p_speaker_pos + i ) = 0;
+        }
+        else
+        {
+            GetSpeakerPos ( p_filter, p_sys->p_speaker_pos );
+        }
+        vlc_mutex_unlock( &p_sys->lock ) ;
+        LoadIR ( p_filter, p_sys->f_rotation, p_sys->f_elevation, p_sys->f_radius );
+        msg_Dbg( p_filter, "New Switch-Position: %d", (int) newval.f_float );
+    }
+    return VLC_SUCCESS;
+}
+
+static int SelectCallback( vlc_object_t *p_this, char const *psz_var,
+                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
+    filter_sys_t *p_sys1 = (filter_sys_t *) p_data;
+    filter_t *p_filter = p_sys1->p_parent;
+    struct filter_sys_t *p_sys = p_filter->p_sys;
+    if ( p_sys && p_sys->b_Enable )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        if ( p_sys->sofa[((int)newval.f_float + 5 - 1 ) % 5].i_ncid )
+        {
+            p_sys->i_i_sofa = ( (int) newval.f_float + 5 - 1) % 5 ;
+            p_sys->b_mute = false;
+            vlc_mutex_unlock( &p_sys->lock ) ;
+            LoadIR ( p_filter, p_sys->f_rotation, p_sys->f_elevation , p_sys->f_radius );
+            msg_Dbg( p_filter, "New Sofa-Select: %f", newval.f_float );
+        }
+        else
+        {
+            msg_Dbg( p_filter, "Invalid File selected!" );
+            p_sys->b_mute = true;
+            vlc_mutex_unlock( &p_sys->lock ) ;
+        }
+    }
+    return VLC_SUCCESS;
+}
+
+static void CloseSofa ( filter_t *p_filter )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    for ( int i = 0 ; i < N_SOFA ; i++)
+    {
+        if ( p_sys->sofa[i].i_ncid )
+        {
+            free ( p_sys->sofa[i].p_sp_a );
+            free ( p_sys->sofa[i].p_sp_e );
+            free ( p_sys->sofa[i].p_sp_r );
+            free ( p_sys->sofa[i].p_data_delay );
+            free ( p_sys->sofa[i].p_data_ir );
+        }
+    } 
+}
+
+static void Close( vlc_object_t *p_this )
+{
+    filter_t *p_filter = ( filter_t* )p_this;
+    filter_sys_t *p_sys = p_filter->p_sys;
+    vlc_object_t *p_aout = p_filter->p_parent;
+
+    var_DelCallback( p_aout, "sofalizer-gain", GainCallback, p_sys );
+    var_DelCallback( p_aout, "sofalizer-rotation", RotationCallback, p_sys );
+    var_DelCallback( p_aout, "sofalizer-elevation", ElevationCallback, p_sys );
+    var_DelCallback( p_aout, "sofalizer-switch", SwitchCallback, p_sys );
+    var_DelCallback( p_aout, "sofalizer-select", SelectCallback, p_sys );
+    var_DelCallback( p_aout, "sofalizer-radius", RadiusCallback, p_sys );
+
+    vlc_mutex_destroy( &p_sys->lock );
+
+    for ( int i = 0 ; i < N_SOFA ; i++)
+    {
+        if ( p_sys->sofa[i].i_ncid )
+        {
+            free ( p_sys->sofa[i].p_sp_a );
+            free ( p_sys->sofa[i].p_sp_e );
+            free ( p_sys->sofa[i].p_sp_r );
+            free ( p_sys->sofa[i].p_data_delay );
+            free ( p_sys->sofa[i].p_data_ir );
+        }
+    }
+    free ( p_sys->p_delay_l );
+    free ( p_sys->p_delay_r );
+    free ( p_sys->p_ir_l );
+    free ( p_sys->p_ir_r );
+    free ( p_sys->p_ringbuffer_l );
+    free ( p_sys->p_ringbuffer_r );
+    free ( p_sys->p_speaker_pos );
+    free ( p_sys );
+}
-- 
1.8.1.2




More information about the vlc-devel mailing list