1 /*
2 * Copyright (c) 2012 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
40
48 int sliding;
///< 1 if sliding mode, 0 otherwise
53 int xpos;
///< x position (current column)
55 int rdft_bits;
///< number of bits (RDFT window size = 1<<rdft_bits)
57 int filled;
///< number of samples (per channel) filled in current rdft_buffer
58 int consumed;
///< number of samples (per channel) consumed from the input frame
62
63 #define OFFSET(x) offsetof(ShowSpectrumContext, x)
64 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
65
82 { NULL }
83 };
84
86
87 static const struct {
90 { 0, 0, 0, 0 },
91 { 0.13, .03587126228984074, .1573300977624594, -.02548747583751842 },
92 { 0.30, .18572281794568020, .1772436246393981, .17475554840414750 },
93 { 0.60, .28184980583656130, -.1593064119945782, .47132074554608920 },
94 { 0.73, .65830621175547810, -.3716070802232764, .24352759331252930 },
95 { 0.78, .76318535758242900, -.4307467689263783, .16866496622310430 },
96 { 0.91, .95336363636363640, -.2045454545454546, .03313636363636363 },
97 { 1, 1, 0, 0 }
98 };
99
101 {
103 int i;
104
112 }
113
115 {
122
123 /* set input audio formats */
125 if (!formats)
128
130 if (!layouts)
133
135 if (!formats)
138
139 /* set output video format */
141 if (!formats)
144
145 return 0;
146 }
147
149 {
153 int i, rdft_bits, win_size, h;
154
157
160
161 /* RDFT window size (precision) according to the requested output frame height */
162 for (rdft_bits = 1; 1 << rdft_bits < 2 * h; rdft_bits++);
163 win_size = 1 << rdft_bits;
164
165 /* (re-)configuration if the video output changed (or first init) */
167 size_t rdft_size, rdft_listsize;
169
173
174 /* RDFT buffers: x2 for each (display) channel buffer.
175 * Note: we use free and malloc instead of a realloc-like function to
176 * make sure the buffer is aligned in memory for the FFT functions. */
181
186 win_size, &rdft_size) < 0)
195 }
197
198 /* pre-calc windowing function (hann here) */
204 for (i = 0; i < win_size; i++)
206
207 /* prepare the initial picref buffer (black frame) */
211 if (!outpicref)
214 for (i = 0; i < outlink->
h; i++) {
215 memset(outpicref->
data[0] + i * outpicref->
linesize[0], 0, outlink->
w);
216 memset(outpicref->
data[1] + i * outpicref->
linesize[1], 128, outlink->
w);
217 memset(outpicref->
data[2] + i * outpicref->
linesize[2], 128, outlink->
w);
218 }
219 }
220
221 if (s->
xpos >= outlink->
w)
223
227
229 s->
w, s->
h, win_size);
230 return 0;
231 }
232
234 {
236
238 if (s->
xpos >= outlink->
w)
242
244 }
245
247 {
251
253 do {
256
260 }
261
263 {
269
270 /* nb_freq contains the power of two superior or equal to the output image
271 * height (or half the RDFT window size) */
272 const int nb_freq = 1 << (s->
rdft_bits - 1);
273 const int win_size = nb_freq << 1;
274 const double w = 1. / (sqrt(nb_freq) * 32768.);
275
278 const int add_samples =
FFMIN(win_size - start, nb_samples);
279
280 /* fill RDFT input with the number of samples available */
283
285 for (n = 0; n < add_samples; n++)
287 }
289
290 /* complete RDFT window size? */
291 if (s->
filled == win_size) {
292
293 /* channel height */
295
296 /* run RDFT on each samples set */
299
300 /* fill a new spectrum column */
301 #define RE(y, ch) s->rdft_data[ch][2 * y + 0]
302 #define IM(y, ch) s->rdft_data[ch][2 * y + 1]
303 #define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch))
304
305 /* initialize buffer for combining to black */
306 for (y = 0; y < outlink->
h; y++) {
310 }
311
313 float yf, uf, vf;
314
315 /* decide color range */
318 // reduce range by channel count
322 uf = yf;
323 vf = yf;
324 break;
326 /* adjust saturation for mixed UV coloring */
327 /* this factor is correct for infinite channels, an approximation otherwise */
330 break;
331 default:
333 }
334 break;
336 // full range
337 yf = 256.0f;
338 uf = 256.0f;
339 vf = 256.0f;
340 break;
341 default:
343 }
344
349 } else {
350 uf = 0.0f;
351 vf = 0.0f;
352 }
353 }
356
357 /* draw the channel */
358 for (y = 0; y < h; y++) {
361
362 /* get magnitude */
364
365 /* apply scale */
368 break;
370 a = sqrt(a);
371 break;
374 break;
376 a = 1 - log(
FFMAX(
FFMIN(1, a), 1e-6)) / log(1e-6);
// zero = -120dBFS
377 break;
378 default:
380 }
381
384 int i;
385
388 break;
389 // i now is the first item >= the color
390 // now we know to interpolate between item i - 1 and i
399 } else {
402 float lerpfrac = (a -
start) / (end - start);
409 }
410
411 out[0] += y * yf;
412 out[1] += u * uf;
413 out[2] += v * vf;
414 } else {
415 out[0] += a * yf;
416 out[1] += a * uf;
417 out[2] += a * vf;
418 }
419 }
420 }
421
422 /* copy to output */
424 for (plane = 0; plane < 3; plane++) {
425 for (y = 0; y < outlink->
h; y++) {
428 memmove(p, p + 1, outlink->
w - 1);
429 }
430 }
431 s->
xpos = outlink->
w - 1;
432 }
433 for (plane = 0; plane < 3; plane++) {
435 (outlink->
h - 1) * outpicref->
linesize[plane] +
437 for (y = 0; y < outlink->
h; y++) {
440 }
441 }
442
443 outpicref->
pts = insamples->
pts +
448 if (ret < 0)
450 }
451
452 return add_samples;
453 }
454
456 {
460
462 while (left_samples) {
464 if (ret < 0)
465 break;
468 }
469
472 }
473
475 {
479 },
480 { NULL }
481 };
482
484 {
489 },
490 { NULL }
491 };
492
494 .
name =
"showspectrum",
499 .
inputs = showspectrum_inputs,
500 .
outputs = showspectrum_outputs,
501 .priv_class = &showspectrum_class,
502 };