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