From 618ac71354cf406a652109a90e6aa5e4e00d9463 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Tue, 1 Nov 2011 21:42:14 +0100 Subject: [PATCH] lavfi: add volume filter --- Changelog | 2 + doc/filters.texi | 50 ++++++++++ libavfilter/Makefile | 1 + libavfilter/af_volume.c | 191 +++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 4 +- 6 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 libavfilter/af_volume.c diff --git a/Changelog b/Changelog index ceeead5af7..f35477e13d 100644 --- a/Changelog +++ b/Changelog @@ -73,6 +73,8 @@ easier to use. The changes are: - Video Decoder Acceleration (VDA) HWAccel module. - replacement Indeo 3 decoder - new ffmpeg option: -map_channel +- volume audio filter added + version 0.8: diff --git a/doc/filters.texi b/doc/filters.texi index 0da5702566..d21ddf10dd 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -224,6 +224,56 @@ expressed in the form "[@var{c0} @var{c1} @var{c2} @var{c3} @var{c4} @var{c5} @var{c6} @var{c7}]" @end table +@section volume + +Adjust the input audio volume. + +The filter accepts exactly one parameter @var{vol}, which expresses +how the audio volume will be increased or decresed. + +Output values are clipped to the maximum value. + +If @var{vol} 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} +@end example + +If @var{vol} 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} +@end example + +Otherwise @var{vol} 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. + +@subsection Examples + +@itemize +@item +Half the input audio volume: +@example +volume=0.5 +@end example + +The above example is equivalent to: +@example +volume=1/2 +@end example + +@item +Decrease input audio power by 12 decibels: +@example +volume=-12dB +@end example +@end itemize + @c man end AUDIO FILTERS @chapter Audio Sources diff --git a/libavfilter/Makefile b/libavfilter/Makefile index cfe5d7416a..edfb12fadf 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -28,6 +28,7 @@ OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o +OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o OBJS-$(CONFIG_ABUFFER_FILTER) += asrc_abuffer.o OBJS-$(CONFIG_AEVALSRC_FILTER) += asrc_aevalsrc.o diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c new file mode 100644 index 0000000000..74e0bbb36b --- /dev/null +++ b/libavfilter/af_volume.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * + * 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 + */ + +/** + * @file + * audio volume filter + * based on ffmpeg.c code + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/eval.h" +#include "avfilter.h" + +typedef struct { + double volume; + int volume_i; +} VolumeContext; + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + VolumeContext *vol = ctx->priv; + char *tail; + int ret = 0; + + vol->volume = 1.0; + + if (args) { + /* parse the number as a decimal number */ + double d = strtod(args, &tail); + + if (*tail) { + if (!strcmp(tail, "dB")) { + /* consider the argument an adjustement in decibels */ + if (!strcmp(tail, "dB")) { + d = exp10(d/20); + } + } else { + /* parse the argument as an expression */ + ret = av_expr_parse_and_eval(&d, args, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, 0, ctx); + } + } + + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, + "Invalid volume argument '%s'\n", args); + return AVERROR(EINVAL); + } + + 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); + } + + vol->volume = d; + } + + vol->volume_i = (int)(vol->volume * 256 + 0.5); + av_log(ctx, AV_LOG_INFO, "volume=%f\n", vol->volume); + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + 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 + }; + int packing_fmts[] = { AVFILTER_PACKED, -1 }; + + formats = avfilter_make_all_channel_layouts(); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_channel_layouts(ctx, formats); + + formats = avfilter_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_sample_formats(ctx, formats); + + formats = avfilter_make_format_list(packing_fmts); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_packing_formats(ctx, formats); + + return 0; +} + +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples) +{ + VolumeContext *vol = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + const int nb_samples = insamples->audio->nb_samples * + av_get_channel_layout_nb_channels(insamples->audio->channel_layout); + const double volume = vol->volume; + const int volume_i = vol->volume_i; + int i; + + if (volume_i != 256) { + switch (insamples->format) { + case AV_SAMPLE_FMT_U8: + { + uint8_t *p = (void *)insamples->data[0]; + for (i = 0; i < nb_samples; i++) { + int v = (((*p - 128) * volume_i + 128) >> 8) + 128; + *p++ = av_clip_uint8(v); + } + break; + } + case AV_SAMPLE_FMT_S16: + { + int16_t *p = (void *)insamples->data[0]; + for (i = 0; i < nb_samples; i++) { + int v = ((int64_t)*p * volume_i + 128) >> 8; + *p++ = av_clip_int16(v); + } + break; + } + case AV_SAMPLE_FMT_S32: + { + int32_t *p = (void *)insamples->data[0]; + for (i = 0; i < nb_samples; i++) { + int64_t v = (((int64_t)*p * volume_i + 128) >> 8); + *p++ = av_clipl_int32(v); + } + break; + } + case AV_SAMPLE_FMT_FLT: + { + float *p = (void *)insamples->data[0]; + float scale = (float)volume; + for (i = 0; i < nb_samples; i++) { + *p++ *= scale; + } + break; + } + case AV_SAMPLE_FMT_DBL: + { + double *p = (void *)insamples->data[0]; + for (i = 0; i < nb_samples; i++) { + *p *= volume; + p++; + } + break; + } + } + } + avfilter_filter_samples(outlink, insamples); +} + +AVFilter avfilter_af_volume = { + .name = "volume", + .description = NULL_IF_CONFIG_SMALL("Change input volume."), + .query_formats = query_formats, + .priv_size = sizeof(VolumeContext), + .init = init, + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = filter_samples, + .min_perms = AV_PERM_READ|AV_PERM_WRITE}, + { .name = NULL}}, + + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, }, + { .name = NULL}}, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 3c77adb23d..e80fc17632 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -39,6 +39,7 @@ void avfilter_register_all(void) REGISTER_FILTER (ANULL, anull, af); REGISTER_FILTER (ARESAMPLE, aresample, af); REGISTER_FILTER (ASHOWINFO, ashowinfo, af); + REGISTER_FILTER (VOLUME, volume, af); REGISTER_FILTER (ABUFFER, abuffer, asrc); REGISTER_FILTER (AEVALSRC, aevalsrc, asrc); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 9c67b35da0..402152288a 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,8 +29,8 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 45 -#define LIBAVFILTER_VERSION_MICRO 3 +#define LIBAVFILTER_VERSION_MINOR 46 +#define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \