From patchwork Thu Jun 11 16:12:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20293 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id AA832449D39 for ; Thu, 11 Jun 2020 19:43:33 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9363A68B5C2; Thu, 11 Jun 2020 19:43:33 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f65.google.com (mail-ej1-f65.google.com [209.85.218.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D3FDF68B5B3 for ; Thu, 11 Jun 2020 19:43:26 +0300 (EEST) Received: by mail-ej1-f65.google.com with SMTP id mb16so7159663ejb.4 for ; Thu, 11 Jun 2020 09:43:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=jKXYaoc2LmJx9YYbKLGPSVW/H8CKAEd01u7TNMB/QDk=; b=FYE1oj10k1OEXnSv4Q3ad7pcCXIxrAWKBeXeIqyIQBW5HGozGmJGYwGcrombD4TXjH CK8A51fFpjTYemrjuxqiyDj3bXSu1UkrCK8sXVAYknvwNPNX7lzAMGgzcp/huRURBKU1 ZdNG+sXImB5GPrPWD4DDAhUONQFK4D2ifEKNBf2faLA8hGmNkLQf0pl8NgN32SdpZz/8 mFxJd6r/Uv3kHehTT3BWNuHFOGG8CmSFHY+BdKsQUl7b4S9AdNXZJKME9TwZlSOTdYBp jcjgvxnjNI39ICVptOwp0wXNl0cUIb4PRmoI6H0DrOoktLhmYFv5+4sQlLAs9H7jjPnf Afmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=jKXYaoc2LmJx9YYbKLGPSVW/H8CKAEd01u7TNMB/QDk=; b=WE7HNyvVTI+IxTI3locJaR1tbix/7sJ7+vUTLAZXJy/s2yfoKVZ4+3HyM2cMJdE+Jg wCG3pW8L6nYU7a1jimdHqgECHhsq4JyDk8qgBqqLjaoNEol9w6458TE2bW8kQ6UObQU9 MHNrVqOIdLuvAVfsK8+oCh8Nn84uFfnAnOF1zzqbllCmoQywXnlfIo4We8gqx1nVTlfM M9cqUQR/hOfk49ld2MXypPxWQ8aBd+gMsNhTPz4PZyc/MVhFrOcBKD+9Azh9d0y+/dJ8 KoB7aiehNmscEbz6gk8hoBvuxl5HNjAfM1uRGfszMylz+WwVwrQfTiNClnnDCXJ3ibWB 8lmQ== X-Gm-Message-State: AOAM533iINXPY4z4CUsNbWGe0JoUC+oHHeTWtTO2/X3IkJLTDGnygRjG y2c92shZbccs2QzTxF24QpmfQ8VNbWo= X-Google-Smtp-Source: ABdhPJyYLj9qbzmQXUC2lJQsPMfTkEcgSoTdDVCStXH3iRm5aby+WdtqS7oV/jRCgkVii2QfiaBibw== X-Received: by 2002:adf:f205:: with SMTP id p5mr10933090wro.302.1591891957337; Thu, 11 Jun 2020 09:12:37 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.36 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:36 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:21 +0200 Message-Id: <20200611161227.5622-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH 1/7] avfilter/af_biquads: switch to activate() X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Needed by following commits. Signed-off-by: Paul B Mahol --- libavfilter/af_biquads.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index 81cdb0c10e..ef28db741a 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -67,6 +67,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" enum FilterType { @@ -523,6 +524,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) return ff_filter_frame(outlink, out_buf); } +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in = NULL; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) { + ret = filter_frame(inlink, in); + if (ret < 0) + return ret; + } + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags) { @@ -547,7 +572,6 @@ static const AVFilterPad inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, }, { NULL } }; @@ -567,19 +591,20 @@ static const AVFilterPad outputs[] = { #define DEFINE_BIQUAD_FILTER(name_, description_) \ AVFILTER_DEFINE_CLASS(name_); \ -static av_cold int name_##_init(AVFilterContext *ctx) \ +static av_cold int name_##_init(AVFilterContext *ctx) \ { \ BiquadsContext *s = ctx->priv; \ s->class = &name_##_class; \ s->filter_type = name_; \ - return init(ctx); \ + return init(ctx); \ } \ \ -AVFilter ff_af_##name_ = { \ +AVFilter ff_af_##name_ = { \ .name = #name_, \ .description = NULL_IF_CONFIG_SMALL(description_), \ .priv_size = sizeof(BiquadsContext), \ .init = name_##_init, \ + .activate = activate, \ .uninit = uninit, \ .query_formats = query_formats, \ .inputs = inputs, \ From patchwork Thu Jun 11 16:12:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20292 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id E7EAD449D39 for ; Thu, 11 Jun 2020 19:43:31 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C939A68B56F; Thu, 11 Jun 2020 19:43:31 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f194.google.com (mail-lj1-f194.google.com [209.85.208.194]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A8BF568B4FD for ; Thu, 11 Jun 2020 19:43:24 +0300 (EEST) Received: by mail-lj1-f194.google.com with SMTP id 9so7721141ljc.8 for ; Thu, 11 Jun 2020 09:43:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=+qWxMZPfhcjbf5aX6MR9fUooOzh8T0JT4ASTLD6Dt2Q=; b=S3fzDcmP0NWRK+7/eeanHIG/PRUvrJWfeueNLHJUVGurzgRJyaqg4F97X5qGQInP2w 5jvHujD2sVhAHlbGxMUn0V1vqV17f83aRM0/SVT4DR/mZr4EW7/0meV7sYoDLz6xGA78 sx8BD/B2YHewbgh6bZZLwGj/r5sQUJfmAGbShYR4onrmgPHJxHEkt0EbbvdjmdWGkoYf aDTGxg0Pki5X41bU4Yu38kJ6ZKwZXttswWVgU9+tOlanU0it7zrCyrxOV0Z7kzvQrnVY +13nVobvBdPBjAN8gObV3/ta4FltVjOx+XafQakcs5shsodd19oP6dP6ZzsVfmg7hATu YdUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=+qWxMZPfhcjbf5aX6MR9fUooOzh8T0JT4ASTLD6Dt2Q=; b=O3JZhTfv07R9ai3vcA0QPsWWq7Ja+RAMMtHqTL3v3/zOLGzU7DiH/+Jm+5U77MRxfX 2X1pBWouEu5niyWZUCMBcvii2NTTFK0MtOJbYQx2SlLGRjBbKdoSjmTvre/T9IDi3rhf 4/e025CK+iJO5CCWkWJSFb0li5C94Nq9bYWq/JpCTsJVnhtW0TfAUl3wDii4qy3pMAyT HRBQlWrT9U0L/aQqBZpI/IsHPMJjtfXs6zO1ll4rMfBo3jn2NwAlBY1JMAGgEXPxdQnh fFIRUMIrKzYcfYx8m/5PVQqtWkb+U3gM6nOjvmLjM4XIR5MLz1tckJcMy2F2Y08j/AIW YBBg== X-Gm-Message-State: AOAM531CLw3GJ5gezfMVb6vXWkv8PVio+bnoj6Svd2lUbmeH2aoBYVe/ dHHAYTm3d6gTUVEwvodTGYUeyw7/mAY= X-Google-Smtp-Source: ABdhPJzBwXRWGcCZ+iHLi6H0+m/4EHYC3cuMqUDxG7lqItVioyNwmC9rZkjL3K1tGTUXfblf7NnimQ== X-Received: by 2002:a5d:4d89:: with SMTP id b9mr10893087wru.210.1591891958580; Thu, 11 Jun 2020 09:12:38 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.37 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:37 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:22 +0200 Message-Id: <20200611161227.5622-2-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/7] avfilter: add ff_inlink_peek_samples and ff_inlink_skip samples X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- libavfilter/avfilter.c | 61 +++++++++++++++++++++++++++++++++++++----- libavfilter/filters.h | 17 ++++++++++++ 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index dd8074e462..b1db92de4b 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -1147,6 +1147,7 @@ static int samples_ready(AVFilterLink *link, unsigned min) } static int take_samples(AVFilterLink *link, unsigned min, unsigned max, + unsigned peek_samples, AVFrame **rframe) { AVFrame *frame0, *frame, *buf; @@ -1158,7 +1159,10 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, av_assert1(samples_ready(link, link->min_samples)); frame0 = frame = ff_framequeue_peek(&link->fifo, 0); if (!link->fifo.samples_skipped && frame->nb_samples >= min && frame->nb_samples <= max) { - *rframe = ff_framequeue_take(&link->fifo); + if (peek_samples) + *rframe = av_frame_clone(frame); + else + *rframe = ff_framequeue_take(&link->fifo); return 0; } nb_frames = 0; @@ -1188,18 +1192,20 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, p = 0; for (i = 0; i < nb_frames; i++) { - frame = ff_framequeue_take(&link->fifo); + frame = peek_samples ? ff_framequeue_peek(&link->fifo, i) : ff_framequeue_take(&link->fifo); av_samples_copy(buf->extended_data, frame->extended_data, p, 0, frame->nb_samples, link->channels, link->format); p += frame->nb_samples; - av_frame_free(&frame); + if (!peek_samples) + av_frame_free(&frame); } if (p < nb_samples) { unsigned n = nb_samples - p; - frame = ff_framequeue_peek(&link->fifo, 0); + frame = ff_framequeue_peek(&link->fifo, peek_samples ? i : 0); av_samples_copy(buf->extended_data, frame->extended_data, p, 0, n, link->channels, link->format); - ff_framequeue_skip_samples(&link->fifo, n, link->time_base); + if (!peek_samples) + ff_framequeue_skip_samples(&link->fifo, n, link->time_base); } *rframe = buf; @@ -1520,7 +1526,7 @@ int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max, return 0; if (link->status_in) min = FFMIN(min, ff_framequeue_queued_samples(&link->fifo)); - ret = take_samples(link, min, max, &frame); + ret = take_samples(link, min, max, 0, &frame); if (ret < 0) return ret; consume_update(link, frame); @@ -1528,6 +1534,49 @@ int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max, return 1; } +int ff_inlink_peek_samples(AVFilterLink *link, unsigned nb_samples, + AVFrame **rframe) +{ + AVFrame *frame; + int ret; + + av_assert1(nb_samples); + *rframe = NULL; + if (!ff_inlink_check_available_samples(link, nb_samples)) + return 0; + if (link->status_in) + nb_samples = FFMIN(nb_samples, ff_framequeue_queued_samples(&link->fifo)); + ret = take_samples(link, nb_samples, nb_samples, 1, &frame); + if (ret < 0) + return ret; + *rframe = frame; + return !!frame; +} + +void ff_inlink_skip_samples(AVFilterLink *link, unsigned skip_samples) +{ + skip_samples = FFMIN(skip_samples, ff_framequeue_queued_samples(&link->fifo)); + + while (skip_samples > 0) { + AVFrame *frame = ff_inlink_peek_frame(link, 0); + if (skip_samples >= frame->nb_samples) { + frame = ff_framequeue_take(&link->fifo); + skip_samples -= frame->nb_samples; + av_frame_free(&frame); + } else { + break; + } + } + + if (skip_samples) + ff_framequeue_skip_samples(&link->fifo, skip_samples, link->time_base); + + if (ff_inlink_queued_frames(link)) { + AVFrame *frame = ff_inlink_peek_frame(link, 0); + consume_update(link, frame); + } +} + AVFrame *ff_inlink_peek_frame(AVFilterLink *link, size_t idx) { return ff_framequeue_peek(&link->fifo, idx); diff --git a/libavfilter/filters.h b/libavfilter/filters.h index 1157755403..7dc0b35981 100644 --- a/libavfilter/filters.h +++ b/libavfilter/filters.h @@ -115,6 +115,23 @@ int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe); int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max, AVFrame **rframe); +/** + * Peek samples from the link's FIFO. + * + * @return >0 if a samples are available, + * 0 and set rframe to NULL if no samples are available, + * or AVERROR code + */ +int ff_inlink_peek_samples(AVFilterLink *link, unsigned nb_samples, + AVFrame **rframe); + +/** + * Skip samples from the link's FIFO. + * + * @note May trigger process_command() and/or update is_disabled. + */ +void ff_inlink_skip_samples(AVFilterLink *link, unsigned skip); + /** * Access a frame in the link fifo without consuming it. * The first frame is numbered 0; the designated frame must exist. From patchwork Thu Jun 11 16:12:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20288 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id DB088449671 for ; Thu, 11 Jun 2020 19:18:14 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id ACE4468B5D0; Thu, 11 Jun 2020 19:18:14 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f66.google.com (mail-wm1-f66.google.com [209.85.128.66]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5F347687FC7 for ; Thu, 11 Jun 2020 19:18:08 +0300 (EEST) Received: by mail-wm1-f66.google.com with SMTP id k26so5617318wmi.4 for ; Thu, 11 Jun 2020 09:18:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=P0wJtKu/OIcRpY3iSL62nUTFdbRh5txVQseEPH5+6ps=; b=Khp9/vViOkpco/yVR7S7N7TDwHORuBFIoVO02xGu+S9jKHKePqA+xoWTe+E4zNetMA NkugEa+2+NXhK2lhUMi9yhfq+PzVn1ZO4x0DPWSknWAbWZZ8LfC4DaBj4j9YbGdhvU9Q dx+bOYKLOaZu3qazpwLy6KkTqebAK3luoDOgWOdQDPMiIohHtcCMwNV/Ju7M9pqY3EGU 583G8GZbnMcjg08yB5D4CQm6Mnomlxd9nwR0VwcHiAbjR5xG5c9ocvVDKFuyDzwsu3aj NXe83bs8k/p/WRMIRkp7whcwxBPMgfIG4voj7Tr+daVp7VDUBPJY6XLwR5AsOtsbO2P7 LpRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=P0wJtKu/OIcRpY3iSL62nUTFdbRh5txVQseEPH5+6ps=; b=oGMiPwsnrl4tXN90h8Cx2Hp9XCbmBUzpQgYYLMMn2xdSYhdE7+jI21AgmDMIehT1W8 0RjehslvfOmMpMiXA/B4BIYCSzX2EZcu4/NQrIjfb12Rwwd3nGtFVf5GYGcpfwmhZrGA 8eCOxRQJL1jB4aYHVD3eGOONorU3RMEED6/aJAALu2Ev+3i8jYQkvPDX0iiaAeI4ApLq x9pNbtGgy3iefHOySwXFxf0n131ZUdUi/1hnxsWGvhLTSg/z5VkLnCCFsLCGNN3Zp+hy hDYRiKX5Dwjr2usbltmJmJLEiBcnzk+HvHlQHeu9A13p5AYnroPoiLGV5imWx/I0oyoh iUew== X-Gm-Message-State: AOAM530PCO4/41w03oE42gPiFNgKWEipvl6FeXp78p0wwHoK2LOSL+LE aqXHlobSBDFnAhRgQB245oBGAqgWYgM= X-Google-Smtp-Source: ABdhPJzcLss4567ttUAM8golW67X7B7l+78Jhcw27nCZTGBGDDy/HvvopdRc64xVJkUDghM7bOGJyQ== X-Received: by 2002:a7b:cf35:: with SMTP id m21mr9208047wmg.181.1591891959964; Thu, 11 Jun 2020 09:12:39 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.38 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:39 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:23 +0200 Message-Id: <20200611161227.5622-3-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 3/7] avfilter/af_biquads: add reverse filtering support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Add it to all filters, except allpass as it does not make sense to use it in such case. Signed-off-by: Paul B Mahol --- doc/filters.texi | 72 +++++++++++ libavfilter/af_biquads.c | 271 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 333 insertions(+), 10 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 84567dec16..c2960e33c7 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2693,6 +2693,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Commands @@ -2756,6 +2765,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Commands @@ -2826,6 +2844,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Commands @@ -2884,6 +2911,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @section bs2b @@ -3607,6 +3643,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Examples @@ -4084,6 +4129,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Commands @@ -4407,6 +4461,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Examples @@ -5527,6 +5590,15 @@ Specify which channels to filter, by default all available are filtered. @item normalize, n Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + +@item reverse, r +Enable reverse filter processing. Makes filter's phase response +linear at cost of introduced latency. + +@item size, s +Set size of window for reverse filter processing. +Allowed range is from 256 to 32768. +Default value is 2048. @end table @subsection Commands diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index ef28db741a..5e2d74f8f9 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -97,6 +97,8 @@ enum WidthType { typedef struct ChanCache { double i1, i2; double o1, o2; + double ri1, ri2; + double ro1, ro2; int clippings; } ChanCache; @@ -107,6 +109,7 @@ typedef struct BiquadsContext { int width_type; int poles; int csg; + int reverse; double gain; double frequency; @@ -122,10 +125,18 @@ typedef struct BiquadsContext { ChanCache *cache; int block_align; + AVFrame *frame; + int window_size; + int hop_size; + double *window_func_lut; + void (*filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, int len, double *i1, double *i2, double *o1, double *o2, double b0, double b1, double b2, double a1, double a2, int *clippings, int disabled); + void (*reverse_filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, void *dst, + int len, double *i1, double *i2, double *o1, double *o2, + double b0, double b1, double b2, double a1, double a2, int *clippings); } BiquadsContext; static av_cold int init(AVFilterContext *ctx) @@ -259,6 +270,99 @@ BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) BIQUAD_FILTER(flt, float, -1., 1., 0) BIQUAD_FILTER(dbl, double, -1., 1., 0) +#define BIQUAD_FILTER_REVERSE(name, type, min, max, need_clipping) \ +static void biquad_reverse_## name (BiquadsContext *s, \ + const void *input, void *output, void *dst, \ + int len, \ + double *in1, double *in2, \ + double *out1, double *out2, \ + double b0, double b1, double b2, \ + double a1, double a2, int *clippings) \ +{ \ + const type *ibuf = input; \ + type *obuf = output; \ + type *dbuf = dst; \ + double o0; \ + double i1 = *in1; \ + double i2 = *in2; \ + double o1 = *out1; \ + double o2 = *out2; \ + double wet = s->mix; \ + double dry = 1. - wet; \ + double out; \ + int i, j; \ + const double *w = s->window_func_lut; \ + \ + a1 = -a1; \ + a2 = -a2; \ + \ + for (i = 0; i+1 < len; i++) { \ + j = len - 1 - i; \ + o2 = i2 * b2 + i1 * b1 + ibuf[j] * b0 + o2 * a2 + o1 * a1; \ + i2 = ibuf[j]; \ + out = o2 * wet + i2 * dry; \ + if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[j] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[j] = max; \ + } else { \ + obuf[j] = out; \ + } \ + i++; \ + j = len - 1 - i; \ + o1 = i1 * b2 + i2 * b1 + ibuf[j] * b0 + o1 * a2 + o2 * a1; \ + i1 = ibuf[j]; \ + out = o1 * wet + i1 * dry; \ + if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[j] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[j] = max; \ + } else { \ + obuf[j] = out; \ + } \ + } \ + if (i < len) { \ + j = len - 1 - i; \ + o0 = ibuf[j] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ + i2 = i1; \ + i1 = ibuf[j]; \ + o2 = o1; \ + o1 = o0; \ + out = o0 * wet + i1 * dry; \ + if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[j] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[j] = max; \ + } else { \ + obuf[j] = out; \ + } \ + } \ + *in1 = i1; \ + *in2 = i2; \ + *out1 = o1; \ + *out2 = o2; \ + \ + for (i = 0; i < len; i++) \ + dbuf[i] += obuf[i] * w[i]; \ + for (i = 0; i < s->hop_size; i++) \ + obuf[i] = dbuf[i]; \ + memmove(dbuf, dbuf + s->hop_size, \ + (s->window_size * 2 - s->hop_size) * s->block_align); \ + memset(dbuf + s->window_size * 2 - s->hop_size, 0, \ + s->hop_size * s->block_align); \ +} + +BIQUAD_FILTER_REVERSE(s16, int16_t, INT16_MIN, INT16_MAX, 1) +BIQUAD_FILTER_REVERSE(s32, int32_t, INT32_MIN, INT32_MAX, 1) +BIQUAD_FILTER_REVERSE(flt, float, -1., 1., 0) +BIQUAD_FILTER_REVERSE(dbl, double, -1., 1., 0) + static int config_filter(AVFilterLink *outlink, int reset) { AVFilterContext *ctx = outlink->src; @@ -269,6 +373,18 @@ static int config_filter(AVFilterLink *outlink, int reset) double K = tan(w0 / 2.); double alpha, beta; + if (s->reverse && reset) { + s->window_size = s->hop_size * 2; + s->window_func_lut = av_calloc(s->window_size, sizeof(*s->window_func_lut)); + if (!s->window_func_lut) + return AVERROR(ENOMEM); + for (int i = 0; i < s->window_size; i++) + s->window_func_lut[i] = .5*(1-cos(2*M_PI*i/(s->window_size-1))); + s->frame = ff_get_audio_buffer(outlink, s->window_size * 2); + if (!s->frame) + return AVERROR(ENOMEM); + } + if (w0 > M_PI) { av_log(ctx, AV_LOG_ERROR, "Invalid frequency %f. Frequency must be less than half the sample-rate %d.\n", @@ -439,10 +555,22 @@ static int config_filter(AVFilterLink *outlink, int reset) memset(s->cache, 0, sizeof(ChanCache) * inlink->channels); switch (inlink->format) { - case AV_SAMPLE_FMT_S16P: s->filter = biquad_s16; break; - case AV_SAMPLE_FMT_S32P: s->filter = biquad_s32; break; - case AV_SAMPLE_FMT_FLTP: s->filter = biquad_flt; break; - case AV_SAMPLE_FMT_DBLP: s->filter = biquad_dbl; break; + case AV_SAMPLE_FMT_S16P: + s->filter = biquad_s16; + s->reverse_filter = biquad_reverse_s16; + break; + case AV_SAMPLE_FMT_S32P: + s->filter = biquad_s32; + s->reverse_filter = biquad_reverse_s32; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter = biquad_flt; + s->reverse_filter = biquad_reverse_flt; + break; + case AV_SAMPLE_FMT_DBLP: + s->filter = biquad_dbl; + s->reverse_filter = biquad_reverse_dbl; + break; default: av_assert0(0); } @@ -457,9 +585,44 @@ static int config_output(AVFilterLink *outlink) } typedef struct ThreadData { - AVFrame *in, *out; + AVFrame *in, *out, *tmp; } ThreadData; +static int reverse_filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + BiquadsContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + ThreadData *td = arg; + AVFrame *buf = td->in; + AVFrame *out_buf = td->out; + AVFrame *tmp_buf = td->tmp; + AVFrame *dst_buf = s->frame; + const int start = (buf->channels * jobnr) / nb_jobs; + const int end = (buf->channels * (jobnr+1)) / nb_jobs; + int ch; + + for (ch = start; ch < end; ch++) { + if (!((av_channel_layout_extract_channel(inlink->channel_layout, ch) & s->channels))) { + if (buf != out_buf) + memcpy(out_buf->extended_data[ch], buf->extended_data[ch], + buf->nb_samples * s->block_align); + continue; + } + + s->filter(s, buf->extended_data[ch], tmp_buf->extended_data[ch], buf->nb_samples, + &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, + s->b0, s->b1, s->b2, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->reverse_filter(s, tmp_buf->extended_data[ch], out_buf->extended_data[ch], + dst_buf->extended_data[ch], + tmp_buf->nb_samples, &s->cache[ch].ri1, &s->cache[ch].ri2, + &s->cache[ch].ro1, &s->cache[ch].ro2, + s->b0, s->b1, s->b2, s->a1, s->a2, + &s->cache[ch].clippings); + } + + return 0; +} + static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { AVFilterLink *inlink = ctx->inputs[0]; @@ -487,6 +650,46 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job return 0; } +static int reverse_filter_frame(AVFilterLink *inlink, AVFrame *buf) +{ + AVFilterContext *ctx = inlink->dst; + BiquadsContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out_buf, *tmp_buf; + ThreadData td; + int ch; + + out_buf = ff_get_audio_buffer(outlink, buf->nb_samples); + tmp_buf = ff_get_audio_buffer(outlink, buf->nb_samples); + if (!out_buf || !tmp_buf) { + av_frame_free(&out_buf); + av_frame_free(&tmp_buf); + av_frame_free(&buf); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out_buf, buf); + + td.in = buf; + td.out = out_buf; + td.tmp = tmp_buf; + ctx->internal->execute(ctx, reverse_filter_channel, &td, NULL, + FFMIN(outlink->channels, ff_filter_get_nb_threads(ctx))); + + for (ch = 0; ch < outlink->channels; ch++) { + if (s->cache[ch].clippings > 0) + av_log(ctx, AV_LOG_WARNING, "Channel %d clipping %d times. Please reduce gain.\n", + ch, s->cache[ch].clippings); + s->cache[ch].clippings = 0; + } + + out_buf->nb_samples = FFMIN(buf->nb_samples, s->hop_size); + av_frame_free(&buf); + av_frame_free(&tmp_buf); + ff_inlink_skip_samples(inlink, s->hop_size); + + return ff_filter_frame(outlink, out_buf); +} + static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { AVFilterContext *ctx = inlink->dst; @@ -526,6 +729,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static int activate(AVFilterContext *ctx) { + BiquadsContext *s = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; AVFrame *in = NULL; @@ -533,13 +737,18 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - ret = ff_inlink_consume_frame(inlink, &in); - if (ret < 0) - return ret; - if (ret > 0) { - ret = filter_frame(inlink, in); + if (s->reverse) { + ret = ff_inlink_peek_samples(inlink, s->window_size, &in); + if (ret < 0) + return ret; + if (ret > 0) + return reverse_filter_frame(inlink, in); + } else { + ret = ff_inlink_consume_frame(inlink, &in); if (ret < 0) return ret; + if (ret > 0) + return filter_frame(inlink, in); } FF_FILTER_FORWARD_STATUS(inlink, outlink); @@ -566,6 +775,8 @@ static av_cold void uninit(AVFilterContext *ctx) BiquadsContext *s = ctx->priv; av_freep(&s->cache); + av_freep(&s->window_func_lut); + av_frame_free(&s->frame); } static const AVFilterPad inputs[] = { @@ -635,6 +846,10 @@ static const AVOption equalizer_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -661,6 +876,10 @@ static const AVOption bass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -687,6 +906,10 @@ static const AVOption treble_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -712,6 +935,10 @@ static const AVOption bandpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -736,6 +963,10 @@ static const AVOption bandreject_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -762,6 +993,10 @@ static const AVOption lowpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -788,6 +1023,10 @@ static const AVOption highpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -840,6 +1079,10 @@ static const AVOption lowshelf_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -866,6 +1109,10 @@ static const AVOption highshelf_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; @@ -885,6 +1132,10 @@ static const AVOption biquad_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, + {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, + {"s", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, {NULL} }; From patchwork Thu Jun 11 16:12:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20291 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 52DB24497B4 for ; Thu, 11 Jun 2020 19:36:25 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 353E068B501; Thu, 11 Jun 2020 19:36:25 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f194.google.com (mail-lj1-f194.google.com [209.85.208.194]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AF0F168834D for ; Thu, 11 Jun 2020 19:36:18 +0300 (EEST) Received: by mail-lj1-f194.google.com with SMTP id s1so7715564ljo.0 for ; Thu, 11 Jun 2020 09:36:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=u2TRGOSreSXCOGQhsTN4bHlRgOSFvhz/JpG0q4NLewI=; b=i4WX2L4XvCZNCVeuz6I9MzXYtioQhAzmIgKOwz7uR4vHLK8OctIw8kzOGao/hqkajW Gj2U/Q+c2rRU2wy2yeJmofJT5fHdtnL+BqVvsEMYqt8HUlM5W51ucx3El8vrSY2DIofQ PJsGDhFLc5IqScC6a8IWbJ8Zo0HCjR1aOIGhWDatWWnXksZoxy8M2z7eYxYUgPMdjVE1 NeXPoPX4OiBxQWlpKvRxR3k8JC5QLbzXQUB3Fax8E8USKHWXjxOPmXaefkw1Erf7n6oj 1o5gRk5LL6hE8P3rEO8saJG+xIlSUxAhkXpX9kMr4ILraieKudAnNaES05/uUIpbRtRo xWRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=u2TRGOSreSXCOGQhsTN4bHlRgOSFvhz/JpG0q4NLewI=; b=kINv2+Jd0IvZ50o5uJ1l394zEE/ZVd3IlYXC7C/1pi5IqRW/1twrgqGh3mG0hnG1rO KTBe+SS/0sVO2zoA7zLA599CEtFcF8ZNsDN8bVEQhkoffnwcy+M1ClPe4fsl8v4MnWjD lbHuG/7fpNIfnx6B2SlTfxj5JklYvKNPJATjlpXchMfprOSRJNv0u6keshPosBaUjx0G 9FFsukCGKwjm4i6Xl9zuUCEOexnXYdrwXOdp30fR6MqfQDqzPntm+wut+U6s5EEyUMHZ nNH28WDRJy/rj7uErgi/zUmLsv59FA/QR6rSqEJP/UaMW2mmR92AHIe7idXgWLnF/It4 M5Rg== X-Gm-Message-State: AOAM531JyTg24bWQCcu9S4lN4RVhT1iiCwb8VM1oI5lTnzGy02LL0vPK MC+gVhCkaH+UbGzfzaQfcy9rh66eVBE= X-Google-Smtp-Source: ABdhPJzp0ueXI5ZCmf3xa8svrsW/8PY57uUPyVuiUKlRSL6vv1doHtW5oGHpUpTlN+f418OKxQ2xCw== X-Received: by 2002:adf:eec2:: with SMTP id a2mr9834383wrp.136.1591891961596; Thu, 11 Jun 2020 09:12:41 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.40 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:40 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:24 +0200 Message-Id: <20200611161227.5622-4-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 4/7] avfilter/af_biquads: add different transform types X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- libavfilter/af_biquads.c | 351 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 15 deletions(-) diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index 5e2d74f8f9..f3ef3660cc 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -94,6 +94,13 @@ enum WidthType { NB_WTYPE, }; +enum TransformType { + DI, + DII, + TDII, + NB_TTYPE, +}; + typedef struct ChanCache { double i1, i2; double o1, o2; @@ -110,6 +117,7 @@ typedef struct BiquadsContext { int poles; int csg; int reverse; + int transform_type; double gain; double frequency; @@ -363,6 +371,216 @@ BIQUAD_FILTER_REVERSE(s32, int32_t, INT32_MIN, INT32_MAX, 1) BIQUAD_FILTER_REVERSE(flt, float, -1., 1., 0) BIQUAD_FILTER_REVERSE(dbl, double, -1., 1., 0) +#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping) \ +static void biquad_dii_## name (BiquadsContext *s, \ + const void *input, void *output, int len, \ + double *z1, double *z2, \ + double *unused1, double *unused2, \ + double b0, double b1, double b2, \ + double a1, double a2, int *clippings, \ + int disabled) \ +{ \ + const type *ibuf = input; \ + type *obuf = output; \ + double w1 = *z1; \ + double w2 = *z2; \ + double wet = s->mix; \ + double dry = 1. - wet; \ + double in, out, w0; \ + \ + a1 = -a1; \ + a2 = -a2; \ + \ + for (int i = 0; i < len; i++) { \ + in = ibuf[i]; \ + w0 = in + a1 * w1 + a2 * w2; \ + out = b0 * w0 + b1 * w1 + b2 * w2; \ + w2 = w1; \ + w1 = w0; \ + out = out * wet + in * dry; \ + if (disabled) { \ + obuf[i] = in; \ + } else if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[i] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[i] = max; \ + } else { \ + obuf[i] = out; \ + } \ + } \ + *z1 = w1; \ + *z2 = w2; \ +} + +BIQUAD_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) +BIQUAD_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) +BIQUAD_DII_FILTER(flt, float, -1., 1., 0) +BIQUAD_DII_FILTER(dbl, double, -1., 1., 0) + +#define BIQUAD_REVERSE_DII_FILTER(name, type, min, max, need_clipping) \ +static void biquad_reverse_dii_## name (BiquadsContext *s, \ + const void *input, void *output, void *dst, \ + int len, \ + double *z1, double *z2, \ + double *unused1, double *unused2, \ + double b0, double b1, double b2, \ + double a1, double a2, int *clippings) \ +{ \ + const double *w = s->window_func_lut; \ + const type *ibuf = input; \ + type *obuf = output; \ + type *dbuf = dst; \ + double w1 = *z1; \ + double w2 = *z2; \ + double wet = s->mix; \ + double dry = 1. - wet; \ + double in, out, w0; \ + \ + a1 = -a1; \ + a2 = -a2; \ + \ + for (int i = 0; i < len; i++) { \ + int j = len - 1 - i; \ + in = ibuf[j]; \ + w0 = in + a1 * w1 + a2 * w2; \ + out = b0 * w0 + b1 * w1 + b2 * w2; \ + w2 = w1; \ + w1 = w0; \ + out = out * wet + in * dry; \ + if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[j] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[j] = max; \ + } else { \ + obuf[j] = out; \ + } \ + } \ + *z1 = w1; \ + *z2 = w2; \ + \ + for (int i = 0; i < len; i++) \ + dbuf[i] += obuf[i] * w[i]; \ + for (int i = 0; i < s->hop_size; i++) \ + obuf[i] = dbuf[i]; \ + memmove(dbuf, dbuf + s->hop_size, \ + (s->window_size * 2 - s->hop_size) * s->block_align); \ + memset(dbuf + s->window_size * 2 - s->hop_size, 0, \ + s->hop_size * s->block_align); \ +} + +BIQUAD_REVERSE_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) +BIQUAD_REVERSE_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) +BIQUAD_REVERSE_DII_FILTER(flt, float, -1., 1., 0) +BIQUAD_REVERSE_DII_FILTER(dbl, double, -1., 1., 0) + +#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping) \ +static void biquad_tdii_## name (BiquadsContext *s, \ + const void *input, void *output, int len, \ + double *z1, double *z2, \ + double *unused1, double *unused2, \ + double b0, double b1, double b2, \ + double a1, double a2, int *clippings, \ + int disabled) \ +{ \ + const type *ibuf = input; \ + type *obuf = output; \ + double w1 = *z1; \ + double w2 = *z2; \ + double wet = s->mix; \ + double dry = 1. - wet; \ + double in, out; \ + \ + a1 = -a1; \ + a2 = -a2; \ + \ + for (int i = 0; i < len; i++) { \ + in = ibuf[i]; \ + out = b0 * in + w1; \ + w1 = b1 * in + w2 + a1 * out; \ + w2 = b2 * in + a2 * out; \ + out = out * wet + in * dry; \ + if (disabled) { \ + obuf[i] = in; \ + } else if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[i] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[i] = max; \ + } else { \ + obuf[i] = out; \ + } \ + } \ + *z1 = w1; \ + *z2 = w2; \ +} + +BIQUAD_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) +BIQUAD_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) +BIQUAD_TDII_FILTER(flt, float, -1., 1., 0) +BIQUAD_TDII_FILTER(dbl, double, -1., 1., 0) + +#define BIQUAD_REVERSE_TDII_FILTER(name, type, min, max, need_clipping) \ +static void biquad_reverse_tdii_## name (BiquadsContext *s, \ + const void *input, void *output, void *dst, \ + int len, \ + double *z1, double *z2, \ + double *unused1, double *unused2, \ + double b0, double b1, double b2, \ + double a1, double a2, int *clippings) \ +{ \ + const double *w = s->window_func_lut; \ + const type *ibuf = input; \ + type *obuf = output; \ + type *dbuf = dst; \ + double w1 = *z1; \ + double w2 = *z2; \ + double wet = s->mix; \ + double dry = 1. - wet; \ + double in, out; \ + \ + a1 = -a1; \ + a2 = -a2; \ + \ + for (int i = 0; i < len; i++) { \ + int j = len - 1 - i; \ + in = ibuf[j]; \ + out = b0 * in + w1; \ + w1 = b1 * in + w2 + a1 * out; \ + w2 = b2 * in + a2 * out; \ + out = out * wet + in * dry; \ + if (need_clipping && out < min) { \ + (*clippings)++; \ + obuf[j] = min; \ + } else if (need_clipping && out > max) { \ + (*clippings)++; \ + obuf[j] = max; \ + } else { \ + obuf[j] = out; \ + } \ + } \ + *z1 = w1; \ + *z2 = w2; \ + \ + for (int i = 0; i < len; i++) \ + dbuf[i] += obuf[i] * w[i]; \ + for (int i = 0; i < s->hop_size; i++) \ + obuf[i] = dbuf[i]; \ + memmove(dbuf, dbuf + s->hop_size, \ + (s->window_size * 2 - s->hop_size) * s->block_align); \ + memset(dbuf + s->window_size * 2 - s->hop_size, 0, \ + s->hop_size * s->block_align); \ +} + +BIQUAD_REVERSE_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) +BIQUAD_REVERSE_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) +BIQUAD_REVERSE_TDII_FILTER(flt, float, -1., 1., 0) +BIQUAD_REVERSE_TDII_FILTER(dbl, double, -1., 1., 0) + static int config_filter(AVFilterLink *outlink, int reset) { AVFilterContext *ctx = outlink->src; @@ -554,24 +772,72 @@ static int config_filter(AVFilterLink *outlink, int reset) if (reset) memset(s->cache, 0, sizeof(ChanCache) * inlink->channels); - switch (inlink->format) { - case AV_SAMPLE_FMT_S16P: - s->filter = biquad_s16; - s->reverse_filter = biquad_reverse_s16; - break; - case AV_SAMPLE_FMT_S32P: - s->filter = biquad_s32; - s->reverse_filter = biquad_reverse_s32; + switch (s->transform_type) { + case DI: + switch (inlink->format) { + case AV_SAMPLE_FMT_S16P: + s->filter = biquad_s16; + s->reverse_filter = biquad_reverse_s16; + break; + case AV_SAMPLE_FMT_S32P: + s->filter = biquad_s32; + s->reverse_filter = biquad_reverse_s32; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter = biquad_flt; + s->reverse_filter = biquad_reverse_flt; + break; + case AV_SAMPLE_FMT_DBLP: + s->filter = biquad_dbl; + s->reverse_filter = biquad_reverse_dbl; + break; + default: av_assert0(0); + } break; - case AV_SAMPLE_FMT_FLTP: - s->filter = biquad_flt; - s->reverse_filter = biquad_reverse_flt; + case DII: + switch (inlink->format) { + case AV_SAMPLE_FMT_S16P: + s->filter = biquad_dii_s16; + s->reverse_filter = biquad_reverse_dii_s16; + break; + case AV_SAMPLE_FMT_S32P: + s->filter = biquad_dii_s32; + s->reverse_filter = biquad_reverse_dii_s32; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter = biquad_dii_flt; + s->reverse_filter = biquad_reverse_dii_flt; + break; + case AV_SAMPLE_FMT_DBLP: + s->filter = biquad_dii_dbl; + s->reverse_filter = biquad_reverse_dii_dbl; + break; + default: av_assert0(0); + } break; - case AV_SAMPLE_FMT_DBLP: - s->filter = biquad_dbl; - s->reverse_filter = biquad_reverse_dbl; + case TDII: + switch (inlink->format) { + case AV_SAMPLE_FMT_S16P: + s->filter = biquad_tdii_s16; + s->reverse_filter = biquad_reverse_tdii_s16; + break; + case AV_SAMPLE_FMT_S32P: + s->filter = biquad_tdii_s32; + s->reverse_filter = biquad_reverse_tdii_s32; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter = biquad_tdii_flt; + s->reverse_filter = biquad_reverse_tdii_flt; + break; + case AV_SAMPLE_FMT_DBLP: + s->filter = biquad_tdii_dbl; + s->reverse_filter = biquad_reverse_tdii_dbl; + break; + default: av_assert0(0); + } break; - default: av_assert0(0); + default: + av_assert0(0); } s->block_align = av_get_bytes_per_sample(inlink->format); @@ -846,6 +1112,11 @@ static const AVOption equalizer_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -876,6 +1147,11 @@ static const AVOption bass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -906,6 +1182,11 @@ static const AVOption treble_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -935,6 +1216,11 @@ static const AVOption bandpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -963,6 +1249,11 @@ static const AVOption bandreject_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -993,6 +1284,11 @@ static const AVOption lowpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -1023,6 +1319,11 @@ static const AVOption highpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -1051,6 +1352,11 @@ static const AVOption allpass_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"order", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, {"o", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, {NULL} @@ -1079,6 +1385,11 @@ static const AVOption lowshelf_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -1109,6 +1420,11 @@ static const AVOption highshelf_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, @@ -1132,6 +1448,11 @@ static const AVOption biquad_options[] = { {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"}, + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, {"reverse", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"r", "reverse filtering", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF}, {"size", "set window size for reverse filtering", OFFSET(hop_size), AV_OPT_TYPE_INT, {.i64=2048}, 256, 32768, AF}, From patchwork Thu Jun 11 16:12:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20289 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 022B6449705 for ; Thu, 11 Jun 2020 19:20:00 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CDE5868B120; Thu, 11 Jun 2020 19:19:59 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f65.google.com (mail-wm1-f65.google.com [209.85.128.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D67E968AE6F for ; Thu, 11 Jun 2020 19:19:53 +0300 (EEST) Received: by mail-wm1-f65.google.com with SMTP id y20so5620325wmi.2 for ; Thu, 11 Jun 2020 09:19:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=vijHILUzah9lntITfE05an1FeBIkonXysDB1ryHzpSk=; b=mLTlV9Pz+824MxloMEHUyGeeyCph58QE2XOiiR1AAb4/BXYLrUqfrHL3B9iTORSIjW PZW/i0JKZ3Zmrl3bn4aV1QEKVsXhNbUSXaHa6F1cRhhijBmrat8TnEkGrodWCq0062aB iItr89BUHWO4RzHc48Clc/Ac8wdijeOd7K8MsAQ1GyKwFkIrvV9ZYYznbya4VtMiLnKP QS8hV56UFu69YfZdotNQxMfZDh6O2xkaXFIEMTlbzsEjpch89nhdLWOoHrLb77ocXmyI vWpLyupiL2cHXPNB4sMR6dUKeIMc5EHzngnym7CL1zxvNuNbORelLCaai+UwbjKBAhJi WbZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=vijHILUzah9lntITfE05an1FeBIkonXysDB1ryHzpSk=; b=UGhVf5/uD1Sm9BeDgflxvd5csE2qdX8BG+Fs9FDhS9J0rk/Im5uonylMEZHd/dEOMn jxhdidwlvf3GOjHEmIPN07oIAXSNq4VVhqBWV2Lb9pb5cYsVrz3rCLGDb75GYk6FWEjL 23CDahIOkqiiL71jvnWoLN89udv4PJCU3imJW7gVPj80LhNv2EZ4BvfLfrmfmS4r2uqB 9VD0027SxI2dc42PT03JrrLhPdZF/LgzQsWAyFopzu0yM4zVS1R92Ia00DsV6BMSRHpn ma9LEzKYm0lpFC4UKVPFBwtIjyYLJxkUy8EU62Ksp8zsoJWAudMvXNI1xPXP+pB6DaLC pGDg== X-Gm-Message-State: AOAM533ToPU3aaDM8Rv7Dn0Xw3VTTNeNDs11JidArQsLXWy2Hta7X3uu /OFUzz2kCpeU0Emo1NWhgkgPIe9dozM= X-Google-Smtp-Source: ABdhPJyiqMvgQyW5ULtI1j7Cltt6oBIld0QcQhGB1sBi3H148iZO4MNXSRfT1XO+A8d8r4PqjO3fVQ== X-Received: by 2002:a7b:c4cc:: with SMTP id g12mr8781954wmk.171.1591891962956; Thu, 11 Jun 2020 09:12:42 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.41 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:42 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:25 +0200 Message-Id: <20200611161227.5622-5-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 5/7] avfilter/af_biquads: refactor code X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- libavfilter/af_biquads.c | 279 ++++++++++----------------------------- 1 file changed, 68 insertions(+), 211 deletions(-) diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index f3ef3660cc..c998f3e982 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -138,13 +138,14 @@ typedef struct BiquadsContext { int hop_size; double *window_func_lut; - void (*filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, int len, + void (*filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, void *dst, int len, double *i1, double *i2, double *o1, double *o2, double b0, double b1, double b2, double a1, double a2, int *clippings, int disabled); void (*reverse_filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, void *dst, int len, double *i1, double *i2, double *o1, double *o2, - double b0, double b1, double b2, double a1, double a2, int *clippings); + double b0, double b1, double b2, double a1, double a2, int *clippings, + int disabled); } BiquadsContext; static av_cold int init(AVFilterContext *ctx) @@ -195,97 +196,15 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates(ctx, formats); } -#define BIQUAD_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_FILTER(name, type, min, max, need_clipping, reverse) \ static void biquad_## name (BiquadsContext *s, \ - const void *input, void *output, int len, \ - double *in1, double *in2, \ - double *out1, double *out2, \ - double b0, double b1, double b2, \ - double a1, double a2, int *clippings, \ - int disabled) \ -{ \ - const type *ibuf = input; \ - type *obuf = output; \ - double i1 = *in1; \ - double i2 = *in2; \ - double o1 = *out1; \ - double o2 = *out2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double out; \ - int i; \ - a1 = -a1; \ - a2 = -a2; \ - \ - for (i = 0; i+1 < len; i++) { \ - o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1; \ - i2 = ibuf[i]; \ - out = o2 * wet + i2 * dry; \ - if (disabled) { \ - obuf[i] = i2; \ - } else if (need_clipping && out < min) { \ - (*clippings)++; \ - obuf[i] = min; \ - } else if (need_clipping && out > max) { \ - (*clippings)++; \ - obuf[i] = max; \ - } else { \ - obuf[i] = out; \ - } \ - i++; \ - o1 = i1 * b2 + i2 * b1 + ibuf[i] * b0 + o1 * a2 + o2 * a1; \ - i1 = ibuf[i]; \ - out = o1 * wet + i1 * dry; \ - if (disabled) { \ - obuf[i] = i1; \ - } else if (need_clipping && out < min) { \ - (*clippings)++; \ - obuf[i] = min; \ - } else if (need_clipping && out > max) { \ - (*clippings)++; \ - obuf[i] = max; \ - } else { \ - obuf[i] = out; \ - } \ - } \ - if (i < len) { \ - double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ - i2 = i1; \ - i1 = ibuf[i]; \ - o2 = o1; \ - o1 = o0; \ - out = o0 * wet + i1 * dry; \ - if (disabled) { \ - obuf[i] = i1; \ - } else if (need_clipping && out < min) { \ - (*clippings)++; \ - obuf[i] = min; \ - } else if (need_clipping && out > max) { \ - (*clippings)++; \ - obuf[i] = max; \ - } else { \ - obuf[i] = out; \ - } \ - } \ - *in1 = i1; \ - *in2 = i2; \ - *out1 = o1; \ - *out2 = o2; \ -} - -BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_FILTER(flt, float, -1., 1., 0) -BIQUAD_FILTER(dbl, double, -1., 1., 0) - -#define BIQUAD_FILTER_REVERSE(name, type, min, max, need_clipping) \ -static void biquad_reverse_## name (BiquadsContext *s, \ const void *input, void *output, void *dst, \ int len, \ double *in1, double *in2, \ double *out1, double *out2, \ double b0, double b1, double b2, \ - double a1, double a2, int *clippings) \ + double a1, double a2, int *clippings, \ + int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ @@ -305,11 +224,13 @@ static void biquad_reverse_## name (BiquadsContext *s, \ a2 = -a2; \ \ for (i = 0; i+1 < len; i++) { \ - j = len - 1 - i; \ + j = reverse ? len - 1 - i : i; \ o2 = i2 * b2 + i1 * b1 + ibuf[j] * b0 + o2 * a2 + o1 * a1; \ i2 = ibuf[j]; \ out = o2 * wet + i2 * dry; \ - if (need_clipping && out < min) { \ + if (disabled) { \ + obuf[j] = ibuf[j]; \ + } else if (need_clipping && out < min) { \ (*clippings)++; \ obuf[j] = min; \ } else if (need_clipping && out > max) { \ @@ -319,11 +240,13 @@ static void biquad_reverse_## name (BiquadsContext *s, \ obuf[j] = out; \ } \ i++; \ - j = len - 1 - i; \ + j = reverse ? len - 1 - i : i; \ o1 = i1 * b2 + i2 * b1 + ibuf[j] * b0 + o1 * a2 + o2 * a1; \ i1 = ibuf[j]; \ out = o1 * wet + i1 * dry; \ - if (need_clipping && out < min) { \ + if (disabled) { \ + obuf[j] = ibuf[j]; \ + } else if (need_clipping && out < min) { \ (*clippings)++; \ obuf[j] = min; \ } else if (need_clipping && out > max) { \ @@ -334,14 +257,16 @@ static void biquad_reverse_## name (BiquadsContext *s, \ } \ } \ if (i < len) { \ - j = len - 1 - i; \ + j = reverse ? len - 1 - i : i; \ o0 = ibuf[j] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ i2 = i1; \ i1 = ibuf[j]; \ o2 = o1; \ o1 = o0; \ out = o0 * wet + i1 * dry; \ - if (need_clipping && out < min) { \ + if (disabled) { \ + obuf[j] = ibuf[j]; \ + } else if (need_clipping && out < min) { \ (*clippings)++; \ obuf[j] = min; \ } else if (need_clipping && out > max) { \ @@ -356,6 +281,8 @@ static void biquad_reverse_## name (BiquadsContext *s, \ *out1 = o1; \ *out2 = o2; \ \ + if (!reverse) return; \ + \ for (i = 0; i < len; i++) \ dbuf[i] += obuf[i] * w[i]; \ for (i = 0; i < s->hop_size; i++) \ @@ -366,67 +293,25 @@ static void biquad_reverse_## name (BiquadsContext *s, \ s->hop_size * s->block_align); \ } -BIQUAD_FILTER_REVERSE(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_FILTER_REVERSE(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_FILTER_REVERSE(flt, float, -1., 1., 0) -BIQUAD_FILTER_REVERSE(dbl, double, -1., 1., 0) - -#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping) \ -static void biquad_dii_## name (BiquadsContext *s, \ - const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a1, double a2, int *clippings, \ - int disabled) \ -{ \ - const type *ibuf = input; \ - type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out, w0; \ - \ - a1 = -a1; \ - a2 = -a2; \ - \ - for (int i = 0; i < len; i++) { \ - in = ibuf[i]; \ - w0 = in + a1 * w1 + a2 * w2; \ - out = b0 * w0 + b1 * w1 + b2 * w2; \ - w2 = w1; \ - w1 = w0; \ - out = out * wet + in * dry; \ - if (disabled) { \ - obuf[i] = in; \ - } else if (need_clipping && out < min) { \ - (*clippings)++; \ - obuf[i] = min; \ - } else if (need_clipping && out > max) { \ - (*clippings)++; \ - obuf[i] = max; \ - } else { \ - obuf[i] = out; \ - } \ - } \ - *z1 = w1; \ - *z2 = w2; \ -} +BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1, 0) +BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1, 0) +BIQUAD_FILTER(flt, float, -1., 1., 0, 0) +BIQUAD_FILTER(dbl, double, -1., 1., 0, 0) -BIQUAD_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_DII_FILTER(flt, float, -1., 1., 0) -BIQUAD_DII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_FILTER(reverse_s16, int16_t, INT16_MIN, INT16_MAX, 1, 1) +BIQUAD_FILTER(reverse_s32, int32_t, INT32_MIN, INT32_MAX, 1, 1) +BIQUAD_FILTER(reverse_flt, float, -1., 1., 0, 1) +BIQUAD_FILTER(reverse_dbl, double, -1., 1., 0, 1) -#define BIQUAD_REVERSE_DII_FILTER(name, type, min, max, need_clipping) \ -static void biquad_reverse_dii_## name (BiquadsContext *s, \ +#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping, reverse) \ +static void biquad_## name (BiquadsContext *s, \ const void *input, void *output, void *dst, \ int len, \ double *z1, double *z2, \ double *unused1, double *unused2, \ double b0, double b1, double b2, \ - double a1, double a2, int *clippings) \ + double a1, double a2, int *clippings, \ + int disabled) \ { \ const double *w = s->window_func_lut; \ const type *ibuf = input; \ @@ -442,14 +327,16 @@ static void biquad_reverse_dii_## name (BiquadsContext *s, \ a2 = -a2; \ \ for (int i = 0; i < len; i++) { \ - int j = len - 1 - i; \ + int j = reverse ? len - 1 - i : i; \ in = ibuf[j]; \ w0 = in + a1 * w1 + a2 * w2; \ out = b0 * w0 + b1 * w1 + b2 * w2; \ w2 = w1; \ w1 = w0; \ out = out * wet + in * dry; \ - if (need_clipping && out < min) { \ + if (disabled) { \ + obuf[j] = in; \ + } else if (need_clipping && out < min) { \ (*clippings)++; \ obuf[j] = min; \ } else if (need_clipping && out > max) { \ @@ -462,6 +349,8 @@ static void biquad_reverse_dii_## name (BiquadsContext *s, \ *z1 = w1; \ *z2 = w2; \ \ + if (!reverse) return; \ + \ for (int i = 0; i < len; i++) \ dbuf[i] += obuf[i] * w[i]; \ for (int i = 0; i < s->hop_size; i++) \ @@ -472,66 +361,25 @@ static void biquad_reverse_dii_## name (BiquadsContext *s, \ s->hop_size * s->block_align); \ } -BIQUAD_REVERSE_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_REVERSE_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_REVERSE_DII_FILTER(flt, float, -1., 1., 0) -BIQUAD_REVERSE_DII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_DII_FILTER(dii_s16, int16_t, INT16_MIN, INT16_MAX, 1, 0) +BIQUAD_DII_FILTER(dii_s32, int32_t, INT32_MIN, INT32_MAX, 1, 0) +BIQUAD_DII_FILTER(dii_flt, float, -1., 1., 0, 0) +BIQUAD_DII_FILTER(dii_dbl, double, -1., 1., 0, 0) -#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping) \ -static void biquad_tdii_## name (BiquadsContext *s, \ - const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a1, double a2, int *clippings, \ - int disabled) \ -{ \ - const type *ibuf = input; \ - type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - \ - a1 = -a1; \ - a2 = -a2; \ - \ - for (int i = 0; i < len; i++) { \ - in = ibuf[i]; \ - out = b0 * in + w1; \ - w1 = b1 * in + w2 + a1 * out; \ - w2 = b2 * in + a2 * out; \ - out = out * wet + in * dry; \ - if (disabled) { \ - obuf[i] = in; \ - } else if (need_clipping && out < min) { \ - (*clippings)++; \ - obuf[i] = min; \ - } else if (need_clipping && out > max) { \ - (*clippings)++; \ - obuf[i] = max; \ - } else { \ - obuf[i] = out; \ - } \ - } \ - *z1 = w1; \ - *z2 = w2; \ -} - -BIQUAD_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_TDII_FILTER(flt, float, -1., 1., 0) -BIQUAD_TDII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_DII_FILTER(reverse_dii_s16, int16_t, INT16_MIN, INT16_MAX, 1, 1) +BIQUAD_DII_FILTER(reverse_dii_s32, int32_t, INT32_MIN, INT32_MAX, 1, 1) +BIQUAD_DII_FILTER(reverse_dii_flt, float, -1., 1., 0, 1) +BIQUAD_DII_FILTER(reverse_dii_dbl, double, -1., 1., 0, 1) -#define BIQUAD_REVERSE_TDII_FILTER(name, type, min, max, need_clipping) \ -static void biquad_reverse_tdii_## name (BiquadsContext *s, \ +#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping, reverse) \ +static void biquad_## name (BiquadsContext *s, \ const void *input, void *output, void *dst, \ int len, \ double *z1, double *z2, \ double *unused1, double *unused2, \ double b0, double b1, double b2, \ - double a1, double a2, int *clippings) \ + double a1, double a2, int *clippings, \ + int disabled) \ { \ const double *w = s->window_func_lut; \ const type *ibuf = input; \ @@ -547,13 +395,15 @@ static void biquad_reverse_tdii_## name (BiquadsContext *s, \ a2 = -a2; \ \ for (int i = 0; i < len; i++) { \ - int j = len - 1 - i; \ + int j = reverse ? len - 1 - i : i; \ in = ibuf[j]; \ out = b0 * in + w1; \ w1 = b1 * in + w2 + a1 * out; \ w2 = b2 * in + a2 * out; \ out = out * wet + in * dry; \ - if (need_clipping && out < min) { \ + if (disabled) { \ + obuf[j] = in; \ + } else if (need_clipping && out < min) { \ (*clippings)++; \ obuf[j] = min; \ } else if (need_clipping && out > max) { \ @@ -566,6 +416,8 @@ static void biquad_reverse_tdii_## name (BiquadsContext *s, \ *z1 = w1; \ *z2 = w2; \ \ + if (!reverse) return; \ + \ for (int i = 0; i < len; i++) \ dbuf[i] += obuf[i] * w[i]; \ for (int i = 0; i < s->hop_size; i++) \ @@ -576,10 +428,15 @@ static void biquad_reverse_tdii_## name (BiquadsContext *s, \ s->hop_size * s->block_align); \ } -BIQUAD_REVERSE_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_REVERSE_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_REVERSE_TDII_FILTER(flt, float, -1., 1., 0) -BIQUAD_REVERSE_TDII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_TDII_FILTER(tdii_s16, int16_t, INT16_MIN, INT16_MAX, 1, 0) +BIQUAD_TDII_FILTER(tdii_s32, int32_t, INT32_MIN, INT32_MAX, 1, 0) +BIQUAD_TDII_FILTER(tdii_flt, float, -1., 1., 0, 0) +BIQUAD_TDII_FILTER(tdii_dbl, double, -1., 1., 0, 0) + +BIQUAD_TDII_FILTER(reverse_tdii_s16, int16_t, INT16_MIN, INT16_MAX, 1, 1) +BIQUAD_TDII_FILTER(reverse_tdii_s32, int32_t, INT32_MIN, INT32_MAX, 1, 1) +BIQUAD_TDII_FILTER(reverse_tdii_flt, float, -1., 1., 0, 1) +BIQUAD_TDII_FILTER(reverse_tdii_dbl, double, -1., 1., 0, 1) static int config_filter(AVFilterLink *outlink, int reset) { @@ -875,7 +732,7 @@ static int reverse_filter_channel(AVFilterContext *ctx, void *arg, int jobnr, in continue; } - s->filter(s, buf->extended_data[ch], tmp_buf->extended_data[ch], buf->nb_samples, + s->filter(s, buf->extended_data[ch], tmp_buf->extended_data[ch], NULL, buf->nb_samples, &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, s->b0, s->b1, s->b2, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); s->reverse_filter(s, tmp_buf->extended_data[ch], out_buf->extended_data[ch], @@ -883,7 +740,7 @@ static int reverse_filter_channel(AVFilterContext *ctx, void *arg, int jobnr, in tmp_buf->nb_samples, &s->cache[ch].ri1, &s->cache[ch].ri2, &s->cache[ch].ro1, &s->cache[ch].ro2, s->b0, s->b1, s->b2, s->a1, s->a2, - &s->cache[ch].clippings); + &s->cache[ch].clippings, ctx->is_disabled); } return 0; @@ -908,7 +765,7 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job continue; } - s->filter(s, buf->extended_data[ch], out_buf->extended_data[ch], buf->nb_samples, + s->filter(s, buf->extended_data[ch], out_buf->extended_data[ch], NULL, buf->nb_samples, &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, s->b0, s->b1, s->b2, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); } From patchwork Thu Jun 11 16:12:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20290 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 823E4449FDA for ; Thu, 11 Jun 2020 19:20:54 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 64FFB68B5D0; Thu, 11 Jun 2020 19:20:54 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f195.google.com (mail-lj1-f195.google.com [209.85.208.195]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A41BD68AF29 for ; Thu, 11 Jun 2020 19:20:47 +0300 (EEST) Received: by mail-lj1-f195.google.com with SMTP id c17so7602484lji.11 for ; Thu, 11 Jun 2020 09:20:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=npAnOjqlbmbFO08sjb88XRyGZ2pSNNg7Hhwv+6ze67Q=; b=Rjg+Dvzg0l2TwVQj8ps+6KfvlYhd0SqsUgI/RhD5kjQaXArsVqKvhCd40+7QIrl6Im bokqfTuJZcTPo/jVhXnKLEvY5PcHmSXD2YSvnFHhOLBYfb+1fO1QhSZdzcyb7DU557ch 0aRATgfV4Pz4Unog8Cy76SkzBgeCqCvNyUMoi8DChRLPSBLs61du0kMLBDeE/Eljumo9 4Yn6KgixytX3n2sRxEWRGCMelQDk9Vi9sHmVy+cgY8NF5fe8SEPjdKNDVrbT9wMYsWwy GES1afZQ5PwSEPjl+aelWocU+ZGmmLertTqBd3jRQsCTDT4tseJgvFoe7s+j04kyKiXj hl1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=npAnOjqlbmbFO08sjb88XRyGZ2pSNNg7Hhwv+6ze67Q=; b=EcxU/orBLBSgNfS5xlRo7Vej1dJNB1NMlANT0sJKn02A8Tw0jdUMeQjkDcKRMGarc1 4ZmE+Ce/fuwTOKh4GIcN8AAsRmDToKPNw7dQ0SB8ls41hVfLj0VOWRkCOI5faX33FT1F 7evShr796hrmWITYCJ7nah8xF+j5S+1/han+r0S+Rm/ia5jg18rkHgyhhGuoEP0uAq7W HjgTCtUoG2ArLtgZ2Vy+7Axok6uEdYUrkFSa+/vfDmcbB0nPVfV+j6tzNX3cOmZgfzVJ fm0xryDyIITtMNf83nwdj+tw91OqYq3e9w2fP1Q1VrBrz/cilhV97MWWPckavRei44P+ 0tYg== X-Gm-Message-State: AOAM530nJYyxz7/JtGk8EsRHNCYm4l5H2YQzqqTobWz768NR2EK4tb0f YZlfHn7DupNUcg+YVWjUpieTfH0O7kU= X-Google-Smtp-Source: ABdhPJwSp5BLhq4P41nbNukwy+EENlDIplmolhRw2UHJfacspOMlr84zR5fzeM4L3MYP78Z+P1b9VA== X-Received: by 2002:adf:ec03:: with SMTP id x3mr10286509wrn.297.1591891964032; Thu, 11 Jun 2020 09:12:44 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.43 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:43 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:26 +0200 Message-Id: <20200611161227.5622-6-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 6/7] avfilter/avf_showspectrumpic: switch to internal queue X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Fixes filtering audio with more than 1.5h length. Signed-off-by: Paul B Mahol --- libavfilter/avf_showspectrum.c | 119 ++++++++++++++++----------------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/libavfilter/avf_showspectrum.c b/libavfilter/avf_showspectrum.c index e99f377fb0..ead57aadac 100644 --- a/libavfilter/avf_showspectrum.c +++ b/libavfilter/avf_showspectrum.c @@ -1610,91 +1610,90 @@ static const AVOption showspectrumpic_options[] = { AVFILTER_DEFINE_CLASS(showspectrumpic); -static int showspectrumpic_request_frame(AVFilterLink *outlink) +static int showspectrumpic_request_frame(AVFilterLink *outlink, int64_t samples) { AVFilterContext *ctx = outlink->src; ShowSpectrumContext *s = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; - int ret, samples; + int consumed = 0; + int x = 0, sz = s->orientation == VERTICAL ? s->w : s->h; + int ret, ch, spf, spb; + AVFrame *fin = NULL; - ret = ff_request_frame(inlink); - samples = av_audio_fifo_size(s->fifo); - if (ret == AVERROR_EOF && s->outpicref && samples > 0) { - int consumed = 0; - int x = 0, sz = s->orientation == VERTICAL ? s->w : s->h; - int ch, spf, spb; - AVFrame *fin; + spf = s->win_size * (samples / ((s->win_size * sz) * ceil(samples / (float)(s->win_size * sz)))); + spf = FFMAX(1, spf); - spf = s->win_size * (samples / ((s->win_size * sz) * ceil(samples / (float)(s->win_size * sz)))); - spf = FFMAX(1, spf); + spb = (samples / (spf * sz)) * spf; - spb = (samples / (spf * sz)) * spf; - - fin = ff_get_audio_buffer(inlink, s->win_size); - if (!fin) - return AVERROR(ENOMEM); - - while (x < sz) { - ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data, s->win_size); - if (ret < 0) { - av_frame_free(&fin); - return ret; - } - - av_audio_fifo_drain(s->fifo, spf); - - if (ret < s->win_size) { - for (ch = 0; ch < s->nb_display_channels; ch++) { - memset(fin->extended_data[ch] + ret * sizeof(float), 0, - (s->win_size - ret) * sizeof(float)); - } - } - - ctx->internal->execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels); - acalc_magnitudes(s); - - consumed += spf; - if (consumed >= spb) { - int h = s->orientation == VERTICAL ? s->h : s->w; + while (x < sz) { + ret = ff_inlink_peek_samples(inlink, s->win_size, &fin); + if (ret < 0) + return ret; + if (ret == 0) + break; - scale_magnitudes(s, 1.f / (consumed / spf)); - plot_spectrum_column(inlink, fin); - consumed = 0; - x++; - for (ch = 0; ch < s->nb_display_channels; ch++) - memset(s->magnitudes[ch], 0, h * sizeof(float)); - } + ctx->internal->execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels); + acalc_magnitudes(s); + + consumed += spf; + if (consumed >= spb) { + int h = s->orientation == VERTICAL ? s->h : s->w; + + scale_magnitudes(s, 1.f / (consumed / spf)); + plot_spectrum_column(inlink, fin); + consumed = 0; + x++; + for (ch = 0; ch < s->nb_display_channels; ch++) + memset(s->magnitudes[ch], 0, h * sizeof(float)); } - av_frame_free(&fin); - s->outpicref->pts = 0; + ff_inlink_skip_samples(inlink, spf); + } - if (s->legend) - draw_legend(ctx, samples); + s->outpicref->pts = 0; - ret = ff_filter_frame(outlink, s->outpicref); - s->outpicref = NULL; - } + if (s->legend) + draw_legend(ctx, samples); + + ret = ff_filter_frame(outlink, s->outpicref); + s->outpicref = NULL; return ret; } -static int showspectrumpic_filter_frame(AVFilterLink *inlink, AVFrame *insamples) +static int showspectrumpic_activate(AVFilterContext *ctx) { - AVFilterContext *ctx = inlink->dst; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; ShowSpectrumContext *s = ctx->priv; int ret; - ret = av_audio_fifo_write(s->fifo, (void **)insamples->extended_data, insamples->nb_samples); - av_frame_free(&insamples); - return ret; + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (ff_outlink_get_status(inlink) == AVERROR_EOF && + s->outpicref) { + int64_t samples = 0; + + for (int i = 0; i < ff_inlink_queued_frames(inlink); i++) { + AVFrame *frame = ff_inlink_peek_frame(inlink, i); + + samples += frame->nb_samples; + } + + ret = showspectrumpic_request_frame(outlink, samples); + ff_outlink_set_status(outlink, AVERROR_EOF, 0); + return ret; + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static const AVFilterPad showspectrumpic_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = showspectrumpic_filter_frame, }, { NULL } }; @@ -1704,7 +1703,6 @@ static const AVFilterPad showspectrumpic_outputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_output, - .request_frame = showspectrumpic_request_frame, }, { NULL } }; @@ -1715,6 +1713,7 @@ AVFilter ff_avf_showspectrumpic = { .uninit = uninit, .query_formats = query_formats, .priv_size = sizeof(ShowSpectrumContext), + .activate = showspectrumpic_activate, .inputs = showspectrumpic_inputs, .outputs = showspectrumpic_outputs, .priv_class = &showspectrumpic_class, From patchwork Thu Jun 11 16:12:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 20285 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id E49A044B6C1 for ; Thu, 11 Jun 2020 19:12:54 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CDEF568B47A; Thu, 11 Jun 2020 19:12:54 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f52.google.com (mail-wr1-f52.google.com [209.85.221.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7DD0468B01C for ; Thu, 11 Jun 2020 19:12:48 +0300 (EEST) Received: by mail-wr1-f52.google.com with SMTP id t18so6771040wru.6 for ; Thu, 11 Jun 2020 09:12:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=kiaXiVMB/vKobRQcdgIJaT0PZ0vqwOnCuGi4gmCJR/M=; b=Tb4iUefhE+zdFjcZm8tY1e2hNU45NhMLfWeZJsxl+YaJbm/LPsGhiTjHczgBdpwHWb +NKfRDr4Z45Mj7cj39EZLljJVu0pvOrd5ubMsZseBfxFDyDUk7Muq1wwwrP2Bn1nhYiE sRVMjJfKzVCZ9+HP/c1fmbeRNdUYM1KPVt/YN53Wjc6MatrEqTZ6qQGqMFww1shA2KGh 5ijsO1ac3zik7c3Zl413UGuau03Z8kuNnZgjgrXepFq1iPwAUy+k9cufPi+R5YwGjKwN CKdYpGsPTKwtL3sx6rgLMmjer73I2kfgMrCOfTi9zvcfc76EtMVioAVGGDy0c9po5FWI QI/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=kiaXiVMB/vKobRQcdgIJaT0PZ0vqwOnCuGi4gmCJR/M=; b=ZWrGiAH8P37M3CfXmhdbYSxf1CUYw2CEljhqeK6W4Zzs60ChZbP2BYVnn7HG+v1Zsn eovlAsJEVWsIaocNJjfJ3TsLD0jpYb7jRih+tl/U5v/6ZddDXjqSp84nl3lVxrhFB5N+ XiKr4O/+t5nhJF4JAk3dgS04zuilvesftNRm9EFqxsqVXPhg+1mEbzR25zYFliV05SmD 3aYHT+a8UUbLIsvmxwhBD4dbUI/sYsv8u82RqNVUkVV2UXFbOo4meALUWSz26/IBgnWf gH+PUeH623oxpsbuxpJMwVo9wYj9qbqGcHvDE2gPQYR6OOTws3Qc+hZfJEHU3NGQFEqq 6ysg== X-Gm-Message-State: AOAM532Rqx37oFfNwdADn3UdSxZcgxWB4ZoKPeqUJNmeatQza1STEjBS XYvOmZuaY99rAhgfcSiupC53/uDsTq8= X-Google-Smtp-Source: ABdhPJxdWujHp3upb6avkgoRkqzWDINMwC4it0na6QyrcqEgVCiR8fjmg9QSSYHgaTRqe4coxLegRg== X-Received: by 2002:a5d:6087:: with SMTP id w7mr11207214wrt.158.1591891966071; Thu, 11 Jun 2020 09:12:46 -0700 (PDT) Received: from localhost.localdomain ([37.244.249.74]) by smtp.gmail.com with ESMTPSA id 104sm6074960wrl.25.2020.06.11.09.12.44 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jun 2020 09:12:45 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 11 Jun 2020 18:12:27 +0200 Message-Id: <20200611161227.5622-7-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200611161227.5622-1-onemda@gmail.com> References: <20200611161227.5622-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 7/7] avfilter: add afwtdn filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- doc/filters.texi | 60 ++ libavfilter/Makefile | 1 + libavfilter/af_afwtdn.c | 1345 ++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 4 files changed, 1407 insertions(+) create mode 100644 libavfilter/af_afwtdn.c diff --git a/doc/filters.texi b/doc/filters.texi index c2960e33c7..d89ebc5122 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1314,6 +1314,66 @@ Force the output to either unsigned 8-bit or signed 16-bit stereo aformat=sample_fmts=u8|s16:channel_layouts=stereo @end example +@section afwtdn +Reduce broadband noise from input samples using Wavelets. + +A description of the accepted options follows. + +@table @option +@item sigma +Set the noise sigma, allowed range is from 0 to 1. +Default value is 0. +This option controls strength of denoising applied to input samples. +Most useful way to set this option is via decibels, eg. -45dB. + +@item levels +Set the number of wavelet levels of decomposition. +Allowed range is from 1 to 12. +Default value is 10. +Setting this too low make denoising performance very poor. + +@item wavet +Set wavelet type for decomposition of input frame. +They are sorted by number of coefficients, from lowest to highest. +More coefficients means worse filtering speed, but overall better quality. +Available wavelets are: + +@table @samp +@item sym2 +@item sym4 +@item rbior68 +@item deb10 +@item sym10 +@item coif5 +@item bl3 +@end table + +@item percent +Set percent of full denoising. Allowed range is from 0 to 100 percent. +Default value is 85 percent or partial denoising. + +@item profile +If enabled, first input frame will be used as noise profile. +If first frame samples contain non-noise performance will be very poor. + +@item adaptive +If enabled, input frames are analyzed for presence of noise. +If noise is detected with high possibility then input frame profile will be +used for processing following frames, until new noise frame is detected. + +@item samples +Set size of single frame in number of samples. Allowed range is from 512 to +65536. Default frame size is 8192 samples. + +@item softness +Set softness applied inside thresholding function. Allowed range is from 0 to +10. Default softness is 1. +@end table + +@subsection Commands + +This filter supports subset of its options as @ref{commands}. + @section agate A gate is mainly used to reduce lower parts of a signal. This kind of signal diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 5123540653..191826a622 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -50,6 +50,7 @@ OBJS-$(CONFIG_AFFTDN_FILTER) += af_afftdn.o OBJS-$(CONFIG_AFFTFILT_FILTER) += af_afftfilt.o OBJS-$(CONFIG_AFIR_FILTER) += af_afir.o OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o +OBJS-$(CONFIG_AFWTDN_FILTER) += af_afwtdn.o OBJS-$(CONFIG_AGATE_FILTER) += af_agate.o OBJS-$(CONFIG_AIIR_FILTER) += af_aiir.o OBJS-$(CONFIG_AINTEGRAL_FILTER) += af_aderivative.o diff --git a/libavfilter/af_afwtdn.c b/libavfilter/af_afwtdn.c new file mode 100644 index 0000000000..d2793d4d92 --- /dev/null +++ b/libavfilter/af_afwtdn.c @@ -0,0 +1,1345 @@ +/* + * Copyright (c) 2020 Paul B Mahol + * + * 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 + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "audio.h" +#include "filters.h" +#include "formats.h" + +enum WaveletTypes { + SYM2, + SYM4, + RBIOR68, + DEB10, + SYM10, + COIF5, + BL3, + NB_WAVELET_TYPES, +}; + +static const double bl3_lp[42] = { + 0.000146098, -0.000232304, -0.000285414, 0.000462093, 0.000559952, + -0.000927187, -0.001103748, 0.00188212, 0.002186714, -0.003882426, + -0.00435384, 0.008201477, 0.008685294, -0.017982291, -0.017176331, + 0.042068328, 0.032080869, -0.110036987, -0.050201753, 0.433923147, + 0.766130398, 0.433923147, -0.050201753, -0.110036987, 0.032080869, + 0.042068328, -0.017176331, -0.017982291, 0.008685294, 0.008201477, + -0.00435384, -0.003882426, 0.002186714, 0.00188212, -0.001103748, + -0.000927187, 0.000559952, 0.000462093, -0.000285414, -0.000232304, + 0.000146098, 0.0, +}; + +static const double bl3_hp[42] = { + 0.0, 0.000146098, 0.000232304, -0.000285414, -0.000462093, 0.000559952, + 0.000927187, -0.001103748, -0.00188212, 0.002186714, 0.003882426, + -0.00435384, -0.008201477, 0.008685294, 0.017982291, -0.017176331, + -0.042068328, 0.032080869, 0.110036987, -0.050201753, -0.433923147, + 0.766130398, -0.433923147, -0.050201753, 0.110036987, 0.032080869, + -0.042068328, -0.017176331, 0.017982291, 0.008685294, -0.008201477, + -0.00435384, 0.003882426, 0.002186714, -0.00188212, -0.001103748, + 0.000927187, 0.000559952, -0.000462093, -0.000285414, 0.000232304, + 0.000146098, +}; + +static const double bl3_ilp[42] = { + 0.0, 0.000146098, -0.000232304, -0.000285414, 0.000462093, 0.000559952, + -0.000927187, -0.001103748, 0.00188212, 0.002186714, -0.003882426, + -0.00435384, 0.008201477, 0.008685294, -0.017982291, -0.017176331, + 0.042068328, 0.032080869, -0.110036987, -0.050201753, 0.433923147, + 0.766130398, 0.433923147, -0.050201753, -0.110036987, 0.032080869, + 0.042068328, -0.017176331, -0.017982291, 0.008685294, 0.008201477, + -0.00435384, -0.003882426, 0.002186714, 0.00188212, -0.001103748, + -0.000927187, 0.000559952, 0.000462093, -0.000285414, -0.000232304, + 0.000146098, +}; + +static const double bl3_ihp[42] = { + 0.000146098, 0.000232304, -0.000285414, -0.000462093, 0.000559952, + 0.000927187, -0.001103748, -0.00188212, 0.002186714, 0.003882426, + -0.00435384, -0.008201477, 0.008685294, 0.017982291, -0.017176331, + -0.042068328, 0.032080869, 0.110036987, -0.050201753, -0.433923147, + 0.766130398, -0.433923147, -0.050201753, 0.110036987, 0.032080869, + -0.042068328, -0.017176331, 0.017982291, 0.008685294, -0.008201477, + -0.00435384, 0.003882426, 0.002186714, -0.00188212, -0.001103748, + 0.000927187, 0.000559952, -0.000462093, -0.000285414, 0.000232304, + 0.000146098, +}; + +static const double sym10_lp[20] = { + 0.0007701598091144901, 9.563267072289475e-05, + -0.008641299277022422, -0.0014653825813050513, + 0.0459272392310922, 0.011609893903711381, + -0.15949427888491757, -0.07088053578324385, + 0.47169066693843925, 0.7695100370211071, + 0.38382676106708546, -0.03553674047381755, + -0.0319900568824278, 0.04999497207737669, + 0.005764912033581909, -0.02035493981231129, + -0.0008043589320165449, 0.004593173585311828, + 5.7036083618494284e-05, -0.0004593294210046588, +}; + +static const double sym10_hp[20] = { + 0.0004593294210046588, 5.7036083618494284e-05, + -0.004593173585311828, -0.0008043589320165449, + 0.02035493981231129, 0.005764912033581909, + -0.04999497207737669, -0.0319900568824278, + 0.03553674047381755, 0.38382676106708546, + -0.7695100370211071, 0.47169066693843925, + 0.07088053578324385, -0.15949427888491757, + -0.011609893903711381, 0.0459272392310922, + 0.0014653825813050513, -0.008641299277022422, + -9.563267072289475e-05, 0.0007701598091144901, +}; + +static const double sym10_ilp[20] = { + -0.0004593294210046588, 5.7036083618494284e-05, + 0.004593173585311828, -0.0008043589320165449, + -0.02035493981231129, 0.005764912033581909, + 0.04999497207737669, -0.0319900568824278, + -0.03553674047381755, 0.38382676106708546, + 0.7695100370211071, 0.47169066693843925, + -0.07088053578324385, -0.15949427888491757, + 0.011609893903711381, 0.0459272392310922, + -0.0014653825813050513, -0.008641299277022422, + 9.563267072289475e-05, 0.0007701598091144901, +}; + +static const double sym10_ihp[20] = { + 0.0007701598091144901, -9.563267072289475e-05, + -0.008641299277022422, 0.0014653825813050513, + 0.0459272392310922, -0.011609893903711381, + -0.15949427888491757, 0.07088053578324385, + 0.47169066693843925, -0.7695100370211071, + 0.38382676106708546, 0.03553674047381755, + -0.0319900568824278, -0.04999497207737669, + 0.005764912033581909, 0.02035493981231129, + -0.0008043589320165449, -0.004593173585311828, + 5.7036083618494284e-05, 0.0004593294210046588, +}; + +static const double rbior68_lp[18] = { + 0.0, 0.0, 0.0, 0.0, + 0.014426282505624435, 0.014467504896790148, + -0.07872200106262882, -0.04036797903033992, + 0.41784910915027457, 0.7589077294536541, + 0.41784910915027457, -0.04036797903033992, + -0.07872200106262882, 0.014467504896790148, + 0.014426282505624435, 0.0, 0.0, 0.0, +}; + +static const double rbior68_hp[18] = { + -0.0019088317364812906, -0.0019142861290887667, + 0.016990639867602342, 0.01193456527972926, + -0.04973290349094079, -0.07726317316720414, + 0.09405920349573646, 0.4207962846098268, + -0.8259229974584023, 0.4207962846098268, + 0.09405920349573646, -0.07726317316720414, + -0.04973290349094079, 0.01193456527972926, + 0.016990639867602342, -0.0019142861290887667, + -0.0019088317364812906, 0.0, +}; + +static const double rbior68_ilp[18] = { + 0.0019088317364812906, -0.0019142861290887667, + -0.016990639867602342, 0.01193456527972926, + 0.04973290349094079, -0.07726317316720414, + -0.09405920349573646, 0.4207962846098268, + 0.8259229974584023, 0.4207962846098268, + -0.09405920349573646, -0.07726317316720414, + 0.04973290349094079, 0.01193456527972926, + -0.016990639867602342, -0.0019142861290887667, + 0.0019088317364812906, 0.0, +}; + +static const double rbior68_ihp[18] = { + 0.0, 0.0, 0.0, 0.0, + 0.014426282505624435, -0.014467504896790148, + -0.07872200106262882, 0.04036797903033992, + 0.41784910915027457, -0.7589077294536541, + 0.41784910915027457, 0.04036797903033992, + -0.07872200106262882, -0.014467504896790148, + 0.014426282505624435, 0.0, 0.0, 0.0, +}; + +static const double coif5_lp[30] = { + -9.517657273819165e-08, -1.6744288576823017e-07, + 2.0637618513646814e-06, 3.7346551751414047e-06, + -2.1315026809955787e-05, -4.134043227251251e-05, + 0.00014054114970203437, 0.00030225958181306315, + -0.0006381313430451114, -0.0016628637020130838, + 0.0024333732126576722, 0.006764185448053083, + -0.009164231162481846, -0.01976177894257264, + 0.03268357426711183, 0.0412892087501817, + -0.10557420870333893, -0.06203596396290357, + 0.4379916261718371, 0.7742896036529562, + 0.4215662066908515, -0.05204316317624377, + -0.09192001055969624, 0.02816802897093635, + 0.023408156785839195, -0.010131117519849788, + -0.004159358781386048, 0.0021782363581090178, + 0.00035858968789573785, -0.00021208083980379827, +}; + +static const double coif5_hp[30] = { + 0.00021208083980379827, 0.00035858968789573785, + -0.0021782363581090178, -0.004159358781386048, + 0.010131117519849788, 0.023408156785839195, + -0.02816802897093635, -0.09192001055969624, + 0.05204316317624377, 0.4215662066908515, + -0.7742896036529562, 0.4379916261718371, + 0.06203596396290357, -0.10557420870333893, + -0.0412892087501817, 0.03268357426711183, + 0.01976177894257264, -0.009164231162481846, + -0.006764185448053083, 0.0024333732126576722, + 0.0016628637020130838, -0.0006381313430451114, + -0.00030225958181306315, 0.00014054114970203437, + 4.134043227251251e-05, -2.1315026809955787e-05, + -3.7346551751414047e-06, 2.0637618513646814e-06, + 1.6744288576823017e-07, -9.517657273819165e-08, +}; + +static const double coif5_ilp[30] = { + -0.00021208083980379827, 0.00035858968789573785, + 0.0021782363581090178, -0.004159358781386048, + -0.010131117519849788, 0.023408156785839195, + 0.02816802897093635, -0.09192001055969624, + -0.05204316317624377, 0.4215662066908515, + 0.7742896036529562, 0.4379916261718371, + -0.06203596396290357, -0.10557420870333893, + 0.0412892087501817, 0.03268357426711183, + -0.01976177894257264, -0.009164231162481846, + 0.006764185448053083, 0.0024333732126576722, + -0.0016628637020130838, -0.0006381313430451114, + 0.00030225958181306315, 0.00014054114970203437, + -4.134043227251251e-05, -2.1315026809955787e-05, + 3.7346551751414047e-06, 2.0637618513646814e-06, + -1.6744288576823017e-07, -9.517657273819165e-08, +}; + +static const double coif5_ihp[30] = { + -9.517657273819165e-08, 1.6744288576823017e-07, + 2.0637618513646814e-06, -3.7346551751414047e-06, + -2.1315026809955787e-05, 4.134043227251251e-05, + 0.00014054114970203437, -0.00030225958181306315, + -0.0006381313430451114, 0.0016628637020130838, + 0.0024333732126576722, -0.006764185448053083, + -0.009164231162481846, 0.01976177894257264, + 0.03268357426711183, -0.0412892087501817, + -0.10557420870333893, 0.06203596396290357, + 0.4379916261718371, -0.7742896036529562, + 0.4215662066908515, 0.05204316317624377, + -0.09192001055969624, -0.02816802897093635, + 0.023408156785839195, 0.010131117519849788, + -0.004159358781386048, -0.0021782363581090178, + 0.00035858968789573785, 0.00021208083980379827, +}; + +static const double deb10_lp[20] = { + -1.326420300235487e-05, 9.358867000108985e-05, + -0.0001164668549943862, -0.0006858566950046825, + 0.00199240529499085, 0.0013953517469940798, + -0.010733175482979604, 0.0036065535669883944, + 0.03321267405893324, -0.02945753682194567, + -0.07139414716586077, 0.09305736460380659, + 0.12736934033574265, -0.19594627437659665, + -0.24984642432648865, 0.2811723436604265, + 0.6884590394525921, 0.5272011889309198, + 0.18817680007762133, 0.026670057900950818, +}; + +static const double deb10_hp[20] = { + -0.026670057900950818, 0.18817680007762133, + -0.5272011889309198, 0.6884590394525921, + -0.2811723436604265, -0.24984642432648865, + 0.19594627437659665, 0.12736934033574265, + -0.09305736460380659, -0.07139414716586077, + 0.02945753682194567, 0.03321267405893324, + -0.0036065535669883944, -0.010733175482979604, + -0.0013953517469940798, 0.00199240529499085, + 0.0006858566950046825, -0.0001164668549943862, + -9.358867000108985e-05, -1.326420300235487e-05, +}; + +static const double deb10_ilp[20] = { + 0.026670057900950818, 0.18817680007762133, + 0.5272011889309198, 0.6884590394525921, + 0.2811723436604265, -0.24984642432648865, + -0.19594627437659665, 0.12736934033574265, + 0.09305736460380659, -0.07139414716586077, + -0.02945753682194567, 0.03321267405893324, + 0.0036065535669883944, -0.010733175482979604, + 0.0013953517469940798, 0.00199240529499085, + -0.0006858566950046825, -0.0001164668549943862, + 9.358867000108985e-05, -1.326420300235487e-05, +}; + +static const double deb10_ihp[20] = { + -1.326420300235487e-05, -9.358867000108985e-05, + -0.0001164668549943862, 0.0006858566950046825, + 0.00199240529499085, -0.0013953517469940798, + -0.010733175482979604, -0.0036065535669883944, + 0.03321267405893324, 0.02945753682194567, + -0.07139414716586077, -0.09305736460380659, + 0.12736934033574265, 0.19594627437659665, + -0.24984642432648865, -0.2811723436604265, + 0.6884590394525921, -0.5272011889309198, + 0.18817680007762133, -0.026670057900950818, +}; + +static const double sym4_lp[8] = { + -0.07576571478927333, + -0.02963552764599851, + 0.49761866763201545, + 0.8037387518059161, + 0.29785779560527736, + -0.09921954357684722, + -0.012603967262037833, + 0.0322231006040427, +}; + +static const double sym4_hp[8] = { + -0.0322231006040427, + -0.012603967262037833, + 0.09921954357684722, + 0.29785779560527736, + -0.8037387518059161, + 0.49761866763201545, + 0.02963552764599851, + -0.07576571478927333, +}; + +static const double sym4_ilp[8] = { + 0.0322231006040427, + -0.012603967262037833, + -0.09921954357684722, + 0.29785779560527736, + 0.8037387518059161, + 0.49761866763201545, + -0.02963552764599851, + -0.07576571478927333, +}; + +static const double sym4_ihp[8] = { + -0.07576571478927333, + 0.02963552764599851, + 0.49761866763201545, + -0.8037387518059161, + 0.29785779560527736, + 0.09921954357684722, + -0.012603967262037833, + -0.0322231006040427, +}; + +static const double sym2_lp[4] = { + -0.12940952255092145, 0.22414386804185735, + 0.836516303737469, 0.48296291314469025, +}; + +static const double sym2_hp[4] = { + -0.48296291314469025, 0.836516303737469, + -0.22414386804185735, -0.12940952255092145, +}; + +static const double sym2_ilp[4] = { + 0.48296291314469025, 0.836516303737469, + 0.22414386804185735, -0.12940952255092145, +}; + +static const double sym2_ihp[4] = { + -0.12940952255092145, -0.22414386804185735, + 0.836516303737469, -0.48296291314469025, +}; + +#define MAX_LEVELS 13 + +typedef struct ChannelParams { + int *output_length; + int *filter_length; + double **output_coefs; + double **subbands_to_free; + double **filter_coefs; + + int tempa_length; + int tempa_len_max; + int temp_in_length; + int temp_in_max_length; + int buffer_length; + int min_left_ext; + int max_left_ext; + + double *tempa; + double *tempd; + double *temp_in; + double *buffer; + double *buffer2; + double *prev; + double *overlap; +} ChannelParams; + +typedef struct AudioFWTDNContext { + const AVClass *class; + + double sigma; + double percent; + double softness; + + uint64_t sn; + int64_t eof_pts; + + int wavelet_type; + int channels; + int nb_samples; + int levels; + int wavelet_length; + int need_profile; + int got_profile; + int adaptive; + + int delay; + int drop_samples; + int padd_samples; + int overlap_length; + int prev_length; + ChannelParams *cp; + + const double *lp, *hp; + const double *ilp, *ihp; + + AVFrame *stddev, *absmean, *filter; + AVFrame *new_stddev, *new_absmean; + + int (*filter_channel)(AVFilterContext *ctx, void *arg, int ch, int nb_jobs); +} AudioFWTDNContext; + +#define OFFSET(x) offsetof(AudioFWTDNContext, x) +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AFR AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption afwtdn_options[] = { + { "sigma", "set noise sigma", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, AFR }, + { "levels", "set number of wavelet levels", OFFSET(levels), AV_OPT_TYPE_INT, {.i64=10}, 1, MAX_LEVELS-1, AF }, + { "wavet", "set wavelet type", OFFSET(wavelet_type), AV_OPT_TYPE_INT, {.i64=SYM10}, 0, NB_WAVELET_TYPES - 1, AF, "wavet" }, + { "sym2", "sym2", 0, AV_OPT_TYPE_CONST, {.i64=SYM2}, 0, 0, AF, "wavet" }, + { "sym4", "sym4", 0, AV_OPT_TYPE_CONST, {.i64=SYM4}, 0, 0, AF, "wavet" }, + { "rbior68", "rbior68", 0, AV_OPT_TYPE_CONST, {.i64=RBIOR68}, 0, 0, AF, "wavet" }, + { "deb10", "deb10", 0, AV_OPT_TYPE_CONST, {.i64=DEB10}, 0, 0, AF, "wavet" }, + { "sym10", "sym10", 0, AV_OPT_TYPE_CONST, {.i64=SYM10}, 0, 0, AF, "wavet" }, + { "coif5", "coif5", 0, AV_OPT_TYPE_CONST, {.i64=COIF5}, 0, 0, AF, "wavet" }, + { "bl3", "bl3", 0, AV_OPT_TYPE_CONST, {.i64=BL3}, 0, 0, AF, "wavet" }, + { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_DOUBLE, {.dbl=85}, 0, 100, AFR }, + { "profile", "profile noise", OFFSET(need_profile), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AFR }, + { "adaptive", "adaptive profiling of noise", OFFSET(adaptive), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AFR }, + { "samples", "set frame size in number of samples", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=8192}, 512, 65536, AF }, + { "softness", "set thresholding softness", OFFSET(softness), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 10, AFR }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(afwtdn); + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_DBLP, + AV_SAMPLE_FMT_NONE + }; + int ret; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + layouts = ff_all_channel_counts(); + if (!layouts) + return AVERROR(ENOMEM); + + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_all_samplerates(); + return ff_set_common_samplerates(ctx, formats); +} + +#define pow2(x) (1U << (x)) +#define mod_pow2(x, power_of_two) ((x) & ((power_of_two) - 1)) + +static void conv_down(double *in, int in_length, double *low, double *high, + int out_length, const double *lp, const double *hp, + int wavelet_length, int skip, + double *buffer, int buffer_length) +{ + double thigh = 0.0, tlow = 0.0; + int buff_idx = 1 + skip; + + memcpy(buffer, in, buff_idx * sizeof(*buffer)); + memset(buffer + buff_idx, 0, (buffer_length - buff_idx) * sizeof(*buffer)); + + for (int i = 0; i < out_length - 1; i++) { + double thigh = 0.0, tlow = 0.0; + + for (int j = 0; j < wavelet_length; j++) { + const int idx = mod_pow2(-j + buff_idx - 1, buffer_length); + const double btemp = buffer[idx]; + + thigh += btemp * hp[j]; + tlow += btemp * lp[j]; + } + + high[i] = thigh; + low[i] = tlow; + buffer[buff_idx++] = in[2 * i + 1 + skip]; + buffer[buff_idx++] = in[2 * i + 2 + skip]; + buff_idx = mod_pow2(buff_idx, buffer_length); + } + + for (int i = 0; i < wavelet_length; i++) { + const int idx = mod_pow2(-i + buff_idx - 1, buffer_length); + const double btemp = buffer[idx]; + + thigh += btemp * hp[i]; + tlow += btemp * lp[i]; + } + + high[out_length - 1] = thigh; + low[out_length - 1] = tlow; +} + +static int left_ext(int wavelet_length, int levels, uint64_t sn) +{ + if (!sn) + return 0; + return (pow2(levels) - 1) * (wavelet_length - 2) + mod_pow2(sn, pow2(levels)); +} + +static int nb_coefs(int length, int level, uint64_t sn) +{ + const int pow2_level = pow2(level); + + return (sn + length) / pow2_level - sn / pow2_level; +} + +static int reallocate_inputs(double **out, int *out_length, + int in_length, int levels, int ch, uint64_t sn) +{ + const int temp_length = nb_coefs(in_length, levels, sn); + + for (int level = 0; level < levels; level++) { + const int temp_length = nb_coefs(in_length, level + 1, sn); + + if (temp_length > out_length[level]) { + av_freep(&out[level]); + out_length[level] = 0; + + out[level] = av_calloc(temp_length + 1, sizeof(**out)); + if (!out[level]) + return AVERROR(ENOMEM); + out_length[level] = temp_length + 1; + } + + memset(out[level] + temp_length, 0, + (out_length[level] - temp_length) * sizeof(**out)); + out_length[level] = temp_length; + } + + if (temp_length > out_length[levels]) { + av_freep(&out[levels]); + out_length[levels] = 0; + + out[levels] = av_calloc(temp_length + 1, sizeof(**out)); + if (!out[levels]) + return AVERROR(ENOMEM); + out_length[levels] = temp_length + 1; + } + + memset(out[levels] + temp_length, 0, + (out_length[levels] - temp_length) * sizeof(**out)); + out_length[levels] = temp_length; + + return 0; +} + +static int max_left_zeros_inverse(int levels, int level, int wavelet_length) +{ + return (pow2(levels - level) - 1) * (wavelet_length - 1); +} + +static int reallocate_outputs(AudioFWTDNContext *s, + double **out, int *out_length, + int in_length, int levels, int ch, uint64_t sn) +{ + ChannelParams *cp = &s->cp[ch]; + int temp_length = 0; + int add = 0; + + for (int level = 0; level < levels; level++) { + temp_length = nb_coefs(in_length, level + 1, sn); + if (temp_length > out_length[level]) { + av_freep(&cp->subbands_to_free[level]); + out_length[level] = 0; + + add = max_left_zeros_inverse(levels, level + 1, s->wavelet_length); + cp->subbands_to_free[level] = av_calloc(add + temp_length + 1, sizeof(**out)); + if (!cp->subbands_to_free[level]) + return AVERROR(ENOMEM); + out_length[level] = add + temp_length + 1; + out[level] = cp->subbands_to_free[level] + add; + } + + memset(out[level] + temp_length, 0, + FFMAX(out_length[level] - temp_length - add, 0) * sizeof(**out)); + out_length[level] = temp_length; + } + + temp_length = nb_coefs(in_length, levels, sn); + if (temp_length > out_length[levels]) { + av_freep(&cp->subbands_to_free[levels]); + out_length[levels] = 0; + + cp->subbands_to_free[levels] = av_calloc(temp_length + 1, sizeof(**out)); + if (!cp->subbands_to_free[levels]) + return AVERROR(ENOMEM); + out_length[levels] = temp_length + 1; + out[levels] = cp->subbands_to_free[levels]; + } + + memset(out[levels] + temp_length, 0, + (out_length[levels] - temp_length) * sizeof(**out)); + out_length[levels] = temp_length; + + return 0; +} + +static int discard_left_ext(int wavelet_length, int levels, int level, uint64_t sn) +{ + if (levels == level || sn == 0) + return 0; + return (pow2(levels - level) - 1) * (wavelet_length - 2) + mod_pow2(sn, pow2(levels)) / pow2(level); +} + +static int forward(AudioFWTDNContext *s, + const double *in, int in_length, + double **out, int *out_length, int ch, uint64_t sn) +{ + ChannelParams *cp = &s->cp[ch]; + int levels = s->levels; + int skip = sn ? s->wavelet_length - 1 : 1; + int leftext, ret; + + ret = reallocate_inputs(out, out_length, in_length, levels, ch, sn); + if (ret < 0) + return ret; + ret = reallocate_outputs(s, cp->filter_coefs, cp->filter_length, + in_length, levels, ch, sn); + if (ret < 0) + return ret; + + leftext = left_ext(s->wavelet_length, levels, sn); + + if (cp->temp_in_max_length < in_length + cp->max_left_ext + skip) { + av_freep(&cp->temp_in); + cp->temp_in_max_length = in_length + cp->max_left_ext + skip; + cp->temp_in = av_calloc(cp->temp_in_max_length, sizeof(*cp->temp_in)); + if (!cp->temp_in) { + cp->temp_in_max_length = 0; + return AVERROR(ENOMEM); + } + } + + memset(cp->temp_in, 0, cp->temp_in_max_length * sizeof(*cp->temp_in)); + cp->temp_in_length = in_length + leftext; + + if (leftext) + memcpy(cp->temp_in, cp->prev + s->prev_length - leftext, leftext * sizeof(*cp->temp_in)); + memcpy(cp->temp_in + leftext, in, in_length * sizeof(*in)); + + if (levels == 1) { + conv_down(cp->temp_in, cp->temp_in_length, out[1], out[0], out_length[1], + s->lp, s->hp, s->wavelet_length, skip, + cp->buffer, cp->buffer_length); + } else { + int discard = discard_left_ext(s->wavelet_length, levels, 1, sn); + int tempa_length_prev; + + if (cp->tempa_len_max < (in_length + cp->max_left_ext + s->wavelet_length - 1) / 2) { + av_freep(&cp->tempa); + av_freep(&cp->tempd); + cp->tempa_len_max = (in_length + cp->max_left_ext + s->wavelet_length - 1) / 2; + cp->tempa = av_calloc(cp->tempa_len_max, sizeof(*cp->tempa)); + cp->tempd = av_calloc(cp->tempa_len_max, sizeof(*cp->tempd)); + if (!cp->tempa || !cp->tempd) { + cp->tempa_len_max = 0; + return AVERROR(ENOMEM); + } + } + + memset(cp->tempa, 0, cp->tempa_len_max * sizeof(*cp->tempa)); + memset(cp->tempd, 0, cp->tempa_len_max * sizeof(*cp->tempd)); + + cp->tempa_length = out_length[0] + discard; + conv_down(cp->temp_in, cp->temp_in_length, + cp->tempa, cp->tempd, cp->tempa_length, + s->lp, s->hp, s->wavelet_length, skip, + cp->buffer, cp->buffer_length); + memcpy(out[0], cp->tempd + discard, out_length[0] * sizeof(**out)); + tempa_length_prev = cp->tempa_length; + + for (int level = 1; level < levels - 1; level++) { + if (out_length[level] == 0) + return 0; + discard = discard_left_ext(s->wavelet_length, levels, level + 1, sn); + cp->tempa_length = out_length[level] + discard; + conv_down(cp->tempa, tempa_length_prev, + cp->tempa, cp->tempd, cp->tempa_length, + s->lp, s->hp, s->wavelet_length, skip, + cp->buffer, cp->buffer_length); + memcpy(out[level], cp->tempd + discard, out_length[level] * sizeof(**out)); + tempa_length_prev = cp->tempa_length; + } + + if (out_length[levels] == 0) + return 0; + conv_down(cp->tempa, cp->tempa_length, out[levels], out[levels - 1], out_length[levels], + s->lp, s->hp, s->wavelet_length, skip, + cp->buffer, cp->buffer_length); + } + + if (s->prev_length < in_length) { + memcpy(cp->prev, in + in_length - cp->max_left_ext, cp->max_left_ext * sizeof(*cp->prev)); + } else { + memmove(cp->prev, cp->prev + in_length, (s->prev_length - in_length) * sizeof(*cp->prev)); + memcpy(cp->prev + s->prev_length - in_length, in, in_length * sizeof(*cp->prev)); + } + + return 0; +} + +static void conv_up(double *low, double *high, int in_length, double *out, int out_length, + const double *lp, const double *hp, int filter_length, + double *buffer, double *buffer2, int buffer_length) +{ + int shift = 0, buff_idx = 0, in_idx = 0; + + memset(buffer, 0, buffer_length * sizeof(*buffer)); + memset(buffer2, 0, buffer_length * sizeof(*buffer2)); + + for (int i = 0; i < out_length; i++) { + double sum = 0.0; + + if ((i & 1) == 0) { + if (in_idx < in_length) { + buffer[buff_idx] = low[in_idx]; + buffer2[buff_idx] = high[in_idx++]; + } else { + buffer[buff_idx] = 0; + buffer2[buff_idx] = 0; + } + buff_idx++; + if (buff_idx >= buffer_length) + buff_idx = 0; + shift = 0; + } + + for (int j = 0; j < (filter_length - shift + 1) / 2; j++) { + const int idx = mod_pow2(-j + buff_idx - 1, buffer_length); + + sum += buffer[idx] * lp[j * 2 + shift] + buffer2[idx] * hp[j * 2 + shift]; + } + out[i] = sum; + shift = 1; + } +} + +static int append_left_ext(int wavelet_length, int levels, int level, uint64_t sn) +{ + if (levels == level) + return 0; + + return (pow2(levels - level) - 1) * (wavelet_length - 2) + + mod_pow2(sn, pow2(levels)) / pow2(level); +} + +static int inverse(AudioFWTDNContext *s, + double **in, int *in_length, + double *out, int out_length, int ch, uint64_t sn) +{ + ChannelParams *cp = &s->cp[ch]; + const int levels = s->levels; + int leftext = left_ext(s->wavelet_length, levels, sn); + int temp_skip = 0; + + if (sn == 0) + temp_skip = cp->min_left_ext; + + memset(out, 0, out_length * sizeof(*out)); + + if (cp->temp_in_max_length < out_length + cp->max_left_ext + s->wavelet_length - 1) { + av_freep(&cp->temp_in); + cp->temp_in_max_length = out_length + cp->max_left_ext + s->wavelet_length - 1; + cp->temp_in = av_calloc(cp->temp_in_max_length, sizeof(*cp->temp_in)); + if (!cp->temp_in) { + cp->temp_in_max_length = 0; + return AVERROR(ENOMEM); + } + } + + memset(cp->temp_in, 0, cp->temp_in_max_length * sizeof(*cp->temp_in)); + cp->temp_in_length = out_length + cp->max_left_ext; + + if (levels == 1) { + conv_up(in[1], in[0], in_length[1], cp->temp_in, cp->temp_in_length, + s->ilp, s->ihp, s->wavelet_length, + cp->buffer, cp->buffer2, cp->buffer_length); + memcpy(out + cp->max_left_ext - leftext, cp->temp_in + temp_skip, + FFMAX(0, out_length - (cp->max_left_ext - leftext)) * sizeof(*out)); + } else { + double *hp1, *hp2; + int add, add2; + + if (cp->tempa_len_max < (out_length + cp->max_left_ext + s->wavelet_length - 1) / 2) { + av_freep(&cp->tempa); + cp->tempa_len_max = (out_length + cp->max_left_ext + s->wavelet_length - 1) / 2; + cp->tempa = av_calloc(cp->tempa_len_max, sizeof(*cp->tempa)); + if (!cp->tempa) { + cp->tempa_len_max = 0; + return AVERROR(ENOMEM); + } + } + + memset(cp->tempa, 0, cp->tempa_len_max * sizeof(*cp->tempa)); + + hp1 = levels & 1 ? cp->temp_in : cp->tempa; + hp2 = levels & 1 ? cp->tempa : cp->temp_in; + + add = append_left_ext(s->wavelet_length, levels, levels - 1, sn); + conv_up(in[levels], in[levels - 1], in_length[levels], hp1, in_length[levels - 2] + add, + s->ilp, s->ihp, s->wavelet_length, cp->buffer, cp->buffer2, cp->buffer_length); + + for (int level = levels - 1; level > 1; level--) { + add2 = append_left_ext(s->wavelet_length, levels, level - 1, sn); + add = append_left_ext(s->wavelet_length, levels, level, sn); + conv_up(hp1, in[level - 1] - add, in_length[level - 1] + add, + hp2, in_length[level - 2] + add2, + s->ilp, s->ihp, s->wavelet_length, + cp->buffer, cp->buffer2, cp->buffer_length); + FFSWAP(double *, hp1, hp2); + } + + add = append_left_ext(s->wavelet_length, levels, 1, sn); + conv_up(hp1, in[0] - add, in_length[0] + add, cp->temp_in, cp->temp_in_length, + s->ilp, s->ihp, s->wavelet_length, + cp->buffer, cp->buffer2, cp->buffer_length); + } + + memset(cp->temp_in, 0, temp_skip * sizeof(*cp->temp_in)); + if (s->overlap_length <= out_length) { + memcpy(out + cp->max_left_ext - leftext, cp->temp_in + temp_skip, + FFMAX(0, out_length - (cp->max_left_ext - leftext)) * sizeof(*out)); + for (int i = 0;i < FFMIN(s->overlap_length, out_length); i++) + out[i] += cp->overlap[i]; + + memcpy(cp->overlap, cp->temp_in + out_length - (cp->max_left_ext - leftext), + s->overlap_length * sizeof(*cp->overlap)); + } else { + for (int i = 0;i < s->overlap_length - (cp->max_left_ext - leftext); i++) + cp->overlap[i + cp->max_left_ext - leftext] += cp->temp_in[i]; + memcpy(out, cp->overlap, out_length * sizeof(*out)); + memmove(cp->overlap, cp->overlap + out_length, + (s->overlap_length - out_length) * sizeof(*cp->overlap)); + memcpy(cp->overlap + s->overlap_length - out_length, cp->temp_in + leftext, + out_length * sizeof(*cp->overlap)); + } + + return 0; +} + +static int next_pow2(int in) +{ + return 1 << (av_log2(in) + 1); +} + +static void denoise_level(double *out, const double *in, + const double *filter, + double percent, int length) +{ + const double x = percent * 0.01; + const double y = 1.0 - x; + + for (int i = 0; i < length; i++) + out[i] = x * filter[i] + in[i] * y; +} + +static double sqr(double in) +{ + return in * in; +} + +static double measure_mean(const double *in, int length) +{ + double sum = 0.0; + + for (int i = 0; i < length; i++) + sum += in[i]; + + return sum / length; +} + +static double measure_absmean(const double *in, int length) +{ + double sum = 0.0; + + for (int i = 0; i < length; i++) + sum += fabs(in[i]); + + return sum / length; +} + +static double measure_stddev(const double *in, int length, double mean) +{ + double sum = 0.; + + for (int i = 0; i < length; i++) { + sum += sqr(in[i] - mean); + } + + return sqrt(sum / length); +} + +static void noise_filter(const double stddev, const double *in, + double *out, double absmean, double softness, + double new_stddev, int length) +{ + for (int i = 0; i < length; i++) { + if (new_stddev <= stddev) + out[i] = 0.0; + else if (fabs(in[i]) <= absmean) + out[i] = 0.0; + else + out[i] = in[i] - FFSIGN(in[i]) * absmean / exp(3.0 * softness * (fabs(in[i]) - absmean) / absmean); + } +} + +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int filter_channel(AVFilterContext *ctx, void *arg, int ch, int nb_jobs) +{ + AudioFWTDNContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + ChannelParams *cp = &s->cp[ch]; + const double *src = (const double *)(in->extended_data[ch]); + double *dst = (double *)out->extended_data[ch]; + double *absmean = (double *)s->absmean->extended_data[ch]; + double *new_absmean = (double *)s->new_absmean->extended_data[ch]; + double *stddev = (double *)s->stddev->extended_data[ch]; + double *new_stddev = (double *)s->new_stddev->extended_data[ch]; + double *filter = (double *)s->filter->extended_data[ch]; + double is_noise = 0.0; + int ret; + + ret = forward(s, src, in->nb_samples, cp->output_coefs, cp->output_length, ch, s->sn); + if (ret < 0) + return ret; + + if (!s->got_profile && s->need_profile) { + for (int level = 0; level <= s->levels; level++) { + const int length = cp->output_length[level]; + const double scale = sqrt(2.0 * log(length)); + + stddev[level] = measure_stddev(cp->output_coefs[level], length, + measure_mean(cp->output_coefs[level], length)) * scale; + absmean[level] = measure_absmean(cp->output_coefs[level], length) * scale; + } + } else if (!s->got_profile && !s->need_profile && !s->adaptive) { + for (int level = 0; level <= s->levels; level++) { + const int length = cp->output_length[level]; + const double scale = sqrt(2.0 * log(length)); + + stddev[level] = 0.5 * s->sigma * scale; + absmean[level] = 0.5 * s->sigma * scale; + } + } + + for (int level = 0; level <= s->levels; level++) { + const int length = cp->output_length[level]; + double vad; + + new_stddev[level] = measure_stddev(cp->output_coefs[level], length, + measure_mean(cp->output_coefs[level], length)); + new_absmean[level] = measure_absmean(cp->output_coefs[level], length); + if (new_absmean[level] <= FLT_EPSILON) + vad = 1.0; + else + vad = new_stddev[level] / new_absmean[level]; + if (level < s->levels) + is_noise += sqr(vad - 1.232); + } + + is_noise *= in->sample_rate; + is_noise /= s->nb_samples; + for (int level = 0; level <= s->levels; level++) { + const int length = cp->output_length[level]; + const double scale = sqrt(2.0 * log(length)); + + if (is_noise < 0.05 && s->adaptive) { + stddev[level] = new_stddev[level] * scale; + absmean[level] = new_absmean[level] * scale; + } + + noise_filter(stddev[level], cp->output_coefs[level], filter, absmean[level], + s->softness, new_stddev[level], length); + denoise_level(cp->filter_coefs[level], cp->output_coefs[level], filter, s->percent, length); + } + + ret = inverse(s, cp->filter_coefs, cp->filter_length, dst, out->nb_samples, ch, s->sn); + if (ret < 0) + return ret; + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AudioFWTDNContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + ThreadData td; + AVFrame *out; + int eof = in == NULL; + + out = ff_get_audio_buffer(outlink, s->nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + if (in) { + av_frame_copy_props(out, in); + s->eof_pts = in->pts + in->nb_samples; + } + if (eof) + out->pts = s->eof_pts - s->padd_samples; + + if (!in || in->nb_samples < s->nb_samples) { + AVFrame *new_in = ff_get_audio_buffer(outlink, s->nb_samples); + + if (!new_in) { + av_frame_free(&in); + av_frame_free(&out); + return AVERROR(ENOMEM); + } + if (in) + av_frame_copy_props(new_in, in); + + s->padd_samples -= s->nb_samples - (in ? in->nb_samples: 0); + if (in) + av_samples_copy(new_in->extended_data, in->extended_data, 0, 0, + in->nb_samples, in->channels, in->format); + av_frame_free(&in); + in = new_in; + } + + td.in = in; + td.out = out; + ctx->internal->execute(ctx, s->filter_channel, &td, NULL, inlink->channels); + if (s->need_profile) + s->got_profile = 1; + + s->sn += s->nb_samples; + + if (s->drop_samples >= in->nb_samples) { + s->drop_samples -= in->nb_samples; + s->delay += in->nb_samples; + av_frame_free(&in); + av_frame_free(&out); + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + return 0; + } else if (s->drop_samples > 0) { + for (int ch = 0; ch < out->channels; ch++) { + memmove(out->extended_data[ch], + out->extended_data[ch] + s->drop_samples * sizeof(double), + (in->nb_samples - s->drop_samples) * sizeof(double)); + } + + out->nb_samples = in->nb_samples - s->drop_samples; + out->pts = in->pts - av_rescale_q(s->delay, (AVRational){1, outlink->sample_rate}, outlink->time_base); + s->delay += s->drop_samples; + s->drop_samples = 0; + } else { + if (s->padd_samples < 0 && eof) { + out->nb_samples += s->padd_samples; + s->padd_samples = 0; + } + if (!eof) + out->pts = in->pts - av_rescale_q(s->delay, (AVRational){1, outlink->sample_rate}, outlink->time_base); + } + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static int max_left_ext(int wavelet_length, int levels) +{ + return (pow2(levels) - 1) * (wavelet_length - 1); +} + +static int min_left_ext(int wavelet_length, int levels) +{ + return (pow2(levels) - 1) * (wavelet_length - 2); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioFWTDNContext *s = ctx->priv; + + switch (s->wavelet_type) { + case SYM2: + s->wavelet_length = 4; + s->lp = sym2_lp; + s->hp = sym2_hp; + s->ilp = sym2_ilp; + s->ihp = sym2_ihp; + break; + case SYM4: + s->wavelet_length = 8; + s->lp = sym4_lp; + s->hp = sym4_hp; + s->ilp = sym4_ilp; + s->ihp = sym4_ihp; + break; + case RBIOR68: + s->wavelet_length = 18; + s->lp = rbior68_lp; + s->hp = rbior68_hp; + s->ilp = rbior68_ilp; + s->ihp = rbior68_ihp; + break; + case DEB10: + s->wavelet_length = 20; + s->lp = deb10_lp; + s->hp = deb10_hp; + s->ilp = deb10_ilp; + s->ihp = deb10_ihp; + case SYM10: + s->wavelet_length = 20; + s->lp = sym10_lp; + s->hp = sym10_hp; + s->ilp = sym10_ilp; + s->ihp = sym10_ihp; + break; + case COIF5: + s->wavelet_length = 30; + s->lp = coif5_lp; + s->hp = coif5_hp; + s->ilp = coif5_ilp; + s->ihp = coif5_ihp; + break; + case BL3: + s->wavelet_length = 42; + s->lp = bl3_lp; + s->hp = bl3_hp; + s->ilp = bl3_ilp; + s->ihp = bl3_ihp; + break; + default: + av_assert0(0); + } + + s->levels = FFMIN(s->levels, lrint(log(s->nb_samples / (s->wavelet_length - 1.0)) / M_LN2)); + av_log(ctx, AV_LOG_VERBOSE, "levels: %d\n", s->levels); + s->filter_channel = filter_channel; + + s->stddev = ff_get_audio_buffer(outlink, MAX_LEVELS); + s->new_stddev = ff_get_audio_buffer(outlink, MAX_LEVELS); + s->filter = ff_get_audio_buffer(outlink, s->nb_samples); + s->absmean = ff_get_audio_buffer(outlink, MAX_LEVELS); + s->new_absmean = ff_get_audio_buffer(outlink, MAX_LEVELS); + if (!s->stddev || !s->absmean || !s->filter || + !s->new_stddev || !s->new_absmean) + return AVERROR(ENOMEM); + + s->channels = outlink->channels; + s->overlap_length = max_left_ext(s->wavelet_length, s->levels); + s->prev_length = s->overlap_length; + s->drop_samples = s->overlap_length; + s->padd_samples = s->overlap_length; + s->sn = 1; + + s->cp = av_calloc(s->channels, sizeof(*s->cp)); + if (!s->cp) + return AVERROR(ENOMEM); + + for (int ch = 0; ch < s->channels; ch++) { + ChannelParams *cp = &s->cp[ch]; + + cp->output_coefs = av_calloc(s->levels + 1, sizeof(*cp->output_coefs)); + cp->filter_coefs = av_calloc(s->levels + 1, sizeof(*cp->filter_coefs)); + cp->output_length = av_calloc(s->levels + 1, sizeof(*cp->output_length)); + cp->filter_length = av_calloc(s->levels + 1, sizeof(*cp->filter_length)); + cp->buffer_length = next_pow2(s->wavelet_length); + cp->buffer = av_calloc(cp->buffer_length, sizeof(*cp->buffer)); + cp->buffer2 = av_calloc(cp->buffer_length, sizeof(*cp->buffer2)); + cp->subbands_to_free = av_calloc(s->levels + 1, sizeof(*cp->subbands_to_free)); + cp->prev = av_calloc(s->prev_length, sizeof(*cp->prev)); + cp->overlap = av_calloc(s->overlap_length, sizeof(*cp->overlap)); + cp->max_left_ext = max_left_ext(s->wavelet_length, s->levels); + cp->min_left_ext = min_left_ext(s->wavelet_length, s->levels); + if (!cp->output_coefs || !cp->filter_coefs || !cp->output_length || + !cp->filter_length || !cp->subbands_to_free || !cp->prev || !cp->overlap || + !cp->buffer || !cp->buffer2) + return AVERROR(ENOMEM); + } + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioFWTDNContext *s = ctx->priv; + AVFrame *in = NULL; + int ret, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + while (s->padd_samples != 0) { + ret = filter_frame(inlink, NULL); + if (ret < 0) + return ret; + } + ff_outlink_set_status(outlink, status, pts); + return ret; + } + } + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioFWTDNContext *s = ctx->priv; + + av_frame_free(&s->filter); + av_frame_free(&s->new_stddev); + av_frame_free(&s->stddev); + av_frame_free(&s->new_absmean); + av_frame_free(&s->absmean); + + for (int ch = 0; s->cp && ch < s->channels; ch++) { + ChannelParams *cp = &s->cp[ch]; + + av_freep(&cp->tempa); + av_freep(&cp->tempd); + av_freep(&cp->temp_in); + av_freep(&cp->buffer); + av_freep(&cp->buffer2); + av_freep(&cp->prev); + av_freep(&cp->overlap); + + av_freep(&cp->output_length); + av_freep(&cp->filter_length); + + if (cp->output_coefs) { + for (int level = 0; level <= s->levels; level++) + av_freep(&cp->output_coefs[level]); + } + + if (cp->subbands_to_free) { + for (int level = 0; level <= s->levels; level++) + av_freep(&cp->subbands_to_free[level]); + } + + av_freep(&cp->subbands_to_free); + av_freep(&cp->output_coefs); + av_freep(&cp->filter_coefs); + } + + av_freep(&s->cp); +} + +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + AudioFWTDNContext *s = ctx->priv; + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + if (!strcmp(cmd, "profile") && s->need_profile) + s->got_profile = 0; + + return 0; +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_af_afwtdn = { + .name = "afwtdn", + .description = NULL_IF_CONFIG_SMALL("Denoise audio stream using Wavelets."), + .query_formats = query_formats, + .priv_size = sizeof(AudioFWTDNContext), + .priv_class = &afwtdn_class, + .activate = activate, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, + .process_command = process_command, + .flags = AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 1183e40267..de5884529c 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -43,6 +43,7 @@ extern AVFilter ff_af_afftdn; extern AVFilter ff_af_afftfilt; extern AVFilter ff_af_afir; extern AVFilter ff_af_aformat; +extern AVFilter ff_af_afwtdn; extern AVFilter ff_af_agate; extern AVFilter ff_af_aiir; extern AVFilter ff_af_aintegral;