1 /*
2 * Copyright (c) 2012-2013 Clément Bœsch
3 * Copyright (c) 2013 Rudolf Polzer <divverent@xonotic.org>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * audio to spectrum (video) transmedia filter, based on ffplay rdft showmode
25 * (by Michael Niedermayer) and lavfi/avf_showwaves (by Stefano Sabatini).
26 */
27
28 #include <math.h>
29
36
42
50 int sliding;
///< 1 if sliding mode, 0 otherwise
51 int mode;
///< channel display mode
55 int xpos;
///< x position (current column)
57 int rdft_bits;
///< number of bits (RDFT window size = 1<<rdft_bits)
63
64 #define OFFSET(x) offsetof(ShowSpectrumContext, x)
65 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
66
91 };
92
94
95 static const struct {
98 { 0, 0, 0, 0 },
99 { 0.13, .03587126228984074, .1573300977624594, -.02548747583751842 },
100 { 0.30, .18572281794568020, .1772436246393981, .17475554840414750 },
101 { 0.60, .28184980583656130, -.1593064119945782, .47132074554608920 },
102 { 0.73, .65830621175547810, -.3716070802232764, .24352759331252930 },
103 { 0.78, .76318535758242900, -.4307467689263783, .16866496622310430 },
104 { 0.91, .95336363636363640, -.2045454545454546, .03313636363636363 },
105 { 1, 1, 0, 0 }
106 };
107
109 {
111 int i;
112
120 }
121
123 {
130
131 /* set input audio formats */
133 if (!formats)
136
138 if (!layouts)
141
143 if (!formats)
146
147 /* set output video format */
149 if (!formats)
152
153 return 0;
154 }
155
157 {
161 int i, rdft_bits, win_size, h;
162
165
168
169 /* RDFT window size (precision) according to the requested output frame height */
170 for (rdft_bits = 1; 1 << rdft_bits < 2 * h; rdft_bits++);
171 win_size = 1 << rdft_bits;
172
173 /* (re-)configuration if the video output changed (or first init) */
175 size_t rdft_size, rdft_listsize;
177
182 "The window size might be too high.\n");
184 }
186
187 /* RDFT buffers: x2 for each (display) channel buffer.
188 * Note: we use free and malloc instead of a realloc-like function to
189 * make sure the buffer is aligned in memory for the FFT functions. */
194
199 win_size, &rdft_size) < 0)
208 }
209
210 /* pre-calc windowing function */
218 for (i = 0; i < win_size; i++)
220 break;
222 for (i = 0; i < win_size; i++)
224 break;
226 for (i = 0; i < win_size; i++)
228 break;
230 for (i = 0; i < win_size; i++)
232 break;
233 }
234 default:
236 }
237
238 /* prepare the initial picref buffer (black frame) */
242 if (!outpicref)
245 for (i = 0; i < outlink->
h; i++) {
246 memset(outpicref->
data[0] + i * outpicref->
linesize[0], 0, outlink->
w);
247 memset(outpicref->
data[1] + i * outpicref->
linesize[1], 128, outlink->
w);
248 memset(outpicref->
data[2] + i * outpicref->
linesize[2], 128, outlink->
w);
249 }
250 }
251
252 if (s->
xpos >= outlink->
w)
254
258
260 win_size;
261
265
267 s->
w, s->
h, win_size);
268 return 0;
269 }
270
272 {
275 unsigned i;
277
279 do {
283 for (i = 0; i < outlink->
h; i++) {
287 }
291 }
293
295 }
296
298 {
304
305 /* nb_freq contains the power of two superior or equal to the output image
306 * height (or half the RDFT window size) */
307 const int nb_freq = 1 << (s->
rdft_bits - 1);
308 const int win_size = nb_freq << 1;
309 const double w = 1. / (sqrt(nb_freq) * 32768.);
311
313
315
316 /* fill RDFT input with the number of samples available */
319
320 for (n = 0; n < win_size; n++)
322 }
323
324 /* TODO reindent */
325
326 /* run RDFT on each samples set */
329
330 /* fill a new spectrum column */
331 #define RE(y, ch) s->rdft_data[ch][2 * (y) + 0]
332 #define IM(y, ch) s->rdft_data[ch][2 * (y) + 1]
333 #define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch))
334
335 /* initialize buffer for combining to black */
336 for (y = 0; y < outlink->
h; y++) {
340 }
341
343 float yf, uf, vf;
344
345 /* decide color range */
348 // reduce range by channel count
352 uf = yf;
353 vf = yf;
354 break;
356 /* adjust saturation for mixed UV coloring */
357 /* this factor is correct for infinite channels, an approximation otherwise */
360 break;
361 default:
363 }
364 break;
366 // full range
367 yf = 256.0f;
368 uf = 256.0f;
369 vf = 256.0f;
370 break;
371 default:
373 }
374
379 } else {
380 uf = 0.0f;
381 vf = 0.0f;
382 }
383 }
386
387 /* draw the channel */
388 for (y = 0; y < h; y++) {
391
392 /* get magnitude */
394
395 /* apply scale */
398 break;
400 a = sqrt(a);
401 break;
404 break;
406 a = 1 - log(
FFMAX(
FFMIN(1, a), 1e-6)) / log(1e-6);
// zero = -120dBFS
407 break;
408 default:
410 }
411
414 int i;
415
418 break;
419 // i now is the first item >= the color
420 // now we know to interpolate between item i - 1 and i
429 } else {
432 float lerpfrac = (a -
start) / (end - start);
439 }
440
441 out[0] += y * yf;
442 out[1] += u * uf;
443 out[2] += v * vf;
444 } else {
445 out[0] += a * yf;
446 out[1] += a * uf;
447 out[2] += a * vf;
448 }
449 }
450 }
451
452 /* copy to output */
454 for (plane = 0; plane < 3; plane++) {
455 for (y = 0; y < outlink->
h; y++) {
458 memmove(p, p + 1, outlink->
w - 1);
459 }
460 }
461 s->
xpos = outlink->
w - 1;
462 }
463 for (plane = 0; plane < 3; plane++) {
465 (outlink->
h - 1) * outpicref->
linesize[plane] +
467 for (y = 0; y < outlink->
h; y++) {
470 }
471 }
472
474 outpicref->
pts = insamples->
pts;
475
477 if (s->
xpos >= outlink->
w)
482 if (ret < 0)
484 }
485
486 return win_size;
487 }
488
490 {
495
499
502 }
503
505 {
509 },
511 };
512
514 {
519 },
521 };
522
524 .
name =
"showspectrum",
529 .
inputs = showspectrum_inputs,
530 .
outputs = showspectrum_outputs,
531 .priv_class = &showspectrum_class,
532 };