1 /*****************************************************************************
2 * sofalizer.c : SOFAlizer filter for virtual binaural acoustics
3 *****************************************************************************
4 * Copyright (C) 2013-2015 Andreas Fuchs, Wolfgang Hrauda,
5 * Acoustics Research Institute (ARI), Vienna, Austria
6 *
7 * Authors: Andreas Fuchs <andi.fuchs.mail@gmail.com>
8 * Wolfgang Hrauda <wolfgang.hrauda@gmx.at>
9 *
10 * SOFAlizer project coordinator at ARI, main developer of SOFA:
11 * Piotr Majdak <piotr@majdak.at>
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 2.1 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with this program; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
27
28 #include <math.h>
29 #include <mysofa.h>
30
31 #include "libavutil/tx.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/channel_layout.h"
34 #include "libavutil/float_dsp.h"
35 #include "libavutil/intmath.h"
36 #include "libavutil/opt.h"
37 #include "avfilter.h"
38 #include "filters.h"
39 #include "internal.h"
40 #include "audio.h"
41
42 #define TIME_DOMAIN 0
43 #define FREQUENCY_DOMAIN 1
44
45 typedef struct MySofa { /* contains data of one SOFA file */
46 struct MYSOFA_HRTF *hrtf;
47 struct MYSOFA_LOOKUP *lookup;
48 struct MYSOFA_NEIGHBORHOOD *neighborhood;
49 int ir_samples; /* length of one impulse response (IR) */
50 int n_samples; /* ir_samples to next power of 2 */
51 float *lir, *rir; /* IRs (time-domain) */
52 float *fir;
53 int max_delay;
54 } MySofa;
55
56 typedef struct VirtualSpeaker {
57 uint8_t set;
58 float azim;
59 float elev;
60 } VirtualSpeaker;
61
62 typedef struct SOFAlizerContext {
63 const AVClass *class;
64
65 char *filename; /* name of SOFA file */
66 MySofa sofa; /* contains data of the SOFA file */
67
68 int sample_rate; /* sample rate from SOFA file */
69 float *speaker_azim; /* azimuth of the virtual loudspeakers */
70 float *speaker_elev; /* elevation of the virtual loudspeakers */
71 char *speakers_pos; /* custom positions of the virtual loudspeakers */
72 float lfe_gain; /* initial gain for the LFE channel */
73 float gain_lfe; /* gain applied to LFE channel */
74 int lfe_channel; /* LFE channel position in channel layout */
75
76 int n_conv; /* number of channels to convolute */
77
78 /* buffer variables (for convolution) */
79 float *ringbuffer[2]; /* buffers input samples, length of one buffer: */
80 /* no. input ch. (incl. LFE) x buffer_length */
81 int write[2]; /* current write position to ringbuffer */
82 int buffer_length; /* is: longest IR plus max. delay in all SOFA files */
83 /* then choose next power of 2 */
84 int n_fft; /* number of samples in one FFT block */
85 int nb_samples;
86
87 /* netCDF variables */
88 int *delay[2]; /* broadband delay for each channel/IR to be convolved */
89
90 float *data_ir[2]; /* IRs for all channels to be convolved */
91 /* (this excludes the LFE) */
92 float *temp_src[2];
93 AVComplexFloat *in_fft[2]; /* Array to hold input FFT values */
94 AVComplexFloat *out_fft[2]; /* Array to hold output FFT values */
95 AVComplexFloat *temp_afft[2]; /* Array to accumulate FFT values prior to IFFT */
96
97 /* control variables */
98 float gain; /* filter gain (in dB) */
99 float rotation; /* rotation of virtual loudspeakers (in degrees) */
100 float elevation; /* elevation of virtual loudspeakers (in deg.) */
101 float radius; /* distance virtual loudspeakers to listener (in metres) */
102 int type; /* processing type */
103 int framesize; /* size of buffer */
104 int normalize; /* should all IRs be normalized upon import ? */
105 int interpolate; /* should wanted IRs be interpolated from neighbors ? */
106 int minphase; /* should all IRs be minphased upon import ? */
107 float anglestep; /* neighbor search angle step, in agles */
108 float radstep; /* neighbor search radius step, in meters */
109
110 VirtualSpeaker vspkrpos[64];
111
112 AVTXContext *fft[2], *ifft[2];
113 av_tx_fn tx_fn[2], itx_fn[2];
114 AVComplexFloat *data_hrtf[2];
115
116 AVFloatDSPContext *fdsp;
117 } SOFAlizerContext;
118
close_sofa(struct MySofa * sofa)119 static int close_sofa(struct MySofa *sofa)
120 {
121 if (sofa->neighborhood)
122 mysofa_neighborhood_free(sofa->neighborhood);
123 sofa->neighborhood = NULL;
124 if (sofa->lookup)
125 mysofa_lookup_free(sofa->lookup);
126 sofa->lookup = NULL;
127 if (sofa->hrtf)
128 mysofa_free(sofa->hrtf);
129 sofa->hrtf = NULL;
130 av_freep(&sofa->fir);
131
132 return 0;
133 }
134
preload_sofa(AVFilterContext * ctx,char * filename,int * samplingrate)135 static int preload_sofa(AVFilterContext *ctx, char *filename, int *samplingrate)
136 {
137 struct SOFAlizerContext *s = ctx->priv;
138 struct MYSOFA_HRTF *mysofa;
139 char *license;
140 int ret;
141
142 mysofa = mysofa_load(filename, &ret);
143 s->sofa.hrtf = mysofa;
144 if (ret || !mysofa) {
145 av_log(ctx, AV_LOG_ERROR, "Can't find SOFA-file '%s'\n", filename);
146 return AVERROR(EINVAL);
147 }
148
149 ret = mysofa_check(mysofa);
150 if (ret != MYSOFA_OK) {
151 av_log(ctx, AV_LOG_ERROR, "Selected SOFA file is invalid. Please select valid SOFA file.\n");
152 return ret;
153 }
154
155 if (s->normalize)
156 mysofa_loudness(s->sofa.hrtf);
157
158 if (s->minphase)
159 mysofa_minphase(s->sofa.hrtf, 0.01f);
160
161 mysofa_tocartesian(s->sofa.hrtf);
162
163 s->sofa.lookup = mysofa_lookup_init(s->sofa.hrtf);
164 if (s->sofa.lookup == NULL)
165 return AVERROR(EINVAL);
166
167 if (s->interpolate)
168 s->sofa.neighborhood = mysofa_neighborhood_init_withstepdefine(s->sofa.hrtf,
169 s->sofa.lookup,
170 s->anglestep,
171 s->radstep);
172
173 s->sofa.fir = av_calloc(s->sofa.hrtf->N * s->sofa.hrtf->R, sizeof(*s->sofa.fir));
174 if (!s->sofa.fir)
175 return AVERROR(ENOMEM);
176
177 if (mysofa->DataSamplingRate.elements != 1)
178 return AVERROR(EINVAL);
179 av_log(ctx, AV_LOG_DEBUG, "Original IR length: %d.\n", mysofa->N);
180 *samplingrate = mysofa->DataSamplingRate.values[0];
181 license = mysofa_getAttribute(mysofa->attributes, (char *)"License");
182 if (license)
183 av_log(ctx, AV_LOG_INFO, "SOFA license: %s\n", license);
184
185 return 0;
186 }
187
parse_channel_name(AVFilterContext * ctx,char ** arg,int * rchannel)188 static int parse_channel_name(AVFilterContext *ctx, char **arg, int *rchannel)
189 {
190 int len;
191 enum AVChannel channel_id = 0;
192 char buf[8] = {0};
193
194 /* try to parse a channel name, e.g. "FL" */
195 if (av_sscanf(*arg, "%7[A-Z]%n", buf, &len)) {
196 channel_id = av_channel_from_string(buf);
197 if (channel_id < 0 || channel_id >= 64) {
198 av_log(ctx, AV_LOG_WARNING, "Failed to parse \'%s\' as channel name.\n", buf);
199 return AVERROR(EINVAL);
200 }
201
202 *rchannel = channel_id;
203 *arg += len;
204 return 0;
205 } else if (av_sscanf(*arg, "%d%n", &channel_id, &len) == 1) {
206 if (channel_id < 0 || channel_id >= 64) {
207 av_log(ctx, AV_LOG_WARNING, "Failed to parse \'%d\' as channel number.\n", channel_id);
208 return AVERROR(EINVAL);
209 }
210 *rchannel = channel_id;
211 *arg += len;
212 return 0;
213 }
214 return AVERROR(EINVAL);
215 }
216
parse_speaker_pos(AVFilterContext * ctx)217 static void parse_speaker_pos(AVFilterContext *ctx)
218 {
219 SOFAlizerContext *s = ctx->priv;
220 char *arg, *tokenizer, *p, *args = av_strdup(s->speakers_pos);
221
222 if (!args)
223 return;
224 p = args;
225
226 while ((arg = av_strtok(p, "|", &tokenizer))) {
227 float azim, elev;
228 int out_ch_id;
229
230 p = NULL;
231 if (parse_channel_name(ctx, &arg, &out_ch_id)) {
232 continue;
233 }
234 if (av_sscanf(arg, "%f %f", &azim, &elev) == 2) {
235 s->vspkrpos[out_ch_id].set = 1;
236 s->vspkrpos[out_ch_id].azim = azim;
237 s->vspkrpos[out_ch_id].elev = elev;
238 } else if (av_sscanf(arg, "%f", &azim) == 1) {
239 s->vspkrpos[out_ch_id].set = 1;
240 s->vspkrpos[out_ch_id].azim = azim;
241 s->vspkrpos[out_ch_id].elev = 0;
242 }
243 }
244
245 av_free(args);
246 }
247
get_speaker_pos(AVFilterContext * ctx,float * speaker_azim,float * speaker_elev)248 static int get_speaker_pos(AVFilterContext *ctx,
249 float *speaker_azim, float *speaker_elev)
250 {
251 struct SOFAlizerContext *s = ctx->priv;
252 AVChannelLayout *channel_layout = &ctx->inputs[0]->ch_layout;
253 float azim[64] = { 0 };
254 float elev[64] = { 0 };
255 int ch, n_conv = ctx->inputs[0]->ch_layout.nb_channels; /* get no. input channels */
256
257 if (n_conv < 0 || n_conv > 64)
258 return AVERROR(EINVAL);
259
260 s->lfe_channel = -1;
261
262 if (s->speakers_pos)
263 parse_speaker_pos(ctx);
264
265 /* set speaker positions according to input channel configuration: */
266 for (ch = 0; ch < n_conv; ch++) {
267 int chan = av_channel_layout_channel_from_index(channel_layout, ch);
268
269 switch (chan) {
270 case AV_CHAN_FRONT_LEFT: azim[ch] = 30; break;
271 case AV_CHAN_FRONT_RIGHT: azim[ch] = 330; break;
272 case AV_CHAN_FRONT_CENTER: azim[ch] = 0; break;
273 case AV_CHAN_LOW_FREQUENCY:
274 case AV_CHAN_LOW_FREQUENCY_2: s->lfe_channel = ch; break;
275 case AV_CHAN_BACK_LEFT: azim[ch] = 150; break;
276 case AV_CHAN_BACK_RIGHT: azim[ch] = 210; break;
277 case AV_CHAN_BACK_CENTER: azim[ch] = 180; break;
278 case AV_CHAN_SIDE_LEFT: azim[ch] = 90; break;
279 case AV_CHAN_SIDE_RIGHT: azim[ch] = 270; break;
280 case AV_CHAN_FRONT_LEFT_OF_CENTER: azim[ch] = 15; break;
281 case AV_CHAN_FRONT_RIGHT_OF_CENTER: azim[ch] = 345; break;
282 case AV_CHAN_TOP_CENTER: azim[ch] = 0;
283 elev[ch] = 90; break;
284 case AV_CHAN_TOP_FRONT_LEFT: azim[ch] = 30;
285 elev[ch] = 45; break;
286 case AV_CHAN_TOP_FRONT_CENTER: azim[ch] = 0;
287 elev[ch] = 45; break;
288 case AV_CHAN_TOP_FRONT_RIGHT: azim[ch] = 330;
289 elev[ch] = 45; break;
290 case AV_CHAN_TOP_BACK_LEFT: azim[ch] = 150;
291 elev[ch] = 45; break;
292 case AV_CHAN_TOP_BACK_RIGHT: azim[ch] = 210;
293 elev[ch] = 45; break;
294 case AV_CHAN_TOP_BACK_CENTER: azim[ch] = 180;
295 elev[ch] = 45; break;
296 case AV_CHAN_WIDE_LEFT: azim[ch] = 90; break;
297 case AV_CHAN_WIDE_RIGHT: azim[ch] = 270; break;
298 case AV_CHAN_SURROUND_DIRECT_LEFT: azim[ch] = 90; break;
299 case AV_CHAN_SURROUND_DIRECT_RIGHT: azim[ch] = 270; break;
300 case AV_CHAN_STEREO_LEFT: azim[ch] = 90; break;
301 case AV_CHAN_STEREO_RIGHT: azim[ch] = 270; break;
302 default:
303 return AVERROR(EINVAL);
304 }
305
306 if (s->vspkrpos[ch].set) {
307 azim[ch] = s->vspkrpos[ch].azim;
308 elev[ch] = s->vspkrpos[ch].elev;
309 }
310 }
311
312 memcpy(speaker_azim, azim, n_conv * sizeof(float));
313 memcpy(speaker_elev, elev, n_conv * sizeof(float));
314
315 return 0;
316
317 }
318
319 typedef struct ThreadData {
320 AVFrame *in, *out;
321 int *write;
322 int **delay;
323 float **ir;
324 int *n_clippings;
325 float **ringbuffer;
326 float **temp_src;
327 AVComplexFloat **in_fft;
328 AVComplexFloat **out_fft;
329 AVComplexFloat **temp_afft;
330 } ThreadData;
331
sofalizer_convolute(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)332 static int sofalizer_convolute(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
333 {
334 SOFAlizerContext *s = ctx->priv;
335 ThreadData *td = arg;
336 AVFrame *in = td->in, *out = td->out;
337 int offset = jobnr;
338 int *write = &td->write[jobnr];
339 const int *const delay = td->delay[jobnr];
340 const float *const ir = td->ir[jobnr];
341 int *n_clippings = &td->n_clippings[jobnr];
342 float *ringbuffer = td->ringbuffer[jobnr];
343 float *temp_src = td->temp_src[jobnr];
344 const int ir_samples = s->sofa.ir_samples; /* length of one IR */
345 const int n_samples = s->sofa.n_samples;
346 const int planar = in->format == AV_SAMPLE_FMT_FLTP;
347 const int mult = 1 + !planar;
348 const float *src = (const float *)in->extended_data[0]; /* get pointer to audio input buffer */
349 float *dst = (float *)out->extended_data[jobnr * planar]; /* get pointer to audio output buffer */
350 const int in_channels = s->n_conv; /* number of input channels */
351 /* ring buffer length is: longest IR plus max. delay -> next power of 2 */
352 const int buffer_length = s->buffer_length;
353 /* -1 for AND instead of MODULO (applied to powers of 2): */
354 const uint32_t modulo = (uint32_t)buffer_length - 1;
355 float *buffer[64]; /* holds ringbuffer for each input channel */
356 int wr = *write;
357 int read;
358 int i, l;
359
360 if (!planar)
361 dst += offset;
362
363 for (l = 0; l < in_channels; l++) {
364 /* get starting address of ringbuffer for each input channel */
365 buffer[l] = ringbuffer + l * buffer_length;
366 }
367
368 for (i = 0; i < in->nb_samples; i++) {
369 const float *temp_ir = ir; /* using same set of IRs for each sample */
370
371 dst[0] = 0;
372 if (planar) {
373 for (l = 0; l < in_channels; l++) {
374 const float *srcp = (const float *)in->extended_data[l];
375
376 /* write current input sample to ringbuffer (for each channel) */
377 buffer[l][wr] = srcp[i];
378 }
379 } else {
380 for (l = 0; l < in_channels; l++) {
381 /* write current input sample to ringbuffer (for each channel) */
382 buffer[l][wr] = src[l];
383 }
384 }
385
386 /* loop goes through all channels to be convolved */
387 for (l = 0; l < in_channels; l++) {
388 const float *const bptr = buffer[l];
389
390 if (l == s->lfe_channel) {
391 /* LFE is an input channel but requires no convolution */
392 /* apply gain to LFE signal and add to output buffer */
393 dst[0] += *(buffer[s->lfe_channel] + wr) * s->gain_lfe;
394 temp_ir += n_samples;
395 continue;
396 }
397
398 /* current read position in ringbuffer: input sample write position
399 * - delay for l-th ch. + diff. betw. IR length and buffer length
400 * (mod buffer length) */
401 read = (wr - delay[l] - (ir_samples - 1) + buffer_length) & modulo;
402
403 if (read + ir_samples < buffer_length) {
404 memmove(temp_src, bptr + read, ir_samples * sizeof(*temp_src));
405 } else {
406 int len = FFMIN(n_samples - (read % ir_samples), buffer_length - read);
407
408 memmove(temp_src, bptr + read, len * sizeof(*temp_src));
409 memmove(temp_src + len, bptr, (n_samples - len) * sizeof(*temp_src));
410 }
411
412 /* multiply signal and IR, and add up the results */
413 dst[0] += s->fdsp->scalarproduct_float(temp_ir, temp_src, FFALIGN(ir_samples, 32));
414 temp_ir += n_samples;
415 }
416
417 /* clippings counter */
418 if (fabsf(dst[0]) > 1)
419 n_clippings[0]++;
420
421 /* move output buffer pointer by +2 to get to next sample of processed channel: */
422 dst += mult;
423 src += in_channels;
424 wr = (wr + 1) & modulo; /* update ringbuffer write position */
425 }
426
427 *write = wr; /* remember write position in ringbuffer for next call */
428
429 return 0;
430 }
431
sofalizer_fast_convolute(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)432 static int sofalizer_fast_convolute(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
433 {
434 SOFAlizerContext *s = ctx->priv;
435 ThreadData *td = arg;
436 AVFrame *in = td->in, *out = td->out;
437 int offset = jobnr;
438 int *write = &td->write[jobnr];
439 AVComplexFloat *hrtf = s->data_hrtf[jobnr]; /* get pointers to current HRTF data */
440 int *n_clippings = &td->n_clippings[jobnr];
441 float *ringbuffer = td->ringbuffer[jobnr];
442 const int ir_samples = s->sofa.ir_samples; /* length of one IR */
443 const int planar = in->format == AV_SAMPLE_FMT_FLTP;
444 const int mult = 1 + !planar;
445 float *dst = (float *)out->extended_data[jobnr * planar]; /* get pointer to audio output buffer */
446 const int in_channels = s->n_conv; /* number of input channels */
447 /* ring buffer length is: longest IR plus max. delay -> next power of 2 */
448 const int buffer_length = s->buffer_length;
449 /* -1 for AND instead of MODULO (applied to powers of 2): */
450 const uint32_t modulo = (uint32_t)buffer_length - 1;
451 AVComplexFloat *fft_in = s->in_fft[jobnr]; /* temporary array for FFT input data */
452 AVComplexFloat *fft_out = s->out_fft[jobnr]; /* temporary array for FFT output data */
453 AVComplexFloat *fft_acc = s->temp_afft[jobnr];
454 AVTXContext *ifft = s->ifft[jobnr];
455 av_tx_fn itx_fn = s->itx_fn[jobnr];
456 AVTXContext *fft = s->fft[jobnr];
457 av_tx_fn tx_fn = s->tx_fn[jobnr];
458 const int n_conv = s->n_conv;
459 const int n_fft = s->n_fft;
460 const float fft_scale = 1.0f / s->n_fft;
461 AVComplexFloat *hrtf_offset;
462 int wr = *write;
463 int n_read;
464 int i, j;
465
466 if (!planar)
467 dst += offset;
468
469 /* find minimum between number of samples and output buffer length:
470 * (important, if one IR is longer than the output buffer) */
471 n_read = FFMIN(ir_samples, in->nb_samples);
472 for (j = 0; j < n_read; j++) {
473 /* initialize output buf with saved signal from overflow buf */
474 dst[mult * j] = ringbuffer[wr];
475 ringbuffer[wr] = 0.0f; /* re-set read samples to zero */
476 /* update ringbuffer read/write position */
477 wr = (wr + 1) & modulo;
478 }
479
480 /* initialize rest of output buffer with 0 */
481 for (j = n_read; j < in->nb_samples; j++) {
482 dst[mult * j] = 0;
483 }
484
485 /* fill FFT accumulation with 0 */
486 memset(fft_acc, 0, sizeof(AVComplexFloat) * n_fft);
487
488 for (i = 0; i < n_conv; i++) {
489 const float *src = (const float *)in->extended_data[i * planar]; /* get pointer to audio input buffer */
490
491 if (i == s->lfe_channel) { /* LFE */
492 if (in->format == AV_SAMPLE_FMT_FLT) {
493 for (j = 0; j < in->nb_samples; j++) {
494 /* apply gain to LFE signal and add to output buffer */
495 dst[2 * j] += src[i + j * in_channels] * s->gain_lfe;
496 }
497 } else {
498 for (j = 0; j < in->nb_samples; j++) {
499 /* apply gain to LFE signal and add to output buffer */
500 dst[j] += src[j] * s->gain_lfe;
501 }
502 }
503 continue;
504 }
505
506 /* outer loop: go through all input channels to be convolved */
507 offset = i * n_fft; /* no. samples already processed */
508 hrtf_offset = hrtf + offset;
509
510 /* fill FFT input with 0 (we want to zero-pad) */
511 memset(fft_in, 0, sizeof(AVComplexFloat) * n_fft);
512
513 if (in->format == AV_SAMPLE_FMT_FLT) {
514 for (j = 0; j < in->nb_samples; j++) {
515 /* prepare input for FFT */
516 /* write all samples of current input channel to FFT input array */
517 fft_in[j].re = src[j * in_channels + i];
518 }
519 } else {
520 for (j = 0; j < in->nb_samples; j++) {
521 /* prepare input for FFT */
522 /* write all samples of current input channel to FFT input array */
523 fft_in[j].re = src[j];
524 }
525 }
526
527 /* transform input signal of current channel to frequency domain */
528 tx_fn(fft, fft_out, fft_in, sizeof(float));
529
530 for (j = 0; j < n_fft; j++) {
531 const AVComplexFloat *hcomplex = hrtf_offset + j;
532 const float re = fft_out[j].re;
533 const float im = fft_out[j].im;
534
535 /* complex multiplication of input signal and HRTFs */
536 /* output channel (real): */
537 fft_acc[j].re += re * hcomplex->re - im * hcomplex->im;
538 /* output channel (imag): */
539 fft_acc[j].im += re * hcomplex->im + im * hcomplex->re;
540 }
541 }
542
543 /* transform output signal of current channel back to time domain */
544 itx_fn(ifft, fft_out, fft_acc, sizeof(float));
545
546 for (j = 0; j < in->nb_samples; j++) {
547 /* write output signal of current channel to output buffer */
548 dst[mult * j] += fft_out[j].re * fft_scale;
549 }
550
551 for (j = 0; j < ir_samples - 1; j++) { /* overflow length is IR length - 1 */
552 /* write the rest of output signal to overflow buffer */
553 int write_pos = (wr + j) & modulo;
554
555 *(ringbuffer + write_pos) += fft_out[in->nb_samples + j].re * fft_scale;
556 }
557
558 /* go through all samples of current output buffer: count clippings */
559 for (i = 0; i < out->nb_samples; i++) {
560 /* clippings counter */
561 if (fabsf(dst[i * mult]) > 1) { /* if current output sample > 1 */
562 n_clippings[0]++;
563 }
564 }
565
566 /* remember read/write position in ringbuffer for next call */
567 *write = wr;
568
569 return 0;
570 }
571
filter_frame(AVFilterLink * inlink,AVFrame * in)572 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
573 {
574 AVFilterContext *ctx = inlink->dst;
575 SOFAlizerContext *s = ctx->priv;
576 AVFilterLink *outlink = ctx->outputs[0];
577 int n_clippings[2] = { 0 };
578 ThreadData td;
579 AVFrame *out;
580
581 out = ff_get_audio_buffer(outlink, in->nb_samples);
582 if (!out) {
583 av_frame_free(&in);
584 return AVERROR(ENOMEM);
585 }
586 av_frame_copy_props(out, in);
587
588 td.in = in; td.out = out; td.write = s->write;
589 td.delay = s->delay; td.ir = s->data_ir; td.n_clippings = n_clippings;
590 td.ringbuffer = s->ringbuffer; td.temp_src = s->temp_src;
591 td.in_fft = s->in_fft;
592 td.out_fft = s->out_fft;
593 td.temp_afft = s->temp_afft;
594
595 if (s->type == TIME_DOMAIN) {
596 ff_filter_execute(ctx, sofalizer_convolute, &td, NULL, 2);
597 } else if (s->type == FREQUENCY_DOMAIN) {
598 ff_filter_execute(ctx, sofalizer_fast_convolute, &td, NULL, 2);
599 }
600 emms_c();
601
602 /* display error message if clipping occurred */
603 if (n_clippings[0] + n_clippings[1] > 0) {
604 av_log(ctx, AV_LOG_WARNING, "%d of %d samples clipped. Please reduce gain.\n",
605 n_clippings[0] + n_clippings[1], out->nb_samples * 2);
606 }
607
608 av_frame_free(&in);
609 return ff_filter_frame(outlink, out);
610 }
611
activate(AVFilterContext * ctx)612 static int activate(AVFilterContext *ctx)
613 {
614 AVFilterLink *inlink = ctx->inputs[0];
615 AVFilterLink *outlink = ctx->outputs[0];
616 SOFAlizerContext *s = ctx->priv;
617 AVFrame *in;
618 int ret;
619
620 FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
621
622 if (s->nb_samples)
623 ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in);
624 else
625 ret = ff_inlink_consume_frame(inlink, &in);
626 if (ret < 0)
627 return ret;
628 if (ret > 0)
629 return filter_frame(inlink, in);
630
631 FF_FILTER_FORWARD_STATUS(inlink, outlink);
632 FF_FILTER_FORWARD_WANTED(outlink, inlink);
633
634 return FFERROR_NOT_READY;
635 }
636
query_formats(AVFilterContext * ctx)637 static int query_formats(AVFilterContext *ctx)
638 {
639 struct SOFAlizerContext *s = ctx->priv;
640 AVFilterChannelLayouts *layouts = NULL;
641 int ret, sample_rates[] = { 48000, -1 };
642 static const enum AVSampleFormat sample_fmts[] = {
643 AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
644 AV_SAMPLE_FMT_NONE
645 };
646
647 ret = ff_set_common_formats_from_list(ctx, sample_fmts);
648 if (ret)
649 return ret;
650
651 layouts = ff_all_channel_layouts();
652 if (!layouts)
653 return AVERROR(ENOMEM);
654
655 ret = ff_channel_layouts_ref(layouts, &ctx->inputs[0]->outcfg.channel_layouts);
656 if (ret)
657 return ret;
658
659 layouts = NULL;
660 ret = ff_add_channel_layout(&layouts, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO);
661 if (ret)
662 return ret;
663
664 ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->incfg.channel_layouts);
665 if (ret)
666 return ret;
667
668 sample_rates[0] = s->sample_rate;
669 return ff_set_common_samplerates_from_list(ctx, sample_rates);
670 }
671
getfilter_float(AVFilterContext * ctx,float x,float y,float z,float * left,float * right,float * delay_left,float * delay_right)672 static int getfilter_float(AVFilterContext *ctx, float x, float y, float z,
673 float *left, float *right,
674 float *delay_left, float *delay_right)
675 {
676 struct SOFAlizerContext *s = ctx->priv;
677 float c[3], delays[2];
678 float *fl, *fr;
679 int nearest;
680 int *neighbors;
681 float *res;
682
683 c[0] = x, c[1] = y, c[2] = z;
684 nearest = mysofa_lookup(s->sofa.lookup, c);
685 if (nearest < 0)
686 return AVERROR(EINVAL);
687
688 if (s->interpolate) {
689 neighbors = mysofa_neighborhood(s->sofa.neighborhood, nearest);
690 res = mysofa_interpolate(s->sofa.hrtf, c,
691 nearest, neighbors,
692 s->sofa.fir, delays);
693 } else {
694 if (s->sofa.hrtf->DataDelay.elements > s->sofa.hrtf->R) {
695 delays[0] = s->sofa.hrtf->DataDelay.values[nearest * s->sofa.hrtf->R];
696 delays[1] = s->sofa.hrtf->DataDelay.values[nearest * s->sofa.hrtf->R + 1];
697 } else {
698 delays[0] = s->sofa.hrtf->DataDelay.values[0];
699 delays[1] = s->sofa.hrtf->DataDelay.values[1];
700 }
701 res = s->sofa.hrtf->DataIR.values + nearest * s->sofa.hrtf->N * s->sofa.hrtf->R;
702 }
703
704 *delay_left = delays[0];
705 *delay_right = delays[1];
706
707 fl = res;
708 fr = res + s->sofa.hrtf->N;
709
710 memcpy(left, fl, sizeof(float) * s->sofa.hrtf->N);
711 memcpy(right, fr, sizeof(float) * s->sofa.hrtf->N);
712
713 return 0;
714 }
715
load_data(AVFilterContext * ctx,int azim,int elev,float radius,int sample_rate)716 static int load_data(AVFilterContext *ctx, int azim, int elev, float radius, int sample_rate)
717 {
718 struct SOFAlizerContext *s = ctx->priv;
719 int n_samples;
720 int ir_samples;
721 int n_conv = s->n_conv; /* no. channels to convolve */
722 int n_fft;
723 float delay_l; /* broadband delay for each IR */
724 float delay_r;
725 int nb_input_channels = ctx->inputs[0]->ch_layout.nb_channels; /* no. input channels */
726 float gain_lin = expf((s->gain - 3 * nb_input_channels) / 20 * M_LN10); /* gain - 3dB/channel */
727 AVComplexFloat *data_hrtf_l = NULL;
728 AVComplexFloat *data_hrtf_r = NULL;
729 AVComplexFloat *fft_out_l = NULL;
730 AVComplexFloat *fft_out_r = NULL;
731 AVComplexFloat *fft_in_l = NULL;
732 AVComplexFloat *fft_in_r = NULL;
733 float *data_ir_l = NULL;
734 float *data_ir_r = NULL;
735 int offset = 0; /* used for faster pointer arithmetics in for-loop */
736 int i, j, azim_orig = azim, elev_orig = elev;
737 int ret = 0;
738 int n_current;
739 int n_max = 0;
740
741 av_log(ctx, AV_LOG_DEBUG, "IR length: %d.\n", s->sofa.hrtf->N);
742 s->sofa.ir_samples = s->sofa.hrtf->N;
743 s->sofa.n_samples = 1 << (32 - ff_clz(s->sofa.ir_samples));
744
745 n_samples = s->sofa.n_samples;
746 ir_samples = s->sofa.ir_samples;
747
748 if (s->type == TIME_DOMAIN) {
749 s->data_ir[0] = av_calloc(n_samples, sizeof(float) * s->n_conv);
750 s->data_ir[1] = av_calloc(n_samples, sizeof(float) * s->n_conv);
751
752 if (!s->data_ir[0] || !s->data_ir[1]) {
753 ret = AVERROR(ENOMEM);
754 goto fail;
755 }
756 }
757
758 s->delay[0] = av_calloc(s->n_conv, sizeof(int));
759 s->delay[1] = av_calloc(s->n_conv, sizeof(int));
760
761 if (!s->delay[0] || !s->delay[1]) {
762 ret = AVERROR(ENOMEM);
763 goto fail;
764 }
765
766 /* get temporary IR for L and R channel */
767 data_ir_l = av_calloc(n_conv * n_samples, sizeof(*data_ir_l));
768 data_ir_r = av_calloc(n_conv * n_samples, sizeof(*data_ir_r));
769 if (!data_ir_r || !data_ir_l) {
770 ret = AVERROR(ENOMEM);
771 goto fail;
772 }
773
774 if (s->type == TIME_DOMAIN) {
775 s->temp_src[0] = av_calloc(n_samples, sizeof(float));
776 s->temp_src[1] = av_calloc(n_samples, sizeof(float));
777 if (!s->temp_src[0] || !s->temp_src[1]) {
778 ret = AVERROR(ENOMEM);
779 goto fail;
780 }
781 }
782
783 s->speaker_azim = av_calloc(s->n_conv, sizeof(*s->speaker_azim));
784 s->speaker_elev = av_calloc(s->n_conv, sizeof(*s->speaker_elev));
785 if (!s->speaker_azim || !s->speaker_elev) {
786 ret = AVERROR(ENOMEM);
787 goto fail;
788 }
789
790 /* get speaker positions */
791 if ((ret = get_speaker_pos(ctx, s->speaker_azim, s->speaker_elev)) < 0) {
792 av_log(ctx, AV_LOG_ERROR, "Couldn't get speaker positions. Input channel configuration not supported.\n");
793 goto fail;
794 }
795
796 for (i = 0; i < s->n_conv; i++) {
797 float coordinates[3];
798
799 /* load and store IRs and corresponding delays */
800 azim = (int)(s->speaker_azim[i] + azim_orig) % 360;
801 elev = (int)(s->speaker_elev[i] + elev_orig) % 90;
802
803 coordinates[0] = azim;
804 coordinates[1] = elev;
805 coordinates[2] = radius;
806
807 mysofa_s2c(coordinates);
808
809 /* get id of IR closest to desired position */
810 ret = getfilter_float(ctx, coordinates[0], coordinates[1], coordinates[2],
811 data_ir_l + n_samples * i,
812 data_ir_r + n_samples * i,
813 &delay_l, &delay_r);
814 if (ret < 0)
815 goto fail;
816
817 s->delay[0][i] = delay_l * sample_rate;
818 s->delay[1][i] = delay_r * sample_rate;
819
820 s->sofa.max_delay = FFMAX3(s->sofa.max_delay, s->delay[0][i], s->delay[1][i]);
821 }
822
823 /* get size of ringbuffer (longest IR plus max. delay) */
824 /* then choose next power of 2 for performance optimization */
825 n_current = n_samples + s->sofa.max_delay;
826 /* length of longest IR plus max. delay */
827 n_max = FFMAX(n_max, n_current);
828
829 /* buffer length is longest IR plus max. delay -> next power of 2
830 (32 - count leading zeros gives required exponent) */
831 s->buffer_length = 1 << (32 - ff_clz(n_max));
832 s->n_fft = n_fft = 1 << (32 - ff_clz(n_max + s->framesize));
833
834 if (s->type == FREQUENCY_DOMAIN) {
835 float scale;
836
837 av_tx_uninit(&s->fft[0]);
838 av_tx_uninit(&s->fft[1]);
839 ret = av_tx_init(&s->fft[0], &s->tx_fn[0], AV_TX_FLOAT_FFT, 0, s->n_fft, &scale, 0);
840 if (ret < 0)
841 goto fail;
842 ret = av_tx_init(&s->fft[1], &s->tx_fn[1], AV_TX_FLOAT_FFT, 0, s->n_fft, &scale, 0);
843 if (ret < 0)
844 goto fail;
845 av_tx_uninit(&s->ifft[0]);
846 av_tx_uninit(&s->ifft[1]);
847 ret = av_tx_init(&s->ifft[0], &s->itx_fn[0], AV_TX_FLOAT_FFT, 1, s->n_fft, &scale, 0);
848 if (ret < 0)
849 goto fail;
850 ret = av_tx_init(&s->ifft[1], &s->itx_fn[1], AV_TX_FLOAT_FFT, 1, s->n_fft, &scale, 0);
851 if (ret < 0)
852 goto fail;
853 }
854
855 if (s->type == TIME_DOMAIN) {
856 s->ringbuffer[0] = av_calloc(s->buffer_length, sizeof(float) * nb_input_channels);
857 s->ringbuffer[1] = av_calloc(s->buffer_length, sizeof(float) * nb_input_channels);
858 } else if (s->type == FREQUENCY_DOMAIN) {
859 /* get temporary HRTF memory for L and R channel */
860 data_hrtf_l = av_malloc_array(n_fft, sizeof(*data_hrtf_l) * n_conv);
861 data_hrtf_r = av_malloc_array(n_fft, sizeof(*data_hrtf_r) * n_conv);
862 if (!data_hrtf_r || !data_hrtf_l) {
863 ret = AVERROR(ENOMEM);
864 goto fail;
865 }
866
867 s->ringbuffer[0] = av_calloc(s->buffer_length, sizeof(float));
868 s->ringbuffer[1] = av_calloc(s->buffer_length, sizeof(float));
869 s->in_fft[0] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
870 s->in_fft[1] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
871 s->out_fft[0] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
872 s->out_fft[1] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
873 s->temp_afft[0] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
874 s->temp_afft[1] = av_malloc_array(s->n_fft, sizeof(AVComplexFloat));
875 if (!s->in_fft[0] || !s->in_fft[1] ||
876 !s->out_fft[0] || !s->out_fft[1] ||
877 !s->temp_afft[0] || !s->temp_afft[1]) {
878 ret = AVERROR(ENOMEM);
879 goto fail;
880 }
881 }
882
883 if (!s->ringbuffer[0] || !s->ringbuffer[1]) {
884 ret = AVERROR(ENOMEM);
885 goto fail;
886 }
887
888 if (s->type == FREQUENCY_DOMAIN) {
889 fft_out_l = av_calloc(n_fft, sizeof(*fft_out_l));
890 fft_out_r = av_calloc(n_fft, sizeof(*fft_out_r));
891 fft_in_l = av_calloc(n_fft, sizeof(*fft_in_l));
892 fft_in_r = av_calloc(n_fft, sizeof(*fft_in_r));
893 if (!fft_in_l || !fft_in_r ||
894 !fft_out_l || !fft_out_r) {
895 ret = AVERROR(ENOMEM);
896 goto fail;
897 }
898 }
899
900 for (i = 0; i < s->n_conv; i++) {
901 float *lir, *rir;
902
903 offset = i * n_samples; /* no. samples already written */
904
905 lir = data_ir_l + offset;
906 rir = data_ir_r + offset;
907
908 if (s->type == TIME_DOMAIN) {
909 for (j = 0; j < ir_samples; j++) {
910 /* load reversed IRs of the specified source position
911 * sample-by-sample for left and right ear; and apply gain */
912 s->data_ir[0][offset + j] = lir[ir_samples - 1 - j] * gain_lin;
913 s->data_ir[1][offset + j] = rir[ir_samples - 1 - j] * gain_lin;
914 }
915 } else if (s->type == FREQUENCY_DOMAIN) {
916 memset(fft_in_l, 0, n_fft * sizeof(*fft_in_l));
917 memset(fft_in_r, 0, n_fft * sizeof(*fft_in_r));
918
919 offset = i * n_fft; /* no. samples already written */
920 for (j = 0; j < ir_samples; j++) {
921 /* load non-reversed IRs of the specified source position
922 * sample-by-sample and apply gain,
923 * L channel is loaded to real part, R channel to imag part,
924 * IRs are shifted by L and R delay */
925 fft_in_l[s->delay[0][i] + j].re = lir[j] * gain_lin;
926 fft_in_r[s->delay[1][i] + j].re = rir[j] * gain_lin;
927 }
928
929 /* actually transform to frequency domain (IRs -> HRTFs) */
930 s->tx_fn[0](s->fft[0], fft_out_l, fft_in_l, sizeof(float));
931 memcpy(data_hrtf_l + offset, fft_out_l, n_fft * sizeof(*fft_out_l));
932 s->tx_fn[1](s->fft[1], fft_out_r, fft_in_r, sizeof(float));
933 memcpy(data_hrtf_r + offset, fft_out_r, n_fft * sizeof(*fft_out_r));
934 }
935 }
936
937 if (s->type == FREQUENCY_DOMAIN) {
938 s->data_hrtf[0] = av_malloc_array(n_fft * s->n_conv, sizeof(AVComplexFloat));
939 s->data_hrtf[1] = av_malloc_array(n_fft * s->n_conv, sizeof(AVComplexFloat));
940 if (!s->data_hrtf[0] || !s->data_hrtf[1]) {
941 ret = AVERROR(ENOMEM);
942 goto fail;
943 }
944
945 memcpy(s->data_hrtf[0], data_hrtf_l, /* copy HRTF data to */
946 sizeof(AVComplexFloat) * n_conv * n_fft); /* filter struct */
947 memcpy(s->data_hrtf[1], data_hrtf_r,
948 sizeof(AVComplexFloat) * n_conv * n_fft);
949 }
950
951 fail:
952 av_freep(&data_hrtf_l); /* free temporary HRTF memory */
953 av_freep(&data_hrtf_r);
954
955 av_freep(&data_ir_l); /* free temprary IR memory */
956 av_freep(&data_ir_r);
957
958 av_freep(&fft_out_l); /* free temporary FFT memory */
959 av_freep(&fft_out_r);
960
961 av_freep(&fft_in_l); /* free temporary FFT memory */
962 av_freep(&fft_in_r);
963
964 return ret;
965 }
966
init(AVFilterContext * ctx)967 static av_cold int init(AVFilterContext *ctx)
968 {
969 SOFAlizerContext *s = ctx->priv;
970 int ret;
971
972 if (!s->filename) {
973 av_log(ctx, AV_LOG_ERROR, "Valid SOFA filename must be set.\n");
974 return AVERROR(EINVAL);
975 }
976
977 /* preload SOFA file, */
978 ret = preload_sofa(ctx, s->filename, &s->sample_rate);
979 if (ret) {
980 /* file loading error */
981 av_log(ctx, AV_LOG_ERROR, "Error while loading SOFA file: '%s'\n", s->filename);
982 } else { /* no file loading error, resampling not required */
983 av_log(ctx, AV_LOG_DEBUG, "File '%s' loaded.\n", s->filename);
984 }
985
986 if (ret) {
987 av_log(ctx, AV_LOG_ERROR, "No valid SOFA file could be loaded. Please specify valid SOFA file.\n");
988 return ret;
989 }
990
991 s->fdsp = avpriv_float_dsp_alloc(0);
992 if (!s->fdsp)
993 return AVERROR(ENOMEM);
994
995 return 0;
996 }
997
config_input(AVFilterLink * inlink)998 static int config_input(AVFilterLink *inlink)
999 {
1000 AVFilterContext *ctx = inlink->dst;
1001 SOFAlizerContext *s = ctx->priv;
1002 int ret;
1003
1004 if (s->type == FREQUENCY_DOMAIN)
1005 s->nb_samples = s->framesize;
1006
1007 /* gain -3 dB per channel */
1008 s->gain_lfe = expf((s->gain - 3 * inlink->ch_layout.nb_channels + s->lfe_gain) / 20 * M_LN10);
1009
1010 s->n_conv = inlink->ch_layout.nb_channels;
1011
1012 /* load IRs to data_ir[0] and data_ir[1] for required directions */
1013 if ((ret = load_data(ctx, s->rotation, s->elevation, s->radius, inlink->sample_rate)) < 0)
1014 return ret;
1015
1016 av_log(ctx, AV_LOG_DEBUG, "Samplerate: %d Channels to convolute: %d, Length of ringbuffer: %d x %d\n",
1017 inlink->sample_rate, s->n_conv, inlink->ch_layout.nb_channels, s->buffer_length);
1018
1019 return 0;
1020 }
1021
uninit(AVFilterContext * ctx)1022 static av_cold void uninit(AVFilterContext *ctx)
1023 {
1024 SOFAlizerContext *s = ctx->priv;
1025
1026 close_sofa(&s->sofa);
1027 av_tx_uninit(&s->ifft[0]);
1028 av_tx_uninit(&s->ifft[1]);
1029 av_tx_uninit(&s->fft[0]);
1030 av_tx_uninit(&s->fft[1]);
1031 s->ifft[0] = NULL;
1032 s->ifft[1] = NULL;
1033 s->fft[0] = NULL;
1034 s->fft[1] = NULL;
1035 av_freep(&s->delay[0]);
1036 av_freep(&s->delay[1]);
1037 av_freep(&s->data_ir[0]);
1038 av_freep(&s->data_ir[1]);
1039 av_freep(&s->ringbuffer[0]);
1040 av_freep(&s->ringbuffer[1]);
1041 av_freep(&s->speaker_azim);
1042 av_freep(&s->speaker_elev);
1043 av_freep(&s->temp_src[0]);
1044 av_freep(&s->temp_src[1]);
1045 av_freep(&s->temp_afft[0]);
1046 av_freep(&s->temp_afft[1]);
1047 av_freep(&s->in_fft[0]);
1048 av_freep(&s->in_fft[1]);
1049 av_freep(&s->out_fft[0]);
1050 av_freep(&s->out_fft[1]);
1051 av_freep(&s->data_hrtf[0]);
1052 av_freep(&s->data_hrtf[1]);
1053 av_freep(&s->fdsp);
1054 }
1055
1056 #define OFFSET(x) offsetof(SOFAlizerContext, x)
1057 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
1058
1059 static const AVOption sofalizer_options[] = {
1060 { "sofa", "sofa filename", OFFSET(filename), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
1061 { "gain", "set gain in dB", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl=0}, -20, 40, .flags = FLAGS },
1062 { "rotation", "set rotation" , OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl=0}, -360, 360, .flags = FLAGS },
1063 { "elevation", "set elevation", OFFSET(elevation), AV_OPT_TYPE_FLOAT, {.dbl=0}, -90, 90, .flags = FLAGS },
1064 { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 5, .flags = FLAGS },
1065 { "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, "type" },
1066 { "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, "type" },
1067 { "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, "type" },
1068 { "speakers", "set speaker custom positions", OFFSET(speakers_pos), AV_OPT_TYPE_STRING, {.str=0}, 0, 0, .flags = FLAGS },
1069 { "lfegain", "set lfe gain", OFFSET(lfe_gain), AV_OPT_TYPE_FLOAT, {.dbl=0}, -20,40, .flags = FLAGS },
1070 { "framesize", "set frame size", OFFSET(framesize), AV_OPT_TYPE_INT, {.i64=1024},1024,96000, .flags = FLAGS },
1071 { "normalize", "normalize IRs", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags = FLAGS },
1072 { "interpolate","interpolate IRs from neighbors", OFFSET(interpolate),AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
1073 { "minphase", "minphase IRs", OFFSET(minphase), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
1074 { "anglestep", "set neighbor search angle step", OFFSET(anglestep), AV_OPT_TYPE_FLOAT, {.dbl=.5}, 0.01, 10, .flags = FLAGS },
1075 { "radstep", "set neighbor search radius step", OFFSET(radstep), AV_OPT_TYPE_FLOAT, {.dbl=.01}, 0.01, 1, .flags = FLAGS },
1076 { NULL }
1077 };
1078
1079 AVFILTER_DEFINE_CLASS(sofalizer);
1080
1081 static const AVFilterPad inputs[] = {
1082 {
1083 .name = "default",
1084 .type = AVMEDIA_TYPE_AUDIO,
1085 .config_props = config_input,
1086 },
1087 };
1088
1089 static const AVFilterPad outputs[] = {
1090 {
1091 .name = "default",
1092 .type = AVMEDIA_TYPE_AUDIO,
1093 },
1094 };
1095
1096 const AVFilter ff_af_sofalizer = {
1097 .name = "sofalizer",
1098 .description = NULL_IF_CONFIG_SMALL("SOFAlizer (Spatially Oriented Format for Acoustics)."),
1099 .priv_size = sizeof(SOFAlizerContext),
1100 .priv_class = &sofalizer_class,
1101 .init = init,
1102 .activate = activate,
1103 .uninit = uninit,
1104 FILTER_INPUTS(inputs),
1105 FILTER_OUTPUTS(outputs),
1106 FILTER_QUERY_FUNC(query_formats),
1107 .flags = AVFILTER_FLAG_SLICE_THREADS,
1108 };
1109