From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <ffmpeg-devel-bounces@ffmpeg.org>
Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100])
	by master.gitmailbox.com (Postfix) with ESMTPS id D61594DBC4
	for <ffmpegdev@gitmailbox.com>; Wed, 23 Apr 2025 14:17:44 +0000 (UTC)
Received: from [127.0.1.1] (localhost [127.0.0.1])
	by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1721268A15F;
	Wed, 23 Apr 2025 17:17:40 +0300 (EEST)
Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com
 [209.85.210.182])
 by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A3FB8687AEB
 for <ffmpeg-devel@ffmpeg.org>; Wed, 23 Apr 2025 17:17:33 +0300 (EEST)
Received: by mail-pf1-f182.google.com with SMTP id
 d2e1a72fcca58-739b3fe7ce8so5495381b3a.0
 for <ffmpeg-devel@ffmpeg.org>; Wed, 23 Apr 2025 07:17:33 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=gmail.com; s=20230601; t=1745417851; x=1746022651; darn=ffmpeg.org;
 h=content-transfer-encoding:mime-version:message-id:date:subject:cc
 :to:from:from:to:cc:subject:date:message-id:reply-to;
 bh=K7TDN3JyERkeMFSaqPEeGBMWgu7nyFsZeFAhUJOsrGo=;
 b=TgJdv5whQrZLntj5LUfDRPs8ss75vozYG9Cw5v84oDbquOPKfOy9TiGRCcvZqSXBgZ
 fU7njZg6f0+UpLmrun7QHYRyDEc+1ftFUqFnr7g7HRg403rNYx70ztHknY86+rE2P9aF
 x4xSbq+mHysqpq36V7ezjX2kK7DzyhVT0vVk11p8Vj1vaC4zq4DKW8QnOLb/0CCccpJk
 UxWk+JbWSpp4Z43OXsz6YBosaMpFV0SltFD7R99LWNduw4nVFOIo5JprDa/k3j3/Oo4B
 MmFir4OC+StqUhhNSNSgsfocth0Qz15N3BSIFJqYI3lWPH5Yha3P5dmzgXSEuZIwMdsQ
 B7XQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1745417851; x=1746022651;
 h=content-transfer-encoding:mime-version:message-id:date:subject:cc
 :to:from:x-gm-message-state:from:to:cc:subject:date:message-id
 :reply-to;
 bh=K7TDN3JyERkeMFSaqPEeGBMWgu7nyFsZeFAhUJOsrGo=;
 b=SdP5R93epre612iKW4fo2OSO0CtNzFL91EuqwFHpcCnq0tNeSebGXqrucgWlGgDXwh
 UOhTOabqCVvJLKS4t1xBQZUnkW6PJNErKEqe5sgP/Xp3WXIZmCZ4ECB+IMo/eAP0CjC+
 hmjHC1xnsAiAj56Z7MvV7/LXqdUlI7PyWH/Zn8czS9Lg0k9Pk067tIJbxu3HZKaVjUdL
 OroIr6qKtdh792Rqgi5YLVeN6JRvQ9r+Vwqv5Uv4z2lmOMekzQNgPvN/4j/ZFNRFx9MJ
 fL4unvdLOeBKPL8lHYeatpM2m875P3zVXudVT/GBjS6BGWb6CrHA+z1O6fv1Jh/c6mJI
 eMCg==
X-Gm-Message-State: AOJu0YyEVZgu3uOFhDZFOM1mN/nM5LRT0kLT3AGEj0I4W6lDgya1UX2h
 GiJY8oKH+g74pPD+71OZtbjLsf9W7bGiBsPz6n+U2X7teWyhQPybU4WFdQ==
X-Gm-Gg: ASbGnctYuhmaJLdbrTQ7T0xyXa4SNkMzJyf12LQnRiLXzL0OHm3hYJfeEUZ/OM+6Hh3
 I3aIvk/+B7TKp6c6+bkLe+YlxLcZaPzgJewowT939dSPjhNYUhsyh1Ktv7EI3aikejvEsIH5zcy
 ME/tpljTn/x5PHpp3BHuSpIlSjSPTJp2mgefr1+dQEjTqvyXIs2rthf/A7JvdlX8bhgIBdH/BCa
 4DPiZMbesFvT4/GfY5DtDGXYFpCwetJXZUs1wXW02PVnOQK5Hz3lzxo/1MiDLPo2dBuTiXoRqB0
 C3xYoe+CdQ16bm/lcUnISe9e0N73c/ubkLToWQ6knKQHEmnM
X-Google-Smtp-Source: AGHT+IHEi6qauzY64zGxfi9rcZjDjiAMdTE02Nof8K1fY78p89GmIS7w8V3/LJ8IsM15T+d9LOgvcQ==
X-Received: by 2002:a05:6a20:2d28:b0:1f5:83bd:6cc1 with SMTP id
 adf61e73a8af0-203cba9a9ffmr27791591637.0.1745417851201; 
 Wed, 23 Apr 2025 07:17:31 -0700 (PDT)
Received: from cen.mioffice.cn ([2408:8607:1b00:8:da43:aeff:fee2:88b2])
 by smtp.gmail.com with ESMTPSA id
 d2e1a72fcca58-73dbf8c0200sm10555073b3a.1.2025.04.23.07.17.28
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Wed, 23 Apr 2025 07:17:30 -0700 (PDT)
From: cenzhanquan2@gmail.com
To: ffmpeg-devel@ffmpeg.org
Date: Wed, 23 Apr 2025 22:17:20 +0800
Message-Id: <20250423141720.4033649-1-cenzhanquan2@gmail.com>
X-Mailer: git-send-email 2.34.1
MIME-Version: 1.0
Subject: [FFmpeg-devel] [PATCH 1/1] libavfilter/volume: do fade when
 adjusting the volume.
X-BeenThere: ffmpeg-devel@ffmpeg.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org>
List-Unsubscribe: <https://ffmpeg.org/mailman/options/ffmpeg-devel>,
 <mailto:ffmpeg-devel-request@ffmpeg.org?subject=unsubscribe>
List-Archive: <https://ffmpeg.org/pipermail/ffmpeg-devel>
List-Post: <mailto:ffmpeg-devel@ffmpeg.org>
List-Help: <mailto:ffmpeg-devel-request@ffmpeg.org?subject=help>
List-Subscribe: <https://ffmpeg.org/mailman/listinfo/ffmpeg-devel>,
 <mailto:ffmpeg-devel-request@ffmpeg.org?subject=subscribe>
Reply-To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
Cc: zhanquan cen <cenzhanquan2@gmail.com>
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: ffmpeg-devel-bounces@ffmpeg.org
Sender: "ffmpeg-devel" <ffmpeg-devel-bounces@ffmpeg.org>
Archived-At: <https://master.gitmailbox.com/ffmpegdev/20250423141720.4033649-1-cenzhanquan2@gmail.com/>
List-Archive: <https://master.gitmailbox.com/ffmpegdev/>
List-Post: <mailto:ffmpegdev@gitmailbox.com>

From: zhanquan cen <cenzhanquan2@gmail.com>

1. add simple fade when volume.
2. do fade when adjust volume to maximal.

when to adjust volume we hope that the audio sample is smooth
we need to calculate the gradient step between each sample based
on the total change of the gradient (i.e. dst_volume - src_volume)
and the number of samples (nb_samples) and apply it to the target sample.

Signed-off-by: zhanquan cen <cenzhanquan2@gmail.com>
---
 libavfilter/af_volume.c | 125 ++++++++++++++++++++++++++++++++++++----
 libavfilter/af_volume.h |  30 ++++++++++
 2 files changed, 144 insertions(+), 11 deletions(-)

diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
index 471bffeceb..283865f810 100644
--- a/libavfilter/af_volume.c
+++ b/libavfilter/af_volume.c
@@ -162,6 +162,22 @@ static int query_formats(const AVFilterContext *ctx,
     return 0;
 }
 
+static inline void fade_samples_u8(uint8_t *dst, const uint8_t *src,
+                                   int nb_samples, int chs, int dst_volume, int src_volume)
+{
+    int i, j, k = 0;
+    int64_t sample;
+    int64_t step;
+
+    step = (((int64_t)dst_volume - src_volume) << 8) / nb_samples;
+    for (i = 0; i < nb_samples; i++) {
+        for (j = 0; j < chs; j++, k++) {
+            sample = (int64_t)(src[k] - 128) * (src_volume + (step * i) >> 8) + 128;
+            dst[k] = av_clip_uint8((sample >> 8) + 128);
+        }
+    }
+}
+
 static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
                                     int nb_samples, int volume)
 {
@@ -170,6 +186,22 @@ static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
         dst[i] = av_clip_uint8(((((int64_t)src[i] - 128) * volume + 128) >> 8) + 128);
 }
 
+static inline void fade_samples_u8_small(uint8_t *dst, const uint8_t *src,
+                                         int nb_samples, int chs, int dst_volume, int src_volume)
+{
+    int i, j, k = 0;
+    int sample;
+    int step;
+
+    step = ((dst_volume - src_volume) << 8) / nb_samples;
+    for (i = 0; i < nb_samples; i++) {
+        for (j = 0; j < chs; j++, k++) {
+            sample = (src[k] - 128) * (src_volume + (step * i >> 8)) + 128;
+            dst[k] = av_clip_uint8((sample >> 8) + 128);
+        }
+    }
+}
+
 static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src,
                                           int nb_samples, int volume)
 {
@@ -178,6 +210,22 @@ static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src,
         dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128);
 }
 
+static inline void fade_samples_s16(uint8_t *dst, const uint8_t *src,
+                                    int nb_samples, int chs, int dst_volume, int src_volume)
+{
+    const int16_t *smp_src = (const int16_t *)src;
+    int16_t *smp_dst = (int16_t *)dst;
+    int i, j, k = 0;
+    int64_t step;
+
+    step = (((int64_t)dst_volume - src_volume) << 8) / nb_samples;
+    for (i = 0; i < nb_samples; i++) {
+        for (j = 0; j < chs; j++, k++) {
+            smp_dst[k] = av_clip_int16((int64_t)(smp_src[k] * (src_volume + (step * i >> 8)) + 128) >> 8);
+        }
+    }
+}
+
 static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src,
                                      int nb_samples, int volume)
 {
@@ -188,6 +236,22 @@ static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src,
         smp_dst[i] = av_clip_int16(((int64_t)smp_src[i] * volume + 128) >> 8);
 }
 
+static inline void fade_samples_s16_small(uint8_t *dst, const uint8_t *src,
+                                          int nb_samples, int chs, int dst_volume, int src_volume)
+{
+    const int16_t *smp_src = (const int16_t *)src;
+    int16_t *smp_dst = (int16_t *)dst;
+    int i, j, k = 0;
+    int step;
+
+    step = ((dst_volume - src_volume) << 8) / nb_samples;
+    for (i = 0; i < nb_samples; i++) {
+        for (j = 0; j < chs; j++, k++) {
+            smp_dst[k] = av_clip_int16((smp_src[k] * (src_volume + (step * i >> 8)) + 128) >> 8);
+        }
+    }
+}
+
 static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src,
                                            int nb_samples, int volume)
 {
@@ -198,6 +262,22 @@ static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src,
         smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8);
 }
 
+static inline void fade_samples_s32(uint8_t *dst, const uint8_t *src,
+                                    int nb_samples, int chs, int dst_volume, int src_volume)
+{
+    const int32_t *smp_src = (const int32_t *)src;
+    int32_t *smp_dst = (int32_t *)dst;
+    int i, j, k = 0;
+    int64_t step;
+
+    step = (((int64_t)dst_volume - src_volume) << 8) / nb_samples;
+    for (i = 0; i < nb_samples; i++) {
+        for (j = 0; j < chs; j++, k++) {
+            smp_dst[k] = av_clipl_int32((int64_t)(smp_src[k] * (src_volume + (step * i >> 8)) + 128) >> 8);
+        }
+    }
+}
+
 static inline void scale_samples_s32(uint8_t *dst, const uint8_t *src,
                                      int nb_samples, int volume)
 {
@@ -214,19 +294,26 @@ static av_cold void volume_init(VolumeContext *vol)
 
     switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
     case AV_SAMPLE_FMT_U8:
-        if (vol->volume_i < 0x1000000)
+        if (vol->volume_i < 0x1000000) {
             vol->scale_samples = scale_samples_u8_small;
-        else
+            vol->fade_samples  = fade_samples_u8_small;
+        } else {
             vol->scale_samples = scale_samples_u8;
+            vol->fade_samples  = fade_samples_u8;
+        }
         break;
     case AV_SAMPLE_FMT_S16:
-        if (vol->volume_i < 0x10000)
+        if (vol->volume_i < 0x10000) {
             vol->scale_samples = scale_samples_s16_small;
-        else
+            vol->fade_samples  = fade_samples_s16_small;
+        } else {
             vol->scale_samples = scale_samples_s16;
+            vol->fade_samples  = fade_samples_s16;
+        }
         break;
     case AV_SAMPLE_FMT_S32:
         vol->scale_samples = scale_samples_s32;
+        vol->fade_samples  = fade_samples_s32;
         break;
     case AV_SAMPLE_FMT_FLT:
         vol->samples_align = 4;
@@ -313,8 +400,11 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
     if (!strcmp(cmd, "volume")) {
         if ((ret = set_expr(&vol->volume_pexpr, args, ctx)) < 0)
             return ret;
-        if (vol->eval_mode == EVAL_MODE_ONCE)
+        if (vol->eval_mode == EVAL_MODE_ONCE) {
+            vol->volume_isrc = vol->volume_i;
             set_volume(ctx);
+            vol->voluming = true;
+        }
     }
 
     return ret;
@@ -329,6 +419,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     int nb_samples        = buf->nb_samples;
     AVFrame *out_buf;
     AVFrameSideData *sd = av_frame_get_side_data(buf, AV_FRAME_DATA_REPLAYGAIN);
+    bool planar;
     int ret;
 
     if (sd && vol->replaygain != REPLAYGAIN_IGNORE) {
@@ -380,7 +471,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     if (vol->eval_mode == EVAL_MODE_FRAME)
         set_volume(ctx);
 
-    if (vol->volume == 1.0 || vol->volume_i == 256) {
+    if ((vol->volume == 1.0 || vol->volume_i == 256) && !vol->voluming) {
         out_buf = buf;
         goto end;
     }
@@ -406,16 +497,28 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     if (vol->precision != PRECISION_FIXED || vol->volume_i > 0) {
         int p, plane_samples;
 
-        if (av_sample_fmt_is_planar(buf->format))
+        planar = av_sample_fmt_is_planar(buf->format);
+        if (planar)
             plane_samples = FFALIGN(nb_samples, vol->samples_align);
         else
             plane_samples = FFALIGN(nb_samples * vol->channels, vol->samples_align);
 
         if (vol->precision == PRECISION_FIXED) {
-            for (p = 0; p < vol->planes; p++) {
-                vol->scale_samples(out_buf->extended_data[p],
-                                   buf->extended_data[p], plane_samples,
-                                   vol->volume_i);
+            if (vol->voluming && vol->fade_samples) {
+                for (p = 0; p < vol->planes; p++) {
+                    vol->fade_samples(out_buf->extended_data[p], buf->extended_data[p],
+                                      nb_samples, planar ? 1 : vol->channels,
+                                      vol->volume_i, vol->volume_isrc);
+                }
+
+                vol->voluming = false;
+            } else {
+                for (p = 0; p < vol->planes; p++) {
+                    vol->scale_samples(out_buf->extended_data[p],
+                                       buf->extended_data[p], plane_samples,
+                                       vol->volume_i);
+
+                }
             }
         } else if (av_get_packed_sample_fmt(vol->sample_fmt) == AV_SAMPLE_FMT_FLT) {
             for (p = 0; p < vol->planes; p++) {
diff --git a/libavfilter/af_volume.h b/libavfilter/af_volume.h
index e9527eea8a..a610422a43 100644
--- a/libavfilter/af_volume.h
+++ b/libavfilter/af_volume.h
@@ -25,6 +25,7 @@
 #define AVFILTER_VOLUME_H
 
 #include <stdint.h>
+#include <stdbool.h>
 #include "libavutil/eval.h"
 #include "libavutil/float_dsp.h"
 #include "libavutil/log.h"
@@ -84,7 +85,36 @@ typedef struct VolumeContext {
 
     void (*scale_samples)(uint8_t *dst, const uint8_t *src, int nb_samples,
                           int volume);
+
+    /**
+     * @brief Function pointer for fading samples.
+     *
+     * @param dst Destination buffer for faded samples.
+     * @param src Source buffer for original samples.
+     * @param nb_samples Number of samples to fade.
+     * @param chs Number of channels in the audio samples.
+     * @param dst_volume Destination volume level for fading.
+     * @param src_volume Source volume level for fading.
+     */
+    void (*fade_samples)(uint8_t *dst, const uint8_t *src, int nb_samples, int chs,
+        int dst_volume, int src_volume);
+
     int samples_align;
+
+    /**
+     * @brief Flag indicating whether fading is in progress.
+     *
+     * If this flag is set to true, it means that the audio samples are currently being faded.
+     */
+    bool voluming;
+
+    /**
+     * @brief Source volume level for fading.
+     *
+     * This variable stores the backup volume level for fading. It is used to calculate the
+     * step size when adjusting the volume.
+     */
+    int  volume_isrc;
 } VolumeContext;
 
 void ff_volume_init_x86(VolumeContext *vol);
-- 
2.34.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".