mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-07 11:13:41 +02:00
790f793844
There are lots of files that don't need it: The number of object files that actually need it went down from 2011 to 884 here. Keep it for external users in order to not cause breakages. Also improve the other headers a bit while just at it. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
592 lines
23 KiB
C
592 lines
23 KiB
C
/*
|
|
* Copyright (c) 2020 Paul B Mahol
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg 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.
|
|
*
|
|
* FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "libavutil/cpu.h"
|
|
#include "libavutil/channel_layout.h"
|
|
#include "libavutil/ffmath.h"
|
|
#include "libavutil/eval.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/opt.h"
|
|
#include "libavutil/tx.h"
|
|
#include "audio.h"
|
|
#include "avfilter.h"
|
|
#include "filters.h"
|
|
#include "formats.h"
|
|
#include "internal.h"
|
|
#include "window_func.h"
|
|
|
|
typedef struct AudioFIRSourceContext {
|
|
const AVClass *class;
|
|
|
|
char *freq_points_str;
|
|
char *magnitude_str;
|
|
char *phase_str;
|
|
int nb_taps;
|
|
int sample_rate;
|
|
int nb_samples;
|
|
int win_func;
|
|
int preset;
|
|
int interp;
|
|
int phaset;
|
|
|
|
AVComplexFloat *complexf;
|
|
float *freq;
|
|
float *magnitude;
|
|
float *phase;
|
|
int freq_size;
|
|
int magnitude_size;
|
|
int phase_size;
|
|
int nb_freq;
|
|
int nb_magnitude;
|
|
int nb_phase;
|
|
|
|
float *taps;
|
|
float *win;
|
|
int64_t pts;
|
|
|
|
AVTXContext *tx_ctx, *itx_ctx;
|
|
av_tx_fn tx_fn, itx_fn;
|
|
} AudioFIRSourceContext;
|
|
|
|
#define OFFSET(x) offsetof(AudioFIRSourceContext, x)
|
|
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
|
|
|
|
static const AVOption afirsrc_options[] = {
|
|
{ "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS },
|
|
{ "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS },
|
|
{ "frequency", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS },
|
|
{ "f", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS },
|
|
{ "magnitude", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS },
|
|
{ "m", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS },
|
|
{ "phase", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 0, 0, FLAGS },
|
|
{ "p", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 0, 0, FLAGS },
|
|
{ "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
|
|
{ "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
|
|
{ "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
|
|
{ "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
|
|
WIN_FUNC_OPTION("win_func", OFFSET(win_func), FLAGS, WFUNC_BLACKMAN),
|
|
WIN_FUNC_OPTION("w", OFFSET(win_func), FLAGS, WFUNC_BLACKMAN),
|
|
{NULL}
|
|
};
|
|
|
|
AVFILTER_DEFINE_CLASS(afirsrc);
|
|
|
|
static av_cold int init(AVFilterContext *ctx)
|
|
{
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
|
|
if (!(s->nb_taps & 1)) {
|
|
av_log(s, AV_LOG_WARNING, "Number of taps %d must be odd length.\n", s->nb_taps);
|
|
s->nb_taps |= 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static av_cold void uninit(AVFilterContext *ctx)
|
|
{
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
|
|
av_freep(&s->win);
|
|
av_freep(&s->taps);
|
|
av_freep(&s->freq);
|
|
av_freep(&s->magnitude);
|
|
av_freep(&s->phase);
|
|
av_freep(&s->complexf);
|
|
av_tx_uninit(&s->tx_ctx);
|
|
av_tx_uninit(&s->itx_ctx);
|
|
}
|
|
|
|
static av_cold int query_formats(AVFilterContext *ctx)
|
|
{
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
static const AVChannelLayout chlayouts[] = { AV_CHANNEL_LAYOUT_MONO, { 0 } };
|
|
int sample_rates[] = { s->sample_rate, -1 };
|
|
static const enum AVSampleFormat sample_fmts[] = {
|
|
AV_SAMPLE_FMT_FLT,
|
|
AV_SAMPLE_FMT_NONE
|
|
};
|
|
int ret = ff_set_common_formats_from_list(ctx, sample_fmts);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ff_set_common_channel_layouts_from_list(ctx, chlayouts);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ff_set_common_samplerates_from_list(ctx, sample_rates);
|
|
}
|
|
|
|
static int parse_string(char *str, float **items, int *nb_items, int *items_size)
|
|
{
|
|
float *new_items;
|
|
char *tail;
|
|
|
|
new_items = av_fast_realloc(NULL, items_size, sizeof(float));
|
|
if (!new_items)
|
|
return AVERROR(ENOMEM);
|
|
*items = new_items;
|
|
|
|
tail = str;
|
|
if (!tail)
|
|
return AVERROR(EINVAL);
|
|
|
|
do {
|
|
(*items)[(*nb_items)++] = av_strtod(tail, &tail);
|
|
new_items = av_fast_realloc(*items, items_size, (*nb_items + 2) * sizeof(float));
|
|
if (!new_items)
|
|
return AVERROR(ENOMEM);
|
|
*items = new_items;
|
|
if (tail && *tail)
|
|
tail++;
|
|
} while (tail && *tail);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void lininterp(AVComplexFloat *complexf,
|
|
const float *freq,
|
|
const float *magnitude,
|
|
const float *phase,
|
|
int m, int minterp)
|
|
{
|
|
for (int i = 0; i < minterp; i++) {
|
|
for (int j = 1; j < m; j++) {
|
|
const float x = i / (float)minterp;
|
|
|
|
if (x <= freq[j]) {
|
|
const float mg = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (magnitude[j] - magnitude[j-1]) + magnitude[j-1];
|
|
const float ph = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (phase[j] - phase[j-1]) + phase[j-1];
|
|
|
|
complexf[i].re = mg * cosf(ph);
|
|
complexf[i].im = mg * sinf(ph);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static av_cold int config_output(AVFilterLink *outlink)
|
|
{
|
|
AVFilterContext *ctx = outlink->src;
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
float overlap, scale = 1.f, compensation;
|
|
int fft_size, middle, ret;
|
|
|
|
s->nb_freq = s->nb_magnitude = s->nb_phase = 0;
|
|
|
|
ret = parse_string(s->freq_points_str, &s->freq, &s->nb_freq, &s->freq_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = parse_string(s->magnitude_str, &s->magnitude, &s->nb_magnitude, &s->magnitude_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = parse_string(s->phase_str, &s->phase, &s->nb_phase, &s->phase_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (s->nb_freq != s->nb_magnitude && s->nb_freq != s->nb_phase && s->nb_freq >= 2) {
|
|
av_log(ctx, AV_LOG_ERROR, "Number of frequencies, magnitudes and phases must be same and >= 2.\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
for (int i = 0; i < s->nb_freq; i++) {
|
|
if (i == 0 && s->freq[i] != 0.f) {
|
|
av_log(ctx, AV_LOG_ERROR, "First frequency must be 0.\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (i == s->nb_freq - 1 && s->freq[i] != 1.f) {
|
|
av_log(ctx, AV_LOG_ERROR, "Last frequency must be 1.\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (i && s->freq[i] < s->freq[i-1]) {
|
|
av_log(ctx, AV_LOG_ERROR, "Frequencies must be in increasing order.\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
}
|
|
|
|
fft_size = 1 << (av_log2(s->nb_taps) + 1);
|
|
s->complexf = av_calloc(fft_size * 2, sizeof(*s->complexf));
|
|
if (!s->complexf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ret = av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 1, fft_size, &scale, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->taps = av_calloc(s->nb_taps, sizeof(*s->taps));
|
|
if (!s->taps)
|
|
return AVERROR(ENOMEM);
|
|
|
|
s->win = av_calloc(s->nb_taps, sizeof(*s->win));
|
|
if (!s->win)
|
|
return AVERROR(ENOMEM);
|
|
|
|
generate_window_func(s->win, s->nb_taps, s->win_func, &overlap);
|
|
|
|
lininterp(s->complexf, s->freq, s->magnitude, s->phase, s->nb_freq, fft_size / 2);
|
|
|
|
s->tx_fn(s->tx_ctx, s->complexf + fft_size, s->complexf, sizeof(*s->complexf));
|
|
|
|
compensation = 2.f / fft_size;
|
|
middle = s->nb_taps / 2;
|
|
|
|
for (int i = 0; i <= middle; i++) {
|
|
s->taps[ i] = s->complexf[fft_size + middle - i].re * compensation * s->win[i];
|
|
s->taps[middle + i] = s->complexf[fft_size + i].re * compensation * s->win[middle + i];
|
|
}
|
|
|
|
s->pts = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int activate(AVFilterContext *ctx)
|
|
{
|
|
AVFilterLink *outlink = ctx->outputs[0];
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
AVFrame *frame;
|
|
int nb_samples;
|
|
|
|
if (!ff_outlink_frame_wanted(outlink))
|
|
return FFERROR_NOT_READY;
|
|
|
|
nb_samples = FFMIN(s->nb_samples, s->nb_taps - s->pts);
|
|
if (nb_samples <= 0) {
|
|
ff_outlink_set_status(outlink, AVERROR_EOF, s->pts);
|
|
return 0;
|
|
}
|
|
|
|
if (!(frame = ff_get_audio_buffer(outlink, nb_samples)))
|
|
return AVERROR(ENOMEM);
|
|
|
|
memcpy(frame->data[0], s->taps + s->pts, nb_samples * sizeof(float));
|
|
|
|
frame->pts = s->pts;
|
|
s->pts += nb_samples;
|
|
return ff_filter_frame(outlink, frame);
|
|
}
|
|
|
|
static const AVFilterPad afirsrc_outputs[] = {
|
|
{
|
|
.name = "default",
|
|
.type = AVMEDIA_TYPE_AUDIO,
|
|
.config_props = config_output,
|
|
},
|
|
};
|
|
|
|
const AVFilter ff_asrc_afirsrc = {
|
|
.name = "afirsrc",
|
|
.description = NULL_IF_CONFIG_SMALL("Generate a FIR coefficients audio stream."),
|
|
.init = init,
|
|
.uninit = uninit,
|
|
.activate = activate,
|
|
.priv_size = sizeof(AudioFIRSourceContext),
|
|
.inputs = NULL,
|
|
FILTER_OUTPUTS(afirsrc_outputs),
|
|
FILTER_QUERY_FUNC(query_formats),
|
|
.priv_class = &afirsrc_class,
|
|
};
|
|
|
|
#define DEFAULT_BANDS "25 40 63 100 160 250 400 630 1000 1600 2500 4000 6300 10000 16000 24000"
|
|
|
|
typedef struct EqPreset {
|
|
char name[16];
|
|
float gains[16];
|
|
} EqPreset;
|
|
|
|
static const EqPreset eq_presets[] = {
|
|
{ "flat", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
|
{ "acoustic", { 5.0, 4.5, 4.0, 3.5, 1.5, 1.0, 1.5, 1.5, 2.0, 3.0, 3.5, 4.0, 3.7, 3.0, 3.0 } },
|
|
{ "bass", { 10.0, 8.8, 8.5, 6.5, 2.5, 1.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
|
{ "beats", { -5.5, -5.0, -4.5, -4.2, -3.5, -3.0, -1.9, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
|
{ "classic", { -0.3, 0.3, -3.5, -9.0, -1.0, 0.0, 1.8, 2.1, 0.0, 0.0, 0.0, 4.4, 9.0, 9.0, 9.0 } },
|
|
{ "clear", { 3.5, 5.5, 6.5, 9.5, 8.0, 6.5, 3.5, 2.5, 1.3, 5.0, 7.0, 9.0, 10.0, 11.0, 9.0 } },
|
|
{ "deep bass", { 12.0, 8.0, 0.0, -6.7, -12.0, -9.0, -3.5, -3.5, -6.1, 0.0, -3.0, -5.0, 0.0, 1.2, 3.0 } },
|
|
{ "dubstep", { 12.0, 10.0, 0.5, -1.0, -3.0, -5.0, -5.0, -4.8, -4.5, -2.5, -1.0, 0.0, -2.5, -2.5, 0.0 } },
|
|
{ "electronic", { 4.0, 4.0, 3.5, 1.0, 0.0, -0.5, -2.0, 0.0, 2.0, 0.0, 0.0, 1.0, 3.0, 4.0, 4.5 } },
|
|
{ "hardstyle", { 6.1, 7.0, 12.0, 6.1, -5.0, -12.0, -2.5, 3.0, 6.5, 0.0, -2.2, -4.5, -6.1, -9.2, -10.0 } },
|
|
{ "hip-hop", { 4.5, 4.3, 4.0, 2.5, 1.5, 3.0, -1.0, -1.5, -1.5, 1.5, 0.0, -1.0, 0.0, 1.5, 3.0 } },
|
|
{ "jazz", { 0.0, 0.0, 0.0, 2.0, 4.0, 5.9, -5.9, -4.5, -2.5, 2.5, 1.0, -0.8, -0.8, -0.8, -0.8 } },
|
|
{ "metal", { 10.5, 10.5, 7.5, 0.0, 2.0, 5.5, 0.0, 0.0, 0.0, 6.1, 0.0, 0.0, 6.1, 10.0, 12.0 } },
|
|
{ "movie", { 3.0, 3.0, 6.1, 8.5, 9.0, 7.0, 6.1, 6.1, 5.0, 8.0, 3.5, 3.5, 8.0, 10.0, 8.0 } },
|
|
{ "pop", { 0.0, 0.0, 0.0, 0.0, 0.0, 1.3, 2.0, 2.5, 5.0, -1.5, -2.0, -3.0, -3.0, -3.0, -3.0 } },
|
|
{ "r&b", { 3.0, 3.0, 7.0, 6.1, 4.5, 1.5, -1.5, -2.0, -1.5, 2.0, 2.5, 3.0, 3.5, 3.8, 4.0 } },
|
|
{ "rock", { 0.0, 0.0, 0.0, 3.0, 3.0, -10.0, -4.0, -1.0, 0.8, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 } },
|
|
{ "vocal booster", { -1.5, -2.0, -3.0, -3.0, -0.5, 1.5, 3.5, 3.5, 3.5, 3.0, 2.0, 1.5, 0.0, 0.0, -1.5 } },
|
|
};
|
|
|
|
static const AVOption afireqsrc_options[] = {
|
|
{ "preset","set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, .unit = "preset" },
|
|
{ "p", "set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, .unit = "preset" },
|
|
{ "custom", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 0].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 0}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 1].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 2].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 2}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 3].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 3}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 4].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 4}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 5].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 5}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 6].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 6}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 7].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 7}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 8].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 8}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[ 9].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 9}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[10].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[11].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[12].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[13].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=13}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[14].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=14}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[15].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=15}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[16].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ eq_presets[17].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=17}, 0, 0, FLAGS, .unit = "preset" },
|
|
{ "gains", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS },
|
|
{ "g", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS },
|
|
{ "bands", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS },
|
|
{ "b", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS },
|
|
{ "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS },
|
|
{ "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS },
|
|
{ "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
|
|
{ "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
|
|
{ "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
|
|
{ "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
|
|
{ "interp","set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "interp" },
|
|
{ "i", "set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "interp" },
|
|
{ "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "interp" },
|
|
{ "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "interp" },
|
|
{ "phase","set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "phase" },
|
|
{ "h", "set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "phase" },
|
|
{ "linear", "linear phase", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "phase" },
|
|
{ "min", "minimum phase", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "phase" },
|
|
{NULL}
|
|
};
|
|
|
|
AVFILTER_DEFINE_CLASS(afireqsrc);
|
|
|
|
static void eq_interp(AVComplexFloat *complexf,
|
|
const float *freq,
|
|
const float *magnitude,
|
|
int m, int interp, int minterp,
|
|
const float factor)
|
|
{
|
|
for (int i = 0; i < minterp; i++) {
|
|
for (int j = 0; j < m; j++) {
|
|
const float x = factor * i;
|
|
|
|
if (x <= freq[j+1]) {
|
|
float g;
|
|
|
|
if (interp == 0) {
|
|
const float d = freq[j+1] - freq[j];
|
|
const float d0 = x - freq[j];
|
|
const float d1 = freq[j+1] - x;
|
|
const float g0 = magnitude[j];
|
|
const float g1 = magnitude[j+1];
|
|
|
|
if (d0 && d1) {
|
|
g = (d0 * g1 + d1 * g0) / d;
|
|
} else if (d0) {
|
|
g = g1;
|
|
} else {
|
|
g = g0;
|
|
}
|
|
} else {
|
|
if (x <= freq[j]) {
|
|
g = magnitude[j];
|
|
} else {
|
|
float x1, x2, x3;
|
|
float a, b, c, d;
|
|
float m0, m1, m2, msum;
|
|
const float unit = freq[j+1] - freq[j];
|
|
|
|
m0 = j != 0 ? unit * (magnitude[j] - magnitude[j-1]) / (freq[j] - freq[j-1]) : 0;
|
|
m1 = magnitude[j+1] - magnitude[j];
|
|
m2 = j != minterp - 1 ? unit * (magnitude[j+2] - magnitude[j+1]) / (freq[j+2] - freq[j+1]) : 0;
|
|
|
|
msum = fabsf(m0) + fabsf(m1);
|
|
m0 = msum > 0.f ? (fabsf(m0) * m1 + fabsf(m1) * m0) / msum : 0.f;
|
|
msum = fabsf(m1) + fabsf(m2);
|
|
m1 = msum > 0.f ? (fabsf(m1) * m2 + fabsf(m2) * m1) / msum : 0.f;
|
|
|
|
d = magnitude[j];
|
|
c = m0;
|
|
b = 3.f * magnitude[j+1] - m1 - 2.f * c - 3.f * d;
|
|
a = magnitude[j+1] - b - c - d;
|
|
|
|
x1 = (x - freq[j]) / unit;
|
|
x2 = x1 * x1;
|
|
x3 = x2 * x1;
|
|
|
|
g = a * x3 + b * x2 + c * x1 + d;
|
|
}
|
|
}
|
|
|
|
complexf[i].re = g;
|
|
complexf[i].im = 0;
|
|
complexf[minterp * 2 - i - 1].re = g;
|
|
complexf[minterp * 2 - i - 1].im = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static av_cold int config_eq_output(AVFilterLink *outlink)
|
|
{
|
|
AVFilterContext *ctx = outlink->src;
|
|
AudioFIRSourceContext *s = ctx->priv;
|
|
int fft_size, middle, asize, ret;
|
|
float scale, factor;
|
|
|
|
s->nb_freq = s->nb_magnitude = 0;
|
|
if (s->preset < 0) {
|
|
ret = parse_string(s->freq_points_str, &s->freq, &s->nb_freq, &s->freq_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = parse_string(s->magnitude_str, &s->magnitude, &s->nb_magnitude, &s->magnitude_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
} else {
|
|
char *freq_str;
|
|
|
|
s->nb_magnitude = FF_ARRAY_ELEMS(eq_presets[s->preset].gains);
|
|
|
|
freq_str = av_strdup(DEFAULT_BANDS);
|
|
if (!freq_str)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ret = parse_string(freq_str, &s->freq, &s->nb_freq, &s->freq_size);
|
|
av_free(freq_str);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->magnitude = av_calloc(s->nb_magnitude + 1, sizeof(*s->magnitude));
|
|
if (!s->magnitude)
|
|
return AVERROR(ENOMEM);
|
|
memcpy(s->magnitude, eq_presets[s->preset].gains, sizeof(*s->magnitude) * s->nb_magnitude);
|
|
}
|
|
|
|
if (s->nb_freq != s->nb_magnitude || s->nb_freq < 2) {
|
|
av_log(ctx, AV_LOG_ERROR, "Number of bands and gains must be same and >= 2.\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
s->freq[s->nb_freq] = outlink->sample_rate * 0.5f;
|
|
s->magnitude[s->nb_freq] = s->magnitude[s->nb_freq-1];
|
|
|
|
fft_size = s->nb_taps * 2;
|
|
factor = FFMIN(outlink->sample_rate * 0.5f, s->freq[s->nb_freq - 1]) / (float)fft_size;
|
|
asize = FFALIGN(fft_size, av_cpu_max_align());
|
|
s->complexf = av_calloc(asize * 2, sizeof(*s->complexf));
|
|
if (!s->complexf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
scale = 1.f;
|
|
ret = av_tx_init(&s->itx_ctx, &s->itx_fn, AV_TX_FLOAT_FFT, 1, fft_size, &scale, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->taps = av_calloc(s->nb_taps, sizeof(*s->taps));
|
|
if (!s->taps)
|
|
return AVERROR(ENOMEM);
|
|
|
|
eq_interp(s->complexf, s->freq, s->magnitude, s->nb_freq, s->interp, s->nb_taps, factor);
|
|
|
|
for (int i = 0; i < fft_size; i++)
|
|
s->complexf[i].re = ff_exp10f(s->complexf[i].re / 20.f);
|
|
|
|
if (s->phaset) {
|
|
const float threshold = powf(10.f, -100.f / 20.f);
|
|
const float logt = logf(threshold);
|
|
|
|
scale = 1.f;
|
|
ret = av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 0, fft_size, &scale, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (int i = 0; i < fft_size; i++)
|
|
s->complexf[i].re = s->complexf[i].re < threshold ? logt : logf(s->complexf[i].re);
|
|
|
|
s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float));
|
|
for (int i = 0; i < fft_size; i++) {
|
|
s->complexf[i + asize].re /= fft_size;
|
|
s->complexf[i + asize].im /= fft_size;
|
|
}
|
|
|
|
for (int i = 1; i < s->nb_taps; i++) {
|
|
s->complexf[asize + i].re += s->complexf[asize + fft_size - i].re;
|
|
s->complexf[asize + i].im -= s->complexf[asize + fft_size - i].im;
|
|
s->complexf[asize + fft_size - i].re = 0.f;
|
|
s->complexf[asize + fft_size - i].im = 0.f;
|
|
}
|
|
s->complexf[asize + s->nb_taps - 1].im *= -1.f;
|
|
|
|
s->tx_fn(s->tx_ctx, s->complexf, s->complexf + asize, sizeof(float));
|
|
|
|
for (int i = 0; i < fft_size; i++) {
|
|
float eR = expf(s->complexf[i].re);
|
|
|
|
s->complexf[i].re = eR * cosf(s->complexf[i].im);
|
|
s->complexf[i].im = eR * sinf(s->complexf[i].im);
|
|
}
|
|
|
|
s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float));
|
|
|
|
for (int i = 0; i < s->nb_taps; i++)
|
|
s->taps[i] = s->complexf[i + asize].re / fft_size;
|
|
} else {
|
|
s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float));
|
|
|
|
middle = s->nb_taps / 2;
|
|
for (int i = 0; i < middle; i++) {
|
|
s->taps[middle - i] = s->complexf[i + asize].re / fft_size;
|
|
s->taps[middle + i] = s->complexf[i + asize].re / fft_size;
|
|
}
|
|
}
|
|
|
|
s->pts = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const AVFilterPad afireqsrc_outputs[] = {
|
|
{
|
|
.name = "default",
|
|
.type = AVMEDIA_TYPE_AUDIO,
|
|
.config_props = config_eq_output,
|
|
},
|
|
};
|
|
|
|
const AVFilter ff_asrc_afireqsrc = {
|
|
.name = "afireqsrc",
|
|
.description = NULL_IF_CONFIG_SMALL("Generate a FIR equalizer coefficients audio stream."),
|
|
.uninit = uninit,
|
|
.activate = activate,
|
|
.priv_size = sizeof(AudioFIRSourceContext),
|
|
.inputs = NULL,
|
|
FILTER_OUTPUTS(afireqsrc_outputs),
|
|
FILTER_QUERY_FUNC(query_formats),
|
|
.priv_class = &afireqsrc_class,
|
|
};
|