FFmpeg  4.4.4
asrc_afirsrc.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Paul B Mahol
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "libavutil/eval.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/tx.h"
24 #include "audio.h"
25 #include "avfilter.h"
26 #include "internal.h"
27 #include "window_func.h"
28 
29 typedef struct AudioFIRSourceContext {
30  const AVClass *class;
31 
34  char *phase_str;
35  int nb_taps;
38  int win_func;
39 
41  float *freq;
42  float *magnitude;
43  float *phase;
44  int freq_size;
47  int nb_freq;
49  int nb_phase;
50 
51  float *taps;
52  float *win;
53  int64_t pts;
54 
58 
59 #define OFFSET(x) offsetof(AudioFIRSourceContext, x)
60 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
61 
62 static const AVOption afirsrc_options[] = {
63  { "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS },
64  { "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS },
65  { "frequency", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS },
66  { "f", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS },
67  { "magnitude", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS },
68  { "m", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS },
69  { "phase", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 0, 0, FLAGS },
70  { "p", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 0, 0, FLAGS },
71  { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
72  { "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS },
73  { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
74  { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS },
75  { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64=WFUNC_BLACKMAN}, 0, NB_WFUNC-1, FLAGS, "win_func" },
76  { "w", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64=WFUNC_BLACKMAN}, 0, NB_WFUNC-1, FLAGS, "win_func" },
77  { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, FLAGS, "win_func" },
78  { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, FLAGS, "win_func" },
79  { "hanning", "Hanning", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, FLAGS, "win_func" },
80  { "hamming", "Hamming", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING}, 0, 0, FLAGS, "win_func" },
81  { "blackman", "Blackman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, FLAGS, "win_func" },
82  { "welch", "Welch", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH}, 0, 0, FLAGS, "win_func" },
83  { "flattop", "Flat-top", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP}, 0, 0, FLAGS, "win_func" },
84  { "bharris", "Blackman-Harris", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS}, 0, 0, FLAGS, "win_func" },
85  { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, FLAGS, "win_func" },
86  { "bhann", "Bartlett-Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN}, 0, 0, FLAGS, "win_func" },
87  { "sine", "Sine", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE}, 0, 0, FLAGS, "win_func" },
88  { "nuttall", "Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL}, 0, 0, FLAGS, "win_func" },
89  { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS}, 0, 0, FLAGS, "win_func" },
90  { "gauss", "Gauss", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS}, 0, 0, FLAGS, "win_func" },
91  { "tukey", "Tukey", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY}, 0, 0, FLAGS, "win_func" },
92  { "dolph", "Dolph-Chebyshev", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH}, 0, 0, FLAGS, "win_func" },
93  { "cauchy", "Cauchy", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY}, 0, 0, FLAGS, "win_func" },
94  { "parzen", "Parzen", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN}, 0, 0, FLAGS, "win_func" },
95  { "poisson", "Poisson", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON}, 0, 0, FLAGS, "win_func" },
96  { "bohman" , "Bohman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN}, 0, 0, FLAGS, "win_func" },
97  {NULL}
98 };
99 
101 
103 {
104  AudioFIRSourceContext *s = ctx->priv;
105 
106  if (!(s->nb_taps & 1)) {
107  av_log(s, AV_LOG_WARNING, "Number of taps %d must be odd length.\n", s->nb_taps);
108  s->nb_taps |= 1;
109  }
110 
111  return 0;
112 }
113 
115 {
116  AudioFIRSourceContext *s = ctx->priv;
117 
118  av_freep(&s->win);
119  av_freep(&s->taps);
120  av_freep(&s->freq);
121  av_freep(&s->magnitude);
122  av_freep(&s->phase);
123  av_freep(&s->complexf);
124  av_tx_uninit(&s->tx_ctx);
125 }
126 
128 {
129  AudioFIRSourceContext *s = ctx->priv;
130  static const int64_t chlayouts[] = { AV_CH_LAYOUT_MONO, -1 };
131  int sample_rates[] = { s->sample_rate, -1 };
132  static const enum AVSampleFormat sample_fmts[] = {
135  };
136 
139  int ret;
140 
142  if (!formats)
143  return AVERROR(ENOMEM);
145  if (ret < 0)
146  return ret;
147 
148  layouts = ff_make_format64_list(chlayouts);
149  if (!layouts)
150  return AVERROR(ENOMEM);
152  if (ret < 0)
153  return ret;
154 
156  if (!formats)
157  return AVERROR(ENOMEM);
159 }
160 
161 static int parse_string(char *str, float **items, int *nb_items, int *items_size)
162 {
163  float *new_items;
164  char *tail;
165 
166  new_items = av_fast_realloc(NULL, items_size, 1 * sizeof(float));
167  if (!new_items)
168  return AVERROR(ENOMEM);
169  *items = new_items;
170 
171  tail = str;
172  if (!tail)
173  return AVERROR(EINVAL);
174 
175  do {
176  (*items)[(*nb_items)++] = av_strtod(tail, &tail);
177  new_items = av_fast_realloc(*items, items_size, (*nb_items + 1) * sizeof(float));
178  if (!new_items)
179  return AVERROR(ENOMEM);
180  *items = new_items;
181  if (tail && *tail)
182  tail++;
183  } while (tail && *tail);
184 
185  return 0;
186 }
187 
188 static void lininterp(AVComplexFloat *complexf,
189  const float *freq,
190  const float *magnitude,
191  const float *phase,
192  int m, int minterp)
193 {
194  for (int i = 0; i < minterp; i++) {
195  for (int j = 1; j < m; j++) {
196  const float x = i / (float)minterp;
197 
198  if (x <= freq[j]) {
199  const float mg = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (magnitude[j] - magnitude[j-1]) + magnitude[j-1];
200  const float ph = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (phase[j] - phase[j-1]) + phase[j-1];
201 
202  complexf[i].re = mg * cosf(ph);
203  complexf[i].im = mg * sinf(ph);
204  break;
205  }
206  }
207  }
208 }
209 
210 static av_cold int config_output(AVFilterLink *outlink)
211 {
212  AVFilterContext *ctx = outlink->src;
213  AudioFIRSourceContext *s = ctx->priv;
214  float overlap, scale = 1.f, compensation;
215  int fft_size, middle, ret;
216 
217  s->nb_freq = s->nb_magnitude = s->nb_phase = 0;
218 
219  ret = parse_string(s->freq_points_str, &s->freq, &s->nb_freq, &s->freq_size);
220  if (ret < 0)
221  return ret;
222 
223  ret = parse_string(s->magnitude_str, &s->magnitude, &s->nb_magnitude, &s->magnitude_size);
224  if (ret < 0)
225  return ret;
226 
227  ret = parse_string(s->phase_str, &s->phase, &s->nb_phase, &s->phase_size);
228  if (ret < 0)
229  return ret;
230 
231  if (s->nb_freq != s->nb_magnitude && s->nb_freq != s->nb_phase && s->nb_freq >= 2) {
232  av_log(ctx, AV_LOG_ERROR, "Number of frequencies, magnitudes and phases must be same and >= 2.\n");
233  return AVERROR(EINVAL);
234  }
235 
236  for (int i = 0; i < s->nb_freq; i++) {
237  if (i == 0 && s->freq[i] != 0.f) {
238  av_log(ctx, AV_LOG_ERROR, "First frequency must be 0.\n");
239  return AVERROR(EINVAL);
240  }
241 
242  if (i == s->nb_freq - 1 && s->freq[i] != 1.f) {
243  av_log(ctx, AV_LOG_ERROR, "Last frequency must be 1.\n");
244  return AVERROR(EINVAL);
245  }
246 
247  if (i && s->freq[i] < s->freq[i-1]) {
248  av_log(ctx, AV_LOG_ERROR, "Frequencies must be in increasing order.\n");
249  return AVERROR(EINVAL);
250  }
251  }
252 
253  fft_size = 1 << (av_log2(s->nb_taps) + 1);
254  s->complexf = av_calloc(fft_size * 2, sizeof(*s->complexf));
255  if (!s->complexf)
256  return AVERROR(ENOMEM);
257 
258  ret = av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 1, fft_size, &scale, 0);
259  if (ret < 0)
260  return ret;
261 
262  s->taps = av_calloc(s->nb_taps, sizeof(*s->taps));
263  if (!s->taps)
264  return AVERROR(ENOMEM);
265 
266  s->win = av_calloc(s->nb_taps, sizeof(*s->win));
267  if (!s->win)
268  return AVERROR(ENOMEM);
269 
270  generate_window_func(s->win, s->nb_taps, s->win_func, &overlap);
271 
272  lininterp(s->complexf, s->freq, s->magnitude, s->phase, s->nb_freq, fft_size / 2);
273 
274  s->tx_fn(s->tx_ctx, s->complexf + fft_size, s->complexf, sizeof(float));
275 
276  compensation = 2.f / fft_size;
277  middle = s->nb_taps / 2;
278 
279  for (int i = 0; i <= middle; i++) {
280  s->taps[ i] = s->complexf[fft_size + middle - i].re * compensation * s->win[i];
281  s->taps[middle + i] = s->complexf[fft_size + i].re * compensation * s->win[middle + i];
282  }
283 
284  s->pts = 0;
285 
286  return 0;
287 }
288 
289 static int request_frame(AVFilterLink *outlink)
290 {
291  AVFilterContext *ctx = outlink->src;
292  AudioFIRSourceContext *s = ctx->priv;
293  AVFrame *frame;
294  int nb_samples;
295 
296  nb_samples = FFMIN(s->nb_samples, s->nb_taps - s->pts);
297  if (!nb_samples)
298  return AVERROR_EOF;
299 
300  if (!(frame = ff_get_audio_buffer(outlink, nb_samples)))
301  return AVERROR(ENOMEM);
302 
303  memcpy(frame->data[0], s->taps + s->pts, nb_samples * sizeof(float));
304 
305  frame->pts = s->pts;
306  s->pts += nb_samples;
307  return ff_filter_frame(outlink, frame);
308 }
309 
310 static const AVFilterPad afirsrc_outputs[] = {
311  {
312  .name = "default",
313  .type = AVMEDIA_TYPE_AUDIO,
314  .request_frame = request_frame,
315  .config_props = config_output,
316  },
317  { NULL }
318 };
319 
321  .name = "afirsrc",
322  .description = NULL_IF_CONFIG_SMALL("Generate a FIR coefficients audio stream."),
323  .query_formats = query_formats,
324  .init = init,
325  .uninit = uninit,
326  .priv_size = sizeof(AudioFIRSourceContext),
327  .inputs = NULL,
329  .priv_class = &afirsrc_class,
330 };
static enum AVSampleFormat sample_fmts[]
Definition: adpcmenc.c:925
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
@ WFUNC_HAMMING
@ WFUNC_BNUTTALL
@ WFUNC_BHARRIS
@ WFUNC_BLACKMAN
@ WFUNC_NUTTALL
@ WFUNC_TUKEY
@ NB_WFUNC
AVFILTER_DEFINE_CLASS(afirsrc)
AVFilter ff_asrc_afirsrc
Definition: asrc_afirsrc.c:320
static void lininterp(AVComplexFloat *complexf, const float *freq, const float *magnitude, const float *phase, int m, int minterp)
Definition: asrc_afirsrc.c:188
#define FLAGS
Definition: asrc_afirsrc.c:60
static av_cold int query_formats(AVFilterContext *ctx)
Definition: asrc_afirsrc.c:127
static int request_frame(AVFilterLink *outlink)
Definition: asrc_afirsrc.c:289
static av_cold int config_output(AVFilterLink *outlink)
Definition: asrc_afirsrc.c:210
static int parse_string(char *str, float **items, int *nb_items, int *items_size)
Definition: asrc_afirsrc.c:161
static const AVOption afirsrc_options[]
Definition: asrc_afirsrc.c:62
static const AVFilterPad afirsrc_outputs[]
Definition: asrc_afirsrc.c:310
static av_cold int init(AVFilterContext *ctx)
Definition: asrc_afirsrc.c:102
static av_cold void uninit(AVFilterContext *ctx)
Definition: asrc_afirsrc.c:114
#define OFFSET(x)
Definition: asrc_afirsrc.c:59
#define av_cold
Definition: attributes.h:88
AVFrame * ff_get_audio_buffer(AVFilterLink *link, int nb_samples)
Request an audio samples buffer with a specific set of permissions.
Definition: audio.c:86
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
Main libavfilter public API header.
#define s(width, name)
Definition: cbs_vp9.c:257
#define FFMIN(a, b)
Definition: common.h:105
#define NULL
Definition: coverity.c:32
static AVFrame * frame
double av_strtod(const char *numstr, char **tail)
Parse the string in numstr and return its value as a double.
Definition: eval.c:106
simple arithmetic expression evaluator
sample_rates
sample_rate
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:587
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:286
AVFilterChannelLayouts * ff_make_format64_list(const int64_t *fmts)
Definition: formats.c:295
int ff_set_common_samplerates(AVFilterContext *ctx, AVFilterFormats *samplerates)
Definition: formats.c:575
int ff_set_common_channel_layouts(AVFilterContext *ctx, AVFilterChannelLayouts *channel_layouts)
A helper for query_formats() which sets all links to the same list of channel layouts/sample rates.
Definition: formats.c:568
@ AV_OPT_TYPE_CONST
Definition: opt.h:234
@ AV_OPT_TYPE_INT
Definition: opt.h:225
@ AV_OPT_TYPE_STRING
Definition: opt.h:229
#define AV_CH_LAYOUT_MONO
#define AVERROR_EOF
End of file.
Definition: error.h:55
#define AVERROR(e)
Definition: error.h:43
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
void * av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
Reallocate the given buffer if it is not large enough, otherwise do nothing.
Definition: mem.c:478
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:245
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
AVSampleFormat
Audio sample formats.
Definition: samplefmt.h:58
@ AV_SAMPLE_FMT_FLT
float
Definition: samplefmt.h:63
@ AV_SAMPLE_FMT_NONE
Definition: samplefmt.h:59
int i
Definition: input.c:407
#define av_log2
Definition: intmath.h:83
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
#define sinf(x)
Definition: libm.h:419
#define cosf(x)
Definition: libm.h:78
enum MovChannelLayoutTag * layouts
Definition: mov_chan.c:434
AVOptions.
formats
Definition: signature.h:48
Describe the class of an AVClass context structure.
Definition: log.h:67
float im
Definition: tx.h:28
float re
Definition: tx.h:28
A list of supported channel layouts.
Definition: formats.h:86
An instance of a filter.
Definition: avfilter.h:341
A list of supported formats for one end of a filter link.
Definition: formats.h:65
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:411
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:332
AVOption.
Definition: opt.h:248
AVComplexFloat * complexf
Definition: asrc_afirsrc.c:40
AVTXContext * tx_ctx
Definition: asrc_afirsrc.c:55
#define av_freep(p)
#define av_log(a,...)
AVFormatContext * ctx
Definition: movenc.c:48
av_cold void av_tx_uninit(AVTXContext **ctx)
Frees a context and sets ctx to NULL, does nothing when ctx == NULL.
Definition: tx.c:146
av_cold int av_tx_init(AVTXContext **ctx, av_tx_fn *tx, enum AVTXType type, int inv, int len, const void *scale, uint64_t flags)
Initialize a transform context with the given configuration (i)MDCTs with an odd length are currently...
Definition: tx.c:160
@ AV_TX_FLOAT_FFT
Standard complex to complex FFT with sample data type AVComplexFloat.
Definition: tx.h:45
void(* av_tx_fn)(AVTXContext *s, void *out, void *in, ptrdiff_t stride)
Function pointer to a function to perform the transform.
Definition: tx.h:99
#define mg
static void generate_window_func(float *lut, int N, int win_func, float *overlap)
Definition: window_func.h:36
@ WFUNC_BARTLETT
Definition: window_func.h:29
@ WFUNC_POISSON
Definition: window_func.h:32
@ WFUNC_GAUSS
Definition: window_func.h:31
@ WFUNC_CAUCHY
Definition: window_func.h:32
@ WFUNC_RECT
Definition: window_func.h:28
@ WFUNC_BOHMAN
Definition: window_func.h:33
@ WFUNC_WELCH
Definition: window_func.h:29
@ WFUNC_PARZEN
Definition: window_func.h:32
@ WFUNC_DOLPH
Definition: window_func.h:32
@ WFUNC_HANNING
Definition: window_func.h:28
@ WFUNC_SINE
Definition: window_func.h:30
@ WFUNC_BHANN
Definition: window_func.h:31
@ WFUNC_LANCZOS
Definition: window_func.h:31
@ WFUNC_FLATTOP
Definition: window_func.h:29