From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 45FEA44B3C for ; Mon, 13 Oct 2025 08:17:14 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'RJ8VaswyeY2iJj1k4IMhgUhLuUOUGGsNm5KOYOay/4A=', expected b'0dH8kDUxpUJGYsQFZHV9S/C6p2QA17NwFVsLW8GhZ2E=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1760343411; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=RJ8VaswyeY2iJj1k4IMhgUhLuUOUGGsNm5KOYOay/4A=; b=lV166ueTPvUjOCbS/XcOOAkufRfxZdewNOex6gQN/skp0P/oIRgqf9JYNSt35wBG2g8IA mAitADzG8EkGw1PkoUMcJBhTUzrLqb+wUf/attB45AQU3vXbid4+OEjm3WBnzznbKgffOt+ ZnYJZfvQAIEASGXY8yhaQIS76HsLlfKk4vyt3cbbtztVUj7EX4GWohqLIjZRBr2R/K7AwdJ YwhBcsxpGIKwWDxuXx3q5cIgoyAG9c7q+YOMX/znSpWzi0SjOQGbbMy1I1tfqnzL4nnIoGn mIsq4RfXa8TLnXN62MhLyLVUBZw4Q3w8kCBQFadg6LQZDtGYqNCMXTMMQHYg== Received: from [172.19.0.4] (unknown [172.19.0.4]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 8003C68F1F3; Mon, 13 Oct 2025 11:16:51 +0300 (EEST) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1760343407; b=iiLTUYOMBWOQ4LgRO5a9O/u3e+/Bi9smjnA9Wfx3Z9fUiJc8dX7+QnNZzz4uvG3D4PJPz 6y75bmRPA1U4rzQxs3dlc5/Ve1BG0y4Cp44HEvCxBHQIMYhwAD/LlNVxpJnJ/CbE/H6t9+W wS/RV1Tl4Cw0eyMfTiCwdlezGtrEpQ9TM/BW6rl3Tq+sUjuYgArCIXioVg5NcJB0UP1tj0w 8JtTrME6PhlL5HMr9ZrYSThmv0vjlLyRy15fywc00jgyqIm5pPiUdPNo3MUb8gPai1cfcR3 H9RSTB68lvnH1NtEskxrlOAEMLc2zpvGfwrl1bVewq3aQy6gUGkNKrlNDVoA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1760343407; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=PZ19z4S4yR8sacwXVPLahkZKm88ujdzJUBVhKhnAvoo=; b=rlGpJV6R5OuNKNrhbQ6VuFpCrPXmBpPuffc9XthIf8Vq/Xc3nuMmQLVZhM2nE/SZjaTBs 8dNHNAoVxPj7EISPUVvpUnsfVufsyA4RTOxQP2DQWqCDYopcmtuS6YBbaDezC9mOkT7OikK tF2xwc7ilSkL/F3ZCb+pHzWXOlBf2pn2cEEbZ+eTHaFvBg3PJSdidO6WnCM8Rk65jjvRTuy khiV6jRNazx2nW4NaBQTX81rLShGbt3mPDM32ZoadSYLhWsQECvDCfItdVDYxZkXDjVTXUJ 72vO8qDAAx2H+y+EDTfOPGsi80Memeq4yrkcDIp9UBLt444DteycdRdRio4Q== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1760343400; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=0dH8kDUxpUJGYsQFZHV9S/C6p2QA17NwFVsLW8GhZ2E=; b=z0/SR6TVDSA7Ykw22DxiZGR7vQSmFu9lOSlaQZSz7keMdhKeHvwp36HkPD1tKMdrI8BKf 03SBF9YWi36Xn5ashr2A3tu0WGDMpFEZ5E+YBnFmAiJtOEAIKPviL7iGnlbisZujI+F/Zly ed/FfgbBII2fu/UEp6rOFtgov7TEThcVCy8yN6tPj8UbW1Ff3B21aEQATM0QYwBwz1/mvPe Zfrgi0SIQQcyKikA9DnPCyzyA6uW2/+g5fGGISJ8kKodz5+YbL/+jFKRQz6XB9D4SaZYn3E jzA47m21uzha2DWqUFf7Y6zRlWVVV470BTNJi/RDml9iiZR48l6nF8gk18gg== Received: from be50bb5a3685 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id A309968F1B6 for ; Mon, 13 Oct 2025 11:16:40 +0300 (EEST) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Mon, 13 Oct 2025 08:16:40 -0000 Message-ID: <176034340085.25.16925994522822577862@bf907ddaa564> Message-ID-Hash: OU7LIDH65GJLH7IXSU7TBUCEUNDBRNQW X-Message-ID-Hash: OU7LIDH65GJLH7IXSU7TBUCEUNDBRNQW X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] WIP: avfilter: add Bungee audio stretch filter (PR #20697) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: kupix via ffmpeg-devel Cc: kupix Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #20697 opened by kupix URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20697 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20697.patch This adds an audio filter using the [Bungee](https://github.com/bungee-audio-stretch/bungee) open source audio timescale manipulation library. It also adds support for the proprietary "Bungee Pro" variant. >>From 135787f1a1b3ab51b3b5d9f290ac4575891848f8 Mon Sep 17 00:00:00 2001 From: John Funnell Date: Sun, 23 Feb 2025 20:46:22 +0000 Subject: [PATCH 1/4] avfilter: add Bungee wrapper --- Changelog | 1 + configure | 8 ++ doc/filters.texi | 24 ++++ libavfilter/Makefile | 2 + libavfilter/af_bungee.c | 26 ++++ libavfilter/af_bungeepro.c | 26 ++++ libavfilter/allfilters.c | 2 + libavfilter/bungee.h | 280 +++++++++++++++++++++++++++++++++++++ 8 files changed, 369 insertions(+) create mode 100644 libavfilter/af_bungee.c create mode 100644 libavfilter/af_bungeepro.c create mode 100644 libavfilter/bungee.h diff --git a/Changelog b/Changelog index 6fd95c9136..9986e89452 100644 --- a/Changelog +++ b/Changelog @@ -45,6 +45,7 @@ version 8.0: - ffprobe -codec option - HDR10+ metadata passthrough when decoding/encoding with libaom-av1 +- Bungee audio stretch and pitch shift filter version 7.1: - Raw Captions with Time (RCWT) closed caption demuxer diff --git a/configure b/configure index 7828381b5d..897b3e8d23 100755 --- a/configure +++ b/configure @@ -220,6 +220,8 @@ External library support: needed for subtitles and ass filter [no] --enable-libbluray enable BluRay reading using libbluray [no] --enable-libbs2b enable bs2b DSP library [no] + --enable-libbungee enable enable Bungee audio stretch [no] + --enable-libbungeepro enable enable Bungee Pro audio stretch [no] --enable-libcaca enable textual display using libcaca [no] --enable-libcelt enable CELT decoding via libcelt [no] --enable-libcdio enable audio CD grabbing with libcdio [no] @@ -1962,6 +1964,8 @@ EXTERNAL_LIBRARY_LIST=" libass libbluray libbs2b + libbungee + libbungeepro libcaca libcelt libcodec2 @@ -3978,6 +3982,8 @@ blend_vulkan_filter_deps="vulkan spirv_compiler" boxblur_filter_deps="gpl" boxblur_opencl_filter_deps="opencl gpl" bs2b_filter_deps="libbs2b" +bungee_filter_deps="libbungee" +bungeepro_filter_deps="libbungeepro" bwdif_cuda_filter_deps="ffnvcodec" bwdif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" bwdif_vulkan_filter_deps="vulkan spirv_compiler" @@ -7107,6 +7113,8 @@ enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883 enabled libass && require_pkg_config libass "libass >= 0.11.0" ass/ass.h ass_library_init enabled libbluray && require_pkg_config libbluray libbluray libbluray/bluray.h bd_open enabled libbs2b && require_pkg_config libbs2b libbs2b bs2b.h bs2b_open +enabled libbungee && require_pkg_config libbungee "libbungee >= 0.0.0" bungee/Bungee.h getFunctionsBungeeBasic +enabled libbungeepro && require_pkg_config libbungeepro "libbungeepro >= 0.0.0" bungee/Bungee.h getFunctionsBungeePro enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && { check_lib libcelt celt/celt.h celt_decoder_create_custom -lcelt0 || die "ERROR: libcelt must be installed and version must be >= 0.11.0."; } diff --git a/doc/filters.texi b/doc/filters.texi index 5863041d1a..69afa5fc29 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -4292,6 +4292,30 @@ Feed level (in Hz). @end table +@section bungee + +Stretch audio timescale and/or modify pitch using Bungee Basic. + +This filter accepts the following parameters: +@table @option +@item speed +Controls the speed or tempo of the output audio. The default value 1 will not change speed. +@item pitch +Sets a pitch adjustment as a frequency multiplier. The default value 1 will have no effect on pitch. +@end table + + +To enable compilation of this filter, you need to configure FFmpeg with +@code{--enable-libbungee}. + +@section bungeepro + +Stretch audio timescale and/or modify pitch using Bungee Pro. This filter accepts +the same options as @ref{bungee}. + +To enable compilation of this filter, you need to configure FFmpeg with +@code{--enable-libbungeepron }. + @section channelmap Remap input channels to new locations. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 1f5de29ba2..d02dbe841e 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -126,6 +126,8 @@ OBJS-$(CONFIG_BANDREJECT_FILTER) += af_biquads.o OBJS-$(CONFIG_BASS_FILTER) += af_biquads.o OBJS-$(CONFIG_BIQUAD_FILTER) += af_biquads.o OBJS-$(CONFIG_BS2B_FILTER) += af_bs2b.o +OBJS-$(CONFIG_BUNGEE_FILTER) += af_bungee.o +OBJS-$(CONFIG_BUNGEEPRO_FILTER) += af_bungeepro.o OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o OBJS-$(CONFIG_CHORUS_FILTER) += af_chorus.o generate_wave_table.o diff --git a/libavfilter/af_bungee.c b/libavfilter/af_bungee.c new file mode 100644 index 0000000000..cfcb1c3e58 --- /dev/null +++ b/libavfilter/af_bungee.c @@ -0,0 +1,26 @@ +/* + * 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 + */ + +#define BUNGEE bungee +#define BUNGEE_CLASS bungee_class +#define BUNGEE_AF ff_af_bungee +#define BUNGEE_name "bungee" +#define BUNGEE_Name "Bungee" +#define BUNGEE_GET_FUNCTIONS getFunctionsBungeeBasic + +#include "bungee.h" diff --git a/libavfilter/af_bungeepro.c b/libavfilter/af_bungeepro.c new file mode 100644 index 0000000000..f625e64481 --- /dev/null +++ b/libavfilter/af_bungeepro.c @@ -0,0 +1,26 @@ +/* + * 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 + */ + +#define BUNGEE bungeepro +#define BUNGEE_CLASS bungeepro_class +#define BUNGEE_AF ff_af_bungeepro +#define BUNGEE_name "bungeepro" +#define BUNGEE_Name "Bungee Pro" +#define BUNGEE_GET_FUNCTIONS getFunctionsBungeePro + +#include "bungee.h" \ No newline at end of file diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 84f15f85c5..90409e814d 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -112,6 +112,8 @@ extern const FFFilter ff_af_bandreject; extern const FFFilter ff_af_bass; extern const FFFilter ff_af_biquad; extern const FFFilter ff_af_bs2b; +extern const FFFilter ff_af_bungee; +extern const FFFilter ff_af_bungeepro; extern const FFFilter ff_af_channelmap; extern const FFFilter ff_af_channelsplit; extern const FFFilter ff_af_chorus; diff --git a/libavfilter/bungee.h b/libavfilter/bungee.h new file mode 100644 index 0000000000..9fad792421 --- /dev/null +++ b/libavfilter/bungee.h @@ -0,0 +1,280 @@ +/* + * 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 "bungee/Bungee.h" + +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "filters.h" + +typedef struct BungeeContext +{ + const AVClass *class; + + const struct Functions *functions; + + void *stretcher; + int debug; + + struct SampleRates sampleRates; + int channelCount; + + float *inputBuffer; + int inputBufferSampleCount; + + int64_t inputBufferBeginPosition; + int64_t inputBufferEndPosition; + int64_t inputStartPosition; + int64_t inputFinishPosition; + + int64_t ptsOriginPosition; + + struct Request request; + struct InputChunk inputChunk; + + int64_t start_pts; + int64_t last_pts; + + int64_t outputSampleCount; + int eof; // end of input +} BungeeContext; + +static int samplesNeeded(const BungeeContext *s) +{ + return s->inputChunk.end - s->inputBufferEndPosition; +} + +static void appendToInputBuffer(BungeeContext *s, const AVFrame *in) +{ + int moveOffset = s->inputBufferBeginPosition - s->inputChunk.begin; + int moveCount = s->inputBufferEndPosition - s->inputChunk.begin; + + av_assert1(!in || in->nb_samples <= samplesNeeded(s)); + + if (moveCount < 0) + moveCount = 0; + + for (int c = 0; c < s->channelCount; ++c) + memmove( + &s->inputBuffer[c * s->inputBufferSampleCount], + &s->inputBuffer[c * s->inputBufferSampleCount - moveOffset], + moveCount * sizeof(float)); + + s->inputBufferBeginPosition = s->inputChunk.begin; + + if (in) { + const float *const *source = (const float *const *)in->extended_data; + + for (int c = 0; c < s->channelCount; ++c) + memcpy( + &s->inputBuffer[moveCount + c * s->inputBufferSampleCount], + source[c], + in->nb_samples * sizeof(float)); + + s->inputBufferEndPosition += in->nb_samples; + } +} + +#define BUNGEE_OPTIONS BUNGEE##_options + +static const AVOption BUNGEE_OPTIONS[] = { + {"speed", "set speed as a tempo multiplier", offsetof(BungeeContext, request.speed), AV_OPT_TYPE_DOUBLE, {.dbl = 1}, 0.01, 100, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM}, + {"pitch", "set pitch as a frequency multiplier", offsetof(BungeeContext, request.pitch), AV_OPT_TYPE_DOUBLE, {.dbl = 1}, 0.25, 2, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM}, + {"debug", "verbose debug checks", offsetof(BungeeContext, debug), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_AUDIO_PARAM}, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(BUNGEE); + +static av_cold int init(AVFilterContext *ctx) +{ + BungeeContext *s = ctx->priv; + s->functions = BUNGEE_GET_FUNCTIONS(); + if (!s->functions) + { + av_log(ctx, AV_LOG_ERROR, "No functions found for " BUNGEE_Name "\n"); + return AVERROR_INVALIDDATA; + } + s->stretcher = NULL; + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + BungeeContext *s = ctx->priv; + if (s->stretcher) + s->functions->destroy(s->stretcher); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + BungeeContext *s = ctx->priv; + s->sampleRates.input = inlink->sample_rate; + s->sampleRates.output = inlink->sample_rate; + s->channelCount = inlink->ch_layout.nb_channels; + + if (s->stretcher) + s->functions->destroy(s->stretcher); + s->stretcher = NULL; + + s->stretcher = s->functions->create(s->sampleRates, s->channelCount, 0); + s->functions->enableInstrumentation(s->stretcher, s->debug); + + s->request.position = 0; + s->functions->next(s->stretcher, &s->request); + s->request.reset = 1; + s->inputChunk = s->functions->specifyGrain(s->stretcher, &s->request, 0.); + + s->inputBufferSampleCount = s->inputChunk.end - s->inputChunk.begin; + s->inputStartPosition = s->inputBufferSampleCount / 2; + s->inputBufferBeginPosition = s->inputStartPosition - s->inputBufferSampleCount; + s->inputBufferEndPosition = s->inputStartPosition; + + s->inputBuffer = av_calloc(s->inputBufferSampleCount * s->channelCount, sizeof(float)); + if (!s->inputBuffer) + return AVERROR(ENOMEM); + + s->outputSampleCount = 0; + + s->ptsOriginPosition = AV_NOPTS_VALUE; + s->eof = 0; + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + BungeeContext *s = ctx->priv; + struct OutputChunk outputChunk; + int64_t pts; + int status, ret = 0, endOfOutput = 0; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!s->eof) + { + AVFrame *in; + ret = ff_inlink_consume_samples(inlink, 1, samplesNeeded(s), &in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) + s->eof = status == AVERROR_EOF; + + if (in) + { + if (s->start_pts == AV_NOPTS_VALUE) + s->start_pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + + appendToInputBuffer(s, in); + av_frame_free(&in); + } + + if (s->eof) + s->inputFinishPosition = s->inputBufferEndPosition; + } + else + appendToInputBuffer(s, NULL); + + av_assert1(s->inputBufferEndPosition == s->inputChunk.end); + + if (samplesNeeded(s) == 0 || s->eof) + { + s->functions->next(s->stretcher, &s->request); + s->functions->analyseGrain(s->stretcher, s->inputBuffer, s->inputBufferSampleCount, 0, samplesNeeded(s)); + s->functions->synthesiseGrain(s->stretcher, &outputChunk); + s->inputChunk = s->functions->specifyGrain(s->stretcher, &s->request, 0); + + endOfOutput = s->eof && outputChunk.request[1]->position >= s->inputFinishPosition; + + if (outputChunk.request[0]->position >= s->inputStartPosition) + { + AVRational outputTimebase = {1, s->sampleRates.output}; + AVFrame *out = ff_get_audio_buffer(outlink, outputChunk.frameCount); + float *const *p = (float *const *)out->extended_data; + + if (!out) + return AVERROR(ENOMEM); + + if (endOfOutput) + { + const double fraction = (s->inputFinishPosition - outputChunk.request[0]->position) / (outputChunk.request[1]->position - outputChunk.request[0]->position); + int frameCount = round(outputChunk.frameCount * fraction); + if (frameCount < 0) + frameCount = 0; + if (frameCount > outputChunk.frameCount) + frameCount = outputChunk.frameCount; + outputChunk.frameCount = frameCount; + } + + for (int c = 0; c < s->channelCount; ++c) + memcpy(p[c], &outputChunk.data[c * outputChunk.channelStride], sizeof(float) * outputChunk.frameCount); + + out->pts = s->start_pts + av_rescale_q(s->outputSampleCount, outputTimebase, outlink->time_base); + s->last_pts = out->pts; + + out->nb_samples = outputChunk.frameCount; + s->outputSampleCount += outputChunk.frameCount; + + ret = ff_filter_frame(outlink, out); + if (ret < 0) + return ret; + } + } + + if (ff_inlink_queued_samples(inlink) >= samplesNeeded(s)) + ff_filter_set_ready(ctx, 100); + + if (endOfOutput) + { + ff_outlink_set_status(outlink, AVERROR_EOF, s->last_pts); + return 0; + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static const AVFilterPad bungee_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_input, + }, +}; + +const FFFilter BUNGEE_AF = { + .p.name = BUNGEE_name, + .p.description = NULL_IF_CONFIG_SMALL("Adjust audio speed and pitch using " BUNGEE_Name "."), + .p.priv_class = &BUNGEE_CLASS, + .priv_size = sizeof(BungeeContext), + .init = init, + .uninit = uninit, + .activate = activate, + FILTER_INPUTS(bungee_inputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), +}; -- 2.49.1 >>From ac8a018d6b17a6a9965d272684e4ff3a5fcd9029 Mon Sep 17 00:00:00 2001 From: John Funnell Date: Sun, 12 Oct 2025 11:35:13 +0100 Subject: [PATCH 2/4] use semitones for pitch parameters; add resample parameter --- libavfilter/bungee.h | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/libavfilter/bungee.h b/libavfilter/bungee.h index 9fad792421..e4cc105de7 100644 --- a/libavfilter/bungee.h +++ b/libavfilter/bungee.h @@ -50,6 +50,8 @@ typedef struct BungeeContext int64_t ptsOriginPosition; + double semitones; + struct Request request; struct InputChunk inputChunk; @@ -98,10 +100,35 @@ static void appendToInputBuffer(BungeeContext *s, const AVFrame *in) #define BUNGEE_OPTIONS BUNGEE##_options + +enum BungeeModeCounts { + +#define X_BEGIN(Type, type) count##Type = 0 +#define X_ITEM(Type, type, mode, description) + 1 +#define X_END(Type, type) , +BUNGEE_MODES +#undef X_BEGIN +#undef X_ITEM +#undef X_END + +}; + + +static const int flags = AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM; + static const AVOption BUNGEE_OPTIONS[] = { - {"speed", "set speed as a tempo multiplier", offsetof(BungeeContext, request.speed), AV_OPT_TYPE_DOUBLE, {.dbl = 1}, 0.01, 100, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM}, - {"pitch", "set pitch as a frequency multiplier", offsetof(BungeeContext, request.pitch), AV_OPT_TYPE_DOUBLE, {.dbl = 1}, 0.25, 2, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM}, - {"debug", "verbose debug checks", offsetof(BungeeContext, debug), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_AUDIO_PARAM}, + {"speed", "set speed as a tempo multiplier", offsetof(BungeeContext, request.speed), AV_OPT_TYPE_DOUBLE, {.dbl = 1}, 0.01, 100, flags}, + {"pitch", "set pitch as a semitone offset", offsetof(BungeeContext, semitones), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -24, 24, flags}, + {"debug", "verbose debug checks", offsetof(BungeeContext, debug), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 0, AV_OPT_FLAG_AUDIO_PARAM}, + +#define X_BEGIN(Type, type) { #type, "set " #type " mode", offsetof(BungeeContext, request.type##Mode), AV_OPT_TYPE_INT, {.i64=0}, 0, count##Type - 1, flags, .unit = #type }, +#define X_ITEM(Type, type, mode, description) {#mode, description, 0, AV_OPT_TYPE_CONST, {.i64=type##Mode_##mode}, 0, 0, flags, .unit = #type }, +#define X_END(Type, type) +BUNGEE_MODES +#undef X_BEGIN +#undef X_ITEM +#undef X_END + {NULL}, }; @@ -143,6 +170,7 @@ static int config_input(AVFilterLink *inlink) s->functions->enableInstrumentation(s->stretcher, s->debug); s->request.position = 0; + s->request.pitch = pow(2., s->semitones * (1. / 12)); s->functions->next(s->stretcher, &s->request); s->request.reset = 1; s->inputChunk = s->functions->specifyGrain(s->stretcher, &s->request, 0.); @@ -202,6 +230,8 @@ static int activate(AVFilterContext *ctx) if (samplesNeeded(s) == 0 || s->eof) { + s->request.pitch = pow(2., s->semitones * (1. / 12)); + s->functions->next(s->stretcher, &s->request); s->functions->analyseGrain(s->stretcher, s->inputBuffer, s->inputBufferSampleCount, 0, samplesNeeded(s)); s->functions->synthesiseGrain(s->stretcher, &outputChunk); -- 2.49.1 >>From b95aeb32ba210d3c982f27ac6cd57c36f514d5c7 Mon Sep 17 00:00:00 2001 From: John Funnell Date: Sun, 12 Oct 2025 17:28:15 +0100 Subject: [PATCH 3/4] require libbungee v2.4 --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 897b3e8d23..a76e83c709 100755 --- a/configure +++ b/configure @@ -7113,7 +7113,7 @@ enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883 enabled libass && require_pkg_config libass "libass >= 0.11.0" ass/ass.h ass_library_init enabled libbluray && require_pkg_config libbluray libbluray libbluray/bluray.h bd_open enabled libbs2b && require_pkg_config libbs2b libbs2b bs2b.h bs2b_open -enabled libbungee && require_pkg_config libbungee "libbungee >= 0.0.0" bungee/Bungee.h getFunctionsBungeeBasic +enabled libbungee && require_pkg_config libbungee "libbungee >= 2.4.0 libbungee < 3.0.0" bungee/Bungee.h getFunctionsBungeeBasic enabled libbungeepro && require_pkg_config libbungeepro "libbungeepro >= 0.0.0" bungee/Bungee.h getFunctionsBungeePro enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && { check_lib libcelt celt/celt.h celt_decoder_create_custom -lcelt0 || -- 2.49.1 >>From 529304750258cac5055b0766d70db6f50a34f413 Mon Sep 17 00:00:00 2001 From: John Funnell Date: Mon, 13 Oct 2025 05:48:51 +0100 Subject: [PATCH 4/4] tidying before push --- Changelog | 2 +- doc/filters.texi | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 9986e89452..42bfa1cd1d 100644 --- a/Changelog +++ b/Changelog @@ -7,6 +7,7 @@ version : - gfxcapture: Windows.Graphics.Capture based window/monitor capture - hxvs demuxer for HXVS/HXVT IP camera format - MPEG-H 3D Audio decoding via mpeghdec +- Bungee and Bungee Pro audio stretch filters version 8.0: @@ -45,7 +46,6 @@ version 8.0: - ffprobe -codec option - HDR10+ metadata passthrough when decoding/encoding with libaom-av1 -- Bungee audio stretch and pitch shift filter version 7.1: - Raw Captions with Time (RCWT) closed caption demuxer diff --git a/doc/filters.texi b/doc/filters.texi index 69afa5fc29..00189d41bf 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -4294,7 +4294,7 @@ Feed level (in Hz). @section bungee -Stretch audio timescale and/or modify pitch using Bungee Basic. +Stretch audio timescale and/or modify pitch using Bungee. This filter accepts the following parameters: @table @option @@ -4302,6 +4302,8 @@ This filter accepts the following parameters: Controls the speed or tempo of the output audio. The default value 1 will not change speed. @item pitch Sets a pitch adjustment as a frequency multiplier. The default value 1 will have no effect on pitch. +@item resample +Controls whether Bungee should apply any resampling necessary to change pitch on the input or output of the filter. @end table -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org