From patchwork Sat Dec 2 13:27:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 44864 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:a301:b0:181:818d:5e7f with SMTP id x1csp1730053pzk; Sat, 2 Dec 2023 05:19:48 -0800 (PST) X-Google-Smtp-Source: AGHT+IEa7lKntNUJZHea449mEANfQf4Y2y2kCNdeD+69pE3qV6qrGPZU6pzFgEIF03jgV6e4HWRL X-Received: by 2002:a05:6402:520e:b0:54c:672c:c361 with SMTP id s14-20020a056402520e00b0054c672cc361mr2342740edd.4.1701523187759; Sat, 02 Dec 2023 05:19:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1701523187; cv=none; d=google.com; s=arc-20160816; b=Z+m4a9OCxHezhkQlH2DuCO/ejEfcdzI8JF90PD65xbRyvbI/Yf5Nagt+5AnlcynlRp tLgQ3Bsake6yTPw8sGjlNQwTfPeac8uUbbPx1KwCUM6xIw8CcJK5HQW1bay8ObL3s53X p/8Pohe/qiyrQVY/5l9YE0lit3M3yLmiQoAP+jByY9LgDbfywK5rzDDzbt4LqNoC/CxM qNdX7PmjU+qYett7jtz9hXnLcDJvdKmRQm4fov5HrPr2YBIVSAququ15jhfdvkZ4VglP LtsEI7NyNCf5XQCT47+wVk1X66cP323B0s4s3bKqdml+0yhWbzHuGEBXX0yHkBKW530k BjDQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:to :message-id:date:from:mime-version:dkim-signature:delivered-to; bh=eBRJlzYNV7Fps1sXIcsBmpZgDQjXqtBme+rk551llM0=; fh=e5zN9xSzcxLA6bGo3lF+CqTbY/oLwzApV03EO/RBfgQ=; b=iPPg9DisoWPEFmrMUpIg/R3LTDUJvsZ2S+YDyen4yjadEwJORuxgkW7dfLgzpObS9g 36XL8zYR7VSRkBy4gP4oz192wGINT2FLLIjalx8PN8Cek7itFbYunkPnH7rcqrjz3Ph2 Mp04ndqlI68l0IYDif+JqnQjLmOrRR8aHHwLtnsEuSyvKQFByIZUmG/lMpL5fQ7SPb+4 HYfsl4fOlKYg5JbwD+1eN4hRKj18TJvh9VHn4kO2KRW/+XB3k4cdaYwI4fVGvtR0fnuB 5OjCRoCLfMba0R11Yt9KEn2ORH+xQP9xFrUAQxNF0JN9XxCjtNoKF43gFh10DCUcefvJ Ly7Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=hcH5e0C4; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j25-20020a508a99000000b0054c943a07c4si308702edj.394.2023.12.02.05.19.46; Sat, 02 Dec 2023 05:19:47 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=hcH5e0C4; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1767268C919; Sat, 2 Dec 2023 15:19:43 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-vk1-f175.google.com (mail-vk1-f175.google.com [209.85.221.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4A31A68C919 for ; Sat, 2 Dec 2023 15:19:36 +0200 (EET) Received: by mail-vk1-f175.google.com with SMTP id 71dfb90a1353d-4b292cc7e5dso1014347e0c.2 for ; Sat, 02 Dec 2023 05:19:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1701523175; x=1702127975; darn=ffmpeg.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=njzWCiqJgzM+sAW3yFz7pULgiPXw1UGw4u84fDWJLUg=; b=hcH5e0C4sRWp2plVN1sEW8EcDHmnKFu9Zc15e9kwG29dLmNUyqZnpKQX1uurJ2i7+Q fKpXUZ3cjazrsv0z849wHJKBS/Y6pfEBuIbwIgY0G+hlTwUSLeivZ9O1GPQiSUkYqu9q +6APsyU7IJ+OFko2vQT02wyQwAh3/5h8Wkl0ylHsjBhPP4JUFwYhIUk0459XjKnTws7k R0l/Ye48m2lTrOV3tgQzVmF6Z0t5US7ZOoIAslcHdYmrSQrmaAruKtAyKR8zCioPodUO Fz2cnNngPZQU2gKJBIOPEHFyX46ys91sIJ7fftXihEZHLzBDfG4exAK7UKVkVcjXD0F6 mMCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701523175; x=1702127975; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=njzWCiqJgzM+sAW3yFz7pULgiPXw1UGw4u84fDWJLUg=; b=cfQ0/XeOH9ZGSZj2yYqXpJlpGyzz+VgfAZKhO7F8ZWBER92HpERNiARmit7hWofip5 ikuSiVHt6YQaRFfvBR30+yuta1hX5N5dYJDNvPgSNhNHPNh1gP9uxbwzMoW3OKOylKue 76u6emYYlm3iKu+wUd657Wa9esegTeL1dQfdKGS7cnDyVSMCQOrstrL3rL0XLCwVxfGD L2SKiwknK7Yj6jRKOZEafTPbR/o0ODAAwpCzjtOBjCJPaLf9KujF2AM1/b7pDqZBAGxv uTaQo5K+XCHmcnOwAY67eVZzUts0yZjh69xpL8+SJvubD6OovJ6TIqatDR5lERZiOE1a C7+g== X-Gm-Message-State: AOJu0YzYOJzWaFikmhOFD+TEnkbUPmE13OuHnbTQ1Nkmd2ihRRBkQM91 hFQL3sCs2mO0yQtIxo99ns07blhAnFH0u2WDBcjDh4bk X-Received: by 2002:a05:6122:222a:b0:49a:56d2:562d with SMTP id bb42-20020a056122222a00b0049a56d2562dmr508199vkb.4.1701523174698; Sat, 02 Dec 2023 05:19:34 -0800 (PST) MIME-Version: 1.0 From: Paul B Mahol Date: Sat, 2 Dec 2023 14:27:59 +0100 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.29 Subject: [FFmpeg-devel] [PATCH] avfilter: add aspace filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: qni/VO675Kty Attached. From 0388038ff2c7816194d01cf4c8966aab4a504b92 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Sat, 2 Dec 2023 00:51:54 +0100 Subject: [PATCH] avfilter: add aspace filter Signed-off-by: Paul B Mahol --- doc/filters.texi | 62 +++++ libavfilter/Makefile | 1 + libavfilter/af_aspace.c | 427 ++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/aspace_template.c | 57 +++++ 5 files changed, 548 insertions(+) create mode 100644 libavfilter/af_aspace.c create mode 100644 libavfilter/aspace_template.c diff --git a/doc/filters.texi b/doc/filters.texi index 27437c4a36..dd4dd6c9bd 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -3274,6 +3274,68 @@ Set oversampling factor. This filter supports the all above options as @ref{commands}. +@section aspace +Apply Arbitrary Distance-based Amplitude Panning to input audio stream. + +Accepted input audio channel layout is mono only. + +It accepts the following options: +@table @option +@item layout +The channel layout of the output stream. The default is "stereo". + +@item a +Set the source audio azimuth position in degrees. +Allowed values are from @var{-180.0} to @var{180.0}. + +@item e +Set the source audio elevation position in degrees. +Allowed values are from @var{-90.0} to @var{90.0}. + +@item r +Set the source audio distance from central point. + +@item b +Set the spatial blur factor. Allowed values are from @var{0} to @var{100.0}. + +@item o +Set the rolloff in decibels, this sets inverse distance law for sound propagating +in a free field. Allowed range is from @var{0} to @var{90}. + +@item R +Set the distance of each speaker in regular (circular) layout from central point. +Allowed values are from @var{0.01} to @var{100.0}. + +@item precision +Set which precision to use when processing samples. + +@table @option +@item auto +Auto pick internal sample format depending on other filters. + +@item float +Always use single-floating point precision sample format. + +@item double +Always use double-floating point precision sample format. +@end table +@end table + +@subsection Examples + +@itemize +@item +Pan single-channel input audio stream into 5.1 layout with custom azimuth position of -15 deg +and spatial blur of 0.1 with custom rolloff of 6 dB: +@example +aspace=layout=5.1:b=0.1:o=6:a=-15 +@end example +@end itemize + +@subsection Commands + +This filter supports the some of above options as @ref{commands}. + @section aspectralstats Display frequency domain statistical information about the audio channels. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index f1bf9483bb..b04938406f 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -108,6 +108,7 @@ OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o OBJS-$(CONFIG_ASIDEDATA_FILTER) += f_sidedata.o OBJS-$(CONFIG_ASISDR_FILTER) += af_asdr.o OBJS-$(CONFIG_ASOFTCLIP_FILTER) += af_asoftclip.o +OBJS-$(CONFIG_ASPACE_FILTER) += af_aspace.o OBJS-$(CONFIG_ASPECTRALSTATS_FILTER) += af_aspectralstats.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASR_FILTER) += af_asr.o diff --git a/libavfilter/af_aspace.c b/libavfilter/af_aspace.c new file mode 100644 index 0000000000..a1e2d1ec61 --- /dev/null +++ b/libavfilter/af_aspace.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2023 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 +#include + +#include "libavutil/avstring.h" +#include "libavutil/channel_layout.h" +#include "libavutil/float_dsp.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +enum PrecisionType { + P_AUTO = -1, + P_SINGLE, + P_DOUBLE, + NB_PTYPES, +}; + +typedef struct Speaker { + double position[3]; + double distance; + double gain; +} Speaker; + +typedef struct AudioSpaceContext { + const AVClass *class; + + double polar[3]; + int precision; + int set; + AVChannelLayout outlayout; + AVFrame *w; + + double a, k, dmax; + double blur; + double radius; + double rolloff; + double source[3]; + double prev_source[3]; + double reference[3]; + Speaker *speakers; + + void (*process)(AVFilterContext *ctx, AVFrame *in, AVFrame *out, AVFrame *w); + AVFloatDSPContext *fdsp; +} AudioSpaceContext; + +#define OFFSET(x) offsetof(AudioSpaceContext,x) +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AFT AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption aspace_options[] = { + { "layout", "set the layout of output audio", OFFSET(outlayout), AV_OPT_TYPE_CHLAYOUT, {.str="stereo"}, 0, 0, AF}, + { "a", "set the azimuth of source audio", OFFSET(polar[0]), AV_OPT_TYPE_DOUBLE, {.dbl=0.}, -180, 180.0, AFT }, + { "e", "set the elevation of source audio", OFFSET(polar[1]), AV_OPT_TYPE_DOUBLE, {.dbl=0.}, -90.0, 90.0, AFT }, + { "r", "set the distance of source audio", OFFSET(polar[2]), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0.0, DBL_MAX, AFT }, + { "b", "set the spatial blur factor", OFFSET(blur), AV_OPT_TYPE_DOUBLE, {.dbl=0.2}, 0.0, 100.0, AFT }, + { "o", "set the rolloff", OFFSET(rolloff), AV_OPT_TYPE_DOUBLE, {.dbl=18}, 0, 90, AFT }, + { "R", "set the distance of each speaker in regular layout", OFFSET(radius), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.1, 100, AF }, + { "precision", "processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=P_AUTO}, P_AUTO, NB_PTYPES-1, AF, "pre"}, + { "auto", "auto", 0, AV_OPT_TYPE_CONST, {.i64=P_AUTO}, 0, 0, AF, "pre"}, + { "float", "single floating-point precision", 0, AV_OPT_TYPE_CONST, {.i64=P_SINGLE}, 0, 0, AF, "pre"}, + { "double", "double floating-point precision" , 0, AV_OPT_TYPE_CONST, {.i64=P_DOUBLE}, 0, 0, AF, "pre"}, + {NULL} +}; + +static int query_formats(AVFilterContext *ctx) +{ + AudioSpaceContext *s = ctx->priv; + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *outlayouts = NULL; + AVFilterChannelLayouts *inlayouts = NULL; + AVChannelLayout inlayout = AV_CHANNEL_LAYOUT_MONO; + int ret = 0; + + if (s->precision == P_AUTO) { + ret = ff_add_format(&formats, AV_SAMPLE_FMT_FLTP); + if (ret) + return ret; + ret = ff_add_format(&formats, AV_SAMPLE_FMT_DBLP); + } else if (s->precision == P_SINGLE) { + ret = ff_add_format(&formats, AV_SAMPLE_FMT_FLTP); + } else if (s->precision == P_DOUBLE) { + ret = ff_add_format(&formats, AV_SAMPLE_FMT_DBLP); + } + if (ret) + return ret; + ret = ff_set_common_formats(ctx, formats); + if (ret) + return ret; + + ret = ff_add_channel_layout(&outlayouts, &s->outlayout); + if (ret) + return ret; + + ret = ff_channel_layouts_ref(outlayouts, &ctx->outputs[0]->incfg.channel_layouts); + if (ret) + return ret; + + ret = ff_add_channel_layout(&inlayouts, &inlayout); + if (ret) + return ret; + + ret = ff_channel_layouts_ref(inlayouts, &ctx->inputs[0]->outcfg.channel_layouts); + if (ret) + return ret; + + return ff_set_common_all_samplerates(ctx); +} + +#define DEPTH 32 +#include "aspace_template.c" + +#undef DEPTH +#define DEPTH 64 +#include "aspace_template.c" + +static double sqr(double x) +{ + return x * x; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioSpaceContext *s = ctx->priv; + + switch (s->precision) { + case P_AUTO: + s->process = outlink->format == AV_SAMPLE_FMT_FLTP ? process_float : process_double; + break; + case P_SINGLE: + s->process = process_float; + break; + case P_DOUBLE: + s->process = process_double; + break; + default: av_assert0(0); + } + + if (!s->speakers) { + s->speakers = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->speakers)); + if (!s->speakers) + return AVERROR(ENOMEM); + } + + s->source[0] = s->polar[2] * cos(s->polar[0] * M_PI / 180.0) * cos(s->polar[1] * M_PI / 180.0); + s->source[1] = s->polar[2] * sin(s->polar[0] * M_PI / 180.0) * cos(s->polar[1] * M_PI / 180.0); + s->source[2] = s->polar[2] * sin(s->polar[1] * M_PI / 180.0); + + memcpy(s->prev_source, s->source, sizeof(s->prev_source)); + + s->reference[0] = 0.0; + s->reference[1] = 0.0; + s->reference[2] = 0.0; + + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + Speaker *speaker = &s->speakers[ch]; + int chan = av_channel_layout_channel_from_index(&outlink->ch_layout, ch); + double azim = 0, elev = 0; + + switch (chan) { + case AV_CHAN_FRONT_LEFT: azim = 30; break; + case AV_CHAN_FRONT_RIGHT: azim = 330; break; + case AV_CHAN_FRONT_CENTER: azim = 0; break; + case AV_CHAN_LOW_FREQUENCY: + case AV_CHAN_LOW_FREQUENCY_2: azim = 0; break; + case AV_CHAN_BACK_LEFT: azim = 150; break; + case AV_CHAN_BACK_RIGHT: azim = 210; break; + case AV_CHAN_BACK_CENTER: azim = 180; break; + case AV_CHAN_SIDE_LEFT: azim = 90; break; + case AV_CHAN_SIDE_RIGHT: azim = 270; break; + case AV_CHAN_FRONT_LEFT_OF_CENTER: azim = 15; break; + case AV_CHAN_FRONT_RIGHT_OF_CENTER: azim = 345; break; + case AV_CHAN_TOP_CENTER: azim = 0; + elev = 90; break; + case AV_CHAN_TOP_FRONT_LEFT: azim = 30; + elev = 45; break; + case AV_CHAN_TOP_FRONT_CENTER: azim = 0; + elev = 45; break; + case AV_CHAN_TOP_FRONT_RIGHT: azim = 330; + elev = 45; break; + case AV_CHAN_TOP_BACK_LEFT: azim = 150; + elev = 45; break; + case AV_CHAN_TOP_BACK_RIGHT: azim = 210; + elev = 45; break; + case AV_CHAN_TOP_BACK_CENTER: azim = 180; + elev = 45; break; + case AV_CHAN_WIDE_LEFT: azim = 90; break; + case AV_CHAN_WIDE_RIGHT: azim = 270; break; + case AV_CHAN_SURROUND_DIRECT_LEFT: azim = 90; break; + case AV_CHAN_SURROUND_DIRECT_RIGHT: azim = 270; break; + case AV_CHAN_STEREO_LEFT: azim = 90; break; + case AV_CHAN_STEREO_RIGHT: azim = 270; break; + default: + return AVERROR(EINVAL); + } + + speaker->position[0] = s->radius * cos(azim * M_PI / 180.0) * cos(elev * M_PI / 180.0); + speaker->position[1] = s->radius * sin(azim * M_PI / 180.0) * cos(elev * M_PI / 180.0); + speaker->position[2] = s->radius * sin(elev * M_PI / 180.0); + + s->reference[0] += speaker->position[0]; + s->reference[1] += speaker->position[1]; + s->reference[2] += speaker->position[2]; + } + + s->reference[0] /= outlink->ch_layout.nb_channels; + s->reference[1] /= outlink->ch_layout.nb_channels; + s->reference[2] /= outlink->ch_layout.nb_channels; + + s->dmax = 0.0; + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + Speaker *speaker = &s->speakers[ch]; + double distance; + + distance = sqrt(sqr(speaker->position[0] - s->reference[0]) + + sqr(speaker->position[1] - s->reference[1]) + + sqr(speaker->position[2] - s->reference[2])); + + s->dmax = fmax(s->dmax, distance); + } + + return 0; +} + +static inline void iposition(double *out, const double *a, + const double *b, double f) +{ + out[0] = a[0] + (b[0] - a[0]) * f; + out[1] = a[1] + (b[1] - a[1]) * f; + out[2] = a[2] + (b[2] - a[2]) * f; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AudioSpaceContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + double scale; + AVFrame *out; + + if (!s->w || s->w->nb_samples < in->nb_samples) { + av_frame_free(&s->w); + s->w = ff_get_audio_buffer(outlink, in->nb_samples); + if (!s->w) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + } + + out = ff_get_audio_buffer(outlink, in->nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + s->source[0] = s->polar[2] * cos(s->polar[0] * M_PI / 180.0) * cos(s->polar[1] * M_PI / 180.0); + s->source[1] = s->polar[2] * sin(s->polar[0] * M_PI / 180.0) * cos(s->polar[1] * M_PI / 180.0); + s->source[2] = s->polar[2] * sin(s->polar[1] * M_PI / 180.0); + + scale = 1.0 / out->nb_samples; + s->a = s->rolloff / (20.0 * log10(2.0)); + for (int n = 0; n < out->nb_samples; n++) { + double source[3]; + double p, drs; + + if (s->set && !memcmp(s->prev_source, s->source, sizeof(s->source))) { + switch (outlink->format) { + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + Speaker *speaker = &s->speakers[ch]; + const float gain = speaker->gain; + + for (int n = 0; n < out->nb_samples; n++) { + float *w = (float *)s->w->extended_data[ch]; + + w[n] = gain; + } + } + break; + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + Speaker *speaker = &s->speakers[ch]; + const double gain = speaker->gain; + + for (int n = 0; n < out->nb_samples; n++) { + double *w = (double *)s->w->extended_data[ch]; + + w[n] = gain; + } + } + break; + default: + av_assert0(0); + } + + break; + } + + iposition(source, s->prev_source, s->source, n * scale); + + s->k = 0.0; + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + Speaker *speaker = &s->speakers[ch]; + + speaker->distance = sqrt(sqr(speaker->position[0] - source[0]) + + sqr(speaker->position[1] - source[1]) + + sqr(speaker->position[2] - source[2]) + sqr(s->blur)); + + s->k += 1.0 / pow(speaker->distance, 2.0 * s->a); + } + + drs = sqrt(sqr(s->reference[0] - source[0]) + + sqr(s->reference[1] - source[1]) + + sqr(s->reference[2] - source[2])); + + p = fmin(s->dmax / drs, 1.0); + s->k = pow(p, 2.0 * s->a) / sqrt(s->k); + + switch (outlink->format) { + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + float *w = (float *)s->w->extended_data[ch]; + Speaker *speaker = &s->speakers[ch]; + + w[n] = s->k / powf(speaker->distance, s->a); + speaker->gain = w[n]; + } + break; + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { + double *w = (double *)s->w->extended_data[ch]; + Speaker *speaker = &s->speakers[ch]; + + w[n] = s->k / pow(speaker->distance, s->a); + speaker->gain = w[n]; + } + break; + default: + av_assert0(0); + } + + s->set = 1; + } + + memcpy(s->prev_source, s->source, sizeof(s->prev_source)); + + s->process(ctx, in, out, s->w); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioSpaceContext *s = ctx->priv; + + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioSpaceContext *s = ctx->priv; + + av_freep(&s->fdsp); + av_freep(&s->speakers); + av_frame_free(&s->w); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + +AVFILTER_DEFINE_CLASS(aspace); + +const AVFilter ff_af_aspace = { + .name = "aspace", + .description = NULL_IF_CONFIG_SMALL("Arbitrary Distance Amplitude Panning"), + .priv_size = sizeof(AudioSpaceContext), + .priv_class = &aspace_class, + .init = init, + .uninit = uninit, + FILTER_QUERY_FUNC(query_formats), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 1616661a6f..b7516bbdbf 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -94,6 +94,7 @@ extern const AVFilter ff_af_ashowinfo; extern const AVFilter ff_af_asidedata; extern const AVFilter ff_af_asisdr; extern const AVFilter ff_af_asoftclip; +extern const AVFilter ff_af_aspace; extern const AVFilter ff_af_aspectralstats; extern const AVFilter ff_af_asplit; extern const AVFilter ff_af_asr; diff --git a/libavfilter/aspace_template.c b/libavfilter/aspace_template.c new file mode 100644 index 0000000000..ae8b2ad5bc --- /dev/null +++ b/libavfilter/aspace_template.c @@ -0,0 +1,57 @@ +/* + * 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 "avfilter.h" +#include "internal.h" +#include "audio.h" + +#undef ftype +#undef SAMPLE_FORMAT +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define ftype float +#else +#define SAMPLE_FORMAT double +#define ftype double +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static void fn(process)(AVFilterContext *ctx, + AVFrame *in, AVFrame *out, AVFrame *w) +{ + const int nb_channels = out->ch_layout.nb_channels; + const int nb_samples = FFALIGN(in->nb_samples, 16); + AudioSpaceContext *s = ctx->priv; + + for (int i = 0; i < nb_channels; i++) { +#if DEPTH == 32 + s->fdsp->vector_fmul((ftype *)out->extended_data[i], + (const ftype *)in->extended_data[0], + (const ftype *)w->extended_data[i], + nb_samples); +#else + s->fdsp->vector_dmul((ftype *)out->extended_data[i], + (const ftype *)in->extended_data[0], + (const ftype *)w->extended_data[i], + nb_samples); +#endif + } +} -- 2.42.1