/
sofalizer.c
1613 lines (1450 loc) · 71.6 KB
/
sofalizer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*****************************************************************************
* sofalizer.c : SOFAlizer plugin for virtual binaural acoustics
*****************************************************************************
* Copyright (C) 2013-2015 Andreas Fuchs, Wolfgang Hrauda,
* Acoustics Research Institute (ARI), Vienna, Austria
*
* Authors: Andreas Fuchs <andi.fuchs.mail@gmail.com>
* Wolfgang Hrauda <wolfgang.hrauda@gmx.at>
*
* SOFAlizer project coordinator at ARI, main developer of SOFA:
* Piotr Majdak <piotr@majdak.at>
*
* 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.
******************************************************************************/
/*****************************************************************************
* Commonly used abbreviations in the comments:
* IR ... impulse response
* no. ... number of
* ch. ... channels
* pos. ... position
* L/R ... left/right
******************************************************************************/
/*****************************************************************************
* 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 <samplerate.h>
#include <modules/audio_filter/sofalizer/resampling/samplerate.h>
#include <modules/audio_filter/sofalizer/fft/kiss_fft.h>
#include <math.h>
#include <netcdf.h>
#define N_SOFA 3 /* no. SOFA files loaded (for instant comparison) */
#define N_POSITIONS 4 /* no. virtual source positions (advanced settings) */
/* possible values of i_processing_type: */
#define PROC_TIME_DOM 0
#define PROC_FREQ_DOM 1
/*****************************************************************************
* Local prototypes
*****************************************************************************/
struct nc_sofa_s /* contains data of one SOFA file */
{
int i_ncid; /* netCDF ID of the opened SOFA file */
int i_n_samples; /* length of one impulse response (IR) */
int i_m_dim; /* number of measurement positions */
int *pi_data_delay; /* broadband delay of each IR */
/* all measurement positions for each receiver (i.e. ear): */
float *pf_sp_a; /* azimuth angles */
float *pf_sp_e; /* elevation angles */
float *pf_sp_r; /* radii */
/* dataat at each measurement position for each receiver: */
float *pf_data_ir; /* IRs (time-domain) */
};
struct filter_sys_t /* field of struct filter_t, which describes the filter */
{
struct nc_sofa_s sofa[N_SOFA]; /* contains data of the SOFA files */
/* mutually exclusive lock */
vlc_mutex_t lock; /* avoid interference by simultaneous threads */
float *pf_speaker_pos; /* positions of the virtual loudspekaers */
int i_n_conv; /* number of channels to convolute */
/* buffer variables (for convolution) */
float *pf_ringbuffer_l; /* buffers input samples, length of one buffer: */
float *pf_ringbuffer_r; /* no. input ch. (incl. LFE) x i_buffer_length */
int i_write; /* current write position to ringbuffer */
int i_buffer_length; /* is: longest IR plus max. delay in all SOFA files */
/* then choose next power of 2 */
int i_n_longest_filter; /* longest IR + max. delay in all SOFA files */
int i_output_buffer_length; /* remember no. samples in output buffer */
int i_n_fft; /* no. samples in one FFT block */
/* KissFFT configuration variables */
kiss_fft_cfg p_fft_cfg;
kiss_fft_cfg p_ifft_cfg;
/* netCDF variables */
int i_sofa_id; /* selected SOFA file (zero-based; unlike GUI "Select"!) */
int *pi_delay_l; /* broadband delay for each channel/IR to be convolved */
int *pi_delay_r;
float *pf_data_ir_l; /* IRs for all channels to be convolved */
float *pf_data_ir_r; /* (this excludes the LFE) */
kiss_fft_cpx *p_data_hrtf_l; /* HRTFs for all channels to be convolved */
kiss_fft_cpx *p_data_hrtf_r; /* (this excludes the LFE) */
/* control variables */
/* - from GUI: */
float f_gain; /* filter gain (in dB) */
float f_rotation; /* rotation of virtual loudspeakers (in degrees) */
float f_elevation; /* elevation of virtual loudspeakers (in deg.) */
float f_radius; /* distance virtual loudspeakers to listener (in metres) */
int i_switch; /* 0: source positions according to input format plus */
/* user's rotation and elevation settings on GUI, */
/* 1-4: virtual source pos. defined in advanced settings */
/* - from advanced settings: virtual source positions: */
int pi_azimuth_array[N_POSITIONS]; /* azimuth angles (in deg.) */
int pi_elevation_array[N_POSITIONS]; /* elevation angles (in deg.) */
int i_processing_type; /* type of audio processing algorithm used */
int i_resampling_type; /* quality of libsamplerate conversion */
bool b_mute; /* mutes audio output if set to true */
bool b_lfe; /* whether or not the LFE channel is used */
};
struct thread_data_s /* data for audio processing of one output channel */
{
filter_sys_t *p_sys; /* contains the filter data (see above) */
block_t *p_in_buf; /* contains input samples buffer and information */
int *pi_input_nb; /* points to i_input_nb (no. input ch. incl. LFE) */
int *pi_delay; /* broadband delay for each IR to be convolved */
int i_write; /* current write position to ringbuffer */
int *pi_n_clippings; /* points to clippings counter (output samples >= 1) */
float *pf_ringbuffer; /* buffers input samples, see struct filter_sys_t */
float *pf_dest; /* points to output samples buffer (p_out_buf->p_buffer) */
/* (samples of L and R channel are alternating in memory) */
float *pf_data; /* IRs/HRTFs for all channels to be convolved (excl. LFE) */
float f_gain_lfe; /* LFE gain: gain (GUI) -6dB -3dB/ch., linear, not dB */
};
/* constants for audio processing type menu (in advanced settings) */
static const int pi_processing_type_values[] =
{
PROC_TIME_DOM, PROC_FREQ_DOM,
};
static const char *const psz_processing_type_texts[] =
{
N_("Time-domain convolution"), N_("Fast Convolution"),
};
/* constants for resampling quality menu (in advanced settings) */
static const int pi_resampling_type_values[] =
{
SRC_SINC_BEST_QUALITY, SRC_SINC_MEDIUM_QUALITY, SRC_SINC_FASTEST,
SRC_ZERO_ORDER_HOLD, SRC_LINEAR,
};
static const char *const psz_resampling_type_texts[] =
{
N_("Sinc function (best quality)"), N_("Sinc function (medium quality)"),
N_("Sinc function (fast)"), N_("Zero Order Hold (fastest)"),
N_("Linear (fastest)"),
};
static int Open ( vlc_object_t *p_this ); /* opens the filter module */
static void Close( vlc_object_t * ); /* closes filter module, frees memory */
static block_t *DoWork( filter_t *, block_t * ); /* audio processing */
static int LoadData ( filter_t *p_filter, int i_azim, int i_elev, float f_radius);
void sofalizer_Convolute ( void *data );
int sofalizer_FastConvolution( void *data_l, void *data_r );
static int FindM ( filter_sys_t *p_sys, int i_azim, int i_elev, float f_radius );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define HELP_TEXT N_( "SOFAlizer uses head-related transfer functions (HRTFs) to create virtual loudspeakers around the user for binaural listening via headphones (audio formats up to 8.1 supported).\nThe HRTFs are stored in SOFA files (see www.sofacoustics.org for a database).\nSOFAlizer is developed at the Acoustics Research Institute (ARI) of the Austrian Academy of Sciences." )
#define GAIN_VALUE_TEXT N_( "Gain [dB]" )
#define GAIN_VALUE_LONGTEXT NULL
#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 FILE_NAME_LONGTEXT N_( "Three different SOFA files can be specified and used for instant comparison during playback." )
#define SELECT_VALUE_TEXT N_( "Select SOFA file used for rendering:" )
#define SELECT_VALUE_LONGTEXT N_( "SOFAlizer allows to load 3 different SOFA files and easily switch between them for better comparison." )
#define ROTATION_VALUE_TEXT N_( "Rotation (in deg)" )
#define ROTATION_VALUE_LONGTEXT N_( "Rotates virtual loudspeakers." )
#define ELEVATION_VALUE_TEXT N_( "Elevation (in deg)" )
#define ELEVATION_VALUE_LONGTEXT N_( "Elevates the virtual loudspeakers." )
#define RADIUS_VALUE_TEXT N_( "Radius (in m)")
#define RADIUS_VALUE_LONGTEXT N_( "Varies the distance between the loudspeakers and the listener with near-field HRTFs." )
#define SWITCH_VALUE_TEXT N_( "Switch" )
#define SWITCH_VALUE_LONGTEXT N_( "Presents all audio channels from one of four pre-defined virtual positions. Position 0 activates Rotation and Elevation." )
#define POS_VALUE_LONGTEXT N_( "Only active for Switch 1-4." )
#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 ")
#define PROCESSING_TYPE_VALUE_TEXT N_( "Audio Processing Type ")
#define PROCESSING_TYPE_VALUE_LONGTEXT NULL
#define RESAMPLING_TYPE_VALUE_TEXT N_( "HRTF Resampling Algorithm Type ")
#define RESAMPLING_TYPE_VALUE_LONGTEXT N_( "High quality will result in slower conversion times. Fast conversion means medicore quality.")
vlc_module_begin ()
set_description( "SOFAlizer" )
set_shortname( "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_float_with_range( "sofalizer-select", 1 , 1 , 3, SELECT_VALUE_TEXT, SELECT_VALUE_LONGTEXT, false)
add_float_with_range( "sofalizer-gain", 0.0, -20, 40, GAIN_VALUE_TEXT, GAIN_VALUE_LONGTEXT, false )
add_float_with_range( "sofalizer-rotation", 0, -360, 360, ROTATION_VALUE_TEXT, ROTATION_VALUE_LONGTEXT, false )
add_float_with_range( "sofalizer-elevation", 0, -90, 90, ELEVATION_VALUE_TEXT, ELEVATION_VALUE_LONGTEXT, false )
add_float_with_range( "sofalizer-radius", 1, 0, 2.1, RADIUS_VALUE_TEXT, RADIUS_VALUE_LONGTEXT, false )
add_float_with_range( "sofalizer-switch", 0, 0, 4, SWITCH_VALUE_TEXT, SWITCH_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos1-azi", 90, -180, 180,POS1_AZIMUTH_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos1-ele", 0, -90, 90, POS1_ELEVATION_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos2-azi", 180, -180, 180, POS2_AZIMUTH_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos2-ele", 0, -90, 90, POS2_ELEVATION_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos3-azi", -90, -180, 180, POS3_AZIMUTH_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos3-ele", 0, -90, 90, POS3_ELEVATION_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos4-azi", 0, -180, 180, POS4_AZIMUTH_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer_with_range( "sofalizer-pos4-ele", 90, -90, 90, POS4_ELEVATION_VALUE_TEXT, POS_VALUE_LONGTEXT, false )
add_integer ("sofalizer-processing-type", 1,
PROCESSING_TYPE_VALUE_TEXT, PROCESSING_TYPE_VALUE_LONGTEXT, true)
change_integer_list (pi_processing_type_values, psz_processing_type_texts)
add_integer ("sofalizer-resampling-type", SRC_SINC_FASTEST,
RESAMPLING_TYPE_VALUE_TEXT, RESAMPLING_TYPE_VALUE_LONGTEXT, true)
change_integer_list (pi_resampling_type_values, psz_resampling_type_texts)
add_shortcut( "sofalizer" )
set_category( CAT_AUDIO )
set_subcategory( SUBCAT_AUDIO_AFILTER )
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* CloseSofa: Closes the given SOFA file and frees its allocated memory.
* LoadSofa: Loads the given SOFA file, check for the most important SOFAconventions
* and load the whole IR Data, Source-Positions and Delays
* GetSpeakerPos: Get the Speaker Positions for current input.
* MaxDelay: Find the Maximum Delay in the Sofa File
* CompensateVolume: Compensate the Volume of the Sofa file. The Energy of the
* IR closest to ( 0°, 0°, 1m ) to the left ear is calculated.
* FreeAllSofa: Frees Memory allocated in LoadSofa of all Sofa files
* FreeFilter: Frees Memory allocated in Open
******************************************************************************/
static int CloseSofa ( struct nc_sofa_s *sofa )
{ /* close given SOFA file and free associated memory: */
free( sofa->pi_data_delay );
free( sofa->pf_sp_a );
free( sofa->pf_sp_e );
free( sofa->pf_sp_r );
free( sofa->pf_data_ir );
nc_close( sofa->i_ncid );
sofa->i_ncid = 0;
return VLC_SUCCESS;
}
static int LoadSofa ( filter_t *p_filter, char *psz_filename, /* loads one file*/
int i_sofa_id , int *p_samplingrate)
{
struct filter_sys_t *p_sys = p_filter->p_sys;
/* variables associated with content of SOFA file: */
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_sofa_id].i_ncid = 0;
i_status = nc_open( psz_filename , NC_NOWRITE, &i_ncid); /* open SOFA file read-only */
if (i_status != NC_NOERR)
{
msg_Err(p_filter, "Can't find SOFA-file '%s'", psz_filename);
return VLC_EGENERIC;
}
/* get number of dimensions, vars, global attributes and Id of unlimited dimensions: */
nc_inq(i_ncid, &i_n_dims, &i_n_vars, &i_n_gatts, &i_n_unlim_dim_id);
/* -- get number of measurements ("M") and length of one IR ("N") -- */
char psz_dim_names[i_n_dims][NC_MAX_NAME]; /* names of netCDF dimensions */
uint32_t pi_dim_length[i_n_dims]; /* lengths of netCDF dimensions */
int i_m_dim_id = -1;
int i_n_dim_id = -1;
for( int i = 0; i < i_n_dims; i++ ) /* go through all dimensions of file */
{
nc_inq_dim( i_ncid, i, psz_dim_names[i], &pi_dim_length[i] ); /* get dimensions */
if ( !strncmp("M", psz_dim_names[i], 1 ) ) /* get ID of dimension "M" */
i_m_dim_id = i;
if ( !strncmp("N", psz_dim_names[i], 1 ) ) /* get ID of dimension "N" */
i_n_dim_id = i;
}
if( ( i_m_dim_id == -1 ) || ( i_n_dim_id == -1 ) ) /* dimension "M" or "N" couldn't be found */
{
msg_Err(p_filter, "Can't find required dimensions in SOFA file.");
nc_close(i_ncid);
return VLC_EGENERIC;
}
i_n_samples = pi_dim_length[i_n_dim_id]; /* get number of measurements */
i_m_dim = pi_dim_length[i_m_dim_id]; /* get length of one IR */
/* -- check file type -- */
uint32_t i_att_len; /* get length of attritube "Conventions" */
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[i_att_len + 1]; /* check whether file is SOFA file */
nc_get_att_text( i_ncid , NC_GLOBAL, "Conventions", psz_conventions);
*( psz_conventions + i_att_len ) = 0;
if ( strncmp( "SOFA" , psz_conventions, 4 ) )
{
msg_Err(p_filter, "Not a SOFA file!");
nc_close(i_ncid);
return VLC_EGENERIC;
}
/* -- check if attribute "SOFAConventions" is "SimpleFreeFieldHRIR": -- */
nc_inq_attlen (i_ncid, NC_GLOBAL, "SOFAConventions", &i_att_len );
char psz_sofa_conventions[i_att_len + 1];
nc_get_att_text(i_ncid, NC_GLOBAL, "SOFAConventions", psz_sofa_conventions);
*( psz_sofa_conventions + i_att_len ) = 0;
if ( strncmp( "SimpleFreeFieldHRIR" , psz_sofa_conventions, i_att_len ) )
{
msg_Err(p_filter, "Not a SimpleFreeFieldHRIR file!");
nc_close(i_ncid);
return VLC_EGENERIC;
}
/* -- get sampling rate of HRTFs -- */
int i_samplingrate_id; /* read ID, then value */
i_status = nc_inq_varid( i_ncid, "Data.SamplingRate", &i_samplingrate_id);
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 Data.SamplingRate.");
nc_close(i_ncid);
return VLC_EGENERIC;
}
*p_samplingrate = i_samplingrate; /* remember sampling rate */
/* -- allocate memory for one value for each measurement position: -- */
float *pf_sp_a = p_sys->sofa[i_sofa_id].pf_sp_a = malloc( sizeof(float) * i_m_dim);
float *pf_sp_e = p_sys->sofa[i_sofa_id].pf_sp_e = malloc( sizeof(float) * i_m_dim);
float *pf_sp_r = p_sys->sofa[i_sofa_id].pf_sp_r = malloc( sizeof(float) * i_m_dim);
/* delay and IR values required for each ear and measurement position: */
int *pi_data_delay = p_sys->sofa[i_sofa_id].pi_data_delay =
calloc ( i_m_dim * 2, sizeof( int ) );
float *pf_data_ir = p_sys->sofa[i_sofa_id].pf_data_ir =
malloc( sizeof( float ) * 2 * i_m_dim * i_n_samples );
if ( !pi_data_delay || !pf_sp_a || !pf_sp_e || !pf_sp_r || !pf_data_ir )
{ /* if memory could not be allocated */
CloseSofa( &p_sys->sofa[i_sofa_id] );
return VLC_ENOMEM;
}
/* get impulse responses (HRTFs): */
int i_data_ir_id; /* get corresponding ID */
i_status = nc_inq_varid( i_ncid, "Data.IR", &i_data_ir_id);
i_status += nc_get_var_float( i_ncid, i_data_ir_id, pf_data_ir ); /* read and store IRs */
if ( i_status != NC_NOERR )
{
msg_Err( p_filter, "Couldn't read Data.IR!" );
goto error;
}
/* get source positions of the HRTFs in the SOFA file: */
int i_sp_id;
i_status = nc_inq_varid(i_ncid, "SourcePosition", &i_sp_id); /* get corresponding ID */
i_status += nc_get_vara_float (i_ncid, i_sp_id, (uint32_t[2]){ 0 , 0 } ,
(uint32_t[2]){ i_m_dim , 1 } , pf_sp_a ); /* read & store azimuth angles */
i_status += nc_get_vara_float (i_ncid, i_sp_id, (uint32_t[2]){ 0 , 1 } ,
(uint32_t[2]){ i_m_dim , 1 } , pf_sp_e ); /* read & store elevation angles */
i_status += nc_get_vara_float (i_ncid, i_sp_id, (uint32_t[2]){ 0 , 2 } ,
(uint32_t[2]){ i_m_dim , 1 } , pf_sp_r ); /* read & store radii */
if (i_status != NC_NOERR) /* if any source position variable coudn't be read */
{
msg_Err(p_filter, "Couldn't read SourcePosition.");
goto error;
}
/* read Data.Delay, check for errors and fit it to pi_data_delay */
int i_data_delay_id;
int pi_data_delay_dim_id[2];
char psz_data_delay_dim_name[NC_MAX_NAME];
i_status = nc_inq_varid(i_ncid, "Data.Delay", &i_data_delay_id);
i_status += nc_inq_vardimid ( i_ncid, i_data_delay_id, &pi_data_delay_dim_id[0]);
i_status += nc_inq_dimname ( i_ncid, pi_data_delay_dim_id[0], psz_data_delay_dim_name );
if (i_status != NC_NOERR)
{
msg_Err(p_filter, "Couldn't read Data.Delay." );
goto error;
}
/* Data.Delay dimension check */
/* dimension of Data.Delay is [I R]: */
if ( !strncmp ( psz_data_delay_dim_name, "I", 2 ) )
{ /* check 2 characters to assure string is 0-terminated after "I" */
msg_Dbg ( p_filter, "Data.Delay has dimension [I R]" );
int pi_Delay[2]; /* delays get from SOFA file: */
i_status = nc_get_var_int( i_ncid, i_data_delay_id, &pi_Delay[0] );
if ( i_status != NC_NOERR )
{
msg_Err(p_filter, "Couldn't read Data.Delay");
goto error;
}
int *pi_data_delay_r = pi_data_delay + i_m_dim;
for ( int i = 0 ; i < i_m_dim ; i++ ) /* extend given dimension [I R] to [M R] */
{ /* assign constant delay value for all measurements to data_delay fields */
*( pi_data_delay + i ) = pi_Delay[0];
*( pi_data_delay_r + i ) = pi_Delay[1];
}
}
/* dimension of Data.Delay is [M R] */
else if ( !strncmp ( psz_data_delay_dim_name, "M", 2 ) )
{
msg_Dbg( p_filter, "Data.Delay in dimension [M R]");
/* get delays from SOFA file: */
i_status = nc_get_var_int( i_ncid, i_data_delay_id, pi_data_delay );
if (i_status != NC_NOERR)
{
msg_Err(p_filter, "Couldn't read Data.Delay");
goto error;
}
}
else /* dimension of Data.Delay is neither [I R] nor [M R] */
{
msg_Err ( p_filter,
"Data.Delay does not have the required dimensions [I R] or [M R].");
goto error;
}
/* save information in SOFA struct: */
p_sys->sofa[i_sofa_id].i_m_dim = i_m_dim; /* no. measurement positions */
p_sys->sofa[i_sofa_id].i_n_samples = i_n_samples; /* length on one IR */
p_sys->sofa[i_sofa_id].i_ncid = i_ncid; /* netCDF ID of SOFA file */
nc_close(i_ncid); /* close SOFA file */
return VLC_SUCCESS;
error:
CloseSofa( &p_sys->sofa[i_sofa_id] );
return VLC_EGENERIC;
}
static int GetSpeakerPos ( filter_t *p_filter, float *pf_speaker_pos )
{
/* get input channel configuration: */
uint16_t i_physical_channels = p_filter->fmt_in.audio.i_physical_channels;
float *pf_pos_temp;
int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio ); /* get no. input channels */
int i_n_conv = i_input_nb;
if ( i_physical_channels & AOUT_CHAN_LFE ) /* if LFE is used */
{ /* decrease number of channels to be convolved: */
i_n_conv = i_input_nb - 1;
}
/* set speaker positions according to input channel configuration: */
switch ( i_physical_channels )
{
case AOUT_CHAN_CENTER: pf_pos_temp = (float[1]){ 0 };
break;
case AOUT_CHANS_STEREO:
case AOUT_CHANS_2_1: pf_pos_temp = (float[2]){ 30 , 330 };
break;
case AOUT_CHANS_3_0:
case AOUT_CHANS_3_1: pf_pos_temp = (float[3]){ 30 , 330 , 0 };
break;
case AOUT_CHANS_4_0:
case AOUT_CHANS_4_1: pf_pos_temp = (float[4]){ 30 , 330 , 120 , 240 };
break;
case AOUT_CHANS_5_0:
case AOUT_CHANS_5_1:
case ( AOUT_CHANS_5_0_MIDDLE | AOUT_CHAN_LFE ):
case AOUT_CHANS_5_0_MIDDLE: pf_pos_temp = (float[5]){ 30 , 330 , 120 , 240 , 0 };
break;
case AOUT_CHANS_6_0: pf_pos_temp = (float[6]){ 30 , 330 , 90 , 270 , 150 , 210 };
break;
case AOUT_CHANS_7_0:
case AOUT_CHANS_7_1: pf_pos_temp = (float[7]){ 30 , 330 , 90 , 270 , 150 , 210 , 0 };
break;
case AOUT_CHANS_8_1: pf_pos_temp = (float[8]){ 30 , 330 , 90 , 270 , 150 , 210 , 180 , 0 };
break;
default: return VLC_EGENERIC;
break;
}
memcpy( pf_speaker_pos , pf_pos_temp , i_n_conv * sizeof( float ) );
return VLC_SUCCESS;
}
static int MaxDelay ( struct nc_sofa_s *sofa )
{
int i_max = 0;
for ( int i = 0; i < ( sofa->i_m_dim * 2 ) ; i++ )
{ /* search maximum delay in given SOFA file */
if ( *( sofa->pi_data_delay + i ) > i_max )
i_max = *( sofa->pi_data_delay + i) ;
}
return i_max;
}
static int CompensateVolume( filter_t *p_filter)
{
struct filter_sys_t *p_sys = p_filter->p_sys;
float f_energy = 0;
int i_m;
int i_sofa_id_backup = p_sys->i_sofa_id;
float *pf_ir;
float f_compensate;
/* compensate volume for each SOFA file */
for ( int i = 0 ; i < N_SOFA ; i++ ) /* go through all SOFA files */
{
if( p_sys->sofa[i].i_ncid )
{
/* find IR at front center position in i-th SOFA file (IR closest to 0°,0°,1m) */
struct nc_sofa_s *p_sofa = &p_sys->sofa[i];
p_sys->i_sofa_id = i;
i_m = FindM( p_sys, 0, 0, 1 );
/* get energy of that IR and compensate volume */
pf_ir = p_sofa->pf_data_ir + 2 * i_m * p_sofa->i_n_samples;
for ( int j = 0 ; j < p_sofa->i_n_samples ; j ++ )
{
f_energy += *( pf_ir + j ) * *(pf_ir + j );
}
f_compensate = 256 / ( p_sofa->i_n_samples * sqrt( f_energy ) );
msg_Dbg( p_filter, "Compensate-factor: %f", f_compensate );
pf_ir = p_sofa->pf_data_ir;
for ( int j = 0 ; j < ( p_sofa->i_n_samples * p_sofa->i_m_dim * 2 ) ; j++ )
{
*( pf_ir + j ) *= f_compensate; /* apply volume compensation to IRs */
}
}
}
p_sys->i_sofa_id = i_sofa_id_backup;
return VLC_SUCCESS;
}
static void FreeAllSofa ( filter_t *p_filter )
{
filter_sys_t *p_sys = p_filter->p_sys;
/* go through all SOFA files and free associated memory: */
for ( int i = 0 ; i < N_SOFA ; i++)
{
if ( p_sys->sofa[i].i_ncid )
{
free( p_sys->sofa[i].pf_sp_a );
free( p_sys->sofa[i].pf_sp_e );
free( p_sys->sofa[i].pf_sp_r );
free( p_sys->sofa[i].pi_data_delay );
free( p_sys->sofa[i].pf_data_ir );
}
}
}
static void FreeFilter( filter_t *p_filter )
{
filter_sys_t *p_sys = p_filter->p_sys;
free( p_sys->pi_delay_l );
free( p_sys->pi_delay_r );
free( p_sys->pf_data_ir_l );
free( p_sys->pf_data_ir_r );
free( p_sys->p_data_hrtf_l );
free( p_sys->p_data_hrtf_r );
free( p_sys->pf_ringbuffer_l );
free( p_sys->pf_ringbuffer_r );
free( p_sys->pf_speaker_pos );
free( p_sys->p_fft_cfg );
free( p_sys->p_ifft_cfg );
free( p_sys );
}
/*****************************************************************************
* Callbacks
******************************************************************************/
static int GainCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *pf_data )
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
vlc_mutex_lock( &p_sys->lock );
p_sys->f_gain = newval.f_float;
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData( p_filter, p_sys->f_rotation, p_sys->f_elevation, p_sys->f_radius );
msg_Dbg( p_this , "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 *pf_data)
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
float f_temp= (int) (- newval.f_float + 720 ) % 360 ;
vlc_mutex_lock( &p_sys->lock );
p_sys->f_rotation = f_temp ;
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData( 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 *pf_data )
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
vlc_mutex_lock( &p_sys->lock );
p_sys->f_elevation = newval.f_float ;
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData( p_filter, p_sys->f_rotation, newval.f_float, p_sys->f_radius );
msg_Dbg( p_filter, "New elevation-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 *pf_data )
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
vlc_mutex_lock( &p_sys->lock );
p_sys->f_radius = newval.f_float ;
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData( 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;
}
/* new virtual source position selected */
static int SwitchCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *pf_data )
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
vlc_mutex_lock( &p_sys->lock );
p_sys->i_switch = (int) newval.f_float ;
if ( p_sys->i_switch )
{ /* if switch not equal 0, pre-defined virtual source positions are used: */
for ( int i = 0 ; i < p_sys->i_n_conv ; i++ ) *(p_sys->pf_speaker_pos + i ) = 0;
}
else /* if switch is zero */
{ /* get speaker positions depending on current input format */
GetSpeakerPos ( p_filter, p_sys->pf_speaker_pos );
}
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData ( 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;
}
/* new SOFA file selected */
static int SelectCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *pf_data )
{
VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
filter_t *p_filter = (filter_t *)pf_data;
filter_sys_t *p_sys = p_filter->p_sys;
vlc_mutex_lock( &p_sys->lock );
if ( p_sys->sofa[((int)newval.f_float + 5 - 1 ) % 5].i_ncid )
{
p_sys->i_sofa_id = ( (int) newval.f_float + 5 - 1) % 5 ;
p_sys->b_mute = false;
vlc_mutex_unlock( &p_sys->lock );
/* re-load IRs based on new GUI settings: */
LoadData ( 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_Err( p_filter, "Selected SOFA file is invalid. Please select valid SOFA file." );
p_sys->b_mute = true;
vlc_mutex_unlock( &p_sys->lock );
}
return VLC_SUCCESS;
}
/*****************************************************************************
* Open:
******************************************************************************/
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;
vlc_object_t *p_out = p_filter->p_parent; /* assign filter output */
char *psz_filename[N_SOFA];
const char *psz_var_names_filename[N_SOFA] =
{ "sofalizer-filename1", "sofalizer-filename2", "sofalizer-filename3" };
for ( int i = 0 ; i < N_SOFA ; i++ )
{ /* get SOFA file names from advanced settings */
psz_filename[i] = var_CreateGetStringCommand( p_filter, psz_var_names_filename[i] );
}
/* get user settings */
p_sys->f_rotation = fabs ( ( - (int) var_CreateGetFloat ( p_out, "sofalizer-rotation" ) + 720 ) % 360 );
p_sys->i_sofa_id = (int) ( var_CreateGetFloat ( p_out, "sofalizer-select" ) ) - 1;
p_sys->i_switch = (int) ( var_CreateGetFloat ( p_out, "sofalizer-switch" ) );
p_sys->f_gain = var_CreateGetFloat( p_out, "sofalizer-gain" );
p_sys->f_elevation = var_CreateGetFloat( p_out, "sofalizer-elevation" );
p_sys->f_radius = var_CreateGetFloat( p_out, "sofalizer-radius");
const char *psz_var_names_azimuth_array[N_POSITIONS] =
{ "sofalizer-pos1-azi", "sofalizer-pos2-azi", "sofalizer-pos3-azi", "sofalizer-pos4-azi" };
for ( int i = 0 ; i < N_POSITIONS ; i++ )
{ /* get azimuth angles of virtual source positions from advanced settings */
p_sys->pi_azimuth_array[i] = ( var_InheritInteger ( p_out, psz_var_names_azimuth_array[i] ) + 720 ) % 360 ;
}
const char *psz_var_names_elevation_array[N_POSITIONS] =
{ "sofalizer-pos1-ele", "sofalizer-pos2-ele", "sofalizer-pos3-ele", "sofalizer-pos4-ele" };
for ( int i = 0 ; i < N_POSITIONS ; i++ )
{ /* get elevation angles of virtual source positions from advanced settings */
p_sys->pi_elevation_array[i] = var_InheritInteger( p_out, psz_var_names_elevation_array[i] ) ;
}
p_sys->i_processing_type = var_InheritInteger( p_out,
"sofalizer-processing-type" ); /* get audio processing type */
p_sys->i_resampling_type = var_InheritInteger( p_out,
"sofalizer-resampling-type" ); /* get resampling quality */
p_sys->b_mute = false;
p_sys->i_write = 0;
/* sampling rate and SRC resampling variables */
int i_samplingrate = 0;
/* get sampling rate of audio file/stream: */
int i_samplingrate_stream = p_filter->fmt_in.audio.i_rate;
int i_n_samples_new; /* length of one resampled IR */
int i_n_samples_out; /* number of output samples*/
int i_resampling_type = p_sys->i_resampling_type; /* resampling quality */
int i_channels = 0; /* no. channels to resample (here, 1 channel is 1 IR) */
double d_ratio = 0; /* resampling conversion ratio */
SRC_DATA src; /* contains settings for sample rate conversion using SRC */
int i_err = -1; /* error code of SRC function */
float *pf_ir_temp; /* temporary variable for resampled IRs */
int i_status = 0; /* zero, if no file could be loaded */
/* load SOFA files, resample if sampling rate different from audio file */
for( int i = 0; i < N_SOFA; i++)
{ /* initialize file IDs to 0 before attempting to load SOFA files,
* this assures that in case of error, only the memory of already
* loaded files is free'd ( e.g. in FreeAllSofa() ) */
p_sys->sofa[i].i_ncid = 0;
}
for( int i = 0; i < N_SOFA; i++ )
{
if ( LoadSofa( p_filter, psz_filename[i], i , &i_samplingrate) != VLC_SUCCESS )
{ /* file loading error */
msg_Err(p_filter, "Error while loading SOFA file %d: '%s'", i + 1, psz_filename[i] );
}
else if (i_samplingrate != i_samplingrate_stream)
{ /* no file loading error, resampling required */
msg_Dbg( p_filter, "File %d: '%s' loaded. Resampling impulse responses to %d Hz.",
i + 1 , psz_filename[i], i_samplingrate_stream );
/* -- sampling rate conversion using SRC -- */
/* get conversion ratio, no. channels, no. output samples */
d_ratio = ( double ) i_samplingrate_stream / i_samplingrate;
i_channels = 2 * p_sys->sofa[i].i_m_dim; /* no. channels to resample */
i_n_samples_new = /* length of one resampled IR: */
ceil( ( double ) p_sys->sofa[i].i_n_samples * d_ratio );
i_n_samples_out = /* total no. output samples */
i_channels * i_n_samples_new;
/* get temporary memory for resampled IRs */
pf_ir_temp = malloc( sizeof(float) * i_n_samples_out );
if( !pf_ir_temp ) /* memory allocation failed */
{
FreeAllSofa( p_filter );
free( p_sys );
return VLC_ENOMEM;
}
/* set src data */
src.input_frames = p_sys->sofa[i].i_n_samples; /* length of 1 IR */
src.output_frames = i_n_samples_new; /* new number of samples */
src.src_ratio = d_ratio; /* sampling rate conversion ratio */
/* call SRC resampling function and check for errors */
//i_err = src_simple( &src, i_resampling_type, i_channels );
for( int j = 0; j < i_channels; j++ )
{
src.data_in = p_sys->sofa[i].pf_data_ir + j * p_sys->sofa[i].i_n_samples; /* IRs of current file */
src.data_out = pf_ir_temp + j * i_n_samples_new; /* write to temporary IR memory */
i_err = src_simple( &src, i_resampling_type, 1 );
if( unlikely( i_err ) ) /* if error occured during resampling */
{
msg_Err( p_filter, "cannot resample: %s", src_strerror (i_err) );
FreeAllSofa( p_filter );
free( p_sys );
free( pf_ir_temp );
return VLC_EGENERIC;
}
}
/* reallocate memory for resampled IRs */
p_sys->sofa[i].pf_data_ir = realloc( p_sys->sofa[i].pf_data_ir,
sizeof( float ) * i_n_samples_out );
if( !p_sys->sofa[i].pf_data_ir ) /* memory allocation failed */
{
FreeAllSofa( p_filter );
free( p_sys );
free( pf_ir_temp );
return VLC_ENOMEM;
}
/* copy resampled IRs from temporary to SOFA struct variables */
memcpy( p_sys->sofa[i].pf_data_ir, pf_ir_temp,
sizeof( float ) * i_n_samples_out );
/* update SOFA struct */
p_sys->sofa[i].i_n_samples = i_n_samples_new;
/* free temporary IR memory */
free( pf_ir_temp );
pf_ir_temp = NULL;
msg_Dbg( p_filter,
"Resampled %d impulses responses (each IR: %d samples at %d Hz to %d samples at %d Hz sampling rate, SOFA file %d).",
i_channels, src.input_frames_used, i_samplingrate,
src.output_frames_gen, i_samplingrate_stream, i + 1 );
/* resample broadband delays by multiplication with the ratio to
* maintain same delay time in sec with the new sampling rate */
for( int j = 0; j < 2 * p_sys->sofa[i].i_m_dim; j++ )
{
*( p_sys->sofa[i].pi_data_delay + j ) *= round( d_ratio );
}
i_status++; /* increase status after successful file loading */
}
else /* no file loading error, resampling not required */
{
msg_Dbg( p_filter, "File %d: '%s' loaded", i + 1 , psz_filename[i] );
i_status++; /* increase status after successful file loading */
}
}
if( !i_status )
{
msg_Err( p_filter, "No valid SOFA file could be loaded. Please specify at least one valid SOFA file." );
FreeAllSofa( p_filter );
free( p_sys );
return VLC_EGENERIC;
}
/* set filter settings and calculate speaker positions */
p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
p_filter->fmt_out.audio = p_filter->fmt_in.audio;
/* set physical channels to stereo, required for filter output set to stereo */
p_filter->fmt_out.audio.i_physical_channels = AOUT_CHANS_STEREO;
p_filter->fmt_out.audio.i_original_channels = AOUT_CHANS_STEREO;
int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio ); /* no. input channels */
if ( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) /* if LFE is used */
{
p_sys->b_lfe = true;
p_sys->i_n_conv = i_input_nb - 1 ; /* LFE is an input channel but requires no convolution */
}
else /* if LFE is not used */
{
p_sys->b_lfe = false;
p_sys->i_n_conv = i_input_nb ;
}
/* get size of ringbuffer (longest IR plus max. delay) */
/* then choose next power of 2 for performance optimization */
int i_n_max = 0;
int i_n_current;
int i_n_max_ir = 0;
for ( int i = 0 ; i < N_SOFA ; i++ )
{ /* go through all SOFA files and determine the longest IR */
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 )
{
/* length of longest IR plus max. delay (in all SOFA files) */
i_n_max = i_n_current;
/* length of longest IR (without delay, in all SOFA files) */
i_n_max_ir = p_sys->sofa[i].i_n_samples;
}
}
}
p_sys->i_n_longest_filter = i_n_max; /* longest IR plus max. delay */
/* buffer length is longest IR plus max. delay -> next power of 2
(32 - count leading zeros gives required exponent) */
p_sys->i_buffer_length = pow(2, 32 - clz( (uint32_t) i_n_max ) );
p_sys->i_output_buffer_length = 0; /* initialization */
p_sys->i_n_fft = 0; /* 0 until output buffer length was retrieved */
p_sys->p_fft_cfg = NULL; /* don't leave FFT config uninitialized */
p_sys->p_ifft_cfg = NULL; /* do this before FreeFilter might be called */
p_sys->p_data_hrtf_l = NULL; /* initialization */
p_sys->p_data_hrtf_r = NULL;
/* Allocate memory for the impulse responses, delays and the ringbuffers */
/* size: (longest IR) * (number of channels to convolute), without LFE */
p_sys->pf_data_ir_l = malloc( sizeof(float) * i_n_max_ir * p_sys->i_n_conv );
p_sys->pf_data_ir_r = malloc( sizeof(float) * i_n_max_ir * p_sys->i_n_conv );
/* length: number of channels to convolute */
p_sys->pi_delay_l = malloc ( sizeof( int ) * p_sys->i_n_conv );
p_sys->pi_delay_r = malloc ( sizeof( int ) * p_sys->i_n_conv );
/* length: (buffer length) * (number of input channels),
* OR: buffer length (if frequency domain processing)
* calloc zero-initializes the buffer */
if( p_sys->i_processing_type == PROC_TIME_DOM )
{
p_sys->pf_ringbuffer_l = calloc( p_sys->i_buffer_length * i_input_nb,
sizeof( float ) );
p_sys->pf_ringbuffer_r = calloc( p_sys->i_buffer_length * i_input_nb,
sizeof( float ) );
}
else if( p_sys->i_processing_type == PROC_FREQ_DOM )
{
p_sys->pf_ringbuffer_l = calloc( p_sys->i_buffer_length,
sizeof( float ) );
p_sys->pf_ringbuffer_r = calloc( p_sys->i_buffer_length,
sizeof( float ) );
}
/* length: number of channels to convolute */
p_sys->pf_speaker_pos = malloc( sizeof( float) * p_sys->i_n_conv );
/* memory allocation failed: */
if ( !p_sys->pf_data_ir_l || !p_sys->pf_data_ir_r || !p_sys->pi_delay_l ||
!p_sys->pi_delay_r || !p_sys->pf_ringbuffer_l || !p_sys->pf_ringbuffer_r
|| !p_sys->pf_speaker_pos )
{
FreeAllSofa( p_filter );
FreeFilter( p_filter );
return VLC_ENOMEM;
}
CompensateVolume ( p_filter );
/* get speaker positions */
if ( GetSpeakerPos ( p_filter, p_sys->pf_speaker_pos ) != VLC_SUCCESS )
{
msg_Err (p_filter, "Couldn't get speaker positions. Input channel configuration not supported. ");
FreeAllSofa( p_filter );
FreeFilter( p_filter );
return VLC_EGENERIC;
}
vlc_mutex_init( &p_sys->lock );
/* load IRs to pf_data_ir_l and pf_data_ir_r for required directions */
if( p_sys->i_processing_type == PROC_TIME_DOM )
{ /* only load IRs if time-domain convolution is used,
* otherwise, data is loaded on FFT size change */
if ( LoadData ( p_filter, p_sys->f_rotation, p_sys->f_elevation,
p_sys->f_radius ) != VLC_SUCCESS )
{
FreeAllSofa( p_filter );
FreeFilter( p_filter );