This is done by limiting the allowed input formats depending on whether the
user wants to do fixed-, float-, or double-precision scaling.
---
 doc/filters.texi        |   47 ++++++++++++++++-----
 libavfilter/af_volume.c |  106 ++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 123 insertions(+), 30 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 8bb2040..27b32a6 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -322,30 +322,49 @@ conversion.
 
 Adjust the input audio volume.
 
-The filter accepts exactly one parameter @var{vol}, which expresses
-how the audio volume will be increased or decresed.
+The filter accepts the following named parameters:
+@table @option
+
+@item volume
+Expresses how the audio volume will be increased or decreased.
 
 Output values are clipped to the maximum value.
 
-If @var{vol} is expressed as a decimal number, and the output audio
+If @var{volume} is expressed as a decimal number, and the output audio
 volume is given by the relation:
 @example
-@var{output_volume} = @var{vol} * @var{input_volume}
+@var{output_volume} = @var{volume} * @var{input_volume}
 @end example
 
-If @var{vol} is expressed as a decimal number followed by the string
+If @var{volume} is expressed as a decimal number followed by the string
 "dB", the value represents the requested change in decibels of the
 input audio power, and the output audio volume is given by the
 relation:
 @example
-@var{output_volume} = 10^(@var{vol}/20) * @var{input_volume}
+@var{output_volume} = 10^(@var{volume}/20) * @var{input_volume}
 @end example
 
-Otherwise @var{vol} is considered an expression and its evaluated
+Otherwise @var{volume} is considered an expression and its evaluated
 value is used for computing the output audio volume according to the
 first relation.
 
-Default value for @var{vol} is 1.0.
+Default value for @var{volume} is 1.0.
+
+@item precision
+Mathematical precision.
+
+This determines which input sample formats will be allowed, which affects the
+precision of the volume scaling.
+
+@table @option
+@item fixed
+8-bit fixed-point. Limits input sample format to U8, S16, and S32.
+@item float
+32-bit floating-point. Limits input sample format to FLT. (default)
+@item double
+64-bit floating-point. Limits input sample format to DBL.
+@end table
+@end table
 
 @subsection Examples
 
@@ -353,18 +372,24 @@ Default value for @var{vol} is 1.0.
 @item
 Halve the input audio volume:
 @example
-volume=0.5
+volume=volume=0.5
 @end example
 
 The above example is equivalent to:
 @example
-volume=1/2
+volume=volume=1/2
 @end example
 
 @item
 Decrease input audio power by 12 decibels:
 @example
-volume=-12dB
+volume=volume=-12dB
+@end example
+
+@item
+Increase input audio power by 6 decibels using fixed-point precision:
+@example
+volume=volume=6dB:precision=fixed
 @end example
 @end itemize
 
diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
index aa42e13..4ce8def 100644
--- a/libavfilter/af_volume.c
+++ b/libavfilter/af_volume.c
@@ -26,26 +26,69 @@
 #include "libavutil/audioconvert.h"
 #include "libavutil/common.h"
 #include "libavutil/eval.h"
+#include "libavutil/opt.h"
 #include "audio.h"
 #include "avfilter.h"
 #include "formats.h"
 
+enum PrecisionType {
+    PRECISION_FIXED = 0,
+    PRECISION_FLOAT,
+    PRECISION_DOUBLE,
+};
+
+static const char *precision_str[] = {
+    "fixed", "float", "double"
+};
+
 typedef struct {
+    const AVClass *class;
+    char  *volume_str;
+    enum PrecisionType precision;
     double volume;
     int    volume_i;
 } VolumeContext;
 
+#define OFFSET(x) offsetof(VolumeContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+
+static const AVOption options[] = {
+    { "volume", "Volume adjustment.",
+            OFFSET(volume_str), AV_OPT_TYPE_STRING, { .str = "1.0" }, 0, 0, A 
},
+    { "precision", "Mathematical precision.",
+            OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, 
PRECISION_FIXED, PRECISION_DOUBLE, A, "precision" },
+        { "fixed",  "8-bit fixed-point.",     0, AV_OPT_TYPE_CONST, { .i64 = 
PRECISION_FIXED  }, INT_MIN, INT_MAX, A, "precision" },
+        { "float",  "32-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = 
PRECISION_FLOAT  }, INT_MIN, INT_MAX, A, "precision" },
+        { "double", "64-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = 
PRECISION_DOUBLE }, INT_MIN, INT_MAX, A, "precision" },
+    { NULL },
+};
+
+static const AVClass volume_class = {
+    .class_name = "volume filter",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 static av_cold int init(AVFilterContext *ctx, const char *args)
 {
     VolumeContext *vol = ctx->priv;
     char *tail;
-    int ret = 0;
+    int ret;
+
+    vol->class = &volume_class;
+    av_opt_set_defaults(vol);
+
+    if ((ret = av_set_options_string(vol, args, "=", ":")) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", 
args);
+        return ret;
+    }
 
     vol->volume = 1.0;
 
-    if (args) {
+    if (vol->volume_str) {
         /* parse the number as a decimal number */
-        double d = strtod(args, &tail);
+        double d = strtod(vol->volume_str, &tail);
 
         if (*tail) {
             if (!strcmp(tail, "dB")) {
@@ -53,7 +96,7 @@ static av_cold int init(AVFilterContext *ctx, const char 
*args)
                 d = pow(10, d/20);
             } else {
                 /* parse the argument as an expression */
-                ret = av_expr_parse_and_eval(&d, args, NULL, NULL,
+                ret = av_expr_parse_and_eval(&d, vol->volume_str, NULL, NULL,
                                              NULL, NULL, NULL, NULL,
                                              NULL, 0, ctx);
             }
@@ -61,35 +104,60 @@ static av_cold int init(AVFilterContext *ctx, const char 
*args)
 
         if (ret < 0) {
             av_log(ctx, AV_LOG_ERROR,
-                   "Invalid volume argument '%s'\n", args);
-            return AVERROR(EINVAL);
+                   "Invalid volume argument '%s'\n", vol->volume_str);
+            ret = AVERROR(EINVAL);
+            goto init_end;
         }
 
         if (d < 0 || d > 65536) { /* 65536 = INT_MIN / (128 * 256) */
             av_log(ctx, AV_LOG_ERROR,
                    "Negative or too big volume value %f\n", d);
-            return AVERROR(EINVAL);
+            ret = AVERROR(EINVAL);
+            goto init_end;
         }
 
         vol->volume = d;
     }
 
-    vol->volume_i = (int)(vol->volume * 256 + 0.5);
-    av_log(ctx, AV_LOG_VERBOSE, "volume=%f\n", vol->volume);
-    return 0;
+    if (vol->precision == PRECISION_FIXED) {
+        vol->volume_i = (int)(vol->volume * 256 + 0.5);
+        vol->volume   = vol->volume_i / 256.0;
+        av_log(ctx, AV_LOG_VERBOSE, "volume:(%d/256)(%f)(%1.2fdB) 
precision:fixed\n",
+               vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
+    } else {
+        av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
+               vol->volume, 20.0*log(vol->volume)/M_LN10,
+               precision_str[vol->precision]);
+    }
+
+init_end:
+    av_opt_free(vol);
+    return ret;
 }
 
 static int query_formats(AVFilterContext *ctx)
 {
+    VolumeContext *vol = ctx->priv;
     AVFilterFormats *formats = NULL;
     AVFilterChannelLayouts *layouts;
-    enum AVSampleFormat sample_fmts[] = {
-        AV_SAMPLE_FMT_U8,
-        AV_SAMPLE_FMT_S16,
-        AV_SAMPLE_FMT_S32,
-        AV_SAMPLE_FMT_FLT,
-        AV_SAMPLE_FMT_DBL,
-        AV_SAMPLE_FMT_NONE
+    enum AVSampleFormat sample_fmts[][4] = {
+        /* PRECISION_FIXED */
+        {
+            AV_SAMPLE_FMT_U8,
+            AV_SAMPLE_FMT_S16,
+            AV_SAMPLE_FMT_S32,
+            AV_SAMPLE_FMT_NONE
+        },
+        /* PRECISION_FLOAT */
+        {
+            AV_SAMPLE_FMT_FLT,
+            AV_SAMPLE_FMT_NONE
+        },
+        /* PRECISION_DOUBLE */
+        {
+            AV_SAMPLE_FMT_DBL,
+            AV_SAMPLE_FMT_NONE
+        }
     };
 
     layouts = ff_all_channel_layouts();
@@ -97,7 +165,7 @@ static int query_formats(AVFilterContext *ctx)
         return AVERROR(ENOMEM);
     ff_set_common_channel_layouts(ctx, layouts);
 
-    formats = ff_make_format_list(sample_fmts);
+    formats = ff_make_format_list(sample_fmts[vol->precision]);
     if (!formats)
         return AVERROR(ENOMEM);
     ff_set_common_formats(ctx, formats);
@@ -120,7 +188,7 @@ static int filter_samples(AVFilterLink *inlink, 
AVFilterBufferRef *insamples)
     const int    volume_i = vol->volume_i;
     int i;
 
-    if (volume_i != 256) {
+    if (vol->precision != PRECISION_FIXED || vol->volume_i != 256) {
         switch (insamples->format) {
         case AV_SAMPLE_FMT_U8:
         {
-- 
1.7.1

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to