af_volume.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
27 #include "libavutil/audioconvert.h"
28 #include "libavutil/common.h"
29 #include "libavutil/eval.h"
30 #include "libavutil/float_dsp.h"
31 #include "libavutil/opt.h"
32 #include "audio.h"
33 #include "avfilter.h"
34 #include "formats.h"
35 #include "internal.h"
36 #include "af_volume.h"
37 
38 static const char *precision_str[] = {
39  "fixed", "float", "double"
40 };
41 
42 #define OFFSET(x) offsetof(VolumeContext, x)
43 #define A AV_OPT_FLAG_AUDIO_PARAM
44 
45 static const AVOption options[] = {
46  { "volume", "Volume adjustment.",
47  OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0, 0x7fffff, A },
48  { "precision", "Mathematical precision.",
49  OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A, "precision" },
50  { "fixed", "8-bit fixed-point.", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A, "precision" },
51  { "float", "32-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT }, INT_MIN, INT_MAX, A, "precision" },
52  { "double", "64-bit floating-point.", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A, "precision" },
53  { NULL },
54 };
55 
56 static const AVClass volume_class = {
57  .class_name = "volume filter",
58  .item_name = av_default_item_name,
59  .option = options,
60  .version = LIBAVUTIL_VERSION_INT,
61 };
62 
63 static av_cold int init(AVFilterContext *ctx, const char *args)
64 {
65  VolumeContext *vol = ctx->priv;
66  int ret;
67 
68  vol->class = &volume_class;
70 
71  if ((ret = av_set_options_string(vol, args, "=", ":")) < 0) {
72  av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args);
73  return ret;
74  }
75 
76  if (vol->precision == PRECISION_FIXED) {
77  vol->volume_i = (int)(vol->volume * 256 + 0.5);
78  vol->volume = vol->volume_i / 256.0;
79  av_log(ctx, AV_LOG_VERBOSE, "volume:(%d/256)(%f)(%1.2fdB) precision:fixed\n",
80  vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
81  } else {
82  av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
83  vol->volume, 20.0*log(vol->volume)/M_LN10,
84  precision_str[vol->precision]);
85  }
86 
87  av_opt_free(vol);
88  return ret;
89 }
90 
92 {
93  VolumeContext *vol = ctx->priv;
96  static const enum AVSampleFormat sample_fmts[][7] = {
97  /* PRECISION_FIXED */
98  {
106  },
107  /* PRECISION_FLOAT */
108  {
112  },
113  /* PRECISION_DOUBLE */
114  {
118  }
119  };
120 
121  layouts = ff_all_channel_layouts();
122  if (!layouts)
123  return AVERROR(ENOMEM);
124  ff_set_common_channel_layouts(ctx, layouts);
125 
126  formats = ff_make_format_list(sample_fmts[vol->precision]);
127  if (!formats)
128  return AVERROR(ENOMEM);
129  ff_set_common_formats(ctx, formats);
130 
131  formats = ff_all_samplerates();
132  if (!formats)
133  return AVERROR(ENOMEM);
134  ff_set_common_samplerates(ctx, formats);
135 
136  return 0;
137 }
138 
139 static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
140  int nb_samples, int volume)
141 {
142  int i;
143  for (i = 0; i < nb_samples; i++)
144  dst[i] = av_clip_uint8(((((int64_t)src[i] - 128) * volume + 128) >> 8) + 128);
145 }
146 
147 static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src,
148  int nb_samples, int volume)
149 {
150  int i;
151  for (i = 0; i < nb_samples; i++)
152  dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128);
153 }
154 
155 static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src,
156  int nb_samples, int volume)
157 {
158  int i;
159  int16_t *smp_dst = (int16_t *)dst;
160  const int16_t *smp_src = (const int16_t *)src;
161  for (i = 0; i < nb_samples; i++)
162  smp_dst[i] = av_clip_int16(((int64_t)smp_src[i] * volume + 128) >> 8);
163 }
164 
165 static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src,
166  int nb_samples, int volume)
167 {
168  int i;
169  int16_t *smp_dst = (int16_t *)dst;
170  const int16_t *smp_src = (const int16_t *)src;
171  for (i = 0; i < nb_samples; i++)
172  smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8);
173 }
174 
175 static inline void scale_samples_s32(uint8_t *dst, const uint8_t *src,
176  int nb_samples, int volume)
177 {
178  int i;
179  int32_t *smp_dst = (int32_t *)dst;
180  const int32_t *smp_src = (const int32_t *)src;
181  for (i = 0; i < nb_samples; i++)
182  smp_dst[i] = av_clipl_int32((((int64_t)smp_src[i] * volume + 128) >> 8));
183 }
184 
185 
186 
187 static void volume_init(VolumeContext *vol)
188 {
189  vol->samples_align = 1;
190 
191  switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
192  case AV_SAMPLE_FMT_U8:
193  if (vol->volume_i < 0x1000000)
195  else
197  break;
198  case AV_SAMPLE_FMT_S16:
199  if (vol->volume_i < 0x10000)
201  else
203  break;
204  case AV_SAMPLE_FMT_S32:
206  break;
207  case AV_SAMPLE_FMT_FLT:
208  avpriv_float_dsp_init(&vol->fdsp, 0);
209  vol->samples_align = 4;
210  break;
211  case AV_SAMPLE_FMT_DBL:
212  avpriv_float_dsp_init(&vol->fdsp, 0);
213  vol->samples_align = 8;
214  break;
215  }
216 
217  if (ARCH_X86)
218  ff_volume_init_x86(vol);
219 }
220 
221 static int config_output(AVFilterLink *outlink)
222 {
223  AVFilterContext *ctx = outlink->src;
224  VolumeContext *vol = ctx->priv;
225  AVFilterLink *inlink = ctx->inputs[0];
226 
227  vol->sample_fmt = inlink->format;
229  vol->planes = av_sample_fmt_is_planar(inlink->format) ? vol->channels : 1;
230 
231  volume_init(vol);
232 
233  return 0;
234 }
235 
236 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
237 {
238  VolumeContext *vol = inlink->dst->priv;
239  AVFilterLink *outlink = inlink->dst->outputs[0];
240  int nb_samples = buf->audio->nb_samples;
241  AVFilterBufferRef *out_buf;
242 
243  if (vol->volume == 1.0 || vol->volume_i == 256)
244  return ff_filter_frame(outlink, buf);
245 
246  /* do volume scaling in-place if input buffer is writable */
247  if (buf->perms & AV_PERM_WRITE) {
248  out_buf = buf;
249  } else {
250  out_buf = ff_get_audio_buffer(inlink, AV_PERM_WRITE, nb_samples);
251  if (!out_buf)
252  return AVERROR(ENOMEM);
253  out_buf->pts = buf->pts;
254  }
255 
256  if (vol->precision != PRECISION_FIXED || vol->volume_i > 0) {
257  int p, plane_samples;
258 
260  plane_samples = FFALIGN(nb_samples, vol->samples_align);
261  else
262  plane_samples = FFALIGN(nb_samples * vol->channels, vol->samples_align);
263 
264  if (vol->precision == PRECISION_FIXED) {
265  for (p = 0; p < vol->planes; p++) {
266  vol->scale_samples(out_buf->extended_data[p],
267  buf->extended_data[p], plane_samples,
268  vol->volume_i);
269  }
271  for (p = 0; p < vol->planes; p++) {
272  vol->fdsp.vector_fmul_scalar((float *)out_buf->extended_data[p],
273  (const float *)buf->extended_data[p],
274  vol->volume, plane_samples);
275  }
276  } else {
277  for (p = 0; p < vol->planes; p++) {
278  vol->fdsp.vector_dmul_scalar((double *)out_buf->extended_data[p],
279  (const double *)buf->extended_data[p],
280  vol->volume, plane_samples);
281  }
282  }
283  }
284 
285  emms_c();
286 
287  if (buf != out_buf)
289 
290  return ff_filter_frame(outlink, out_buf);
291 }
292 
294  {
295  .name = "default",
296  .type = AVMEDIA_TYPE_AUDIO,
297  .filter_frame = filter_frame,
298  },
299  { NULL }
300 };
301 
303  {
304  .name = "default",
305  .type = AVMEDIA_TYPE_AUDIO,
306  .config_props = config_output,
307  },
308  { NULL }
309 };
310 
312  .name = "volume",
313  .description = NULL_IF_CONFIG_SMALL("Change input volume."),
314  .query_formats = query_formats,
315  .priv_size = sizeof(VolumeContext),
316  .init = init,
317  .inputs = avfilter_af_volume_inputs,
318  .outputs = avfilter_af_volume_outputs,
319 };