gst_plugins_base/gst/volume/gstvolume.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* -*- c-basic-offset: 2 -*-
       
     2  * vi:si:et:sw=2:sts=8:ts=8:expandtab
       
     3  *
       
     4  * GStreamer
       
     5  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
       
     6  * Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
       
     7  *
       
     8  * This library is free software; you can redistribute it and/or
       
     9  * modify it under the terms of the GNU Library General Public
       
    10  * License as published by the Free Software Foundation; either
       
    11  * version 2 of the License, or (at your option) any later version.
       
    12  *
       
    13  * This library is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    16  * Library General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU Library General Public
       
    19  * License along with this library; if not, write to the
       
    20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    21  * Boston, MA 02111-1307, USA.
       
    22  */
       
    23 
       
    24 /**
       
    25  * SECTION:element-volume
       
    26  *
       
    27  * <refsect2>
       
    28  * <para>
       
    29  * The volume element changes the volume of the audio data.
       
    30  * </para>
       
    31  * <title>Example launch line</title>
       
    32  * <para>
       
    33  * <programlisting>
       
    34  * gst-launch -v -m audiotestsrc ! volume volume=0.5 ! level ! fakesink silent=TRUE
       
    35  * </programlisting>
       
    36  * This pipeline shows that the level of audiotestsrc has been halved
       
    37  * (peak values are around -6 dB and RMS around -9 dB) compared to
       
    38  * the same pipeline without the volume element.
       
    39  * </para>
       
    40  * </refsect2>
       
    41  */
       
    42 
       
    43 #ifdef HAVE_CONFIG_H
       
    44 #include "config.h"
       
    45 #endif
       
    46 #include <gst/gst_global.h>
       
    47 #include <string.h>
       
    48 #include <gst/gst.h>
       
    49 #include <gst/base/gstbasetransform.h>
       
    50 #include <gst/audio/audio.h>
       
    51 #include <gst/interfaces/mixer.h>
       
    52 #include <gst/controller/gstcontroller.h>
       
    53 #include <gst/audio/audio.h>
       
    54 #include <gst/audio/gstaudiofilter.h>
       
    55 
       
    56 #include <gst/liboil.h>
       
    57 
       
    58 #include "gstvolume.h"
       
    59 
       
    60 /* some defines for audio processing */
       
    61 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
       
    62  * we map 1.0 to VOLUME_UNITY_INT*
       
    63  */
       
    64 #define VOLUME_UNITY_INT8            32 /* internal int for unity 2^(8-3) */
       
    65 #define VOLUME_UNITY_INT8_BIT_SHIFT  5  /* number of bits to shift for unity */
       
    66 #define VOLUME_UNITY_INT16           8192       /* internal int for unity 2^(16-3) */
       
    67 #define VOLUME_UNITY_INT16_BIT_SHIFT 13 /* number of bits to shift for unity */
       
    68 #define VOLUME_UNITY_INT24           2097152    /* internal int for unity 2^(24-3) */
       
    69 #define VOLUME_UNITY_INT24_BIT_SHIFT 21 /* number of bits to shift for unity */
       
    70 #define VOLUME_UNITY_INT32           134217728  /* internal int for unity 2^(32-5) */
       
    71 #define VOLUME_UNITY_INT32_BIT_SHIFT 27
       
    72 #define VOLUME_MAX_DOUBLE            10.0
       
    73 #define VOLUME_MAX_INT8              G_MAXINT8
       
    74 #define VOLUME_MIN_INT8              G_MININT8
       
    75 #define VOLUME_MAX_INT16             G_MAXINT16
       
    76 #define VOLUME_MIN_INT16             G_MININT16
       
    77 #define VOLUME_MAX_INT24             8388607
       
    78 #define VOLUME_MIN_INT24             -8388608
       
    79 #define VOLUME_MAX_INT32             G_MAXINT32
       
    80 #define VOLUME_MIN_INT32             G_MININT32
       
    81 
       
    82 /* number of steps we use for the mixer interface to go from 0.0 to 1.0 */
       
    83 # define VOLUME_STEPS           100
       
    84 
       
    85 #define GST_CAT_DEFAULT gst_volume_debug
       
    86 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    87 
       
    88 static const GstElementDetails volume_details = GST_ELEMENT_DETAILS ("Volume",
       
    89     "Filter/Effect/Audio",
       
    90     "Set volume on audio/raw streams",
       
    91     "Andy Wingo <wingo@pobox.com>");
       
    92 
       
    93 /* Filter signals and args */
       
    94 enum
       
    95 {
       
    96   /* FILL ME */
       
    97   LAST_SIGNAL
       
    98 };
       
    99 
       
   100 enum
       
   101 {
       
   102   PROP_0,
       
   103   PROP_SILENT,
       
   104   PROP_MUTE,
       
   105   PROP_VOLUME
       
   106 };
       
   107 
       
   108 #define ALLOWED_CAPS \
       
   109         "audio/x-raw-float, " \
       
   110         "rate = (int) [ 1, MAX ], " \
       
   111         "channels = (int) [ 1, MAX ], " \
       
   112         "endianness = (int) BYTE_ORDER, " \
       
   113         "width = (int) {32, 64}; " \
       
   114         "audio/x-raw-int, " \
       
   115         "channels = (int) [ 1, MAX ], " \
       
   116         "rate = (int) [ 1,  MAX ], " \
       
   117         "endianness = (int) BYTE_ORDER, " \
       
   118         "width = (int) 8, " \
       
   119         "depth = (int) 8, " \
       
   120         "signed = (bool) TRUE; " \
       
   121         "audio/x-raw-int, " \
       
   122         "channels = (int) [ 1, MAX ], " \
       
   123         "rate = (int) [ 1,  MAX ], " \
       
   124         "endianness = (int) BYTE_ORDER, " \
       
   125         "width = (int) 16, " \
       
   126         "depth = (int) 16, " \
       
   127         "signed = (bool) TRUE; " \
       
   128         "audio/x-raw-int, " \
       
   129         "channels = (int) [ 1, MAX ], " \
       
   130         "rate = (int) [ 1,  MAX ], " \
       
   131         "endianness = (int) BYTE_ORDER, " \
       
   132         "width = (int) 24, " \
       
   133         "depth = (int) 24, " \
       
   134         "signed = (bool) TRUE; " \
       
   135         "audio/x-raw-int, " \
       
   136         "channels = (int) [ 1, MAX ], " \
       
   137         "rate = (int) [ 1,  MAX ], " \
       
   138         "endianness = (int) BYTE_ORDER, " \
       
   139         "width = (int) 32, " \
       
   140 	"depth = (int) 32, " \
       
   141 	"signed = (bool) TRUE"
       
   142 
       
   143 static void gst_volume_interface_init (GstImplementsInterfaceClass * klass);
       
   144 static void gst_volume_mixer_init (GstMixerClass * iface);
       
   145 
       
   146 #define _init_interfaces(type)                                          \
       
   147   {                                                                     \
       
   148     static const GInterfaceInfo voliface_info = {                     \
       
   149       (GInterfaceInitFunc) gst_volume_interface_init,                   \
       
   150       NULL,                                                             \
       
   151       NULL                                                              \
       
   152     };                                                                  \
       
   153     static const GInterfaceInfo volmixer_info = {                     \
       
   154       (GInterfaceInitFunc) gst_volume_mixer_init,                       \
       
   155       NULL,                                                             \
       
   156       NULL                                                              \
       
   157     };                                                                  \
       
   158                                                                         \
       
   159     g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,   \
       
   160         &voliface_info);                                                \
       
   161     g_type_add_interface_static (type, GST_TYPE_MIXER, &volmixer_info); \
       
   162   }
       
   163 
       
   164 GST_BOILERPLATE_FULL (GstVolume, gst_volume, GstAudioFilter,
       
   165     GST_TYPE_AUDIO_FILTER, _init_interfaces);
       
   166 
       
   167 static void volume_set_property (GObject * object, guint prop_id,
       
   168     const GValue * value, GParamSpec * pspec);
       
   169 static void volume_get_property (GObject * object, guint prop_id,
       
   170     GValue * value, GParamSpec * pspec);
       
   171 static void volume_update_volume (const GValue * value, gpointer data);
       
   172 static void volume_update_mute (const GValue * value, gpointer data);
       
   173 
       
   174 static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
       
   175     GstBuffer * outbuf);
       
   176 static gboolean volume_setup (GstAudioFilter * filter,
       
   177     GstRingBufferSpec * format);
       
   178 
       
   179 static void volume_process_double (GstVolume * this, gpointer bytes,
       
   180     guint n_bytes);
       
   181 static void volume_process_float (GstVolume * this, gpointer bytes,
       
   182     guint n_bytes);
       
   183 static void volume_process_int32 (GstVolume * this, gpointer bytes,
       
   184     guint n_bytes);
       
   185 static void volume_process_int32_clamp (GstVolume * this, gpointer bytes,
       
   186     guint n_bytes);
       
   187 static void volume_process_int24 (GstVolume * this, gpointer bytes,
       
   188     guint n_bytes);
       
   189 static void volume_process_int24_clamp (GstVolume * this, gpointer bytes,
       
   190     guint n_bytes);
       
   191 static void volume_process_int16 (GstVolume * this, gpointer bytes,
       
   192     guint n_bytes);
       
   193 static void volume_process_int16_clamp (GstVolume * this, gpointer bytes,
       
   194     guint n_bytes);
       
   195 static void volume_process_int8 (GstVolume * this, gpointer bytes,
       
   196     guint n_bytes);
       
   197 static void volume_process_int8_clamp (GstVolume * this, gpointer bytes,
       
   198     guint n_bytes);
       
   199 
       
   200 
       
   201 /* helper functions */
       
   202 
       
   203 static gboolean
       
   204 volume_choose_func (GstVolume * this)
       
   205 {
       
   206   this->process = NULL;
       
   207 
       
   208   if (GST_AUDIO_FILTER (this)->format.caps == NULL)
       
   209     return FALSE;
       
   210 
       
   211   switch (GST_AUDIO_FILTER (this)->format.type) {
       
   212     case GST_BUFTYPE_LINEAR:
       
   213       switch (GST_AUDIO_FILTER (this)->format.width) {
       
   214         case 32:
       
   215           /* only clamp if the gain is greater than 1.0
       
   216            * FIXME: real_vol_i can change while processing the buffer!
       
   217            */
       
   218           if (this->real_vol_i32 > VOLUME_UNITY_INT32)
       
   219             this->process = volume_process_int32_clamp;
       
   220           else
       
   221             this->process = volume_process_int32;
       
   222           break;
       
   223         case 24:
       
   224           /* only clamp if the gain is greater than 1.0
       
   225            * FIXME: real_vol_i can change while processing the buffer!
       
   226            */
       
   227           if (this->real_vol_i24 > VOLUME_UNITY_INT24)
       
   228             this->process = volume_process_int24_clamp;
       
   229           else
       
   230             this->process = volume_process_int24;
       
   231           break;
       
   232         case 16:
       
   233           /* only clamp if the gain is greater than 1.0
       
   234            * FIXME: real_vol_i can change while processing the buffer!
       
   235            */
       
   236           if (this->real_vol_i16 > VOLUME_UNITY_INT16)
       
   237             this->process = volume_process_int16_clamp;
       
   238           else
       
   239             this->process = volume_process_int16;
       
   240           break;
       
   241         case 8:
       
   242           /* only clamp if the gain is greater than 1.0
       
   243            * FIXME: real_vol_i can change while processing the buffer!
       
   244            */
       
   245           if (this->real_vol_i16 > VOLUME_UNITY_INT8)
       
   246             this->process = volume_process_int8_clamp;
       
   247           else
       
   248             this->process = volume_process_int8;
       
   249           break;
       
   250       }
       
   251       break;
       
   252     case GST_BUFTYPE_FLOAT:
       
   253       switch (GST_AUDIO_FILTER (this)->format.width) {
       
   254         case 32:
       
   255           this->process = volume_process_float;
       
   256           break;
       
   257         case 64:
       
   258           this->process = volume_process_double;
       
   259           break;
       
   260       }
       
   261       break;
       
   262     default:
       
   263       break;
       
   264   }
       
   265 
       
   266   return (this->process != NULL);
       
   267 }
       
   268 
       
   269 static void
       
   270 volume_update_real_volume (GstVolume * this)
       
   271 {
       
   272   gboolean passthrough = FALSE;
       
   273 
       
   274   if (this->mute) {
       
   275     this->real_vol_f = 0.0;
       
   276     this->real_vol_i8 = this->real_vol_i16 = this->real_vol_i24 =
       
   277         this->real_vol_i32 = 0;
       
   278   } else {
       
   279     this->real_vol_f = this->volume_f;
       
   280     this->real_vol_i8 = this->volume_i8;
       
   281     this->real_vol_i16 = this->volume_i16;
       
   282     this->real_vol_i24 = this->volume_i24;
       
   283     this->real_vol_i32 = this->volume_i32;
       
   284     passthrough = (this->volume_i16 == VOLUME_UNITY_INT16);
       
   285   }
       
   286   if (this->real_vol_f != 0.0)
       
   287     this->silent_buffer = FALSE;
       
   288   volume_choose_func (this);
       
   289   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (this), passthrough);
       
   290 }
       
   291 
       
   292 /* Mixer interface */
       
   293 
       
   294 static gboolean
       
   295 gst_volume_interface_supported (GstImplementsInterface * iface, GType type)
       
   296 {
       
   297   g_assert (type == GST_TYPE_MIXER);
       
   298   return TRUE;
       
   299 }
       
   300 
       
   301 static void
       
   302 gst_volume_interface_init (GstImplementsInterfaceClass * klass)
       
   303 {
       
   304   klass->supported = gst_volume_interface_supported;
       
   305 }
       
   306 
       
   307 static const GList *
       
   308 gst_volume_list_tracks (GstMixer * mixer)
       
   309 {
       
   310   GstVolume *this = GST_VOLUME (mixer);
       
   311 
       
   312   g_return_val_if_fail (this != NULL, NULL);
       
   313   g_return_val_if_fail (GST_IS_VOLUME (this), NULL);
       
   314 
       
   315   return this->tracklist;
       
   316 }
       
   317 
       
   318 static void
       
   319 gst_volume_set_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
       
   320 {
       
   321   GstVolume *this = GST_VOLUME (mixer);
       
   322 
       
   323   g_return_if_fail (this != NULL);
       
   324   g_return_if_fail (GST_IS_VOLUME (this));
       
   325 
       
   326   this->volume_f = (gfloat) volumes[0] / VOLUME_STEPS;
       
   327   this->volume_i32 = this->volume_f * VOLUME_UNITY_INT32;
       
   328   this->volume_i24 = this->volume_f * VOLUME_UNITY_INT24;
       
   329   this->volume_i16 = this->volume_f * VOLUME_UNITY_INT16;
       
   330   this->volume_i8 = this->volume_f * VOLUME_UNITY_INT8;
       
   331 
       
   332   volume_update_real_volume (this);
       
   333 }
       
   334 
       
   335 static void
       
   336 gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
       
   337 {
       
   338   GstVolume *this = GST_VOLUME (mixer);
       
   339 
       
   340   g_return_if_fail (this != NULL);
       
   341   g_return_if_fail (GST_IS_VOLUME (this));
       
   342 
       
   343   volumes[0] = (gint) this->volume_f * VOLUME_STEPS;
       
   344 }
       
   345 
       
   346 static void
       
   347 gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
       
   348 {
       
   349   GstVolume *this = GST_VOLUME (mixer);
       
   350 
       
   351   g_return_if_fail (this != NULL);
       
   352   g_return_if_fail (GST_IS_VOLUME (this));
       
   353 
       
   354   this->mute = mute;
       
   355 
       
   356   volume_update_real_volume (this);
       
   357 }
       
   358 
       
   359 static void
       
   360 gst_volume_mixer_init (GstMixerClass * klass)
       
   361 {
       
   362   GST_MIXER_TYPE (klass) = GST_MIXER_SOFTWARE;
       
   363 
       
   364   /* default virtual functions */
       
   365   klass->list_tracks = gst_volume_list_tracks;
       
   366   klass->set_volume = gst_volume_set_volume;
       
   367   klass->get_volume = gst_volume_get_volume;
       
   368   klass->set_mute = gst_volume_set_mute;
       
   369 }
       
   370 
       
   371 /* Element class */
       
   372 
       
   373 static void
       
   374 gst_volume_dispose (GObject * object)
       
   375 {
       
   376   GstVolume *volume = GST_VOLUME (object);
       
   377 
       
   378   if (volume->tracklist) {
       
   379     if (volume->tracklist->data)
       
   380       g_object_unref (volume->tracklist->data);
       
   381     g_list_free (volume->tracklist);
       
   382     volume->tracklist = NULL;
       
   383   }
       
   384 
       
   385   G_OBJECT_CLASS (parent_class)->dispose (object);
       
   386 }
       
   387 
       
   388 static void
       
   389 gst_volume_base_init (gpointer g_class)
       
   390 {
       
   391   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   392   GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class);
       
   393   GstCaps *caps;
       
   394 
       
   395   gst_element_class_set_details (element_class, &volume_details);
       
   396 
       
   397   caps = gst_caps_from_string (ALLOWED_CAPS);
       
   398   gst_audio_filter_class_add_pad_templates (filter_class, caps);
       
   399   gst_caps_unref (caps);
       
   400 }
       
   401 
       
   402 static void
       
   403 gst_volume_class_init (GstVolumeClass * klass)
       
   404 {
       
   405   GObjectClass *gobject_class;
       
   406   GstBaseTransformClass *trans_class;
       
   407   GstAudioFilterClass *filter_class;
       
   408 
       
   409   gobject_class = (GObjectClass *) klass;
       
   410   trans_class = (GstBaseTransformClass *) klass;
       
   411   filter_class = (GstAudioFilterClass *) (klass);
       
   412 
       
   413   gobject_class->set_property = volume_set_property;
       
   414   gobject_class->get_property = volume_get_property;
       
   415   gobject_class->dispose = gst_volume_dispose;
       
   416 
       
   417   g_object_class_install_property (gobject_class, PROP_MUTE,
       
   418       g_param_spec_boolean ("mute", "Mute", "mute channel",
       
   419           FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   420 
       
   421   g_object_class_install_property (gobject_class, PROP_VOLUME,
       
   422       g_param_spec_double ("volume", "Volume", "volume factor",
       
   423           0.0, VOLUME_MAX_DOUBLE, 1.0,
       
   424           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   425 
       
   426   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain");
       
   427 
       
   428   trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip);
       
   429   filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup);
       
   430 }
       
   431 
       
   432 static void
       
   433 gst_volume_init (GstVolume * this, GstVolumeClass * g_class)
       
   434 {
       
   435   GstMixerTrack *track = NULL;
       
   436 
       
   437   this->mute = FALSE;
       
   438   this->volume_i8 = this->real_vol_i8 = VOLUME_UNITY_INT8;
       
   439   this->volume_i16 = this->real_vol_i16 = VOLUME_UNITY_INT16;
       
   440   this->volume_i24 = this->real_vol_i24 = VOLUME_UNITY_INT24;
       
   441   this->volume_i32 = this->real_vol_i32 = VOLUME_UNITY_INT32;
       
   442   this->volume_f = this->real_vol_f = 1.0;
       
   443   this->tracklist = NULL;
       
   444 
       
   445   track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
       
   446 
       
   447   if (GST_IS_MIXER_TRACK (track)) {
       
   448     track->label = g_strdup ("volume");
       
   449     track->num_channels = 1;
       
   450     track->min_volume = 0;
       
   451     track->max_volume = VOLUME_STEPS;
       
   452     track->flags = GST_MIXER_TRACK_SOFTWARE;
       
   453     this->tracklist = g_list_append (this->tracklist, track);
       
   454   }
       
   455 
       
   456   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE);
       
   457 }
       
   458 
       
   459 /* NOTE: although it might be tempting to have volume_process_mute() which uses
       
   460  *       memset(bytes, 0, nbytes) for the vol=0 case, this has the downside that
       
   461  *       unmuting would only take place after processing a buffer.
       
   462  */
       
   463 
       
   464 static void
       
   465 volume_process_double (GstVolume * this, gpointer bytes, guint n_bytes)
       
   466 {
       
   467   gdouble *data = (gdouble *) bytes;
       
   468   guint i, num_samples = n_bytes / sizeof (gdouble);
       
   469 
       
   470   for (i = 0; i < num_samples; i++) {
       
   471     *data++ *= this->real_vol_f;
       
   472   }
       
   473 }
       
   474 
       
   475 static void
       
   476 volume_process_float (GstVolume * this, gpointer bytes, guint n_bytes)
       
   477 {
       
   478   gfloat *data = (gfloat *) bytes;
       
   479   guint num_samples = n_bytes / sizeof (gfloat);
       
   480 
       
   481   /*
       
   482      guint i;
       
   483      for (i = 0; i < num_samples; i++) {
       
   484      *data++ *= this->real_vol_f;
       
   485      }
       
   486    */
       
   487   /* time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=10000 ! audio/x-raw-float ! volume volume=1.5 ! fakesink
       
   488    * goes from 0m0.850s -> 0m0.717s with liboil
       
   489    */
       
   490   oil_scalarmultiply_f32_ns (data, data, &this->real_vol_f, num_samples);
       
   491 }
       
   492 
       
   493 static void
       
   494 volume_process_int32 (GstVolume * this, gpointer bytes, guint n_bytes)
       
   495 {
       
   496   gint *data = (gint *) bytes;
       
   497   guint i, num_samples;
       
   498   gint64 val;
       
   499 
       
   500   num_samples = n_bytes / sizeof (gint);
       
   501   for (i = 0; i < num_samples; i++) {
       
   502     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   503     val = (gint64) * data;
       
   504     val = (((gint64) this->real_vol_i32 * val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
       
   505     *data++ = (gint32) val;
       
   506   }
       
   507 }
       
   508 
       
   509 static void
       
   510 volume_process_int32_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
       
   511 {
       
   512   gint *data = (gint *) bytes;
       
   513   guint i, num_samples;
       
   514   gint64 val;
       
   515 
       
   516   num_samples = n_bytes / sizeof (gint);
       
   517 
       
   518   for (i = 0; i < num_samples; i++) {
       
   519     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   520     val = (gint64) * data;
       
   521     val = (((gint64) this->real_vol_i32 * val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
       
   522     *data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
       
   523   }
       
   524 }
       
   525 
       
   526 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
       
   527 #define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
       
   528 #define write_unaligned_u24(_x,samp) do { *(_x)++ = samp & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = (samp >> 16) & 0xFF; } while (0)
       
   529 #else /* BIG ENDIAN */
       
   530 #define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) )
       
   531 #define write_unaligned_u24(_x,samp) do { *(_x)++ = (samp >> 16) & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = samp & 0xFF; } while (0)
       
   532 #endif
       
   533 
       
   534 static void
       
   535 volume_process_int24 (GstVolume * this, gpointer bytes, guint n_bytes)
       
   536 {
       
   537   gint8 *data = (gint8 *) bytes;        /* treat the data as a byte stream */
       
   538   guint i, num_samples;
       
   539   guint32 samp;
       
   540   gint64 val;
       
   541 
       
   542   num_samples = n_bytes / (sizeof (gint8) * 3);
       
   543   for (i = 0; i < num_samples; i++) {
       
   544     samp = get_unaligned_i24 (data);
       
   545 
       
   546     val = (gint32) samp;
       
   547     val = (((gint64) this->real_vol_i24 * val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
       
   548     samp = (guint32) val;
       
   549 
       
   550     /* write the value back into the stream */
       
   551     write_unaligned_u24 (data, samp);
       
   552   }
       
   553 }
       
   554 
       
   555 static void
       
   556 volume_process_int24_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
       
   557 {
       
   558   gint8 *data = (gint8 *) bytes;        /* treat the data as a byte stream */
       
   559   guint i, num_samples;
       
   560   guint32 samp;
       
   561   gint64 val;
       
   562 
       
   563   num_samples = n_bytes / (sizeof (gint8) * 3);
       
   564   for (i = 0; i < num_samples; i++) {
       
   565     samp = get_unaligned_i24 (data);
       
   566 
       
   567     val = (gint32) samp;
       
   568     val = (((gint64) this->real_vol_i24 * val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
       
   569     samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
       
   570 
       
   571     /* write the value back into the stream */
       
   572     write_unaligned_u24 (data, samp);
       
   573   }
       
   574 }
       
   575 
       
   576 static void
       
   577 volume_process_int16 (GstVolume * this, gpointer bytes, guint n_bytes)
       
   578 {
       
   579   gint16 *data = (gint16 *) bytes;
       
   580   guint num_samples = n_bytes / sizeof (gint16);
       
   581 
       
   582 #if 1
       
   583   guint i;
       
   584   gint val;
       
   585 
       
   586   for (i = 0; i < num_samples; i++) {
       
   587     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   588     val = (gint) * data;
       
   589     *data++ =
       
   590         (gint16) ((this->real_vol_i16 * val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
       
   591   }
       
   592 #else
       
   593   /* FIXME: need oil_scalarmultiply_s16_ns ?
       
   594    * https://bugs.freedesktop.org/show_bug.cgi?id=7060
       
   595    * code below
       
   596    * - crashes :/
       
   597    * - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift
       
   598    * time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink
       
   599    */
       
   600   oil_scalarmult_s16 (data, 0, data, 0,
       
   601       ((gint16 *) (void *) (&this->real_vol_i)), num_samples);
       
   602 #endif
       
   603 }
       
   604 
       
   605 static void
       
   606 volume_process_int16_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
       
   607 {
       
   608   gint16 *data = (gint16 *) bytes;
       
   609   guint i, num_samples;
       
   610   gint val;
       
   611 
       
   612   num_samples = n_bytes / sizeof (gint16);
       
   613 
       
   614   /* FIXME: oil_scalarmultiply_s16_ns ?
       
   615    * https://bugs.freedesktop.org/show_bug.cgi?id=7060
       
   616    */
       
   617   for (i = 0; i < num_samples; i++) {
       
   618     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   619     val = (gint) * data;
       
   620     *data++ =
       
   621         (gint16) CLAMP ((this->real_vol_i16 *
       
   622             val) >> VOLUME_UNITY_INT16_BIT_SHIFT, VOLUME_MIN_INT16,
       
   623         VOLUME_MAX_INT16);
       
   624   }
       
   625 }
       
   626 
       
   627 static void
       
   628 volume_process_int8 (GstVolume * this, gpointer bytes, guint n_bytes)
       
   629 {
       
   630   gint8 *data = (gint8 *) bytes;
       
   631   guint num_samples = n_bytes / sizeof (gint8);
       
   632   guint i;
       
   633   gint val;
       
   634 
       
   635   for (i = 0; i < num_samples; i++) {
       
   636     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   637     val = (gint) * data;
       
   638     *data++ =
       
   639         (gint8) ((this->real_vol_i8 * val) >> VOLUME_UNITY_INT8_BIT_SHIFT);
       
   640   }
       
   641 }
       
   642 
       
   643 static void
       
   644 volume_process_int8_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
       
   645 {
       
   646   gint8 *data = (gint8 *) bytes;
       
   647   guint i, num_samples;
       
   648   gint val;
       
   649 
       
   650   num_samples = n_bytes / sizeof (gint8);
       
   651 
       
   652   for (i = 0; i < num_samples; i++) {
       
   653     /* we use bitshifting instead of dividing by UNITY_INT for speed */
       
   654     val = (gint) * data;
       
   655     *data++ =
       
   656         (gint8) CLAMP ((this->real_vol_i8 *
       
   657             val) >> VOLUME_UNITY_INT8_BIT_SHIFT, VOLUME_MIN_INT8,
       
   658         VOLUME_MAX_INT8);
       
   659   }
       
   660 }
       
   661 
       
   662 /* GstBaseTransform vmethod implementations */
       
   663 
       
   664 /* get notified of caps and plug in the correct process function */
       
   665 static gboolean
       
   666 volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format)
       
   667 {
       
   668   GstVolume *this = GST_VOLUME (filter);
       
   669 
       
   670   if (volume_choose_func (this)) {
       
   671     return TRUE;
       
   672   } else {
       
   673     GST_ELEMENT_ERROR (this, CORE, NEGOTIATION,
       
   674         ("Invalid incoming format"), (NULL));
       
   675     return FALSE;
       
   676   }
       
   677 }
       
   678 
       
   679 /* call the plugged-in process function for this instance
       
   680  * needs to be done with this indirection since volume_transform is
       
   681  * a class-global method
       
   682  */
       
   683 static GstFlowReturn
       
   684 volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
       
   685 {
       
   686   GstVolume *this = GST_VOLUME (base);
       
   687   GstClockTime timestamp;
       
   688 
       
   689   /* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small
       
   690    * chunks for smooth fades, what is small? 1/10th sec.
       
   691    */
       
   692   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
       
   693   timestamp =
       
   694       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
       
   695 
       
   696   GST_DEBUG_OBJECT (base, "sync to %" GST_TIME_FORMAT,
       
   697       GST_TIME_ARGS (timestamp));
       
   698 
       
   699   if (GST_CLOCK_TIME_IS_VALID (timestamp))
       
   700     gst_object_sync_values (G_OBJECT (this), timestamp);
       
   701 
       
   702   /* don't process data in passthrough-mode */
       
   703   if (gst_base_transform_is_passthrough (base) ||
       
   704       GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP))
       
   705     return GST_FLOW_OK;
       
   706 
       
   707   if (this->real_vol_f == 0.0)
       
   708     this->silent_buffer = TRUE;
       
   709 
       
   710   this->process (this, GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
       
   711 
       
   712   if (this->silent_buffer)
       
   713     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
       
   714   this->silent_buffer = FALSE;
       
   715 
       
   716   return GST_FLOW_OK;
       
   717 }
       
   718 
       
   719 static void
       
   720 volume_update_mute (const GValue * value, gpointer data)
       
   721 {
       
   722   GstVolume *this = (GstVolume *) data;
       
   723 
       
   724   g_return_if_fail (GST_IS_VOLUME (this));
       
   725 
       
   726   if (G_VALUE_HOLDS_BOOLEAN (value)) {
       
   727     this->mute = g_value_get_boolean (value);
       
   728   } else if (G_VALUE_HOLDS_INT (value)) {
       
   729     this->mute = (g_value_get_int (value) == 1);
       
   730   }
       
   731 
       
   732   volume_update_real_volume (this);
       
   733 }
       
   734 
       
   735 static void
       
   736 volume_update_volume (const GValue * value, gpointer data)
       
   737 {
       
   738   GstVolume *this = (GstVolume *) data;
       
   739 
       
   740   g_return_if_fail (GST_IS_VOLUME (this));
       
   741 
       
   742   this->volume_f = g_value_get_double (value);
       
   743   this->volume_i8 = this->volume_f * VOLUME_UNITY_INT8;
       
   744   this->volume_i16 = this->volume_f * VOLUME_UNITY_INT16;
       
   745   this->volume_i24 = this->volume_f * VOLUME_UNITY_INT24;
       
   746   this->volume_i32 = this->volume_f * VOLUME_UNITY_INT32;
       
   747 
       
   748   volume_update_real_volume (this);
       
   749 }
       
   750 
       
   751 static void
       
   752 volume_set_property (GObject * object, guint prop_id, const GValue * value,
       
   753     GParamSpec * pspec)
       
   754 {
       
   755   GstVolume *this = GST_VOLUME (object);
       
   756 
       
   757   switch (prop_id) {
       
   758     case PROP_MUTE:
       
   759       volume_update_mute (value, this);
       
   760       break;
       
   761     case PROP_VOLUME:
       
   762       volume_update_volume (value, this);
       
   763       break;
       
   764     default:
       
   765       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   766       break;
       
   767   }
       
   768 }
       
   769 
       
   770 static void
       
   771 volume_get_property (GObject * object, guint prop_id, GValue * value,
       
   772     GParamSpec * pspec)
       
   773 {
       
   774   GstVolume *this = GST_VOLUME (object);
       
   775 
       
   776   switch (prop_id) {
       
   777     case PROP_MUTE:
       
   778       g_value_set_boolean (value, this->mute);
       
   779       break;
       
   780     case PROP_VOLUME:
       
   781       g_value_set_double (value, this->volume_f);
       
   782       break;
       
   783     default:
       
   784       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   785       break;
       
   786   }
       
   787 }
       
   788 
       
   789 static gboolean
       
   790 plugin_init (GstPlugin * plugin)
       
   791 {
       
   792 #ifndef __SYMBIAN32__
       
   793   oil_init ();
       
   794 #endif  
       
   795 
       
   796   /* initialize gst controller library */
       
   797   gst_controller_init (NULL, NULL);
       
   798 
       
   799   return gst_element_register (plugin, "volume", GST_RANK_NONE,
       
   800       GST_TYPE_VOLUME);
       
   801 }
       
   802 
       
   803 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
       
   804     GST_VERSION_MINOR,
       
   805     "volume",
       
   806     "plugin for controlling audio volume",
       
   807     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
       
   808 
       
   809 
       
   810 #ifdef __SYMBIAN32__
       
   811 EXPORT_C 
       
   812 #endif
       
   813 GstPluginDesc* _GST_PLUGIN_DESC()
       
   814 {
       
   815 	return &gst_plugin_desc;
       
   816 }
       
   817