audioamplify.c 20.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* 
 * GStreamer
 * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
 * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18 19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
20 21 22 23 24 25 26
 */

/**
 * SECTION:element-audioamplify
 *
 * Amplifies an audio stream by a given factor and allows the selection of different clipping modes.
 * The difference between the clipping modes is best evaluated by testing.
Stefan Kost's avatar
Stefan Kost committed
27
 *
28
 * <refsect2>
Stefan Kost's avatar
Stefan Kost committed
29
 * <title>Example launch line</title>
30
 * |[
31
 * gst-launch-1.0 audiotestsrc wave=saw ! audioamplify amplification=1.5 ! alsasink
32 33
 * gst-launch-1.0 filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audioamplify amplification=1.5 clipping-method=wrap-negative ! alsasink
 * gst-launch-1.0 audiotestsrc wave=saw ! audioconvert ! audioamplify amplification=1.5 clipping-method=wrap-positive ! audioconvert ! alsasink
34
 * ]|
35 36 37 38 39 40 41 42 43
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
44 45
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h>
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

#include "audioamplify.h"

#define GST_CAT_DEFAULT gst_audio_amplify_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_AMPLIFICATION,
  PROP_CLIPPING_METHOD
};

enum
{
  METHOD_CLIP = 0,
  METHOD_WRAP_NEGATIVE,
  METHOD_WRAP_POSITIVE,
71
  METHOD_NOCLIP,
72 73 74 75 76 77 78 79 80 81 82
  NUM_METHODS
};

#define GST_TYPE_AUDIO_AMPLIFY_CLIPPING_METHOD (gst_audio_amplify_clipping_method_get_type ())
static GType
gst_audio_amplify_clipping_method_get_type (void)
{
  static GType gtype = 0;

  if (gtype == 0) {
    static const GEnumValue values[] = {
83
      {METHOD_CLIP, "Normal clipping (default)", "clip"},
84 85 86 87 88
      {METHOD_WRAP_NEGATIVE,
            "Push overdriven values back from the opposite side",
          "wrap-negative"},
      {METHOD_WRAP_POSITIVE, "Push overdriven values back from the same side",
          "wrap-positive"},
89
      {METHOD_NOCLIP, "No clipping", "none"},
90 91
      {0, NULL, NULL}
    };
Wim Taymans's avatar
Wim Taymans committed
92
    gtype = g_enum_register_static ("GstAudioAmplifyClippingMethod", values);
93 94 95 96
  }
  return gtype;
}

97
#define ALLOWED_CAPS                                                  \
Wim Taymans's avatar
Wim Taymans committed
98 99 100
    "audio/x-raw,"                                                    \
    " format=(string) {S8,"GST_AUDIO_NE(S16)","GST_AUDIO_NE(S32)","   \
                           GST_AUDIO_NE(F32)","GST_AUDIO_NE(F64)"},"  \
101
    " rate=(int)[1,MAX],"                                             \
102 103
    " channels=(int)[1,MAX], "                                        \
    " layout=(string) {interleaved, non-interleaved}"
104

Wim Taymans's avatar
Wim Taymans committed
105
G_DEFINE_TYPE (GstAudioAmplify, gst_audio_amplify, GST_TYPE_AUDIO_FILTER);
106

107
static gboolean gst_audio_amplify_set_process_function (GstAudioAmplify *
Wim Taymans's avatar
Wim Taymans committed
108
    filter, gint clipping, GstAudioFormat format);
109 110 111 112 113
static void gst_audio_amplify_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_audio_amplify_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

114
static gboolean gst_audio_amplify_setup (GstAudioFilter * filter,
Wim Taymans's avatar
Wim Taymans committed
115
    const GstAudioInfo * info);
116 117 118
static GstFlowReturn gst_audio_amplify_transform_ip (GstBaseTransform * base,
    GstBuffer * buf);

119 120 121 122 123 124 125
#define MIN_gint8 G_MININT8
#define MAX_gint8 G_MAXINT8
#define MIN_gint16 G_MININT16
#define MAX_gint16 G_MAXINT16
#define MIN_gint32 G_MININT32
#define MAX_gint32 G_MAXINT32

126
#define MAKE_INT_FUNCS(type,largetype)                                        \
127 128 129 130 131 132 133
static void                                                                   \
gst_audio_amplify_transform_##type##_clip (GstAudioAmplify * filter,          \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
134
    largetype val = *d * filter->amplification;                               \
135 136 137 138 139 140 141 142 143 144
    *d++ =  CLAMP (val, MIN_##type, MAX_##type);                              \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_wrap_negative (GstAudioAmplify * filter, \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
145
    largetype val = *d * filter->amplification;                               \
146
    if (val > MAX_##type)                                                     \
147
      val = MIN_##type + (val - MIN_##type) % ((largetype) MAX_##type + 1 -   \
148 149
          MIN_##type);                                                        \
    else if (val < MIN_##type)                                                \
150
      val = MAX_##type - (MAX_##type - val) % ((largetype) MAX_##type + 1 -   \
151 152 153 154 155 156 157 158 159 160 161
          MIN_##type);                                                        \
    *d++ = val;                                                               \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_wrap_positive (GstAudioAmplify * filter, \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
162
    largetype val = *d * filter->amplification;                               \
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    do {                                                                      \
      if (val > MAX_##type)                                                   \
        val = MAX_##type - (val - MAX_##type);                                \
      else if (val < MIN_##type)                                              \
        val = MIN_##type + (MIN_##type - val);                                \
      else                                                                    \
        break;                                                                \
    } while (1);                                                              \
    *d++ = val;                                                               \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_noclip (GstAudioAmplify * filter,        \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--)                                                       \
    *d++ *= filter->amplification;                                            \
}

#define MAKE_FLOAT_FUNCS(type)                                                \
static void                                                                   \
gst_audio_amplify_transform_##type##_clip (GstAudioAmplify * filter,          \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
    type val = *d* filter->amplification;                                     \
    *d++ = CLAMP (val, -1.0, +1.0);                                           \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_wrap_negative (GstAudioAmplify *         \
    filter, void * data, guint num_samples)                                   \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
    type val = *d * filter->amplification;                                    \
    do {                                                                      \
      if (val > 1.0)                                                          \
        val = -1.0 + (val - 1.0);                                             \
      else if (val < -1.0)                                                    \
        val = 1.0 - (1.0 - val);                                              \
      else                                                                    \
        break;                                                                \
    } while (1);                                                              \
    *d++ = val;                                                               \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_wrap_positive (GstAudioAmplify * filter, \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--) {                                                     \
    type val = *d* filter->amplification;                                     \
    do {                                                                      \
      if (val > 1.0)                                                          \
        val = 1.0 - (val - 1.0);                                              \
      else if (val < -1.0)                                                    \
        val = -1.0 + (-1.0 - val);                                            \
      else                                                                    \
        break;                                                                \
    } while (1);                                                              \
    *d++ = val;                                                               \
  }                                                                           \
}                                                                             \
static void                                                                   \
gst_audio_amplify_transform_##type##_noclip (GstAudioAmplify * filter,        \
    void * data, guint num_samples)                                           \
{                                                                             \
  type *d = data;                                                             \
                                                                              \
  while (num_samples--)                                                       \
    *d++ *= filter->amplification;                                            \
}

/* *INDENT-OFF* */
245 246 247
MAKE_INT_FUNCS (gint8,gint)
MAKE_INT_FUNCS (gint16,gint)
MAKE_INT_FUNCS (gint32,gint64)
248 249
MAKE_FLOAT_FUNCS (gfloat)
MAKE_FLOAT_FUNCS (gdouble)
250
/* *INDENT-ON* */
251 252 253 254 255 256 257

/* GObject vmethod implementations */

static void
gst_audio_amplify_class_init (GstAudioAmplifyClass * klass)
{
  GObjectClass *gobject_class;
Wim Taymans's avatar
Wim Taymans committed
258 259 260 261 262
  GstElementClass *gstelement_class;
  GstCaps *caps;

  GST_DEBUG_CATEGORY_INIT (gst_audio_amplify_debug, "audioamplify", 0,
      "audioamplify element");
263 264

  gobject_class = (GObjectClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
265 266
  gstelement_class = (GstElementClass *) klass;

267 268 269 270 271
  gobject_class->set_property = gst_audio_amplify_set_property;
  gobject_class->get_property = gst_audio_amplify_get_property;

  g_object_class_install_property (gobject_class, PROP_AMPLIFICATION,
      g_param_spec_float ("amplification", "Amplification",
272
          "Factor of amplification", -G_MAXFLOAT, G_MAXFLOAT,
273 274
          1.0,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
275 276 277 278 279 280 281 282 283 284 285 286 287

  /**
   * GstAudioAmplify:clipping-method
   *
   * Clipping method: clip mode set values higher than the maximum to the
   * maximum. The wrap-negative mode pushes those values back from the
   * opposite side, wrap-positive pushes them back from the same side.
   *
   **/
  g_object_class_install_property (gobject_class, PROP_CLIPPING_METHOD,
      g_param_spec_enum ("clipping-method", "Clipping method",
          "Selects how to handle values higher than the maximum",
          GST_TYPE_AUDIO_AMPLIFY_CLIPPING_METHOD, METHOD_CLIP,
288
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289

290
  gst_element_class_set_static_metadata (gstelement_class, "Audio amplifier",
Wim Taymans's avatar
Wim Taymans committed
291 292 293 294 295 296 297 298 299
      "Filter/Effect/Audio",
      "Amplifies an audio stream by a given factor",
      "Sebastian Dröge <slomo@circular-chaos.org>");

  caps = gst_caps_from_string (ALLOWED_CAPS);
  gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
      caps);
  gst_caps_unref (caps);

300 301
  GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
      GST_DEBUG_FUNCPTR (gst_audio_amplify_transform_ip);
Wim Taymans's avatar
Wim Taymans committed
302 303 304 305
  GST_BASE_TRANSFORM_CLASS (klass)->transform_ip_on_passthrough = FALSE;

  GST_AUDIO_FILTER_CLASS (klass)->setup =
      GST_DEBUG_FUNCPTR (gst_audio_amplify_setup);
306 307 308
}

static void
Wim Taymans's avatar
Wim Taymans committed
309
gst_audio_amplify_init (GstAudioAmplify * filter)
310 311
{
  filter->amplification = 1.0;
312
  gst_audio_amplify_set_process_function (filter, METHOD_CLIP,
Wim Taymans's avatar
Wim Taymans committed
313
      GST_AUDIO_FORMAT_S16);
314
  gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
315
  gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
316 317
}

318
static GstAudioAmplifyProcessFunc
Wim Taymans's avatar
Wim Taymans committed
319
gst_audio_amplify_process_function (gint clipping, GstAudioFormat format)
320
{
321 322
  static const struct process
  {
Wim Taymans's avatar
Wim Taymans committed
323
    GstAudioFormat format;
324 325
    gint clipping;
    GstAudioAmplifyProcessFunc func;
326 327
  } process[] = {
    {
Wim Taymans's avatar
Wim Taymans committed
328 329
    GST_AUDIO_FORMAT_F32, METHOD_CLIP, gst_audio_amplify_transform_gfloat_clip}, {
    GST_AUDIO_FORMAT_F32, METHOD_WRAP_NEGATIVE,
330
          gst_audio_amplify_transform_gfloat_wrap_negative}, {
Wim Taymans's avatar
Wim Taymans committed
331
    GST_AUDIO_FORMAT_F32, METHOD_WRAP_POSITIVE,
332
          gst_audio_amplify_transform_gfloat_wrap_positive}, {
Wim Taymans's avatar
Wim Taymans committed
333
    GST_AUDIO_FORMAT_F32, METHOD_NOCLIP,
334
          gst_audio_amplify_transform_gfloat_noclip}, {
Wim Taymans's avatar
Wim Taymans committed
335
    GST_AUDIO_FORMAT_F64, METHOD_CLIP,
336
          gst_audio_amplify_transform_gdouble_clip}, {
Wim Taymans's avatar
Wim Taymans committed
337
    GST_AUDIO_FORMAT_F64, METHOD_WRAP_NEGATIVE,
338
          gst_audio_amplify_transform_gdouble_wrap_negative}, {
Wim Taymans's avatar
Wim Taymans committed
339
    GST_AUDIO_FORMAT_F64, METHOD_WRAP_POSITIVE,
340
          gst_audio_amplify_transform_gdouble_wrap_positive}, {
Wim Taymans's avatar
Wim Taymans committed
341
    GST_AUDIO_FORMAT_F64, METHOD_NOCLIP,
342
          gst_audio_amplify_transform_gdouble_noclip}, {
Wim Taymans's avatar
Wim Taymans committed
343 344
    GST_AUDIO_FORMAT_S8, METHOD_CLIP, gst_audio_amplify_transform_gint8_clip}, {
    GST_AUDIO_FORMAT_S8, METHOD_WRAP_NEGATIVE,
345
          gst_audio_amplify_transform_gint8_wrap_negative}, {
Wim Taymans's avatar
Wim Taymans committed
346
    GST_AUDIO_FORMAT_S8, METHOD_WRAP_POSITIVE,
347
          gst_audio_amplify_transform_gint8_wrap_positive}, {
Wim Taymans's avatar
Wim Taymans committed
348
    GST_AUDIO_FORMAT_S8, METHOD_NOCLIP,
349
          gst_audio_amplify_transform_gint8_noclip}, {
Wim Taymans's avatar
Wim Taymans committed
350 351
    GST_AUDIO_FORMAT_S16, METHOD_CLIP, gst_audio_amplify_transform_gint16_clip}, {
    GST_AUDIO_FORMAT_S16, METHOD_WRAP_NEGATIVE,
352
          gst_audio_amplify_transform_gint16_wrap_negative}, {
Wim Taymans's avatar
Wim Taymans committed
353
    GST_AUDIO_FORMAT_S16, METHOD_WRAP_POSITIVE,
354
          gst_audio_amplify_transform_gint16_wrap_positive}, {
Wim Taymans's avatar
Wim Taymans committed
355
    GST_AUDIO_FORMAT_S16, METHOD_NOCLIP,
356
          gst_audio_amplify_transform_gint16_noclip}, {
Wim Taymans's avatar
Wim Taymans committed
357 358
    GST_AUDIO_FORMAT_S32, METHOD_CLIP, gst_audio_amplify_transform_gint32_clip}, {
    GST_AUDIO_FORMAT_S32, METHOD_WRAP_NEGATIVE,
359
          gst_audio_amplify_transform_gint32_wrap_negative}, {
Wim Taymans's avatar
Wim Taymans committed
360
    GST_AUDIO_FORMAT_S32, METHOD_WRAP_POSITIVE,
361
          gst_audio_amplify_transform_gint32_wrap_positive}, {
Wim Taymans's avatar
Wim Taymans committed
362
    GST_AUDIO_FORMAT_S32, METHOD_NOCLIP,
363
          gst_audio_amplify_transform_gint32_noclip}, {
Wim Taymans's avatar
Wim Taymans committed
364
    0, 0, NULL}
365
  };
366
  const struct process *p;
367 368

  for (p = process; p->func; p++)
Wim Taymans's avatar
Wim Taymans committed
369
    if (p->format == format && p->clipping == clipping)
370 371 372 373
      return p->func;
  return NULL;
}

374
static gboolean
375
gst_audio_amplify_set_process_function (GstAudioAmplify * filter, gint
Wim Taymans's avatar
Wim Taymans committed
376
    clipping_method, GstAudioFormat format)
377
{
378
  GstAudioAmplifyProcessFunc process;
379 380 381

  /* set processing function */

Wim Taymans's avatar
Wim Taymans committed
382
  process = gst_audio_amplify_process_function (clipping_method, format);
383 384 385 386 387 388 389 390
  if (!process) {
    GST_DEBUG ("wrong format");
    return FALSE;
  }

  filter->process = process;
  filter->clipping_method = clipping_method;
  filter->format = format;
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

  return TRUE;
}

static void
gst_audio_amplify_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (object);

  switch (prop_id) {
    case PROP_AMPLIFICATION:
      filter->amplification = g_value_get_float (value);
      gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter),
          filter->amplification == 1.0);
      break;
    case PROP_CLIPPING_METHOD:
408
      gst_audio_amplify_set_process_function (filter, g_value_get_enum (value),
Wim Taymans's avatar
Wim Taymans committed
409
          filter->format);
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_audio_amplify_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (object);

  switch (prop_id) {
    case PROP_AMPLIFICATION:
      g_value_set_float (value, filter->amplification);
      break;
    case PROP_CLIPPING_METHOD:
      g_value_set_enum (value, filter->clipping_method);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

436
/* GstAudioFilter vmethod implementations */
437
static gboolean
Wim Taymans's avatar
Wim Taymans committed
438
gst_audio_amplify_setup (GstAudioFilter * base, const GstAudioInfo * info)
439 440 441
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);

442
  return gst_audio_amplify_set_process_function (filter,
Wim Taymans's avatar
Wim Taymans committed
443
      filter->clipping_method, GST_AUDIO_INFO_FORMAT (info));
444 445
}

446
/* GstBaseTransform vmethod implementations */
447 448 449 450
static GstFlowReturn
gst_audio_amplify_transform_ip (GstBaseTransform * base, GstBuffer * buf)
{
  GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);
451 452
  guint num_samples;
  GstClockTime timestamp, stream_time;
Wim Taymans's avatar
Wim Taymans committed
453
  GstMapInfo map;
454 455 456 457 458 459 460

  timestamp = GST_BUFFER_TIMESTAMP (buf);
  stream_time =
      gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);

  GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));
461

462
  if (GST_CLOCK_TIME_IS_VALID (stream_time))
463
    gst_object_sync_values (GST_OBJECT (filter), stream_time);
464

Wim Taymans's avatar
Wim Taymans committed
465
  if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
466 467
    return GST_FLOW_OK;

Wim Taymans's avatar
Wim Taymans committed
468 469
  gst_buffer_map (buf, &map, GST_MAP_READWRITE);
  num_samples = map.size / GST_AUDIO_FILTER_BPS (filter);
Wim Taymans's avatar
Wim Taymans committed
470

Wim Taymans's avatar
Wim Taymans committed
471
  filter->process (filter, map.data, num_samples);
Wim Taymans's avatar
Wim Taymans committed
472

Wim Taymans's avatar
Wim Taymans committed
473
  gst_buffer_unmap (buf, &map);
474 475 476

  return GST_FLOW_OK;
}