avfilter/af_sofalizer: stop using easy API
authorPaul B Mahol <onemda@gmail.com>
Sun, 23 Dec 2018 16:56:59 +0000 (17:56 +0100)
committerPaul B Mahol <onemda@gmail.com>
Sun, 23 Dec 2018 17:29:53 +0000 (18:29 +0100)
Easy API is not flexible enough for our needs.

doc/filters.texi
libavfilter/af_sofalizer.c

index aac51be..65ce25b 100644 (file)
@@ -4511,6 +4511,23 @@ Set custom gain for LFE channels. Value is in dB. Default is 0.
 Set custom frame size in number of samples. Default is 1024.
 Allowed range is from 1024 to 96000. Only used if option @samp{type}
 is set to @var{freq}.
+
+@item normalize
+Should all IRs be normalized upon importing SOFA file.
+By default is enabled.
+
+@item interpolate
+Should nearest IRs be interpolated with neighbor IRs if exact position
+does not match. By default is disabled.
+
+@item minphase
+Minphase all IRs upon loading of SOFA file. By default is disabled.
+
+@item anglestep
+Set neighbor search angle step. Only used if option @var{interpolate} is enabled.
+
+@item radstep
+Set neighbor search radius step. Only used if option @var{interpolate} is enabled.
 @end table
 
 @subsection Examples
index 94e5b73..7812e25 100644 (file)
 #define FREQUENCY_DOMAIN 1
 
 typedef struct MySofa {  /* contains data of one SOFA file */
-    struct MYSOFA_EASY *easy;
+    struct MYSOFA_HRTF *hrtf;
+    struct MYSOFA_LOOKUP *lookup;
+    struct MYSOFA_NEIGHBORHOOD *neighborhood;
     int ir_samples;      /* length of one impulse response (IR) */
     int n_samples;       /* ir_samples to next power of 2 */
     float *lir, *rir;    /* IRs (time-domain) */
+    float *fir;
     int max_delay;
 } MySofa;
 
@@ -94,6 +97,11 @@ typedef struct SOFAlizerContext {
     float radius;        /* distance virtual loudspeakers to listener (in metres) */
     int type;            /* processing type */
     int framesize;       /* size of buffer */
+    int normalize;       /* should all IRs be normalized upon import ? */
+    int interpolate;     /* should wanted IRs be interpolated from neighbors ? */
+    int minphase;        /* should all IRs be minphased upon import ? */
+    float anglestep;     /* neighbor search angle step, in agles */
+    float radstep;       /* neighbor search radius step, in meters */
 
     VirtualSpeaker vspkrpos[64];
 
@@ -105,24 +113,62 @@ typedef struct SOFAlizerContext {
 
 static int close_sofa(struct MySofa *sofa)
 {
-    mysofa_close(sofa->easy);
-    sofa->easy = NULL;
+    if (sofa->neighborhood)
+        mysofa_neighborhood_free(sofa->neighborhood);
+    sofa->neighborhood = NULL;
+    if (sofa->lookup)
+        mysofa_lookup_free(sofa->lookup);
+    sofa->lookup = NULL;
+    if (sofa->hrtf)
+        mysofa_free(sofa->hrtf);
+    sofa->hrtf = NULL;
+    av_freep(&sofa->fir);
 
     return 0;
 }
 
 static int preload_sofa(AVFilterContext *ctx, char *filename, int *samplingrate)
 {
+    struct SOFAlizerContext *s = ctx->priv;
     struct MYSOFA_HRTF *mysofa;
     char *license;
     int ret;
 
     mysofa = mysofa_load(filename, &ret);
+    s->sofa.hrtf = mysofa;
     if (ret || !mysofa) {
         av_log(ctx, AV_LOG_ERROR, "Can't find SOFA-file '%s'\n", filename);
         return AVERROR(EINVAL);
     }
 
+    ret = mysofa_check(mysofa);
+    if (ret != MYSOFA_OK) {
+        av_log(ctx, AV_LOG_ERROR, "Selected SOFA file is invalid. Please select valid SOFA file.\n");
+        return ret;
+    }
+
+    if (s->normalize)
+        mysofa_loudness(s->sofa.hrtf);
+
+    if (s->minphase)
+        mysofa_minphase(s->sofa.hrtf, 0.01);
+
+    mysofa_tocartesian(s->sofa.hrtf);
+
+    s->sofa.lookup = mysofa_lookup_init(s->sofa.hrtf);
+    if (s->sofa.lookup == NULL)
+        return AVERROR(EINVAL);
+
+    if (s->interpolate)
+        s->sofa.neighborhood = mysofa_neighborhood_init_withstepdefine(s->sofa.hrtf,
+                                                                       s->sofa.lookup,
+                                                                       s->anglestep,
+                                                                       s->radstep);
+
+    s->sofa.fir = av_calloc(s->sofa.hrtf->N * s->sofa.hrtf->R, sizeof(*s->sofa.fir));
+    if (!s->sofa.fir)
+        return AVERROR(ENOMEM);
+
     if (mysofa->DataSamplingRate.elements != 1)
         return AVERROR(EINVAL);
     av_log(ctx, AV_LOG_DEBUG, "Original IR length: %d.\n", mysofa->N);
@@ -130,7 +176,6 @@ static int preload_sofa(AVFilterContext *ctx, char *filename, int *samplingrate)
     license = mysofa_getAttribute(mysofa->attributes, (char *)"License");
     if (license)
         av_log(ctx, AV_LOG_INFO, "SOFA license: %s\n", license);
-    mysofa_free(mysofa);
 
     return 0;
 }
@@ -560,6 +605,43 @@ static int query_formats(AVFilterContext *ctx)
     return ff_set_common_samplerates(ctx, formats);
 }
 
+static int getfilter_float(AVFilterContext *ctx, float x, float y, float z,
+                           float *left, float *right,
+                           float *delay_left, float *delay_right)
+{
+    struct SOFAlizerContext *s = ctx->priv;
+    float c[3], delays[2];
+    float *fl, *fr;
+    int nearest;
+    int *neighbors;
+    float *res;
+
+    c[0] = x, c[1] = y, c[2] = z;
+    nearest = mysofa_lookup(s->sofa.lookup, c);
+    if (nearest < 0)
+        return AVERROR(EINVAL);
+
+    if (s->interpolate) {
+        neighbors = mysofa_neighborhood(s->sofa.neighborhood, nearest);
+        res = mysofa_interpolate(s->sofa.hrtf, c,
+                                 nearest, neighbors,
+                                 s->sofa.fir, delays);
+    } else {
+        res = s->sofa.hrtf->DataIR.values + nearest * s->sofa.hrtf->N * s->sofa.hrtf->R;
+    }
+
+    *delay_left  = delays[0];
+    *delay_right = delays[1];
+
+    fl = res;
+    fr = res + s->sofa.hrtf->N;
+
+    memcpy(left, fl, sizeof(float) * s->sofa.hrtf->N);
+    memcpy(right, fr, sizeof(float) * s->sofa.hrtf->N);
+
+    return 0;
+}
+
 static int load_data(AVFilterContext *ctx, int azim, int elev, float radius, int sample_rate)
 {
     struct SOFAlizerContext *s = ctx->priv;
@@ -579,18 +661,12 @@ static int load_data(AVFilterContext *ctx, int azim, int elev, float radius, int
     float *data_ir_r = NULL;
     int offset = 0; /* used for faster pointer arithmetics in for-loop */
     int i, j, azim_orig = azim, elev_orig = elev;
-    int filter_length, ret = 0;
+    int ret = 0;
     int n_current;
     int n_max = 0;
 
-    s->sofa.easy = mysofa_open(s->filename, sample_rate, &filter_length, &ret);
-    if (!s->sofa.easy || ret) { /* if an invalid SOFA file has been selected */
-        av_log(ctx, AV_LOG_ERROR, "Selected SOFA file is invalid. Please select valid SOFA file.\n");
-        return AVERROR_INVALIDDATA;
-    }
-
-    av_log(ctx, AV_LOG_DEBUG, "IR length: %d.\n", s->sofa.easy->hrtf->N);
-    s->sofa.ir_samples = s->sofa.easy->hrtf->N;
+    av_log(ctx, AV_LOG_DEBUG, "IR length: %d.\n", s->sofa.hrtf->N);
+    s->sofa.ir_samples = s->sofa.hrtf->N;
     s->sofa.n_samples = 1 << (32 - ff_clz(s->sofa.ir_samples));
 
     n_samples = s->sofa.n_samples;
@@ -650,10 +726,12 @@ static int load_data(AVFilterContext *ctx, int azim, int elev, float radius, int
         mysofa_s2c(coordinates);
 
         /* get id of IR closest to desired position */
-        mysofa_getfilter_float(s->sofa.easy, coordinates[0], coordinates[1], coordinates[2],
-                               data_ir_l + n_samples * i,
-                               data_ir_r + n_samples * i,
-                               &delay_l, &delay_r);
+        ret = getfilter_float(ctx, coordinates[0], coordinates[1], coordinates[2],
+                              data_ir_l + n_samples * i,
+                              data_ir_r + n_samples * i,
+                              &delay_l, &delay_r);
+        if (ret < 0)
+            return ret;
 
         s->delay[0][i] = delay_l * sample_rate;
         s->delay[1][i] = delay_r * sample_rate;
@@ -894,6 +972,11 @@ static const AVOption sofalizer_options[] = {
     { "speakers",  "set speaker custom positions", OFFSET(speakers_pos), AV_OPT_TYPE_STRING,  {.str=0},    0, 0, .flags = FLAGS },
     { "lfegain",   "set lfe gain",                 OFFSET(lfe_gain),     AV_OPT_TYPE_FLOAT,   {.dbl=0},  -20,40, .flags = FLAGS },
     { "framesize", "set frame size", OFFSET(framesize), AV_OPT_TYPE_INT,    {.i64=1024},1024,96000, .flags = FLAGS },
+    { "normalize", "normalize IRs",  OFFSET(normalize), AV_OPT_TYPE_BOOL,   {.i64=1},       0,   1, .flags = FLAGS },
+    { "interpolate","interpolate IRs from neighbors",   OFFSET(interpolate),AV_OPT_TYPE_BOOL,    {.i64=0},       0,   1, .flags = FLAGS },
+    { "minphase",  "minphase IRs",   OFFSET(minphase),  AV_OPT_TYPE_BOOL,   {.i64=0},       0,   1, .flags = FLAGS },
+    { "anglestep", "set neighbor search angle step",    OFFSET(anglestep),  AV_OPT_TYPE_FLOAT,   {.dbl=.5},      0.01, 10, .flags = FLAGS },
+    { "radstep",   "set neighbor search radius step",   OFFSET(radstep),    AV_OPT_TYPE_FLOAT,   {.dbl=.01},     0.01,  1, .flags = FLAGS },
     { NULL }
 };