1103 lines · 40.6 KB · C
RawDownload
/***************************************************************************** * sofalizer.c : SOFAlizer filter 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. *****************************************************************************/ #include <math.h>#include <mysofa.h> #include "libavutil/mem.h"#include "libavutil/tx.h"#include "libavutil/avstring.h"#include "libavutil/channel_layout.h"#include "libavutil/float_dsp.h"#include "libavutil/intmath.h"#include "libavutil/opt.h"#include "avfilter.h"#include "filters.h"#include "formats.h"#include "audio.h" #define TIME_DOMAIN      0#define FREQUENCY_DOMAIN 1 typedef struct MySofa {  /* contains data of one SOFA file */    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; typedef struct VirtualSpeaker {    uint8_t set;    float azim;    float elev;} VirtualSpeaker; typedef struct SOFAlizerContext {    const AVClass *class;     char *filename;             /* name of SOFA file */    MySofa sofa;                /* contains data of the SOFA file */     int sample_rate;            /* sample rate from SOFA file */    float *speaker_azim;        /* azimuth of the virtual loudspeakers */    float *speaker_elev;        /* elevation of the virtual loudspeakers */    char *speakers_pos;         /* custom positions of the virtual loudspeakers */    float lfe_gain;             /* initial gain for the LFE channel */    float gain_lfe;             /* gain applied to LFE channel */    int lfe_channel;            /* LFE channel position in channel layout */     int n_conv;                 /* number of channels to convolute */                                 /* buffer variables (for convolution) */    float *ringbuffer[2];       /* buffers input samples, length of one buffer: */                                /* no. input ch. (incl. LFE) x buffer_length */    int write[2];               /* current write position to ringbuffer */    int buffer_length;          /* is: longest IR plus max. delay in all SOFA files */                                /* then choose next power of 2 */    int n_fft;                  /* number of samples in one FFT block */    int nb_samples;                                 /* netCDF variables */    int *delay[2];              /* broadband delay for each channel/IR to be convolved */     float *data_ir[2];          /* IRs for all channels to be convolved */                                /* (this excludes the LFE) */    float *temp_src[2];    AVComplexFloat *in_fft[2];   /* Array to hold input FFT values */    AVComplexFloat *out_fft[2];  /* Array to hold output FFT values */    AVComplexFloat *temp_afft[2];   /* Array to accumulate FFT values prior to IFFT */                          /* control variables */    float gain;          /* filter gain (in dB) */    float rotation;      /* rotation of virtual loudspeakers (in degrees)  */    float elevation;     /* elevation of virtual loudspeakers (in deg.) */    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];     AVTXContext *fft[2], *ifft[2];    av_tx_fn tx_fn[2], itx_fn[2];    AVComplexFloat *data_hrtf[2];     AVFloatDSPContext *fdsp;} SOFAlizerContext; static int close_sofa(struct MySofa *sofa){    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.01f);     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);    *samplingrate = mysofa->DataSamplingRate.values[0];    license = mysofa_getAttribute(mysofa->attributes, (char *)"License");    if (license)        av_log(ctx, AV_LOG_INFO, "SOFA license: %s\n", license);     return 0;} static int parse_channel_name(AVFilterContext *ctx, char **arg, int *rchannel){    int len;    enum AVChannel channel_id = 0;    char buf[8] = {0};     /* try to parse a channel name, e.g. "FL" */    if (av_sscanf(*arg, "%7[A-Z]%n", buf, &len)) {        channel_id = av_channel_from_string(buf);        if (channel_id < 0 || channel_id >= 64) {            av_log(ctx, AV_LOG_WARNING, "Failed to parse \'%s\' as channel name.\n", buf);            return AVERROR(EINVAL);        }         *rchannel = channel_id;        *arg += len;        return 0;    } else if (av_sscanf(*arg, "%d%n", &channel_id, &len) == 1) {        if (channel_id < 0 || channel_id >= 64) {            av_log(ctx, AV_LOG_WARNING, "Failed to parse \'%d\' as channel number.\n", channel_id);            return AVERROR(EINVAL);        }        *rchannel = channel_id;        *arg += len;        return 0;    }    return AVERROR(EINVAL);} static void parse_speaker_pos(AVFilterContext *ctx){    SOFAlizerContext *s = ctx->priv;    char *arg, *tokenizer, *p, *args = av_strdup(s->speakers_pos);     if (!args)        return;    p = args;     while ((arg = av_strtok(p, "|", &tokenizer))) {        float azim, elev;        int out_ch_id;         p = NULL;        if (parse_channel_name(ctx, &arg, &out_ch_id)) {            continue;        }        if (av_sscanf(arg, "%f %f", &azim, &elev) == 2) {            s->vspkrpos[out_ch_id].set = 1;            s->vspkrpos[out_ch_id].azim = azim;            s->vspkrpos[out_ch_id].elev = elev;        } else if (av_sscanf(arg, "%f", &azim) == 1) {            s->vspkrpos[out_ch_id].set = 1;            s->vspkrpos[out_ch_id].azim = azim;            s->vspkrpos[out_ch_id].elev = 0;        }    }     av_free(args);} static int get_speaker_pos(AVFilterContext *ctx,                           float *speaker_azim, float *speaker_elev){    struct SOFAlizerContext *s = ctx->priv;    AVChannelLayout *channel_layout = &ctx->inputs[0]->ch_layout;    float azim[64] = { 0 };    float elev[64] = { 0 };    int ch, n_conv = ctx->inputs[0]->ch_layout.nb_channels; /* get no. input channels */     if (n_conv < 0 || n_conv > 64)        return AVERROR(EINVAL);     s->lfe_channel = -1;     if (s->speakers_pos)        parse_speaker_pos(ctx);     /* set speaker positions according to input channel configuration: */    for (ch = 0; ch < n_conv; ch++) {        int chan = av_channel_layout_channel_from_index(channel_layout, ch);         switch (chan) {        case AV_CHAN_FRONT_LEFT:          azim[ch] =  30;      break;        case AV_CHAN_FRONT_RIGHT:         azim[ch] = 330;      break;        case AV_CHAN_FRONT_CENTER:        azim[ch] =   0;      break;        case AV_CHAN_LOW_FREQUENCY:        case AV_CHAN_LOW_FREQUENCY_2:     s->lfe_channel = ch; break;        case AV_CHAN_BACK_LEFT:           azim[ch] = 150;      break;        case AV_CHAN_BACK_RIGHT:          azim[ch] = 210;      break;        case AV_CHAN_BACK_CENTER:         azim[ch] = 180;      break;        case AV_CHAN_SIDE_LEFT:           azim[ch] =  90;      break;        case AV_CHAN_SIDE_RIGHT:          azim[ch] = 270;      break;        case AV_CHAN_FRONT_LEFT_OF_CENTER:  azim[ch] =  15;    break;        case AV_CHAN_FRONT_RIGHT_OF_CENTER: azim[ch] = 345;    break;        case AV_CHAN_TOP_CENTER:          azim[ch] =   0;                                          elev[ch] =  90;      break;        case AV_CHAN_TOP_FRONT_LEFT:      azim[ch] =  30;                                          elev[ch] =  45;      break;        case AV_CHAN_TOP_FRONT_CENTER:    azim[ch] =   0;                                          elev[ch] =  45;      break;        case AV_CHAN_TOP_FRONT_RIGHT:     azim[ch] = 330;                                          elev[ch] =  45;      break;        case AV_CHAN_TOP_BACK_LEFT:       azim[ch] = 150;                                          elev[ch] =  45;      break;        case AV_CHAN_TOP_BACK_RIGHT:      azim[ch] = 210;                                          elev[ch] =  45;      break;        case AV_CHAN_TOP_BACK_CENTER:     azim[ch] = 180;                                          elev[ch] =  45;      break;        case AV_CHAN_WIDE_LEFT:           azim[ch] =  90;      break;        case AV_CHAN_WIDE_RIGHT:          azim[ch] = 270;      break;        case AV_CHAN_SURROUND_DIRECT_LEFT:  azim[ch] =  90;    break;        case AV_CHAN_SURROUND_DIRECT_RIGHT: azim[ch] = 270;    break;        case AV_CHAN_STEREO_LEFT:         azim[ch] =  90;      break;        case AV_CHAN_STEREO_RIGHT:        azim[ch] = 270;      break;        default:            return AVERROR(EINVAL);        }         if (s->vspkrpos[ch].set) {            azim[ch] = s->vspkrpos[ch].azim;            elev[ch] = s->vspkrpos[ch].elev;        }    }     memcpy(speaker_azim, azim, n_conv * sizeof(float));    memcpy(speaker_elev, elev, n_conv * sizeof(float));     return 0; } typedef struct ThreadData {    AVFrame *in, *out;    int *write;    int **delay;    float **ir;    int *n_clippings;    float **ringbuffer;    float **temp_src;    AVComplexFloat **in_fft;    AVComplexFloat **out_fft;    AVComplexFloat **temp_afft;} ThreadData; static int sofalizer_convolute(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs){    SOFAlizerContext *s = ctx->priv;    ThreadData *td = arg;    AVFrame *in = td->in, *out = td->out;    int offset = jobnr;    int *write = &td->write[jobnr];    const int *const delay = td->delay[jobnr];    const float *const ir = td->ir[jobnr];    int *n_clippings = &td->n_clippings[jobnr];    float *ringbuffer = td->ringbuffer[jobnr];    float *temp_src = td->temp_src[jobnr];    const int ir_samples = s->sofa.ir_samples; /* length of one IR */    const int n_samples = s->sofa.n_samples;    const int planar = in->format == AV_SAMPLE_FMT_FLTP;    const int mult = 1 + !planar;    const float *src = (const float *)in->extended_data[0]; /* get pointer to audio input buffer */    float *dst = (float *)out->extended_data[jobnr * planar]; /* get pointer to audio output buffer */    const int in_channels = s->n_conv; /* number of input channels */    /* ring buffer length is: longest IR plus max. delay -> next power of 2 */    const int buffer_length = s->buffer_length;    /* -1 for AND instead of MODULO (applied to powers of 2): */    const uint32_t modulo = (uint32_t)buffer_length - 1;    float *buffer[64]; /* holds ringbuffer for each input channel */    int wr = *write;    int read;    int i, l;     if (!planar)        dst += offset;     for (l = 0; l < in_channels; l++) {        /* get starting address of ringbuffer for each input channel */        buffer[l] = ringbuffer + l * buffer_length;    }     for (i = 0; i < in->nb_samples; i++) {        const float *temp_ir = ir; /* using same set of IRs for each sample */         dst[0] = 0;        if (planar) {            for (l = 0; l < in_channels; l++) {                const float *srcp = (const float *)in->extended_data[l];                 /* write current input sample to ringbuffer (for each channel) */                buffer[l][wr] = srcp[i];            }        } else {            for (l = 0; l < in_channels; l++) {                /* write current input sample to ringbuffer (for each channel) */                buffer[l][wr] = src[l];            }        }         /* loop goes through all channels to be convolved */        for (l = 0; l < in_channels; l++) {            const float *const bptr = buffer[l];             if (l == s->lfe_channel) {                /* LFE is an input channel but requires no convolution */                /* apply gain to LFE signal and add to output buffer */                dst[0] += *(buffer[s->lfe_channel] + wr) * s->gain_lfe;                temp_ir += n_samples;                continue;            }             /* current read position in ringbuffer: input sample write position             * - delay for l-th ch. + diff. betw. IR length and buffer length             * (mod buffer length) */            read = (wr - delay[l] - (ir_samples - 1) + buffer_length) & modulo;             if (read + ir_samples < buffer_length) {                memmove(temp_src, bptr + read, ir_samples * sizeof(*temp_src));            } else {                int len = FFMIN(n_samples - (read % ir_samples), buffer_length - read);                 memmove(temp_src, bptr + read, len * sizeof(*temp_src));                memmove(temp_src + len, bptr, (n_samples - len) * sizeof(*temp_src));            }             /* multiply signal and IR, and add up the results */            dst[0] += s->fdsp->scalarproduct_float(temp_ir, temp_src, FFALIGN(ir_samples, 32));            temp_ir += n_samples;        }         /* clippings counter */        if (fabsf(dst[0]) > 1)            n_clippings[0]++;         /* move output buffer pointer by +2 to get to next sample of processed channel: */        dst += mult;        src += in_channels;        wr   = (wr + 1) & modulo; /* update ringbuffer write position */    }     *write = wr; /* remember write position in ringbuffer for next call */     return 0;} static int sofalizer_fast_convolute(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs){    SOFAlizerContext *s = ctx->priv;    ThreadData *td = arg;    AVFrame *in = td->in, *out = td->out;    int offset = jobnr;    int *write = &td->write[jobnr];    AVComplexFloat *hrtf = s->data_hrtf[jobnr]; /* get pointers to current HRTF data */    int *n_clippings = &td->n_clippings[jobnr];    float *ringbuffer = td->ringbuffer[jobnr];    const int ir_samples = s->sofa.ir_samples; /* length of one IR */    const int planar = in->format == AV_SAMPLE_FMT_FLTP;    const int mult = 1 + !planar;    float *dst = (float *)out->extended_data[jobnr * planar]; /* get pointer to audio output buffer */    const int in_channels = s->n_conv; /* number of input channels */    /* ring buffer length is: longest IR plus max. delay -> next power of 2 */    const int buffer_length = s->buffer_length;    /* -1 for AND instead of MODULO (applied to powers of 2): */    const uint32_t modulo = (uint32_t)buffer_length - 1;    AVComplexFloat *fft_in = s->in_fft[jobnr]; /* temporary array for FFT input data */    AVComplexFloat *fft_out = s->out_fft[jobnr]; /* temporary array for FFT output data */    AVComplexFloat *fft_acc = s->temp_afft[jobnr];    AVTXContext *ifft = s->ifft[jobnr];    av_tx_fn itx_fn = s->itx_fn[jobnr];    AVTXContext *fft = s->fft[jobnr];    av_tx_fn tx_fn = s->tx_fn[jobnr];    const int n_conv = s->n_conv;    const int n_fft = s->n_fft;    const float fft_scale = 1.0f / s->n_fft;    AVComplexFloat *hrtf_offset;    int wr = *write;    int n_read;    int i, j;     if (!planar)        dst += offset;     /* find minimum between number of samples and output buffer length:     * (important, if one IR is longer than the output buffer) */    n_read = FFMIN(ir_samples, in->nb_samples);    for (j = 0; j < n_read; j++) {        /* initialize output buf with saved signal from overflow buf */        dst[mult * j]  = ringbuffer[wr];        ringbuffer[wr] = 0.0f; /* re-set read samples to zero */        /* update ringbuffer read/write position */        wr  = (wr + 1) & modulo;    }     /* initialize rest of output buffer with 0 */    for (j = n_read; j < in->nb_samples; j++) {        dst[mult * j] = 0;    }     /* fill FFT accumulation with 0 */    memset(fft_acc, 0, sizeof(AVComplexFloat) * n_fft);     for (i = 0; i < n_conv; i++) {        const float *src = (const float *)in->extended_data[i * planar]; /* get pointer to audio input buffer */         if (i == s->lfe_channel) { /* LFE */            if (in->format == AV_SAMPLE_FMT_FLT) {                for (j = 0; j < in->nb_samples; j++) {                    /* apply gain to LFE signal and add to output buffer */                    dst[2 * j] += src[i + j * in_channels] * s->gain_lfe;                }            } else {                for (j = 0; j < in->nb_samples; j++) {                    /* apply gain to LFE signal and add to output buffer */

Lines 1–500 of 1103.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t