|
|
@@ -69,6 +69,7 @@ typedef struct { |
|
|
|
RDFTContext *analysis_irdft; |
|
|
|
RDFTContext *rdft; |
|
|
|
RDFTContext *irdft; |
|
|
|
FFTContext *fft_ctx; |
|
|
|
int analysis_rdft_len; |
|
|
|
int rdft_len; |
|
|
|
|
|
|
@@ -97,6 +98,7 @@ typedef struct { |
|
|
|
int scale; |
|
|
|
char *dumpfile; |
|
|
|
int dumpscale; |
|
|
|
int fft2; |
|
|
|
|
|
|
|
int nb_gain_entry; |
|
|
|
int gain_entry_err; |
|
|
@@ -132,6 +134,7 @@ static const AVOption firequalizer_options[] = { |
|
|
|
{ "loglog", "logarithmic-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLOG }, 0, 0, FLAGS, "scale" }, |
|
|
|
{ "dumpfile", "set dump file", OFFSET(dumpfile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, |
|
|
|
{ "dumpscale", "set dump scale", OFFSET(dumpscale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" }, |
|
|
|
{ "fft2", "set 2-channels fft", OFFSET(fft2), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, |
|
|
|
{ NULL } |
|
|
|
}; |
|
|
|
|
|
|
@@ -143,7 +146,9 @@ static void common_uninit(FIREqualizerContext *s) |
|
|
|
av_rdft_end(s->analysis_irdft); |
|
|
|
av_rdft_end(s->rdft); |
|
|
|
av_rdft_end(s->irdft); |
|
|
|
av_fft_end(s->fft_ctx); |
|
|
|
s->analysis_rdft = s->analysis_irdft = s->rdft = s->irdft = NULL; |
|
|
|
s->fft_ctx = NULL; |
|
|
|
|
|
|
|
av_freep(&s->analysis_buf); |
|
|
|
av_freep(&s->dump_buf); |
|
|
@@ -230,6 +235,70 @@ static void fast_convolute(FIREqualizerContext *s, const float *kernel_buf, floa |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void fast_convolute2(FIREqualizerContext *s, const float *kernel_buf, FFTComplex *conv_buf, |
|
|
|
OverlapIndex *idx, float *data0, float *data1, int nsamples) |
|
|
|
{ |
|
|
|
if (nsamples <= s->nsamples_max) { |
|
|
|
FFTComplex *buf = conv_buf + idx->buf_idx * s->rdft_len; |
|
|
|
FFTComplex *obuf = conv_buf + !idx->buf_idx * s->rdft_len + idx->overlap_idx; |
|
|
|
int center = s->fir_len/2; |
|
|
|
int k; |
|
|
|
float tmp; |
|
|
|
|
|
|
|
memset(buf, 0, center * sizeof(*buf)); |
|
|
|
for (k = 0; k < nsamples; k++) { |
|
|
|
buf[center+k].re = data0[k]; |
|
|
|
buf[center+k].im = data1[k]; |
|
|
|
} |
|
|
|
memset(buf + center + nsamples, 0, (s->rdft_len - nsamples - center) * sizeof(*buf)); |
|
|
|
av_fft_permute(s->fft_ctx, buf); |
|
|
|
av_fft_calc(s->fft_ctx, buf); |
|
|
|
|
|
|
|
/* swap re <-> im, do backward fft using forward fft_ctx */ |
|
|
|
/* normalize with 0.5f */ |
|
|
|
tmp = buf[0].re; |
|
|
|
buf[0].re = 0.5f * kernel_buf[0] * buf[0].im; |
|
|
|
buf[0].im = 0.5f * kernel_buf[0] * tmp; |
|
|
|
for (k = 1; k < s->rdft_len/2; k++) { |
|
|
|
int m = s->rdft_len - k; |
|
|
|
tmp = buf[k].re; |
|
|
|
buf[k].re = 0.5f * kernel_buf[k] * buf[k].im; |
|
|
|
buf[k].im = 0.5f * kernel_buf[k] * tmp; |
|
|
|
tmp = buf[m].re; |
|
|
|
buf[m].re = 0.5f * kernel_buf[k] * buf[m].im; |
|
|
|
buf[m].im = 0.5f * kernel_buf[k] * tmp; |
|
|
|
} |
|
|
|
tmp = buf[k].re; |
|
|
|
buf[k].re = 0.5f * kernel_buf[k] * buf[k].im; |
|
|
|
buf[k].im = 0.5f * kernel_buf[k] * tmp; |
|
|
|
|
|
|
|
av_fft_permute(s->fft_ctx, buf); |
|
|
|
av_fft_calc(s->fft_ctx, buf); |
|
|
|
|
|
|
|
for (k = 0; k < s->rdft_len - idx->overlap_idx; k++) { |
|
|
|
buf[k].re += obuf[k].re; |
|
|
|
buf[k].im += obuf[k].im; |
|
|
|
} |
|
|
|
|
|
|
|
/* swapped re <-> im */ |
|
|
|
for (k = 0; k < nsamples; k++) { |
|
|
|
data0[k] = buf[k].im; |
|
|
|
data1[k] = buf[k].re; |
|
|
|
} |
|
|
|
idx->buf_idx = !idx->buf_idx; |
|
|
|
idx->overlap_idx = nsamples; |
|
|
|
} else { |
|
|
|
while (nsamples > s->nsamples_max * 2) { |
|
|
|
fast_convolute2(s, kernel_buf, conv_buf, idx, data0, data1, s->nsamples_max); |
|
|
|
data0 += s->nsamples_max; |
|
|
|
data1 += s->nsamples_max; |
|
|
|
nsamples -= s->nsamples_max; |
|
|
|
} |
|
|
|
fast_convolute2(s, kernel_buf, conv_buf, idx, data0, data1, nsamples/2); |
|
|
|
fast_convolute2(s, kernel_buf, conv_buf, idx, data0 + nsamples/2, data1 + nsamples/2, nsamples - nsamples/2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void dump_fir(AVFilterContext *ctx, FILE *fp, int ch) |
|
|
|
{ |
|
|
|
FIREqualizerContext *s = ctx->priv; |
|
|
@@ -598,6 +667,9 @@ static int config_input(AVFilterLink *inlink) |
|
|
|
if (!(s->rdft = av_rdft_init(rdft_bits, DFT_R2C)) || !(s->irdft = av_rdft_init(rdft_bits, IDFT_C2R))) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
if (s->fft2 && !s->multi && inlink->channels > 1 && !(s->fft_ctx = av_fft_init(rdft_bits, 0))) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
for ( ; rdft_bits <= RDFT_BITS_MAX; rdft_bits++) { |
|
|
|
s->analysis_rdft_len = 1 << rdft_bits; |
|
|
|
if (inlink->sample_rate <= s->accuracy * s->analysis_rdft_len) |
|
|
@@ -640,7 +712,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
|
|
|
FIREqualizerContext *s = ctx->priv; |
|
|
|
int ch; |
|
|
|
|
|
|
|
for (ch = 0; ch < inlink->channels; ch++) { |
|
|
|
for (ch = 0; ch + 1 < inlink->channels && s->fft_ctx; ch += 2) { |
|
|
|
fast_convolute2(s, s->kernel_buf, (FFTComplex *)(s->conv_buf + 2 * ch * s->rdft_len), |
|
|
|
s->conv_idx + ch, (float *) frame->extended_data[ch], |
|
|
|
(float *) frame->extended_data[ch+1], frame->nb_samples); |
|
|
|
} |
|
|
|
|
|
|
|
for ( ; ch < inlink->channels; ch++) { |
|
|
|
fast_convolute(s, s->kernel_buf + (s->multi ? ch * s->rdft_len : 0), |
|
|
|
s->conv_buf + 2 * ch * s->rdft_len, s->conv_idx + ch, |
|
|
|
(float *) frame->extended_data[ch], frame->nb_samples); |
|
|
|