[FFmpeg-devel] [PATCH][BULK][again] new multimedia filter avf_showcqt.c
Clément Bœsch
u at pkh.me
Wed May 28 09:03:39 CEST 2014
On Wed, May 28, 2014 at 10:59:20AM +0700, Muhammad Faiz wrote:
> Hi,
> this filter is same as showspectrum but with constant Q transform,
> so frequency is spaced logarithmically
> diff -ruN a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> --- a/libavfilter/allfilters.c 2014-05-19 13:35:09.327627603 +0700
> +++ b/libavfilter/allfilters.c 2014-05-19 11:07:44.977770955 +0700
Would it be possible to provide a git-format-patch instead?
> @@ -233,6 +233,7 @@
> REGISTER_FILTER(CONCAT, concat, avf);
> REGISTER_FILTER(SHOWSPECTRUM, showspectrum, avf);
> REGISTER_FILTER(SHOWWAVES, showwaves, avf);
> + REGISTER_FILTER(SHOWCQT, showcqt, avf);
>
> /* multimedia sources */
> REGISTER_FILTER(AMOVIE, amovie, avsrc);
> diff -ruN a/libavfilter/avf_showcqt.c b/libavfilter/avf_showcqt.c
> --- a/libavfilter/avf_showcqt.c 1970-01-01 07:00:00.000000000 +0700
> +++ b/libavfilter/avf_showcqt.c 2014-05-28 10:41:20.000000000 +0700
> @@ -0,0 +1,880 @@
> +/*
> + * Copyright (c) 2014 Muhammad Faiz <mfcc64 at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavcodec/avfft.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/channel_layout.h"
> +#include "libavutil/opt.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +#include <math.h>
> +#include <stdlib.h>
> +
> +/* this filter is designed to do 16 bins/semitones constant Q transform with Brown-Puckette algorithm*/
> +/* start from E0 to D#10 (10 octaves) */
> +/* so there are 16 bins/semitones * 12 semitones/octaves * 10 octaves = 1920 bins */
> +/* match with full HD resolution */
> +
> +#define VIDEO_WIDTH 1920
> +#define VIDEO_HEIGHT 1080
> +#define FONT_HEIGHT 24
> +#define SPECTOGRAM_HEIGHT ((VIDEO_HEIGHT-FONT_HEIGHT)/2)
> +#define SPECTOGRAM_START (VIDEO_HEIGHT-SPECTOGRAM_HEIGHT)
> +#define BASE_FREQ 20.051392800492
> +#define COEFF_CLAMP 1.0e-4
> +
> +/* image 'A BC D EF G ' from font 'Nimbus Mono L, Bold' */
> +static uint8_t abcdefg_string[] = {
Could you use avpriv_vga16_font or avpriv_cga_font instead? See in
libavfilter/f_ebur128.c for example
[...]
> +typedef struct {
> + FFTSample value;
> + int index;
> +} SparseCoeff;
> +
> +
> +static double window_function(double x)
> +{
> + double a0 = 0.355768;
> + double a1 = 0.487396/a0;
> + double a2 = 0.144232/a0;
> + double a3 = 0.012604/a0;
> + double c1, c2, c3, val;
> + if(fabs(x) >= 0.5)
nit: "if ("
Please fix this style issue in all your code, it's present is several
places.
> + return 0.0;
> + c1 = a1 * cos(2.0*M_PI*x);
> + c2 = a2 * cos(4.0*M_PI*x);
> + c3 = a3 * cos(6.0*M_PI*x);
> + val = 1.0 + c1 + c2 + c3;
> + return val;
> +}
> +
> +static int qsort_sparsecoeff(const void *a, const void *b)
> +{
> + const SparseCoeff *x = (SparseCoeff*) a;
> + const SparseCoeff *y = (SparseCoeff*) b;
> + if(fabsf(x->value) > fabsf(y->value))
> + return 1;
> + if(fabsf(x->value) < fabsf(y->value))
> + return -1;
> + if((x->index) > (y->index))
Pointless braces
> + return 1;
> + return 0;
> +}
> +
> +
> +typedef struct {
> + const AVClass *class;
> + AVFrame *outpicref;
> + FFTContext *fft_context;
> + FFTComplex *fft_data;
> + FFTComplex *fft_result_left;
> + FFTComplex *fft_result_right;
> + SparseCoeff *coeff_sort;
> + SparseCoeff *coeffs[VIDEO_WIDTH];
> + int coeffs_len[VIDEO_WIDTH];
> + float color[VIDEO_WIDTH];
> + float alpha[FONT_HEIGHT][VIDEO_WIDTH];
> + uint8_t spectogram[SPECTOGRAM_HEIGHT][VIDEO_WIDTH][3];
> + int64_t frame_count;
> + int spectogram_count;
> + int spectogram_index;
> + int fft_bits;
> + int req_fullfilled;
> + int remaining_fill;
> + double volume;
> + double timeclamp; /* lower timeclamp, time-accurate, higher timeclamp, freq-accurate (at low freq)*/
> + float coeffclamp; /* lower coeffclamp, more precise, higher coeffclamp, faster */
> + float gamma; /* lower gamma, more contrast, higher gamma, more range */
> + int fps; /* the required fps is so strict, so it's enough to be int, but 24000/1001 etc cannot be encoded */
> + int count; /* fps * count = transform rate */
> +} ShowCQTContext;
> +
> +#define OFFSET(x) offsetof(ShowCQTContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption showcqt_options[] = {
> + { "volume", "set volume", OFFSET(volume), AV_OPT_TYPE_DOUBLE, {.dbl = 16}, 0.1, 100, FLAGS },
> + { "timeclamp", "set timeclamp", OFFSET(timeclamp), AV_OPT_TYPE_DOUBLE, {.dbl = 0.17 }, 0.1, 1.0, FLAGS},
> + { "coeffclamp", "set coeffclamp", OFFSET(coeffclamp), AV_OPT_TYPE_FLOAT, {.dbl = 1}, 0.1, 10, FLAGS},
> + { "gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_FLOAT, { .dbl = 3}, 1, 7, FLAGS },
> + { "fps", "set video fps", OFFSET(fps), AV_OPT_TYPE_INT, { .i64 = 25 }, 10, 100, FLAGS},
> + { "count", "set number of transform per frame", OFFSET(count), AV_OPT_TYPE_INT, { .i64 = 6 }, 1, 30, FLAGS},
> + { NULL }
stylenit: some lines ends up without a space before '}'; make it
consistent please (yes I know the issue is present in several other
places)
> +};
> +
> +AVFILTER_DEFINE_CLASS(showcqt);
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + int k;
> + ShowCQTContext *s = ctx->priv;
> + av_fft_end(s->fft_context);
> + s->fft_context = NULL;
> + for (k = 0; k < VIDEO_WIDTH; k++)
> + av_freep(&s->coeffs[k]);
> + av_freep(&s->fft_data);
> + av_freep(&s->fft_result_left);
> + av_freep(&s->fft_result_right);
> + av_freep(&s->coeff_sort);
> + av_frame_free(&s->outpicref);
> +}
> +
> +
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + AVFilterFormats *formats = NULL;
> + AVFilterChannelLayouts *layouts = NULL;
> + AVFilterLink *inlink = ctx->inputs[0];
> + AVFilterLink *outlink = ctx->outputs[0];
> + static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE };
> + static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE };
> + static const int64_t channel_layouts[] = { AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_STEREO_DOWNMIX, -1};
> + static const int samplerates[] = { 44100, 48000, -1};
> +
Trailing whitespace here and several times later in the whole file. The
patch won't be pushable. At least Git can show them.
> + /* set input audio formats */
> + formats = ff_make_format_list(sample_fmts);
> + if (!formats)
> + return AVERROR(ENOMEM);
> + ff_formats_ref(formats, &inlink->out_formats);
> +
> + layouts = avfilter_make_format64_list(channel_layouts);
> + if (!layouts)
> + return AVERROR(ENOMEM);
> + ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
> +
> + formats = ff_make_format_list(samplerates);
> + if (!formats)
> + return AVERROR(ENOMEM);
> + ff_formats_ref(formats, &inlink->out_samplerates);
> +
> + /* set output video format */
> + formats = ff_make_format_list(pix_fmts);
> + if (!formats)
> + return AVERROR(ENOMEM);
> + ff_formats_ref(formats, &outlink->in_formats);
> +
> + return 0;
> +}
> +
> +
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> + AVFilterContext *ctx = outlink->src;
> + AVFilterLink *inlink = ctx->inputs[0];
> + ShowCQTContext *s = ctx->priv;
> + int fft_len, k, x, y;
> + int num_coeffs = 0;
> + int rate = inlink->sample_rate;
> + double max_len = rate * (double) s->timeclamp;
> + s->fft_bits = ceil(log2(max_len));
> + fft_len = 1 << s->fft_bits;
> +
> + if((rate % (s->fps * s->count)))
> + {
wrong style, { on the same line. same several time below
> + av_log(ctx, AV_LOG_ERROR, "Rate (%u) is not divisible by fps*count (%u*%u)\n", rate, s->fps, s->count);
> + return AVERROR(EINVAL);
> + }
> +
> + s->fft_data = av_malloc_array(sizeof(FFTComplex), fft_len);
Arguments are swapped (semantically speaking)
Also please use sizeof(*var) form
> + if (!s->fft_data)
> + return AVERROR(ENOMEM);
> +
> + s->coeff_sort = av_malloc_array(sizeof(SparseCoeff), fft_len);
> + if (!s->coeff_sort)
> + return AVERROR(ENOMEM);
> +
> + s->fft_result_left = av_malloc_array(sizeof(FFTComplex), fft_len);
> + if (!s->fft_result_left)
> + return AVERROR(ENOMEM);
> +
> + s->fft_result_right = av_malloc_array(sizeof(FFTComplex), fft_len);
> + if (!s->fft_result_right)
> + return AVERROR(ENOMEM);
s->fft_data = av_malloc_array(fft_len, sizeof(*s->fft_data));
s->coeff_sort = av_malloc_array(fft_len, sizeof(*s->coeff_sort));
s->fft_result_left = av_malloc_array(fft_len, sizeof(*s->fft_result_left));
s->fft_result_right = av_malloc_array(fft_len, sizeof(*s->fft_result_right));
if (!s->fft_result_left || !s->coeff_sort || !s->fft_data || !s->fft_result_right)
return AVERROR(ENOMEM);
...is probably more compact and readable
> +
> + s->fft_context = av_fft_init(s->fft_bits, 0);
> + if (!s->fft_context)
> + return AVERROR(ENOMEM);
> +
> + /* initializing font */
> + for (x = 0; x < VIDEO_WIDTH; x++)
> + {
> + if((x >= (12*3+8)*16) && (x < (12*4+8)*16))
> + {
> + float fx = (x - (12*3+8)*16) * (1.0f/192.0f);
> + float sv = sinf(M_PI*fx);
> + s->color[x] = sv*sv*255.0f;
> + }
> + else
> + s->color[x] = 0.0f;
> +
> + for (y = 0; y < FONT_HEIGHT; y++)
> + {
> + int u = (x + 7*16) % (12*16);
> + s->alpha[y][x] = (1.0f/255.0f) * abcdefg_string[y*(12*16) + u];
> + }
> + }
> +
> + av_log(ctx, AV_LOG_INFO, "Calculating spectral kernel, please wait\n");
> + for (k = 0; k < VIDEO_WIDTH; k++)
> + {
> + int x, hlen = fft_len >> 1;
> + float total = 0;
> + float partial = 0;
> + double freq = BASE_FREQ * exp2(k * (1.0/192.0));
> + double tlen = rate * (24.0 * 16.0) /freq;
> + tlen = tlen * max_len / (tlen + max_len);
> + s->fft_data[0].re = 0;
> + s->fft_data[0].im = 0;
> + s->fft_data[hlen].re = window_function(0) * (1.0/tlen) * s->volume * (1.0/fft_len);
> + s->fft_data[hlen].im = 0;
> + for (x = 1; x < hlen; x++)
> + {
> + double sv, cv, w;
> + sv = sin(2.0*M_PI*freq*x*(1.0/rate));
> + cv = cos(2.0*M_PI*freq*x*(1.0/rate));
I think you might be able here to use that trick:
http://iquilezles.org/www/articles/sincos/sincos.htm
> + w = window_function(x * (1.0/tlen)) * (1.0/tlen) * s->volume * (1.0/fft_len);
> + s->fft_data[hlen + x].re = w * cv;
> + s->fft_data[hlen + x].im = w * sv;
> + s->fft_data[hlen - x].re = s->fft_data[hlen + x].re;
> + s->fft_data[hlen - x].im = -s->fft_data[hlen + x].im;
> + }
> + av_fft_permute(s->fft_context, s->fft_data);
> + av_fft_calc(s->fft_context, s->fft_data);
> +
> + for (x = 0; x < fft_len; x++)
> + {
> + s->coeff_sort[x].index = x;
> + s->coeff_sort[x].value = s->fft_data[x].re;
> + }
> +
> + qsort(s->coeff_sort, fft_len, sizeof(SparseCoeff), qsort_sparsecoeff);
> + for (x = 0; x < fft_len; x++)
> + total += fabsf(s->coeff_sort[x].value);
> +
> + for (x = 0; x < fft_len; x++)
> + {
> + partial += fabsf(s->coeff_sort[x].value);
> + if (partial > (total * s->coeffclamp * COEFF_CLAMP))
> + {
> + int y;
> + s->coeffs_len[k] = fft_len - x;
> + num_coeffs += s->coeffs_len[k];
> + s->coeffs[k] = av_malloc_array(sizeof(SparseCoeff), s->coeffs_len[k]);
> + if (!s->coeffs[k])
> + return AVERROR(ENOMEM);
> + for (y = 0; y < (s->coeffs_len[k]); y++)
> + {
> + s->coeffs[k][y] = s->coeff_sort[x+y];
> + }
> + break;
> + }
> + }
> + }
> +
> + outlink->w = VIDEO_WIDTH;
> + outlink->h = VIDEO_HEIGHT;
> +
> + s->req_fullfilled = 0;
> + s->spectogram_index = 0;
> + s->frame_count = 0;
> + s->spectogram_count = 0;
> + s->remaining_fill = fft_len >> 1;
> + memset(s->spectogram, 0, VIDEO_WIDTH*SPECTOGRAM_HEIGHT);
> + memset(s->fft_data, 0, fft_len * sizeof(FFTComplex));
> +
> + s->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> + if (!s->outpicref)
> + return AVERROR(ENOMEM);
> +
> + outlink->sample_aspect_ratio = (AVRational){1,1};
> + outlink->time_base = (AVRational){ 1, s->fps };
> + outlink->frame_rate = (AVRational){s->fps, 1};
You can use av_make_q() here, but it's at your own discretion
[...]
> diff -ruN a/libavfilter/Makefile b/libavfilter/Makefile
> --- a/libavfilter/Makefile 2014-05-19 13:35:09.329627630 +0700
> +++ b/libavfilter/Makefile 2014-05-19 11:06:27.353131460 +0700
> @@ -226,6 +226,7 @@
> OBJS-$(CONFIG_CONCAT_FILTER) += avf_concat.o
> OBJS-$(CONFIG_SHOWSPECTRUM_FILTER) += avf_showspectrum.o
> OBJS-$(CONFIG_SHOWWAVES_FILTER) += avf_showwaves.o
> +OBJS-$(CONFIG_SHOWCQT_FILTER) += avf_showcqt.o
'C' comes before 'W' and 'S', please keep alphabetical order when
possible.
[...]
Please also add the documentation and all the little remaining bits you
can find at the end of the doc/writing_filters.txt document
("Finalization" step).
Thank you.
--
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140528/fc464288/attachment.asc>
More information about the ffmpeg-devel
mailing list