LCOV - code coverage report
Current view: top level - libavfilter - avf_showspectrum.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 630 0.0 %
Date: 2017-12-18 20:14:19 Functions: 0 18 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2012-2013 Clément Bœsch
       3             :  * Copyright (c) 2013 Rudolf Polzer <divverent@xonotic.org>
       4             :  * Copyright (c) 2015 Paul B Mahol
       5             :  *
       6             :  * This file is part of FFmpeg.
       7             :  *
       8             :  * FFmpeg is free software; you can redistribute it and/or
       9             :  * modify it under the terms of the GNU Lesser General Public
      10             :  * License as published by the Free Software Foundation; either
      11             :  * version 2.1 of the License, or (at your option) any later version.
      12             :  *
      13             :  * FFmpeg is distributed in the hope that it will be useful,
      14             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :  * Lesser General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU Lesser General Public
      19             :  * License along with FFmpeg; if not, write to the Free Software
      20             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      21             :  */
      22             : 
      23             : /**
      24             :  * @file
      25             :  * audio to spectrum (video) transmedia filter, based on ffplay rdft showmode
      26             :  * (by Michael Niedermayer) and lavfi/avf_showwaves (by Stefano Sabatini).
      27             :  */
      28             : 
      29             : #include <math.h>
      30             : 
      31             : #include "libavcodec/avfft.h"
      32             : #include "libavutil/audio_fifo.h"
      33             : #include "libavutil/avassert.h"
      34             : #include "libavutil/avstring.h"
      35             : #include "libavutil/channel_layout.h"
      36             : #include "libavutil/opt.h"
      37             : #include "libavutil/xga_font_data.h"
      38             : #include "audio.h"
      39             : #include "video.h"
      40             : #include "avfilter.h"
      41             : #include "internal.h"
      42             : #include "window_func.h"
      43             : 
      44             : enum DisplayMode  { COMBINED, SEPARATE, NB_MODES };
      45             : enum DataMode     { D_MAGNITUDE, D_PHASE, NB_DMODES };
      46             : enum DisplayScale { LINEAR, SQRT, CBRT, LOG, FOURTHRT, FIFTHRT, NB_SCALES };
      47             : enum ColorMode    { CHANNEL, INTENSITY, RAINBOW, MORELAND, NEBULAE, FIRE, FIERY, FRUIT, COOL, NB_CLMODES };
      48             : enum SlideMode    { REPLACE, SCROLL, FULLFRAME, RSCROLL, NB_SLIDES };
      49             : enum Orientation  { VERTICAL, HORIZONTAL, NB_ORIENTATIONS };
      50             : 
      51             : typedef struct ShowSpectrumContext {
      52             :     const AVClass *class;
      53             :     int w, h;
      54             :     AVFrame *outpicref;
      55             :     int nb_display_channels;
      56             :     int orientation;
      57             :     int channel_width;
      58             :     int channel_height;
      59             :     int sliding;                ///< 1 if sliding mode, 0 otherwise
      60             :     int mode;                   ///< channel display mode
      61             :     int color_mode;             ///< display color scheme
      62             :     int scale;
      63             :     float saturation;           ///< color saturation multiplier
      64             :     float rotation;             ///< color rotation
      65             :     int data;
      66             :     int xpos;                   ///< x position (current column)
      67             :     FFTContext **fft;           ///< Fast Fourier Transform context
      68             :     int fft_bits;               ///< number of bits (FFT window size = 1<<fft_bits)
      69             :     FFTComplex **fft_data;      ///< bins holder for each (displayed) channels
      70             :     float *window_func_lut;     ///< Window function LUT
      71             :     float **magnitudes;
      72             :     float **phases;
      73             :     int win_func;
      74             :     int win_size;
      75             :     double win_scale;
      76             :     float overlap;
      77             :     float gain;
      78             :     int hop_size;
      79             :     float *combine_buffer;      ///< color combining buffer (3 * h items)
      80             :     float **color_buffer;       ///< color buffer (3 * h * ch items)
      81             :     AVAudioFifo *fifo;
      82             :     int64_t pts;
      83             :     int single_pic;
      84             :     int legend;
      85             :     int start_x, start_y;
      86             : } ShowSpectrumContext;
      87             : 
      88             : #define OFFSET(x) offsetof(ShowSpectrumContext, x)
      89             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
      90             : 
      91             : static const AVOption showspectrum_options[] = {
      92             :     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS },
      93             :     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS },
      94             :     { "slide", "set sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_SLIDES-1, FLAGS, "slide" },
      95             :         { "replace", "replace old columns with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, "slide" },
      96             :         { "scroll", "scroll from right to left", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, "slide" },
      97             :         { "fullframe", "return full frames", 0, AV_OPT_TYPE_CONST, {.i64=FULLFRAME}, 0, 0, FLAGS, "slide" },
      98             :         { "rscroll", "scroll from left to right", 0, AV_OPT_TYPE_CONST, {.i64=RSCROLL}, 0, 0, FLAGS, "slide" },
      99             :     { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, COMBINED, NB_MODES-1, FLAGS, "mode" },
     100             :         { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" },
     101             :         { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" },
     102             :     { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=CHANNEL}, CHANNEL, NB_CLMODES-1, FLAGS, "color" },
     103             :         { "channel",   "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL},   0, 0, FLAGS, "color" },
     104             :         { "intensity", "intensity based coloring",        0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" },
     105             :         { "rainbow",   "rainbow based coloring",          0, AV_OPT_TYPE_CONST, {.i64=RAINBOW},   0, 0, FLAGS, "color" },
     106             :         { "moreland",  "moreland based coloring",         0, AV_OPT_TYPE_CONST, {.i64=MORELAND},  0, 0, FLAGS, "color" },
     107             :         { "nebulae",   "nebulae based coloring",          0, AV_OPT_TYPE_CONST, {.i64=NEBULAE},   0, 0, FLAGS, "color" },
     108             :         { "fire",      "fire based coloring",             0, AV_OPT_TYPE_CONST, {.i64=FIRE},      0, 0, FLAGS, "color" },
     109             :         { "fiery",     "fiery based coloring",            0, AV_OPT_TYPE_CONST, {.i64=FIERY},     0, 0, FLAGS, "color" },
     110             :         { "fruit",     "fruit based coloring",            0, AV_OPT_TYPE_CONST, {.i64=FRUIT},     0, 0, FLAGS, "color" },
     111             :         { "cool",      "cool based coloring",             0, AV_OPT_TYPE_CONST, {.i64=COOL},      0, 0, FLAGS, "color" },
     112             :     { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=SQRT}, LINEAR, NB_SCALES-1, FLAGS, "scale" },
     113             :         { "lin",  "linear",      0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" },
     114             :         { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT},   0, 0, FLAGS, "scale" },
     115             :         { "cbrt", "cubic root",  0, AV_OPT_TYPE_CONST, {.i64=CBRT},   0, 0, FLAGS, "scale" },
     116             :         { "log",  "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG},    0, 0, FLAGS, "scale" },
     117             :         { "4thrt","4th root",    0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, "scale" },
     118             :         { "5thrt","5th root",    0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT},  0, 0, FLAGS, "scale" },
     119             :     { "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS },
     120             :     { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64 = WFUNC_HANNING}, 0, NB_WFUNC-1, FLAGS, "win_func" },
     121             :         { "rect",     "Rectangular",      0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT},     0, 0, FLAGS, "win_func" },
     122             :         { "bartlett", "Bartlett",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, FLAGS, "win_func" },
     123             :         { "hann",     "Hann",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, FLAGS, "win_func" },
     124             :         { "hanning",  "Hanning",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, FLAGS, "win_func" },
     125             :         { "hamming",  "Hamming",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING},  0, 0, FLAGS, "win_func" },
     126             :         { "blackman", "Blackman",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, FLAGS, "win_func" },
     127             :         { "welch",    "Welch",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH},    0, 0, FLAGS, "win_func" },
     128             :         { "flattop",  "Flat-top",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP},  0, 0, FLAGS, "win_func" },
     129             :         { "bharris",  "Blackman-Harris",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS},  0, 0, FLAGS, "win_func" },
     130             :         { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, FLAGS, "win_func" },
     131             :         { "bhann",    "Bartlett-Hann",    0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN},    0, 0, FLAGS, "win_func" },
     132             :         { "sine",     "Sine",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE},     0, 0, FLAGS, "win_func" },
     133             :         { "nuttall",  "Nuttall",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL},  0, 0, FLAGS, "win_func" },
     134             :         { "lanczos",  "Lanczos",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS},  0, 0, FLAGS, "win_func" },
     135             :         { "gauss",    "Gauss",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS},    0, 0, FLAGS, "win_func" },
     136             :         { "tukey",    "Tukey",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY},    0, 0, FLAGS, "win_func" },
     137             :         { "dolph",    "Dolph-Chebyshev",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH},    0, 0, FLAGS, "win_func" },
     138             :         { "cauchy",   "Cauchy",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY},   0, 0, FLAGS, "win_func" },
     139             :         { "parzen",   "Parzen",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN},   0, 0, FLAGS, "win_func" },
     140             :         { "poisson",  "Poisson",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON},  0, 0, FLAGS, "win_func" },
     141             :     { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, "orientation" },
     142             :         { "vertical",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL},   0, 0, FLAGS, "orientation" },
     143             :         { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, "orientation" },
     144             :     { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, 1, FLAGS },
     145             :     { "gain", "set scale gain", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl = 1}, 0, 128, FLAGS },
     146             :     { "data", "set data mode", OFFSET(data), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_DMODES-1, FLAGS, "data" },
     147             :         { "magnitude", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_MAGNITUDE}, 0, 0, FLAGS, "data" },
     148             :         { "phase",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_PHASE},     0, 0, FLAGS, "data" },
     149             :     { "rotation", "color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS },
     150             :     { NULL }
     151             : };
     152             : 
     153             : AVFILTER_DEFINE_CLASS(showspectrum);
     154             : 
     155             : static const struct ColorTable {
     156             :     float a, y, u, v;
     157             : } color_table[][8] = {
     158             :     [INTENSITY] = {
     159             :     {    0,                  0,                  0,                   0 },
     160             :     { 0.13, .03587126228984074,  .1573300977624594, -.02548747583751842 },
     161             :     { 0.30, .18572281794568020,  .1772436246393981,  .17475554840414750 },
     162             :     { 0.60, .28184980583656130, -.1593064119945782,  .47132074554608920 },
     163             :     { 0.73, .65830621175547810, -.3716070802232764,  .24352759331252930 },
     164             :     { 0.78, .76318535758242900, -.4307467689263783,  .16866496622310430 },
     165             :     { 0.91, .95336363636363640, -.2045454545454546,  .03313636363636363 },
     166             :     {    1,                  1,                  0,                   0 }},
     167             :     [RAINBOW] = {
     168             :     {    0,                  0,                  0,                   0 },
     169             :     { 0.13,            44/256.,     (189-128)/256.,      (138-128)/256. },
     170             :     { 0.25,            29/256.,     (186-128)/256.,      (119-128)/256. },
     171             :     { 0.38,           119/256.,     (194-128)/256.,       (53-128)/256. },
     172             :     { 0.60,           111/256.,      (73-128)/256.,       (59-128)/256. },
     173             :     { 0.73,           205/256.,      (19-128)/256.,      (149-128)/256. },
     174             :     { 0.86,           135/256.,      (83-128)/256.,      (200-128)/256. },
     175             :     {    1,            73/256.,      (95-128)/256.,      (225-128)/256. }},
     176             :     [MORELAND] = {
     177             :     {    0,            44/256.,     (181-128)/256.,      (112-128)/256. },
     178             :     { 0.13,           126/256.,     (177-128)/256.,      (106-128)/256. },
     179             :     { 0.25,           164/256.,     (163-128)/256.,      (109-128)/256. },
     180             :     { 0.38,           200/256.,     (140-128)/256.,      (120-128)/256. },
     181             :     { 0.60,           201/256.,     (117-128)/256.,      (141-128)/256. },
     182             :     { 0.73,           177/256.,     (103-128)/256.,      (165-128)/256. },
     183             :     { 0.86,           136/256.,     (100-128)/256.,      (183-128)/256. },
     184             :     {    1,            68/256.,     (117-128)/256.,      (203-128)/256. }},
     185             :     [NEBULAE] = {
     186             :     {    0,            10/256.,     (134-128)/256.,      (132-128)/256. },
     187             :     { 0.23,            21/256.,     (137-128)/256.,      (130-128)/256. },
     188             :     { 0.45,            35/256.,     (134-128)/256.,      (134-128)/256. },
     189             :     { 0.57,            51/256.,     (130-128)/256.,      (139-128)/256. },
     190             :     { 0.67,           104/256.,     (116-128)/256.,      (162-128)/256. },
     191             :     { 0.77,           120/256.,     (105-128)/256.,      (188-128)/256. },
     192             :     { 0.87,           140/256.,     (105-128)/256.,      (188-128)/256. },
     193             :     {    1,                  1,                  0,                   0 }},
     194             :     [FIRE] = {
     195             :     {    0,                  0,                  0,                   0 },
     196             :     { 0.23,            44/256.,     (132-128)/256.,      (127-128)/256. },
     197             :     { 0.45,            62/256.,     (116-128)/256.,      (140-128)/256. },
     198             :     { 0.57,            75/256.,     (105-128)/256.,      (152-128)/256. },
     199             :     { 0.67,            95/256.,      (91-128)/256.,      (166-128)/256. },
     200             :     { 0.77,           126/256.,      (74-128)/256.,      (172-128)/256. },
     201             :     { 0.87,           164/256.,      (73-128)/256.,      (162-128)/256. },
     202             :     {    1,                  1,                  0,                   0 }},
     203             :     [FIERY] = {
     204             :     {    0,                  0,                  0,                   0 },
     205             :     { 0.23,            36/256.,     (116-128)/256.,      (163-128)/256. },
     206             :     { 0.45,            52/256.,     (102-128)/256.,      (200-128)/256. },
     207             :     { 0.57,           116/256.,      (84-128)/256.,      (196-128)/256. },
     208             :     { 0.67,           157/256.,      (67-128)/256.,      (181-128)/256. },
     209             :     { 0.77,           193/256.,      (40-128)/256.,      (155-128)/256. },
     210             :     { 0.87,           221/256.,     (101-128)/256.,      (134-128)/256. },
     211             :     {    1,                  1,                  0,                   0 }},
     212             :     [FRUIT] = {
     213             :     {    0,                  0,                  0,                   0 },
     214             :     { 0.20,            29/256.,     (136-128)/256.,      (119-128)/256. },
     215             :     { 0.30,            60/256.,     (119-128)/256.,       (90-128)/256. },
     216             :     { 0.40,            85/256.,      (91-128)/256.,       (85-128)/256. },
     217             :     { 0.50,           116/256.,      (70-128)/256.,      (105-128)/256. },
     218             :     { 0.60,           151/256.,      (50-128)/256.,      (146-128)/256. },
     219             :     { 0.70,           191/256.,      (63-128)/256.,      (178-128)/256. },
     220             :     {    1,            98/256.,      (80-128)/256.,      (221-128)/256. }},
     221             :     [COOL] = {
     222             :     {    0,                  0,                  0,                   0 },
     223             :     {  .15,                  0,                 .5,                 -.5 },
     224             :     {    1,                  1,                -.5,                  .5 }},
     225             : };
     226             : 
     227           0 : static av_cold void uninit(AVFilterContext *ctx)
     228             : {
     229           0 :     ShowSpectrumContext *s = ctx->priv;
     230             :     int i;
     231             : 
     232           0 :     av_freep(&s->combine_buffer);
     233           0 :     if (s->fft) {
     234           0 :         for (i = 0; i < s->nb_display_channels; i++)
     235           0 :             av_fft_end(s->fft[i]);
     236             :     }
     237           0 :     av_freep(&s->fft);
     238           0 :     if (s->fft_data) {
     239           0 :         for (i = 0; i < s->nb_display_channels; i++)
     240           0 :             av_freep(&s->fft_data[i]);
     241             :     }
     242           0 :     av_freep(&s->fft_data);
     243           0 :     if (s->color_buffer) {
     244           0 :         for (i = 0; i < s->nb_display_channels; i++)
     245           0 :             av_freep(&s->color_buffer[i]);
     246             :     }
     247           0 :     av_freep(&s->color_buffer);
     248           0 :     av_freep(&s->window_func_lut);
     249           0 :     if (s->magnitudes) {
     250           0 :         for (i = 0; i < s->nb_display_channels; i++)
     251           0 :             av_freep(&s->magnitudes[i]);
     252             :     }
     253           0 :     av_freep(&s->magnitudes);
     254           0 :     av_frame_free(&s->outpicref);
     255           0 :     av_audio_fifo_free(s->fifo);
     256           0 :     if (s->phases) {
     257           0 :         for (i = 0; i < s->nb_display_channels; i++)
     258           0 :             av_freep(&s->phases[i]);
     259             :     }
     260           0 :     av_freep(&s->phases);
     261           0 : }
     262             : 
     263           0 : static int query_formats(AVFilterContext *ctx)
     264             : {
     265           0 :     AVFilterFormats *formats = NULL;
     266           0 :     AVFilterChannelLayouts *layouts = NULL;
     267           0 :     AVFilterLink *inlink = ctx->inputs[0];
     268           0 :     AVFilterLink *outlink = ctx->outputs[0];
     269             :     static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
     270             :     static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE };
     271             :     int ret;
     272             : 
     273             :     /* set input audio formats */
     274           0 :     formats = ff_make_format_list(sample_fmts);
     275           0 :     if ((ret = ff_formats_ref(formats, &inlink->out_formats)) < 0)
     276           0 :         return ret;
     277             : 
     278           0 :     layouts = ff_all_channel_layouts();
     279           0 :     if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0)
     280           0 :         return ret;
     281             : 
     282           0 :     formats = ff_all_samplerates();
     283           0 :     if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0)
     284           0 :         return ret;
     285             : 
     286             :     /* set output video format */
     287           0 :     formats = ff_make_format_list(pix_fmts);
     288           0 :     if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
     289           0 :         return ret;
     290             : 
     291           0 :     return 0;
     292             : }
     293             : 
     294           0 : static int config_output(AVFilterLink *outlink)
     295             : {
     296           0 :     AVFilterContext *ctx = outlink->src;
     297           0 :     AVFilterLink *inlink = ctx->inputs[0];
     298           0 :     ShowSpectrumContext *s = ctx->priv;
     299             :     int i, fft_bits, h, w;
     300             :     float overlap;
     301             : 
     302           0 :     s->pts = AV_NOPTS_VALUE;
     303             : 
     304           0 :     if (!strcmp(ctx->filter->name, "showspectrumpic"))
     305           0 :         s->single_pic = 1;
     306             : 
     307           0 :     outlink->w = s->w;
     308           0 :     outlink->h = s->h;
     309           0 :     outlink->sample_aspect_ratio = (AVRational){1,1};
     310             : 
     311           0 :     if (s->legend) {
     312           0 :         s->start_x = log10(inlink->sample_rate) * 25;
     313           0 :         s->start_y = 64;
     314           0 :         outlink->w += s->start_x * 2;
     315           0 :         outlink->h += s->start_y * 2;
     316             :     }
     317             : 
     318           0 :     h = (s->mode == COMBINED || s->orientation == HORIZONTAL) ? s->h : s->h / inlink->channels;
     319           0 :     w = (s->mode == COMBINED || s->orientation == VERTICAL)   ? s->w : s->w / inlink->channels;
     320           0 :     s->channel_height = h;
     321           0 :     s->channel_width  = w;
     322             : 
     323           0 :     if (s->orientation == VERTICAL) {
     324             :         /* FFT window size (precision) according to the requested output frame height */
     325           0 :         for (fft_bits = 1; 1 << fft_bits < 2 * h; fft_bits++);
     326             :     } else {
     327             :         /* FFT window size (precision) according to the requested output frame width */
     328           0 :         for (fft_bits = 1; 1 << fft_bits < 2 * w; fft_bits++);
     329             :     }
     330           0 :     s->win_size = 1 << fft_bits;
     331             : 
     332           0 :     if (!s->fft) {
     333           0 :         s->fft = av_calloc(inlink->channels, sizeof(*s->fft));
     334           0 :         if (!s->fft)
     335           0 :             return AVERROR(ENOMEM);
     336             :     }
     337             : 
     338             :     /* (re-)configuration if the video output changed (or first init) */
     339           0 :     if (fft_bits != s->fft_bits) {
     340             :         AVFrame *outpicref;
     341             : 
     342           0 :         s->fft_bits = fft_bits;
     343             : 
     344             :         /* FFT buffers: x2 for each (display) channel buffer.
     345             :          * Note: we use free and malloc instead of a realloc-like function to
     346             :          * make sure the buffer is aligned in memory for the FFT functions. */
     347           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     348           0 :             av_fft_end(s->fft[i]);
     349           0 :             av_freep(&s->fft_data[i]);
     350             :         }
     351           0 :         av_freep(&s->fft_data);
     352             : 
     353           0 :         s->nb_display_channels = inlink->channels;
     354           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     355           0 :             s->fft[i] = av_fft_init(fft_bits, 0);
     356           0 :             if (!s->fft[i]) {
     357           0 :                 av_log(ctx, AV_LOG_ERROR, "Unable to create FFT context. "
     358             :                        "The window size might be too high.\n");
     359           0 :                 return AVERROR(EINVAL);
     360             :             }
     361             :         }
     362             : 
     363           0 :         s->magnitudes = av_calloc(s->nb_display_channels, sizeof(*s->magnitudes));
     364           0 :         if (!s->magnitudes)
     365           0 :             return AVERROR(ENOMEM);
     366           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     367           0 :             s->magnitudes[i] = av_calloc(s->orientation == VERTICAL ? s->h : s->w, sizeof(**s->magnitudes));
     368           0 :             if (!s->magnitudes[i])
     369           0 :                 return AVERROR(ENOMEM);
     370             :         }
     371             : 
     372           0 :         s->phases = av_calloc(s->nb_display_channels, sizeof(*s->phases));
     373           0 :         if (!s->phases)
     374           0 :             return AVERROR(ENOMEM);
     375           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     376           0 :             s->phases[i] = av_calloc(s->orientation == VERTICAL ? s->h : s->w, sizeof(**s->phases));
     377           0 :             if (!s->phases[i])
     378           0 :                 return AVERROR(ENOMEM);
     379             :         }
     380             : 
     381           0 :         av_freep(&s->color_buffer);
     382           0 :         s->color_buffer = av_calloc(s->nb_display_channels, sizeof(*s->color_buffer));
     383           0 :         if (!s->color_buffer)
     384           0 :             return AVERROR(ENOMEM);
     385           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     386           0 :             s->color_buffer[i] = av_calloc(s->orientation == VERTICAL ? s->h * 3 : s->w * 3, sizeof(**s->color_buffer));
     387           0 :             if (!s->color_buffer[i])
     388           0 :                 return AVERROR(ENOMEM);
     389             :         }
     390             : 
     391           0 :         s->fft_data = av_calloc(s->nb_display_channels, sizeof(*s->fft_data));
     392           0 :         if (!s->fft_data)
     393           0 :             return AVERROR(ENOMEM);
     394           0 :         for (i = 0; i < s->nb_display_channels; i++) {
     395           0 :             s->fft_data[i] = av_calloc(s->win_size, sizeof(**s->fft_data));
     396           0 :             if (!s->fft_data[i])
     397           0 :                 return AVERROR(ENOMEM);
     398             :         }
     399             : 
     400             :         /* pre-calc windowing function */
     401           0 :         s->window_func_lut =
     402           0 :             av_realloc_f(s->window_func_lut, s->win_size,
     403             :                          sizeof(*s->window_func_lut));
     404           0 :         if (!s->window_func_lut)
     405           0 :             return AVERROR(ENOMEM);
     406           0 :         generate_window_func(s->window_func_lut, s->win_size, s->win_func, &overlap);
     407           0 :         if (s->overlap == 1)
     408           0 :             s->overlap = overlap;
     409           0 :         s->hop_size = (1. - s->overlap) * s->win_size;
     410           0 :         if (s->hop_size < 1) {
     411           0 :             av_log(ctx, AV_LOG_ERROR, "overlap %f too big\n", s->overlap);
     412           0 :             return AVERROR(EINVAL);
     413             :         }
     414             : 
     415           0 :         for (s->win_scale = 0, i = 0; i < s->win_size; i++) {
     416           0 :             s->win_scale += s->window_func_lut[i] * s->window_func_lut[i];
     417             :         }
     418           0 :         s->win_scale = 1. / sqrt(s->win_scale);
     419             : 
     420             :         /* prepare the initial picref buffer (black frame) */
     421           0 :         av_frame_free(&s->outpicref);
     422           0 :         s->outpicref = outpicref =
     423           0 :             ff_get_video_buffer(outlink, outlink->w, outlink->h);
     424           0 :         if (!outpicref)
     425           0 :             return AVERROR(ENOMEM);
     426           0 :         outpicref->sample_aspect_ratio = (AVRational){1,1};
     427           0 :         for (i = 0; i < outlink->h; i++) {
     428           0 :             memset(outpicref->data[0] + i * outpicref->linesize[0],   0, outlink->w);
     429           0 :             memset(outpicref->data[1] + i * outpicref->linesize[1], 128, outlink->w);
     430           0 :             memset(outpicref->data[2] + i * outpicref->linesize[2], 128, outlink->w);
     431             :         }
     432           0 :         outpicref->color_range = AVCOL_RANGE_JPEG;
     433             :     }
     434             : 
     435           0 :     if ((s->orientation == VERTICAL   && s->xpos >= s->w) ||
     436           0 :         (s->orientation == HORIZONTAL && s->xpos >= s->h))
     437           0 :         s->xpos = 0;
     438             : 
     439           0 :     outlink->frame_rate = av_make_q(inlink->sample_rate, s->win_size * (1.-s->overlap));
     440           0 :     if (s->orientation == VERTICAL && s->sliding == FULLFRAME)
     441           0 :         outlink->frame_rate.den *= s->w;
     442           0 :     if (s->orientation == HORIZONTAL && s->sliding == FULLFRAME)
     443           0 :         outlink->frame_rate.den *= s->h;
     444             : 
     445           0 :     if (s->orientation == VERTICAL) {
     446           0 :         s->combine_buffer =
     447           0 :             av_realloc_f(s->combine_buffer, s->h * 3,
     448             :                          sizeof(*s->combine_buffer));
     449             :     } else {
     450           0 :         s->combine_buffer =
     451           0 :             av_realloc_f(s->combine_buffer, s->w * 3,
     452             :                          sizeof(*s->combine_buffer));
     453             :     }
     454             : 
     455           0 :     av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d FFT window size:%d\n",
     456             :            s->w, s->h, s->win_size);
     457             : 
     458           0 :     av_audio_fifo_free(s->fifo);
     459           0 :     s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->win_size);
     460           0 :     if (!s->fifo)
     461           0 :         return AVERROR(ENOMEM);
     462           0 :     return 0;
     463             : }
     464             : 
     465           0 : static int run_channel_fft(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     466             : {
     467           0 :     ShowSpectrumContext *s = ctx->priv;
     468           0 :     const float *window_func_lut = s->window_func_lut;
     469           0 :     AVFrame *fin = arg;
     470           0 :     const int ch = jobnr;
     471             :     int n;
     472             : 
     473             :     /* fill FFT input with the number of samples available */
     474           0 :     const float *p = (float *)fin->extended_data[ch];
     475             : 
     476           0 :     for (n = 0; n < s->win_size; n++) {
     477           0 :         s->fft_data[ch][n].re = p[n] * window_func_lut[n];
     478           0 :         s->fft_data[ch][n].im = 0;
     479             :     }
     480             : 
     481             :     /* run FFT on each samples set */
     482           0 :     av_fft_permute(s->fft[ch], s->fft_data[ch]);
     483           0 :     av_fft_calc(s->fft[ch], s->fft_data[ch]);
     484             : 
     485           0 :     return 0;
     486             : }
     487             : 
     488             : #define RE(y, ch) s->fft_data[ch][y].re
     489             : #define IM(y, ch) s->fft_data[ch][y].im
     490             : #define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch))
     491             : #define PHASE(y, ch) atan2(IM(y, ch), RE(y, ch))
     492             : 
     493           0 : static int calc_channel_magnitudes(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     494             : {
     495           0 :     ShowSpectrumContext *s = ctx->priv;
     496           0 :     const double w = s->win_scale * (s->scale == LOG ? s->win_scale : 1);
     497           0 :     int y, h = s->orientation == VERTICAL ? s->h : s->w;
     498           0 :     const float f = s->gain * w;
     499           0 :     const int ch = jobnr;
     500           0 :     float *magnitudes = s->magnitudes[ch];
     501             : 
     502           0 :     for (y = 0; y < h; y++)
     503           0 :         magnitudes[y] = MAGNITUDE(y, ch) * f;
     504             : 
     505           0 :     return 0;
     506             : }
     507             : 
     508           0 : static int calc_channel_phases(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     509             : {
     510           0 :     ShowSpectrumContext *s = ctx->priv;
     511           0 :     const int h = s->orientation == VERTICAL ? s->h : s->w;
     512           0 :     const int ch = jobnr;
     513           0 :     float *phases = s->phases[ch];
     514             :     int y;
     515             : 
     516           0 :     for (y = 0; y < h; y++)
     517           0 :         phases[y] = (PHASE(y, ch) / M_PI + 1) / 2;
     518             : 
     519           0 :     return 0;
     520             : }
     521             : 
     522           0 : static void acalc_magnitudes(ShowSpectrumContext *s)
     523             : {
     524           0 :     const double w = s->win_scale * (s->scale == LOG ? s->win_scale : 1);
     525           0 :     int ch, y, h = s->orientation == VERTICAL ? s->h : s->w;
     526           0 :     const float f = s->gain * w;
     527             : 
     528           0 :     for (ch = 0; ch < s->nb_display_channels; ch++) {
     529           0 :         float *magnitudes = s->magnitudes[ch];
     530             : 
     531           0 :         for (y = 0; y < h; y++)
     532           0 :             magnitudes[y] += MAGNITUDE(y, ch) * f;
     533             :     }
     534           0 : }
     535             : 
     536           0 : static void scale_magnitudes(ShowSpectrumContext *s, float scale)
     537             : {
     538           0 :     int ch, y, h = s->orientation == VERTICAL ? s->h : s->w;
     539             : 
     540           0 :     for (ch = 0; ch < s->nb_display_channels; ch++) {
     541           0 :         float *magnitudes = s->magnitudes[ch];
     542             : 
     543           0 :         for (y = 0; y < h; y++)
     544           0 :             magnitudes[y] *= scale;
     545             :     }
     546           0 : }
     547             : 
     548           0 : static void color_range(ShowSpectrumContext *s, int ch,
     549             :                         float *yf, float *uf, float *vf)
     550             : {
     551           0 :     switch (s->mode) {
     552           0 :     case COMBINED:
     553             :         // reduce range by channel count
     554           0 :         *yf = 256.0f / s->nb_display_channels;
     555           0 :         switch (s->color_mode) {
     556           0 :         case RAINBOW:
     557             :         case MORELAND:
     558             :         case NEBULAE:
     559             :         case FIRE:
     560             :         case FIERY:
     561             :         case FRUIT:
     562             :         case COOL:
     563             :         case INTENSITY:
     564           0 :             *uf = *yf;
     565           0 :             *vf = *yf;
     566           0 :             break;
     567           0 :         case CHANNEL:
     568             :             /* adjust saturation for mixed UV coloring */
     569             :             /* this factor is correct for infinite channels, an approximation otherwise */
     570           0 :             *uf = *yf * M_PI;
     571           0 :             *vf = *yf * M_PI;
     572           0 :             break;
     573           0 :         default:
     574           0 :             av_assert0(0);
     575             :         }
     576           0 :         break;
     577           0 :     case SEPARATE:
     578             :         // full range
     579           0 :         *yf = 256.0f;
     580           0 :         *uf = 256.0f;
     581           0 :         *vf = 256.0f;
     582           0 :         break;
     583           0 :     default:
     584           0 :         av_assert0(0);
     585             :     }
     586             : 
     587           0 :     if (s->color_mode == CHANNEL) {
     588           0 :         if (s->nb_display_channels > 1) {
     589           0 :             *uf *= 0.5 * sin((2 * M_PI * ch) / s->nb_display_channels + M_PI * s->rotation);
     590           0 :             *vf *= 0.5 * cos((2 * M_PI * ch) / s->nb_display_channels + M_PI * s->rotation);
     591             :         } else {
     592           0 :             *uf *= 0.5 * sin(M_PI * s->rotation);
     593           0 :             *vf *= 0.5 * cos(M_PI * s->rotation + M_PI_2);
     594             :         }
     595             :     } else {
     596           0 :         *uf += *uf * sin(M_PI * s->rotation);
     597           0 :         *vf += *vf * cos(M_PI * s->rotation + M_PI_2);
     598             :     }
     599             : 
     600           0 :     *uf *= s->saturation;
     601           0 :     *vf *= s->saturation;
     602           0 : }
     603             : 
     604           0 : static void pick_color(ShowSpectrumContext *s,
     605             :                        float yf, float uf, float vf,
     606             :                        float a, float *out)
     607             : {
     608           0 :     if (s->color_mode > CHANNEL) {
     609           0 :         const int cm = s->color_mode;
     610             :         float y, u, v;
     611             :         int i;
     612             : 
     613           0 :         for (i = 1; i < FF_ARRAY_ELEMS(color_table[cm]) - 1; i++)
     614           0 :             if (color_table[cm][i].a >= a)
     615           0 :                 break;
     616             :         // i now is the first item >= the color
     617             :         // now we know to interpolate between item i - 1 and i
     618           0 :         if (a <= color_table[cm][i - 1].a) {
     619           0 :             y = color_table[cm][i - 1].y;
     620           0 :             u = color_table[cm][i - 1].u;
     621           0 :             v = color_table[cm][i - 1].v;
     622           0 :         } else if (a >= color_table[cm][i].a) {
     623           0 :             y = color_table[cm][i].y;
     624           0 :             u = color_table[cm][i].u;
     625           0 :             v = color_table[cm][i].v;
     626             :         } else {
     627           0 :             float start = color_table[cm][i - 1].a;
     628           0 :             float end = color_table[cm][i].a;
     629           0 :             float lerpfrac = (a - start) / (end - start);
     630           0 :             y = color_table[cm][i - 1].y * (1.0f - lerpfrac)
     631           0 :               + color_table[cm][i].y * lerpfrac;
     632           0 :             u = color_table[cm][i - 1].u * (1.0f - lerpfrac)
     633           0 :               + color_table[cm][i].u * lerpfrac;
     634           0 :             v = color_table[cm][i - 1].v * (1.0f - lerpfrac)
     635           0 :               + color_table[cm][i].v * lerpfrac;
     636             :         }
     637             : 
     638           0 :         out[0] = y * yf;
     639           0 :         out[1] = u * uf;
     640           0 :         out[2] = v * vf;
     641             :     } else {
     642           0 :         out[0] = a * yf;
     643           0 :         out[1] = a * uf;
     644           0 :         out[2] = a * vf;
     645             :     }
     646           0 : }
     647             : 
     648           0 : static void clear_combine_buffer(ShowSpectrumContext *s, int size)
     649             : {
     650             :     int y;
     651             : 
     652           0 :     for (y = 0; y < size; y++) {
     653           0 :         s->combine_buffer[3 * y    ] = 0;
     654           0 :         s->combine_buffer[3 * y + 1] = 127.5;
     655           0 :         s->combine_buffer[3 * y + 2] = 127.5;
     656             :     }
     657           0 : }
     658             : 
     659           0 : static int plot_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     660             : {
     661           0 :     ShowSpectrumContext *s = ctx->priv;
     662           0 :     const int h = s->orientation == VERTICAL ? s->channel_height : s->channel_width;
     663           0 :     const int ch = jobnr;
     664           0 :     float *magnitudes = s->magnitudes[ch];
     665           0 :     float *phases = s->phases[ch];
     666             :     float yf, uf, vf;
     667             :     int y;
     668             : 
     669             :     /* decide color range */
     670           0 :     color_range(s, ch, &yf, &uf, &vf);
     671             : 
     672             :     /* draw the channel */
     673           0 :     for (y = 0; y < h; y++) {
     674           0 :         int row = (s->mode == COMBINED) ? y : ch * h + y;
     675           0 :         float *out = &s->color_buffer[ch][3 * row];
     676             :         float a;
     677             : 
     678           0 :         switch (s->data) {
     679           0 :         case D_MAGNITUDE:
     680             :             /* get magnitude */
     681           0 :             a = magnitudes[y];
     682           0 :             break;
     683           0 :         case D_PHASE:
     684             :             /* get phase */
     685           0 :             a = phases[y];
     686           0 :             break;
     687           0 :         default:
     688           0 :             av_assert0(0);
     689             :         }
     690             : 
     691             :         /* apply scale */
     692           0 :         switch (s->scale) {
     693           0 :         case LINEAR:
     694           0 :             a = av_clipf(a, 0, 1);
     695           0 :             break;
     696           0 :         case SQRT:
     697           0 :             a = av_clipf(sqrt(a), 0, 1);
     698           0 :             break;
     699           0 :         case CBRT:
     700           0 :             a = av_clipf(cbrt(a), 0, 1);
     701           0 :             break;
     702           0 :         case FOURTHRT:
     703           0 :             a = av_clipf(sqrt(sqrt(a)), 0, 1);
     704           0 :             break;
     705           0 :         case FIFTHRT:
     706           0 :             a = av_clipf(pow(a, 0.20), 0, 1);
     707           0 :             break;
     708           0 :         case LOG:
     709           0 :             a = 1 + log10(av_clipd(a, 1e-6, 1)) / 6; // zero = -120dBFS
     710           0 :             break;
     711           0 :         default:
     712           0 :             av_assert0(0);
     713             :         }
     714             : 
     715           0 :         pick_color(s, yf, uf, vf, a, out);
     716             :     }
     717             : 
     718           0 :     return 0;
     719             : }
     720             : 
     721           0 : static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples)
     722             : {
     723           0 :     AVFilterContext *ctx = inlink->dst;
     724           0 :     AVFilterLink *outlink = ctx->outputs[0];
     725           0 :     ShowSpectrumContext *s = ctx->priv;
     726           0 :     AVFrame *outpicref = s->outpicref;
     727           0 :     int ret, plane, x, y, z = s->orientation == VERTICAL ? s->h : s->w;
     728             : 
     729             :     /* fill a new spectrum column */
     730             :     /* initialize buffer for combining to black */
     731           0 :     clear_combine_buffer(s, z);
     732             : 
     733           0 :     ctx->internal->execute(ctx, plot_channel, NULL, NULL, s->nb_display_channels);
     734             : 
     735           0 :     for (y = 0; y < z * 3; y++) {
     736           0 :         for (x = 0; x < s->nb_display_channels; x++) {
     737           0 :             s->combine_buffer[y] += s->color_buffer[x][y];
     738             :         }
     739             :     }
     740             : 
     741           0 :     av_frame_make_writable(s->outpicref);
     742             :     /* copy to output */
     743           0 :     if (s->orientation == VERTICAL) {
     744           0 :         if (s->sliding == SCROLL) {
     745           0 :             for (plane = 0; plane < 3; plane++) {
     746           0 :                 for (y = 0; y < s->h; y++) {
     747           0 :                     uint8_t *p = outpicref->data[plane] +
     748           0 :                                  y * outpicref->linesize[plane];
     749           0 :                     memmove(p, p + 1, s->w - 1);
     750             :                 }
     751             :             }
     752           0 :             s->xpos = s->w - 1;
     753           0 :         } else if (s->sliding == RSCROLL) {
     754           0 :             for (plane = 0; plane < 3; plane++) {
     755           0 :                 for (y = 0; y < s->h; y++) {
     756           0 :                     uint8_t *p = outpicref->data[plane] +
     757           0 :                                  y * outpicref->linesize[plane];
     758           0 :                     memmove(p + 1, p, s->w - 1);
     759             :                 }
     760             :             }
     761           0 :             s->xpos = 0;
     762             :         }
     763           0 :         for (plane = 0; plane < 3; plane++) {
     764           0 :             uint8_t *p = outpicref->data[plane] + s->start_x +
     765           0 :                          (outlink->h - 1 - s->start_y) * outpicref->linesize[plane] +
     766           0 :                          s->xpos;
     767           0 :             for (y = 0; y < s->h; y++) {
     768           0 :                 *p = lrintf(av_clipf(s->combine_buffer[3 * y + plane], 0, 255));
     769           0 :                 p -= outpicref->linesize[plane];
     770             :             }
     771             :         }
     772             :     } else {
     773           0 :         if (s->sliding == SCROLL) {
     774           0 :             for (plane = 0; plane < 3; plane++) {
     775           0 :                 for (y = 1; y < s->h; y++) {
     776           0 :                     memmove(outpicref->data[plane] + (y-1) * outpicref->linesize[plane],
     777           0 :                             outpicref->data[plane] + (y  ) * outpicref->linesize[plane],
     778           0 :                             s->w);
     779             :                 }
     780             :             }
     781           0 :             s->xpos = s->h - 1;
     782           0 :         } else if (s->sliding == RSCROLL) {
     783           0 :             for (plane = 0; plane < 3; plane++) {
     784           0 :                 for (y = s->h - 1; y >= 1; y--) {
     785           0 :                     memmove(outpicref->data[plane] + (y  ) * outpicref->linesize[plane],
     786           0 :                             outpicref->data[plane] + (y-1) * outpicref->linesize[plane],
     787           0 :                             s->w);
     788             :                 }
     789             :             }
     790           0 :             s->xpos = 0;
     791             :         }
     792           0 :         for (plane = 0; plane < 3; plane++) {
     793           0 :             uint8_t *p = outpicref->data[plane] + s->start_x +
     794           0 :                          (s->xpos + s->start_y) * outpicref->linesize[plane];
     795           0 :             for (x = 0; x < s->w; x++) {
     796           0 :                 *p = lrintf(av_clipf(s->combine_buffer[3 * x + plane], 0, 255));
     797           0 :                 p++;
     798             :             }
     799             :         }
     800             :     }
     801             : 
     802           0 :     if (s->sliding != FULLFRAME || s->xpos == 0)
     803           0 :         outpicref->pts = insamples->pts;
     804             : 
     805           0 :     s->xpos++;
     806           0 :     if (s->orientation == VERTICAL && s->xpos >= s->w)
     807           0 :         s->xpos = 0;
     808           0 :     if (s->orientation == HORIZONTAL && s->xpos >= s->h)
     809           0 :         s->xpos = 0;
     810           0 :     if (!s->single_pic && (s->sliding != FULLFRAME || s->xpos == 0)) {
     811           0 :         ret = ff_filter_frame(outlink, av_frame_clone(s->outpicref));
     812           0 :         if (ret < 0)
     813           0 :             return ret;
     814             :     }
     815             : 
     816           0 :     return s->win_size;
     817             : }
     818             : 
     819             : #if CONFIG_SHOWSPECTRUM_FILTER
     820             : 
     821           0 : static int request_frame(AVFilterLink *outlink)
     822             : {
     823           0 :     ShowSpectrumContext *s = outlink->src->priv;
     824           0 :     AVFilterLink *inlink = outlink->src->inputs[0];
     825             :     unsigned i;
     826             :     int ret;
     827             : 
     828           0 :     ret = ff_request_frame(inlink);
     829           0 :     if (ret == AVERROR_EOF && s->sliding == FULLFRAME && s->xpos > 0 &&
     830           0 :         s->outpicref) {
     831           0 :         if (s->orientation == VERTICAL) {
     832           0 :             for (i = 0; i < outlink->h; i++) {
     833           0 :                 memset(s->outpicref->data[0] + i * s->outpicref->linesize[0] + s->xpos,   0, outlink->w - s->xpos);
     834           0 :                 memset(s->outpicref->data[1] + i * s->outpicref->linesize[1] + s->xpos, 128, outlink->w - s->xpos);
     835           0 :                 memset(s->outpicref->data[2] + i * s->outpicref->linesize[2] + s->xpos, 128, outlink->w - s->xpos);
     836             :             }
     837             :         } else {
     838           0 :             for (i = s->xpos; i < outlink->h; i++) {
     839           0 :                 memset(s->outpicref->data[0] + i * s->outpicref->linesize[0],   0, outlink->w);
     840           0 :                 memset(s->outpicref->data[1] + i * s->outpicref->linesize[1], 128, outlink->w);
     841           0 :                 memset(s->outpicref->data[2] + i * s->outpicref->linesize[2], 128, outlink->w);
     842             :             }
     843             :         }
     844           0 :         ret = ff_filter_frame(outlink, s->outpicref);
     845           0 :         s->outpicref = NULL;
     846             :     }
     847             : 
     848           0 :     return ret;
     849             : }
     850             : 
     851           0 : static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
     852             : {
     853           0 :     AVFilterContext *ctx = inlink->dst;
     854           0 :     ShowSpectrumContext *s = ctx->priv;
     855           0 :     AVFrame *fin = NULL;
     856           0 :     int ret = 0, consumed = 0;
     857             : 
     858           0 :     if (s->pts == AV_NOPTS_VALUE)
     859           0 :         s->pts = insamples->pts - av_audio_fifo_size(s->fifo);
     860             : 
     861           0 :     av_audio_fifo_write(s->fifo, (void **)insamples->extended_data, insamples->nb_samples);
     862           0 :     av_frame_free(&insamples);
     863           0 :     while (av_audio_fifo_size(s->fifo) >= s->win_size) {
     864           0 :         fin = ff_get_audio_buffer(inlink, s->win_size);
     865           0 :         if (!fin) {
     866           0 :             ret = AVERROR(ENOMEM);
     867           0 :             goto fail;
     868             :         }
     869             : 
     870           0 :         fin->pts = s->pts + consumed;
     871           0 :         consumed += s->hop_size;
     872           0 :         ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data, s->win_size);
     873           0 :         if (ret < 0)
     874           0 :             goto fail;
     875             : 
     876           0 :         av_assert0(fin->nb_samples == s->win_size);
     877             : 
     878           0 :         ctx->internal->execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels);
     879             : 
     880           0 :         if (s->data == D_MAGNITUDE)
     881           0 :             ctx->internal->execute(ctx, calc_channel_magnitudes, NULL, NULL, s->nb_display_channels);
     882             : 
     883           0 :         if (s->data == D_PHASE)
     884           0 :             ctx->internal->execute(ctx, calc_channel_phases, NULL, NULL, s->nb_display_channels);
     885             : 
     886           0 :         ret = plot_spectrum_column(inlink, fin);
     887           0 :         av_frame_free(&fin);
     888           0 :         av_audio_fifo_drain(s->fifo, s->hop_size);
     889           0 :         if (ret < 0)
     890           0 :             goto fail;
     891             :     }
     892             : 
     893           0 : fail:
     894           0 :     s->pts = AV_NOPTS_VALUE;
     895           0 :     av_frame_free(&fin);
     896           0 :     return ret;
     897             : }
     898             : 
     899             : static const AVFilterPad showspectrum_inputs[] = {
     900             :     {
     901             :         .name         = "default",
     902             :         .type         = AVMEDIA_TYPE_AUDIO,
     903             :         .filter_frame = filter_frame,
     904             :     },
     905             :     { NULL }
     906             : };
     907             : 
     908             : static const AVFilterPad showspectrum_outputs[] = {
     909             :     {
     910             :         .name          = "default",
     911             :         .type          = AVMEDIA_TYPE_VIDEO,
     912             :         .config_props  = config_output,
     913             :         .request_frame = request_frame,
     914             :     },
     915             :     { NULL }
     916             : };
     917             : 
     918             : AVFilter ff_avf_showspectrum = {
     919             :     .name          = "showspectrum",
     920             :     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."),
     921             :     .uninit        = uninit,
     922             :     .query_formats = query_formats,
     923             :     .priv_size     = sizeof(ShowSpectrumContext),
     924             :     .inputs        = showspectrum_inputs,
     925             :     .outputs       = showspectrum_outputs,
     926             :     .priv_class    = &showspectrum_class,
     927             :     .flags         = AVFILTER_FLAG_SLICE_THREADS,
     928             : };
     929             : #endif // CONFIG_SHOWSPECTRUM_FILTER
     930             : 
     931             : #if CONFIG_SHOWSPECTRUMPIC_FILTER
     932             : 
     933             : static const AVOption showspectrumpic_options[] = {
     934             :     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS },
     935             :     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS },
     936             :     { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_MODES-1, FLAGS, "mode" },
     937             :         { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" },
     938             :         { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" },
     939             :     { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=INTENSITY}, 0, NB_CLMODES-1, FLAGS, "color" },
     940             :         { "channel",   "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL},   0, 0, FLAGS, "color" },
     941             :         { "intensity", "intensity based coloring",        0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" },
     942             :         { "rainbow",   "rainbow based coloring",          0, AV_OPT_TYPE_CONST, {.i64=RAINBOW},   0, 0, FLAGS, "color" },
     943             :         { "moreland",  "moreland based coloring",         0, AV_OPT_TYPE_CONST, {.i64=MORELAND},  0, 0, FLAGS, "color" },
     944             :         { "nebulae",   "nebulae based coloring",          0, AV_OPT_TYPE_CONST, {.i64=NEBULAE},   0, 0, FLAGS, "color" },
     945             :         { "fire",      "fire based coloring",             0, AV_OPT_TYPE_CONST, {.i64=FIRE},      0, 0, FLAGS, "color" },
     946             :         { "fiery",     "fiery based coloring",            0, AV_OPT_TYPE_CONST, {.i64=FIERY},     0, 0, FLAGS, "color" },
     947             :         { "fruit",     "fruit based coloring",            0, AV_OPT_TYPE_CONST, {.i64=FRUIT},     0, 0, FLAGS, "color" },
     948             :         { "cool",      "cool based coloring",             0, AV_OPT_TYPE_CONST, {.i64=COOL},      0, 0, FLAGS, "color" },
     949             :     { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, 0, NB_SCALES-1, FLAGS, "scale" },
     950             :         { "lin",  "linear",      0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" },
     951             :         { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT},   0, 0, FLAGS, "scale" },
     952             :         { "cbrt", "cubic root",  0, AV_OPT_TYPE_CONST, {.i64=CBRT},   0, 0, FLAGS, "scale" },
     953             :         { "log",  "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG},    0, 0, FLAGS, "scale" },
     954             :         { "4thrt","4th root",    0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, "scale" },
     955             :         { "5thrt","5th root",    0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT},  0, 0, FLAGS, "scale" },
     956             :     { "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS },
     957             :     { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64 = WFUNC_HANNING}, 0, NB_WFUNC-1, FLAGS, "win_func" },
     958             :         { "rect",     "Rectangular",      0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT},     0, 0, FLAGS, "win_func" },
     959             :         { "bartlett", "Bartlett",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, FLAGS, "win_func" },
     960             :         { "hann",     "Hann",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, FLAGS, "win_func" },
     961             :         { "hanning",  "Hanning",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING},  0, 0, FLAGS, "win_func" },
     962             :         { "hamming",  "Hamming",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING},  0, 0, FLAGS, "win_func" },
     963             :         { "blackman", "Blackman",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, FLAGS, "win_func" },
     964             :         { "welch",    "Welch",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH},    0, 0, FLAGS, "win_func" },
     965             :         { "flattop",  "Flat-top",         0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP},  0, 0, FLAGS, "win_func" },
     966             :         { "bharris",  "Blackman-Harris",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS},  0, 0, FLAGS, "win_func" },
     967             :         { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, FLAGS, "win_func" },
     968             :         { "bhann",    "Bartlett-Hann",    0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN},    0, 0, FLAGS, "win_func" },
     969             :         { "sine",     "Sine",             0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE},     0, 0, FLAGS, "win_func" },
     970             :         { "nuttall",  "Nuttall",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL},  0, 0, FLAGS, "win_func" },
     971             :         { "lanczos",  "Lanczos",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS},  0, 0, FLAGS, "win_func" },
     972             :         { "gauss",    "Gauss",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS},    0, 0, FLAGS, "win_func" },
     973             :         { "tukey",    "Tukey",            0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY},    0, 0, FLAGS, "win_func" },
     974             :         { "dolph",    "Dolph-Chebyshev",  0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH},    0, 0, FLAGS, "win_func" },
     975             :         { "cauchy",   "Cauchy",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY},   0, 0, FLAGS, "win_func" },
     976             :         { "parzen",   "Parzen",           0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN},   0, 0, FLAGS, "win_func" },
     977             :         { "poisson",  "Poisson",          0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON},  0, 0, FLAGS, "win_func" },
     978             :     { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, "orientation" },
     979             :         { "vertical",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL},   0, 0, FLAGS, "orientation" },
     980             :         { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, "orientation" },
     981             :     { "gain", "set scale gain", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl = 1}, 0, 128, FLAGS },
     982             :     { "legend", "draw legend", OFFSET(legend), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
     983             :     { "rotation", "color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS },
     984             :     { NULL }
     985             : };
     986             : 
     987             : AVFILTER_DEFINE_CLASS(showspectrumpic);
     988             : 
     989           0 : static void drawtext(AVFrame *pic, int x, int y, const char *txt, int o)
     990             : {
     991             :     const uint8_t *font;
     992             :     int font_height;
     993             :     int i;
     994             : 
     995           0 :     font = avpriv_cga_font,   font_height =  8;
     996             : 
     997           0 :     for (i = 0; txt[i]; i++) {
     998             :         int char_y, mask;
     999             : 
    1000           0 :         if (o) {
    1001           0 :             for (char_y = font_height - 1; char_y >= 0; char_y--) {
    1002           0 :                 uint8_t *p = pic->data[0] + (y + i * 10) * pic->linesize[0] + x;
    1003           0 :                 for (mask = 0x80; mask; mask >>= 1) {
    1004           0 :                     if (font[txt[i] * font_height + font_height - 1 - char_y] & mask)
    1005           0 :                         p[char_y] = ~p[char_y];
    1006           0 :                     p += pic->linesize[0];
    1007             :                 }
    1008             :             }
    1009             :         } else {
    1010           0 :             uint8_t *p = pic->data[0] + y*pic->linesize[0] + (x + i*8);
    1011           0 :             for (char_y = 0; char_y < font_height; char_y++) {
    1012           0 :                 for (mask = 0x80; mask; mask >>= 1) {
    1013           0 :                     if (font[txt[i] * font_height + char_y] & mask)
    1014           0 :                         *p = ~(*p);
    1015           0 :                     p++;
    1016             :                 }
    1017           0 :                 p += pic->linesize[0] - 8;
    1018             :             }
    1019             :         }
    1020             :     }
    1021           0 : }
    1022             : 
    1023           0 : static int showspectrumpic_request_frame(AVFilterLink *outlink)
    1024             : {
    1025           0 :     AVFilterContext *ctx = outlink->src;
    1026           0 :     ShowSpectrumContext *s = ctx->priv;
    1027           0 :     AVFilterLink *inlink = ctx->inputs[0];
    1028             :     int ret, samples;
    1029             : 
    1030           0 :     ret = ff_request_frame(inlink);
    1031           0 :     samples = av_audio_fifo_size(s->fifo);
    1032           0 :     if (ret == AVERROR_EOF && s->outpicref && samples > 0) {
    1033           0 :         int consumed = 0;
    1034           0 :         int y, x = 0, sz = s->orientation == VERTICAL ? s->w : s->h;
    1035             :         int ch, spf, spb;
    1036             :         AVFrame *fin;
    1037             : 
    1038           0 :         spf = s->win_size * (samples / ((s->win_size * sz) * ceil(samples / (float)(s->win_size * sz))));
    1039           0 :         spf = FFMAX(1, spf);
    1040             : 
    1041           0 :         spb = (samples / (spf * sz)) * spf;
    1042             : 
    1043           0 :         fin = ff_get_audio_buffer(inlink, s->win_size);
    1044           0 :         if (!fin)
    1045           0 :             return AVERROR(ENOMEM);
    1046             : 
    1047           0 :         while (x < sz) {
    1048           0 :             ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data, s->win_size);
    1049           0 :             if (ret < 0) {
    1050           0 :                 av_frame_free(&fin);
    1051           0 :                 return ret;
    1052             :             }
    1053             : 
    1054           0 :             av_audio_fifo_drain(s->fifo, spf);
    1055             : 
    1056           0 :             if (ret < s->win_size) {
    1057           0 :                 for (ch = 0; ch < s->nb_display_channels; ch++) {
    1058           0 :                     memset(fin->extended_data[ch] + ret * sizeof(float), 0,
    1059           0 :                            (s->win_size - ret) * sizeof(float));
    1060             :                 }
    1061             :             }
    1062             : 
    1063           0 :             ctx->internal->execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels);
    1064           0 :             acalc_magnitudes(s);
    1065             : 
    1066           0 :             consumed += spf;
    1067           0 :             if (consumed >= spb) {
    1068           0 :                 int h = s->orientation == VERTICAL ? s->h : s->w;
    1069             : 
    1070           0 :                 scale_magnitudes(s, 1. / (consumed / spf));
    1071           0 :                 plot_spectrum_column(inlink, fin);
    1072           0 :                 consumed = 0;
    1073           0 :                 x++;
    1074           0 :                 for (ch = 0; ch < s->nb_display_channels; ch++)
    1075           0 :                     memset(s->magnitudes[ch], 0, h * sizeof(float));
    1076             :             }
    1077             :         }
    1078             : 
    1079           0 :         av_frame_free(&fin);
    1080           0 :         s->outpicref->pts = 0;
    1081             : 
    1082           0 :         if (s->legend) {
    1083           0 :             int multi = (s->mode == SEPARATE && s->color_mode == CHANNEL);
    1084           0 :             float spp = samples / (float)sz;
    1085             :             uint8_t *dst;
    1086             : 
    1087           0 :             drawtext(s->outpicref, 2, outlink->h - 10, "CREATED BY LIBAVFILTER", 0);
    1088             : 
    1089           0 :             dst = s->outpicref->data[0] + (s->start_y - 1) * s->outpicref->linesize[0] + s->start_x - 1;
    1090           0 :             for (x = 0; x < s->w + 1; x++)
    1091           0 :                 dst[x] = 200;
    1092           0 :             dst = s->outpicref->data[0] + (s->start_y + s->h) * s->outpicref->linesize[0] + s->start_x - 1;
    1093           0 :             for (x = 0; x < s->w + 1; x++)
    1094           0 :                 dst[x] = 200;
    1095           0 :             for (y = 0; y < s->h + 2; y++) {
    1096           0 :                 dst = s->outpicref->data[0] + (y + s->start_y - 1) * s->outpicref->linesize[0];
    1097           0 :                 dst[s->start_x - 1] = 200;
    1098           0 :                 dst[s->start_x + s->w] = 200;
    1099             :             }
    1100           0 :             if (s->orientation == VERTICAL) {
    1101           0 :                 int h = s->mode == SEPARATE ? s->h / s->nb_display_channels : s->h;
    1102           0 :                 for (ch = 0; ch < (s->mode == SEPARATE ? s->nb_display_channels : 1); ch++) {
    1103           0 :                     for (y = 0; y < h; y += 20) {
    1104           0 :                         dst = s->outpicref->data[0] + (s->start_y + h * (ch + 1) - y - 1) * s->outpicref->linesize[0];
    1105           0 :                         dst[s->start_x - 2] = 200;
    1106           0 :                         dst[s->start_x + s->w + 1] = 200;
    1107             :                     }
    1108           0 :                     for (y = 0; y < h; y += 40) {
    1109           0 :                         dst = s->outpicref->data[0] + (s->start_y + h * (ch + 1) - y - 1) * s->outpicref->linesize[0];
    1110           0 :                         dst[s->start_x - 3] = 200;
    1111           0 :                         dst[s->start_x + s->w + 2] = 200;
    1112             :                     }
    1113           0 :                     dst = s->outpicref->data[0] + (s->start_y - 2) * s->outpicref->linesize[0] + s->start_x;
    1114           0 :                     for (x = 0; x < s->w; x+=40)
    1115           0 :                         dst[x] = 200;
    1116           0 :                     dst = s->outpicref->data[0] + (s->start_y - 3) * s->outpicref->linesize[0] + s->start_x;
    1117           0 :                     for (x = 0; x < s->w; x+=80)
    1118           0 :                         dst[x] = 200;
    1119           0 :                     dst = s->outpicref->data[0] + (s->h + s->start_y + 1) * s->outpicref->linesize[0] + s->start_x;
    1120           0 :                     for (x = 0; x < s->w; x+=40) {
    1121           0 :                         dst[x] = 200;
    1122             :                     }
    1123           0 :                     dst = s->outpicref->data[0] + (s->h + s->start_y + 2) * s->outpicref->linesize[0] + s->start_x;
    1124           0 :                     for (x = 0; x < s->w; x+=80) {
    1125           0 :                         dst[x] = 200;
    1126             :                     }
    1127           0 :                     for (y = 0; y < h; y += 40) {
    1128           0 :                         float hertz = y * (inlink->sample_rate / 2) / (float)(1 << (int)ceil(log2(h)));
    1129             :                         char *units;
    1130             : 
    1131           0 :                         if (hertz == 0)
    1132           0 :                             units = av_asprintf("DC");
    1133             :                         else
    1134           0 :                             units = av_asprintf("%.2f", hertz);
    1135           0 :                         if (!units)
    1136           0 :                             return AVERROR(ENOMEM);
    1137             : 
    1138           0 :                         drawtext(s->outpicref, s->start_x - 8 * strlen(units) - 4, h * (ch + 1) + s->start_y - y - 4, units, 0);
    1139           0 :                         av_free(units);
    1140             :                     }
    1141             :                 }
    1142             : 
    1143           0 :                 for (x = 0; x < s->w; x+=80) {
    1144           0 :                     float seconds = x * spp / inlink->sample_rate;
    1145             :                     char *units;
    1146             : 
    1147           0 :                     if (x == 0)
    1148           0 :                         units = av_asprintf("0");
    1149           0 :                     else if (log10(seconds) > 6)
    1150           0 :                         units = av_asprintf("%.2fh", seconds / (60 * 60));
    1151           0 :                     else if (log10(seconds) > 3)
    1152           0 :                         units = av_asprintf("%.2fm", seconds / 60);
    1153             :                     else
    1154           0 :                         units = av_asprintf("%.2fs", seconds);
    1155           0 :                     if (!units)
    1156           0 :                         return AVERROR(ENOMEM);
    1157             : 
    1158           0 :                     drawtext(s->outpicref, s->start_x + x - 4 * strlen(units), s->h + s->start_y + 6, units, 0);
    1159           0 :                     drawtext(s->outpicref, s->start_x + x - 4 * strlen(units), s->start_y - 12, units, 0);
    1160           0 :                     av_free(units);
    1161             :                 }
    1162             : 
    1163           0 :                 drawtext(s->outpicref, outlink->w / 2 - 4 * 4, outlink->h - s->start_y / 2, "TIME", 0);
    1164           0 :                 drawtext(s->outpicref, s->start_x / 7, outlink->h / 2 - 14 * 4, "FREQUENCY (Hz)", 1);
    1165             :             } else {
    1166           0 :                 int w = s->mode == SEPARATE ? s->w / s->nb_display_channels : s->w;
    1167           0 :                 for (y = 0; y < s->h; y += 20) {
    1168           0 :                     dst = s->outpicref->data[0] + (s->start_y + y) * s->outpicref->linesize[0];
    1169           0 :                     dst[s->start_x - 2] = 200;
    1170           0 :                     dst[s->start_x + s->w + 1] = 200;
    1171             :                 }
    1172           0 :                 for (y = 0; y < s->h; y += 40) {
    1173           0 :                     dst = s->outpicref->data[0] + (s->start_y + y) * s->outpicref->linesize[0];
    1174           0 :                     dst[s->start_x - 3] = 200;
    1175           0 :                     dst[s->start_x + s->w + 2] = 200;
    1176             :                 }
    1177           0 :                 for (ch = 0; ch < (s->mode == SEPARATE ? s->nb_display_channels : 1); ch++) {
    1178           0 :                     dst = s->outpicref->data[0] + (s->start_y - 2) * s->outpicref->linesize[0] + s->start_x + w * ch;
    1179           0 :                     for (x = 0; x < w; x+=40)
    1180           0 :                         dst[x] = 200;
    1181           0 :                     dst = s->outpicref->data[0] + (s->start_y - 3) * s->outpicref->linesize[0] + s->start_x + w * ch;
    1182           0 :                     for (x = 0; x < w; x+=80)
    1183           0 :                         dst[x] = 200;
    1184           0 :                     dst = s->outpicref->data[0] + (s->h + s->start_y + 1) * s->outpicref->linesize[0] + s->start_x + w * ch;
    1185           0 :                     for (x = 0; x < w; x+=40) {
    1186           0 :                         dst[x] = 200;
    1187             :                     }
    1188           0 :                     dst = s->outpicref->data[0] + (s->h + s->start_y + 2) * s->outpicref->linesize[0] + s->start_x + w * ch;
    1189           0 :                     for (x = 0; x < w; x+=80) {
    1190           0 :                         dst[x] = 200;
    1191             :                     }
    1192           0 :                     for (x = 0; x < w; x += 80) {
    1193           0 :                         float hertz = x * (inlink->sample_rate / 2) / (float)(1 << (int)ceil(log2(w)));
    1194             :                         char *units;
    1195             : 
    1196           0 :                         if (hertz == 0)
    1197           0 :                             units = av_asprintf("DC");
    1198             :                         else
    1199           0 :                             units = av_asprintf("%.2f", hertz);
    1200           0 :                         if (!units)
    1201           0 :                             return AVERROR(ENOMEM);
    1202             : 
    1203           0 :                         drawtext(s->outpicref, s->start_x - 4 * strlen(units) + x + w * ch, s->start_y - 12, units, 0);
    1204           0 :                         drawtext(s->outpicref, s->start_x - 4 * strlen(units) + x + w * ch, s->h + s->start_y + 6, units, 0);
    1205           0 :                         av_free(units);
    1206             :                     }
    1207             :                 }
    1208           0 :                 for (y = 0; y < s->h; y+=40) {
    1209           0 :                     float seconds = y * spp / inlink->sample_rate;
    1210             :                     char *units;
    1211             : 
    1212           0 :                     if (x == 0)
    1213           0 :                         units = av_asprintf("0");
    1214           0 :                     else if (log10(seconds) > 6)
    1215           0 :                         units = av_asprintf("%.2fh", seconds / (60 * 60));
    1216           0 :                     else if (log10(seconds) > 3)
    1217           0 :                         units = av_asprintf("%.2fm", seconds / 60);
    1218             :                     else
    1219           0 :                         units = av_asprintf("%.2fs", seconds);
    1220           0 :                     if (!units)
    1221           0 :                         return AVERROR(ENOMEM);
    1222             : 
    1223           0 :                     drawtext(s->outpicref, s->start_x - 8 * strlen(units) - 4, s->start_y + y - 4, units, 0);
    1224           0 :                     av_free(units);
    1225             :                 }
    1226           0 :                 drawtext(s->outpicref, s->start_x / 7, outlink->h / 2 - 4 * 4, "TIME", 1);
    1227           0 :                 drawtext(s->outpicref, outlink->w / 2 - 14 * 4, outlink->h - s->start_y / 2, "FREQUENCY (Hz)", 0);
    1228             :             }
    1229             : 
    1230           0 :             for (ch = 0; ch < (multi ? s->nb_display_channels : 1); ch++) {
    1231           0 :                 int h = multi ? s->h / s->nb_display_channels : s->h;
    1232             : 
    1233           0 :                 for (y = 0; y < h; y++) {
    1234           0 :                     float out[3] = { 0., 127.5, 127.5};
    1235             :                     int chn;
    1236             : 
    1237           0 :                     for (chn = 0; chn < (s->mode == SEPARATE ? 1 : s->nb_display_channels); chn++) {
    1238             :                         float yf, uf, vf;
    1239           0 :                         int channel = (multi) ? s->nb_display_channels - ch - 1 : chn;
    1240             :                         float lout[3];
    1241             : 
    1242           0 :                         color_range(s, channel, &yf, &uf, &vf);
    1243           0 :                         pick_color(s, yf, uf, vf, y / (float)h, lout);
    1244           0 :                         out[0] += lout[0];
    1245           0 :                         out[1] += lout[1];
    1246           0 :                         out[2] += lout[2];
    1247             :                     }
    1248           0 :                     memset(s->outpicref->data[0]+(s->start_y + h * (ch + 1) - y - 1) * s->outpicref->linesize[0] + s->w + s->start_x + 20, av_clip_uint8(out[0]), 10);
    1249           0 :                     memset(s->outpicref->data[1]+(s->start_y + h * (ch + 1) - y - 1) * s->outpicref->linesize[1] + s->w + s->start_x + 20, av_clip_uint8(out[1]), 10);
    1250           0 :                     memset(s->outpicref->data[2]+(s->start_y + h * (ch + 1) - y - 1) * s->outpicref->linesize[2] + s->w + s->start_x + 20, av_clip_uint8(out[2]), 10);
    1251             :                 }
    1252             : 
    1253           0 :                 for (y = 0; ch == 0 && y < h; y += h / 10) {
    1254           0 :                     float value = 120.0 * log10(1. - y / (float)h);
    1255             :                     char *text;
    1256             : 
    1257           0 :                     if (value < -120)
    1258           0 :                         break;
    1259           0 :                     text = av_asprintf("%.0f dB", value);
    1260           0 :                     if (!text)
    1261           0 :                         continue;
    1262           0 :                     drawtext(s->outpicref, s->w + s->start_x + 35, s->start_y + y - 5, text, 0);
    1263           0 :                     av_free(text);
    1264             :                 }
    1265             :             }
    1266             :         }
    1267             : 
    1268           0 :         ret = ff_filter_frame(outlink, s->outpicref);
    1269           0 :         s->outpicref = NULL;
    1270             :     }
    1271             : 
    1272           0 :     return ret;
    1273             : }
    1274             : 
    1275           0 : static int showspectrumpic_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
    1276             : {
    1277           0 :     AVFilterContext *ctx = inlink->dst;
    1278           0 :     ShowSpectrumContext *s = ctx->priv;
    1279             :     int ret;
    1280             : 
    1281           0 :     ret = av_audio_fifo_write(s->fifo, (void **)insamples->extended_data, insamples->nb_samples);
    1282           0 :     av_frame_free(&insamples);
    1283           0 :     return ret;
    1284             : }
    1285             : 
    1286             : static const AVFilterPad showspectrumpic_inputs[] = {
    1287             :     {
    1288             :         .name         = "default",
    1289             :         .type         = AVMEDIA_TYPE_AUDIO,
    1290             :         .filter_frame = showspectrumpic_filter_frame,
    1291             :     },
    1292             :     { NULL }
    1293             : };
    1294             : 
    1295             : static const AVFilterPad showspectrumpic_outputs[] = {
    1296             :     {
    1297             :         .name          = "default",
    1298             :         .type          = AVMEDIA_TYPE_VIDEO,
    1299             :         .config_props  = config_output,
    1300             :         .request_frame = showspectrumpic_request_frame,
    1301             :     },
    1302             :     { NULL }
    1303             : };
    1304             : 
    1305             : AVFilter ff_avf_showspectrumpic = {
    1306             :     .name          = "showspectrumpic",
    1307             :     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output single picture."),
    1308             :     .uninit        = uninit,
    1309             :     .query_formats = query_formats,
    1310             :     .priv_size     = sizeof(ShowSpectrumContext),
    1311             :     .inputs        = showspectrumpic_inputs,
    1312             :     .outputs       = showspectrumpic_outputs,
    1313             :     .priv_class    = &showspectrumpic_class,
    1314             :     .flags         = AVFILTER_FLAG_SLICE_THREADS,
    1315             : };
    1316             : 
    1317             : #endif // CONFIG_SHOWSPECTRUMPIC_FILTER

Generated by: LCOV version 1.13