gst_plugins_base/gst/volume/gstvolume.c
branchRCL_3
changeset 30 7e817e7e631c
parent 29 567bb019e3e3
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
    22  */
    22  */
    23 
    23 
    24 /**
    24 /**
    25  * SECTION:element-volume
    25  * SECTION:element-volume
    26  *
    26  *
       
    27  * <refsect2>
       
    28  * <para>
    27  * The volume element changes the volume of the audio data.
    29  * The volume element changes the volume of the audio data.
    28  *
    30  * </para>
    29  * <refsect2>
       
    30  * <title>Example launch line</title>
    31  * <title>Example launch line</title>
    31  * |[
    32  * <para>
       
    33  * <programlisting>
    32  * gst-launch -v -m audiotestsrc ! volume volume=0.5 ! level ! fakesink silent=TRUE
    34  * gst-launch -v -m audiotestsrc ! volume volume=0.5 ! level ! fakesink silent=TRUE
    33  * ]| This pipeline shows that the level of audiotestsrc has been halved
    35  * </programlisting>
       
    36  * This pipeline shows that the level of audiotestsrc has been halved
    34  * (peak values are around -6 dB and RMS around -9 dB) compared to
    37  * (peak values are around -6 dB and RMS around -9 dB) compared to
    35  * the same pipeline without the volume element.
    38  * the same pipeline without the volume element.
       
    39  * </para>
    36  * </refsect2>
    40  * </refsect2>
    37  */
    41  */
    38 
    42 
    39 #ifdef HAVE_CONFIG_H
    43 #ifdef HAVE_CONFIG_H
    40 #include "config.h"
    44 #include "config.h"
    46 #include <gst/audio/audio.h>
    50 #include <gst/audio/audio.h>
    47 #include <gst/interfaces/mixer.h>
    51 #include <gst/interfaces/mixer.h>
    48 #include <gst/controller/gstcontroller.h>
    52 #include <gst/controller/gstcontroller.h>
    49 #include <gst/audio/audio.h>
    53 #include <gst/audio/audio.h>
    50 #include <gst/audio/gstaudiofilter.h>
    54 #include <gst/audio/gstaudiofilter.h>
    51 #include <liboil/liboil.h>
    55 
    52 #ifdef __SYMBIAN32__
    56 #include <gst/liboil.h>
    53 #include <liboil/globals.h>
    57 
    54 #endif
       
    55 #include "gstvolume.h"
    58 #include "gstvolume.h"
    56 
    59 
    57 /* some defines for audio processing */
    60 /* some defines for audio processing */
    58 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
    61 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
    59  * we map 1.0 to VOLUME_UNITY_INT*
    62  * we map 1.0 to VOLUME_UNITY_INT*
    80 # define VOLUME_STEPS           100
    83 # define VOLUME_STEPS           100
    81 
    84 
    82 #define GST_CAT_DEFAULT gst_volume_debug
    85 #define GST_CAT_DEFAULT gst_volume_debug
    83 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
    86 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
    84 
    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 
    85 /* Filter signals and args */
    93 /* Filter signals and args */
    86 enum
    94 enum
    87 {
    95 {
    88   /* FILL ME */
    96   /* FILL ME */
    89   LAST_SIGNAL
    97   LAST_SIGNAL
    90 };
    98 };
    91 
    99 
    92 #define DEFAULT_PROP_MUTE       FALSE
       
    93 #define DEFAULT_PROP_VOLUME     1.0
       
    94 
       
    95 enum
   100 enum
    96 {
   101 {
    97   PROP_0,
   102   PROP_0,
       
   103   PROP_SILENT,
    98   PROP_MUTE,
   104   PROP_MUTE,
    99   PROP_VOLUME
   105   PROP_VOLUME
   100 };
   106 };
   101 
   107 
   102 #define ALLOWED_CAPS \
   108 #define ALLOWED_CAPS \
   160 
   166 
   161 static void volume_set_property (GObject * object, guint prop_id,
   167 static void volume_set_property (GObject * object, guint prop_id,
   162     const GValue * value, GParamSpec * pspec);
   168     const GValue * value, GParamSpec * pspec);
   163 static void volume_get_property (GObject * object, guint prop_id,
   169 static void volume_get_property (GObject * object, guint prop_id,
   164     GValue * value, GParamSpec * pspec);
   170     GValue * value, GParamSpec * pspec);
   165 
   171 static void volume_update_volume (const GValue * value, gpointer data);
   166 static void volume_before_transform (GstBaseTransform * base,
   172 static void volume_update_mute (const GValue * value, gpointer data);
   167     GstBuffer * buffer);
   173 
   168 static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
   174 static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
   169     GstBuffer * outbuf);
   175     GstBuffer * outbuf);
   170 static gboolean volume_setup (GstAudioFilter * filter,
   176 static gboolean volume_setup (GstAudioFilter * filter,
   171     GstRingBufferSpec * format);
   177     GstRingBufferSpec * format);
   172 
   178 
   205   switch (GST_AUDIO_FILTER (this)->format.type) {
   211   switch (GST_AUDIO_FILTER (this)->format.type) {
   206     case GST_BUFTYPE_LINEAR:
   212     case GST_BUFTYPE_LINEAR:
   207       switch (GST_AUDIO_FILTER (this)->format.width) {
   213       switch (GST_AUDIO_FILTER (this)->format.width) {
   208         case 32:
   214         case 32:
   209           /* only clamp if the gain is greater than 1.0
   215           /* only clamp if the gain is greater than 1.0
   210            * FIXME: current_vol_i can change while processing the buffer!
   216            * FIXME: real_vol_i can change while processing the buffer!
   211            */
   217            */
   212           if (this->current_vol_i32 > VOLUME_UNITY_INT32)
   218           if (this->real_vol_i32 > VOLUME_UNITY_INT32)
   213             this->process = volume_process_int32_clamp;
   219             this->process = volume_process_int32_clamp;
   214           else
   220           else
   215             this->process = volume_process_int32;
   221             this->process = volume_process_int32;
   216           break;
   222           break;
   217         case 24:
   223         case 24:
   218           /* only clamp if the gain is greater than 1.0
   224           /* only clamp if the gain is greater than 1.0
   219            * FIXME: current_vol_i can change while processing the buffer!
   225            * FIXME: real_vol_i can change while processing the buffer!
   220            */
   226            */
   221           if (this->current_vol_i24 > VOLUME_UNITY_INT24)
   227           if (this->real_vol_i24 > VOLUME_UNITY_INT24)
   222             this->process = volume_process_int24_clamp;
   228             this->process = volume_process_int24_clamp;
   223           else
   229           else
   224             this->process = volume_process_int24;
   230             this->process = volume_process_int24;
   225           break;
   231           break;
   226         case 16:
   232         case 16:
   227           /* only clamp if the gain is greater than 1.0
   233           /* only clamp if the gain is greater than 1.0
   228            * FIXME: current_vol_i can change while processing the buffer!
   234            * FIXME: real_vol_i can change while processing the buffer!
   229            */
   235            */
   230           if (this->current_vol_i16 > VOLUME_UNITY_INT16)
   236           if (this->real_vol_i16 > VOLUME_UNITY_INT16)
   231             this->process = volume_process_int16_clamp;
   237             this->process = volume_process_int16_clamp;
   232           else
   238           else
   233             this->process = volume_process_int16;
   239             this->process = volume_process_int16;
   234           break;
   240           break;
   235         case 8:
   241         case 8:
   236           /* only clamp if the gain is greater than 1.0
   242           /* only clamp if the gain is greater than 1.0
   237            * FIXME: current_vol_i can change while processing the buffer!
   243            * FIXME: real_vol_i can change while processing the buffer!
   238            */
   244            */
   239           if (this->current_vol_i16 > VOLUME_UNITY_INT8)
   245           if (this->real_vol_i16 > VOLUME_UNITY_INT8)
   240             this->process = volume_process_int8_clamp;
   246             this->process = volume_process_int8_clamp;
   241           else
   247           else
   242             this->process = volume_process_int8;
   248             this->process = volume_process_int8;
   243           break;
   249           break;
   244       }
   250       }
   258   }
   264   }
   259 
   265 
   260   return (this->process != NULL);
   266   return (this->process != NULL);
   261 }
   267 }
   262 
   268 
   263 static gboolean
   269 static void
   264 volume_update_volume (GstVolume * this, gfloat volume, gboolean mute)
   270 volume_update_real_volume (GstVolume * this)
   265 {
   271 {
   266   gboolean passthrough;
   272   gboolean passthrough = FALSE;
   267   gboolean res;
   273 
   268 
   274   if (this->mute) {
   269   GST_DEBUG_OBJECT (this, "configure mute %d, volume %f", mute, volume);
   275     this->real_vol_f = 0.0;
   270 
   276     this->real_vol_i8 = this->real_vol_i16 = this->real_vol_i24 =
   271   if (mute) {
   277         this->real_vol_i32 = 0;
   272     this->current_mute = TRUE;
       
   273     this->current_volume = 0.0;
       
   274 
       
   275     this->current_vol_i8 = 0;
       
   276     this->current_vol_i16 = 0;
       
   277     this->current_vol_i24 = 0;
       
   278     this->current_vol_i32 = 0;
       
   279 
       
   280     passthrough = FALSE;
       
   281   } else {
   278   } else {
   282     this->current_mute = FALSE;
   279     this->real_vol_f = this->volume_f;
   283     this->current_volume = volume;
   280     this->real_vol_i8 = this->volume_i8;
   284 
   281     this->real_vol_i16 = this->volume_i16;
   285     this->current_vol_i8 = volume * VOLUME_UNITY_INT8;
   282     this->real_vol_i24 = this->volume_i24;
   286     this->current_vol_i16 = volume * VOLUME_UNITY_INT16;
   283     this->real_vol_i32 = this->volume_i32;
   287     this->current_vol_i24 = volume * VOLUME_UNITY_INT24;
   284     passthrough = (this->volume_i16 == VOLUME_UNITY_INT16);
   288     this->current_vol_i32 = volume * VOLUME_UNITY_INT32;
   285   }
   289 
   286   if (this->real_vol_f != 0.0)
   290     passthrough = (this->current_vol_i16 == VOLUME_UNITY_INT16);
   287     this->silent_buffer = FALSE;
   291   }
   288   volume_choose_func (this);
   292 
       
   293   GST_DEBUG_OBJECT (this, "set passthrough %d", passthrough);
       
   294 
       
   295   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (this), passthrough);
   289   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (this), passthrough);
   296 
       
   297   res = this->negotiated = volume_choose_func (this);
       
   298 
       
   299   return res;
       
   300 }
   290 }
   301 
   291 
   302 /* Mixer interface */
   292 /* Mixer interface */
   303 
   293 
   304 static gboolean
   294 static gboolean
   305 gst_volume_interface_supported (GstImplementsInterface * iface, GType type)
   295 gst_volume_interface_supported (GstImplementsInterface * iface, GType type)
   306 {
   296 {
   307   g_return_val_if_fail (type == GST_TYPE_MIXER, FALSE);
   297   g_assert (type == GST_TYPE_MIXER);
   308   return TRUE;
   298   return TRUE;
   309 }
   299 }
   310 
   300 
   311 static void
   301 static void
   312 gst_volume_interface_init (GstImplementsInterfaceClass * klass)
   302 gst_volume_interface_init (GstImplementsInterfaceClass * klass)
   331   GstVolume *this = GST_VOLUME (mixer);
   321   GstVolume *this = GST_VOLUME (mixer);
   332 
   322 
   333   g_return_if_fail (this != NULL);
   323   g_return_if_fail (this != NULL);
   334   g_return_if_fail (GST_IS_VOLUME (this));
   324   g_return_if_fail (GST_IS_VOLUME (this));
   335 
   325 
   336   GST_OBJECT_LOCK (this);
   326   this->volume_f = (gfloat) volumes[0] / VOLUME_STEPS;
   337   this->volume = (gfloat) volumes[0] / VOLUME_STEPS;
   327   this->volume_i32 = this->volume_f * VOLUME_UNITY_INT32;
   338   GST_OBJECT_UNLOCK (this);
   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);
   339 }
   333 }
   340 
   334 
   341 static void
   335 static void
   342 gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
   336 gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
   343 {
   337 {
   344   GstVolume *this = GST_VOLUME (mixer);
   338   GstVolume *this = GST_VOLUME (mixer);
   345 
   339 
   346   g_return_if_fail (this != NULL);
   340   g_return_if_fail (this != NULL);
   347   g_return_if_fail (GST_IS_VOLUME (this));
   341   g_return_if_fail (GST_IS_VOLUME (this));
   348 
   342 
   349   GST_OBJECT_LOCK (this);
   343   volumes[0] = (gint) this->volume_f * VOLUME_STEPS;
   350   volumes[0] = (gint) this->volume * VOLUME_STEPS;
       
   351   GST_OBJECT_UNLOCK (this);
       
   352 }
   344 }
   353 
   345 
   354 static void
   346 static void
   355 gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
   347 gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
   356 {
   348 {
   357   GstVolume *this = GST_VOLUME (mixer);
   349   GstVolume *this = GST_VOLUME (mixer);
   358 
   350 
   359   g_return_if_fail (this != NULL);
   351   g_return_if_fail (this != NULL);
   360   g_return_if_fail (GST_IS_VOLUME (this));
   352   g_return_if_fail (GST_IS_VOLUME (this));
   361 
   353 
   362   GST_OBJECT_LOCK (this);
       
   363   this->mute = mute;
   354   this->mute = mute;
   364   GST_OBJECT_UNLOCK (this);
   355 
       
   356   volume_update_real_volume (this);
   365 }
   357 }
   366 
   358 
   367 static void
   359 static void
   368 gst_volume_mixer_init (GstMixerClass * klass)
   360 gst_volume_mixer_init (GstMixerClass * klass)
   369 {
   361 {
   398 {
   390 {
   399   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   391   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   400   GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class);
   392   GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class);
   401   GstCaps *caps;
   393   GstCaps *caps;
   402 
   394 
   403   gst_element_class_set_details_simple (element_class, "Volume",
   395   gst_element_class_set_details (element_class, &volume_details);
   404       "Filter/Effect/Audio",
       
   405       "Set volume on audio/raw streams", "Andy Wingo <wingo@pobox.com>");
       
   406 
   396 
   407   caps = gst_caps_from_string (ALLOWED_CAPS);
   397   caps = gst_caps_from_string (ALLOWED_CAPS);
   408   gst_audio_filter_class_add_pad_templates (filter_class, caps);
   398   gst_audio_filter_class_add_pad_templates (filter_class, caps);
   409   gst_caps_unref (caps);
   399   gst_caps_unref (caps);
   410 }
   400 }
   424   gobject_class->get_property = volume_get_property;
   414   gobject_class->get_property = volume_get_property;
   425   gobject_class->dispose = gst_volume_dispose;
   415   gobject_class->dispose = gst_volume_dispose;
   426 
   416 
   427   g_object_class_install_property (gobject_class, PROP_MUTE,
   417   g_object_class_install_property (gobject_class, PROP_MUTE,
   428       g_param_spec_boolean ("mute", "Mute", "mute channel",
   418       g_param_spec_boolean ("mute", "Mute", "mute channel",
   429           DEFAULT_PROP_MUTE,
   419           FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
   430           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   431 
   420 
   432   g_object_class_install_property (gobject_class, PROP_VOLUME,
   421   g_object_class_install_property (gobject_class, PROP_VOLUME,
   433       g_param_spec_double ("volume", "Volume", "volume factor, 1.0=100%",
   422       g_param_spec_double ("volume", "Volume", "volume factor",
   434           0.0, VOLUME_MAX_DOUBLE, DEFAULT_PROP_VOLUME,
   423           0.0, VOLUME_MAX_DOUBLE, 1.0,
   435           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
   424           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
   436 
   425 
   437   trans_class->before_transform = GST_DEBUG_FUNCPTR (volume_before_transform);
   426   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain");
       
   427 
   438   trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip);
   428   trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip);
   439   filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup);
   429   filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup);
   440 }
   430 }
   441 
   431 
   442 static void
   432 static void
   443 gst_volume_init (GstVolume * this, GstVolumeClass * g_class)
   433 gst_volume_init (GstVolume * this, GstVolumeClass * g_class)
   444 {
   434 {
   445   GstMixerTrack *track = NULL;
   435   GstMixerTrack *track = NULL;
   446 
   436 
   447   this->mute = DEFAULT_PROP_MUTE;;
   437   this->mute = FALSE;
   448   this->volume = DEFAULT_PROP_VOLUME;
   438   this->volume_i8 = this->real_vol_i8 = VOLUME_UNITY_INT8;
   449 
   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;
   450   this->tracklist = NULL;
   443   this->tracklist = NULL;
   451   this->negotiated = FALSE;
       
   452 
   444 
   453   track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
   445   track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
   454 
   446 
   455   if (GST_IS_MIXER_TRACK (track)) {
   447   if (GST_IS_MIXER_TRACK (track)) {
   456     track->label = g_strdup ("volume");
   448     track->label = g_strdup ("volume");
   462   }
   454   }
   463 
   455 
   464   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE);
   456   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE);
   465 }
   457 }
   466 
   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 
   467 static void
   464 static void
   468 volume_process_double (GstVolume * this, gpointer bytes, guint n_bytes)
   465 volume_process_double (GstVolume * this, gpointer bytes, guint n_bytes)
   469 {
   466 {
   470   gdouble *data = (gdouble *) bytes;
   467   gdouble *data = (gdouble *) bytes;
   471   guint num_samples = n_bytes / sizeof (gdouble);
   468   guint i, num_samples = n_bytes / sizeof (gdouble);
   472 
   469 
   473   gdouble vol = this->current_volume;
   470   for (i = 0; i < num_samples; i++) {
   474 
   471     *data++ *= this->real_vol_f;
   475   oil_scalarmultiply_f64_ns (data, data, &vol, num_samples);
   472   }
   476 }
   473 }
   477 
   474 
   478 static void
   475 static void
   479 volume_process_float (GstVolume * this, gpointer bytes, guint n_bytes)
   476 volume_process_float (GstVolume * this, gpointer bytes, guint n_bytes)
   480 {
   477 {
   481   gfloat *data = (gfloat *) bytes;
   478   gfloat *data = (gfloat *) bytes;
   482   guint num_samples = n_bytes / sizeof (gfloat);
   479   guint num_samples = n_bytes / sizeof (gfloat);
   483 
   480 
   484 #if 0
   481   /*
   485   guint i;
   482      guint i;
   486 
   483      for (i = 0; i < num_samples; i++) {
   487   for (i = 0; i < num_samples; i++) {
   484      *data++ *= this->real_vol_f;
   488     *data++ *= this->real_vol_f;
   485      }
   489   }
       
   490   /* time "gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=10000 ! audio/x-raw-float !
       
   491    * volume volume=1.5 ! fakesink" goes from 0m0.850s -> 0m0.717s with liboil
       
   492    */
   486    */
   493 #endif
   487   /* time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=10000 ! audio/x-raw-float ! volume volume=1.5 ! fakesink
   494   oil_scalarmultiply_f32_ns (data, data, &this->current_volume, num_samples);
   488    * goes from 0m0.850s -> 0m0.717s with liboil
       
   489    */
       
   490   oil_scalarmultiply_f32_ns (data, data, &this->real_vol_f, num_samples);
   495 }
   491 }
   496 
   492 
   497 static void
   493 static void
   498 volume_process_int32 (GstVolume * this, gpointer bytes, guint n_bytes)
   494 volume_process_int32 (GstVolume * this, gpointer bytes, guint n_bytes)
   499 {
   495 {
   503 
   499 
   504   num_samples = n_bytes / sizeof (gint);
   500   num_samples = n_bytes / sizeof (gint);
   505   for (i = 0; i < num_samples; i++) {
   501   for (i = 0; i < num_samples; i++) {
   506     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   502     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   507     val = (gint64) * data;
   503     val = (gint64) * data;
   508     val =
   504     val = (((gint64) this->real_vol_i32 * val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
   509         (((gint64) this->current_vol_i32 *
       
   510             val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
       
   511     *data++ = (gint32) val;
   505     *data++ = (gint32) val;
   512   }
   506   }
   513 }
   507 }
   514 
   508 
   515 static void
   509 static void
   522   num_samples = n_bytes / sizeof (gint);
   516   num_samples = n_bytes / sizeof (gint);
   523 
   517 
   524   for (i = 0; i < num_samples; i++) {
   518   for (i = 0; i < num_samples; i++) {
   525     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   519     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   526     val = (gint64) * data;
   520     val = (gint64) * data;
   527     val =
   521     val = (((gint64) this->real_vol_i32 * val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
   528         (((gint64) this->current_vol_i32 *
       
   529             val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
       
   530     *data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
   522     *data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
   531   }
   523   }
   532 }
   524 }
   533 
   525 
   534 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
   526 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
   535 #define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
   527 #define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
   536 
   528 #define write_unaligned_u24(_x,samp) do { *(_x)++ = samp & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = (samp >> 16) & 0xFF; } while (0)
   537 #define write_unaligned_u24(_x,samp) \
       
   538 G_STMT_START { \
       
   539   *(_x)++ = samp & 0xFF; \
       
   540   *(_x)++ = (samp >> 8) & 0xFF; \
       
   541   *(_x)++ = (samp >> 16) & 0xFF; \
       
   542 } G_STMT_END
       
   543 
       
   544 #else /* BIG ENDIAN */
   529 #else /* BIG ENDIAN */
   545 #define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) )
   530 #define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) )
   546 #define write_unaligned_u24(_x,samp) \
   531 #define write_unaligned_u24(_x,samp) do { *(_x)++ = (samp >> 16) & 0xFF; *(_x)++ = (samp >> 8) & 0xFF; *(_x)++ = samp & 0xFF; } while (0)
   547 G_STMT_START { \
       
   548   *(_x)++ = (samp >> 16) & 0xFF; \
       
   549   *(_x)++ = (samp >> 8) & 0xFF; \
       
   550   *(_x)++ = samp & 0xFF; \
       
   551 } G_STMT_END
       
   552 #endif
   532 #endif
   553 
   533 
   554 static void
   534 static void
   555 volume_process_int24 (GstVolume * this, gpointer bytes, guint n_bytes)
   535 volume_process_int24 (GstVolume * this, gpointer bytes, guint n_bytes)
   556 {
   536 {
   562   num_samples = n_bytes / (sizeof (gint8) * 3);
   542   num_samples = n_bytes / (sizeof (gint8) * 3);
   563   for (i = 0; i < num_samples; i++) {
   543   for (i = 0; i < num_samples; i++) {
   564     samp = get_unaligned_i24 (data);
   544     samp = get_unaligned_i24 (data);
   565 
   545 
   566     val = (gint32) samp;
   546     val = (gint32) samp;
   567     val =
   547     val = (((gint64) this->real_vol_i24 * val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
   568         (((gint64) this->current_vol_i24 *
       
   569             val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
       
   570     samp = (guint32) val;
   548     samp = (guint32) val;
   571 
   549 
   572     /* write the value back into the stream */
   550     /* write the value back into the stream */
   573     write_unaligned_u24 (data, samp);
   551     write_unaligned_u24 (data, samp);
   574   }
   552   }
   585   num_samples = n_bytes / (sizeof (gint8) * 3);
   563   num_samples = n_bytes / (sizeof (gint8) * 3);
   586   for (i = 0; i < num_samples; i++) {
   564   for (i = 0; i < num_samples; i++) {
   587     samp = get_unaligned_i24 (data);
   565     samp = get_unaligned_i24 (data);
   588 
   566 
   589     val = (gint32) samp;
   567     val = (gint32) samp;
   590     val =
   568     val = (((gint64) this->real_vol_i24 * val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
   591         (((gint64) this->current_vol_i24 *
       
   592             val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
       
   593     samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
   569     samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
   594 
   570 
   595     /* write the value back into the stream */
   571     /* write the value back into the stream */
   596     write_unaligned_u24 (data, samp);
   572     write_unaligned_u24 (data, samp);
   597   }
   573   }
   609 
   585 
   610   for (i = 0; i < num_samples; i++) {
   586   for (i = 0; i < num_samples; i++) {
   611     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   587     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   612     val = (gint) * data;
   588     val = (gint) * data;
   613     *data++ =
   589     *data++ =
   614         (gint16) ((this->current_vol_i16 *
   590         (gint16) ((this->real_vol_i16 * val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
   615             val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
       
   616   }
   591   }
   617 #else
   592 #else
   618   /* FIXME: need oil_scalarmultiply_s16_ns ?
   593   /* FIXME: need oil_scalarmultiply_s16_ns ?
   619    * https://bugs.freedesktop.org/show_bug.cgi?id=7060
   594    * https://bugs.freedesktop.org/show_bug.cgi?id=7060
   620    * code below
   595    * code below
   621    * - crashes :/
   596    * - crashes :/
   622    * - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift
   597    * - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift
   623    * time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink
   598    * time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink
   624    */
   599    */
   625   oil_scalarmult_s16 (data, 0, data, 0,
   600   oil_scalarmult_s16 (data, 0, data, 0,
   626       ((gint16 *) (void *) (&this->current_vol_i)), num_samples);
   601       ((gint16 *) (void *) (&this->real_vol_i)), num_samples);
   627 #endif
   602 #endif
   628 }
   603 }
   629 
   604 
   630 static void
   605 static void
   631 volume_process_int16_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
   606 volume_process_int16_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
   641    */
   616    */
   642   for (i = 0; i < num_samples; i++) {
   617   for (i = 0; i < num_samples; i++) {
   643     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   618     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   644     val = (gint) * data;
   619     val = (gint) * data;
   645     *data++ =
   620     *data++ =
   646         (gint16) CLAMP ((this->current_vol_i16 *
   621         (gint16) CLAMP ((this->real_vol_i16 *
   647             val) >> VOLUME_UNITY_INT16_BIT_SHIFT, VOLUME_MIN_INT16,
   622             val) >> VOLUME_UNITY_INT16_BIT_SHIFT, VOLUME_MIN_INT16,
   648         VOLUME_MAX_INT16);
   623         VOLUME_MAX_INT16);
   649   }
   624   }
   650 }
   625 }
   651 
   626 
   659 
   634 
   660   for (i = 0; i < num_samples; i++) {
   635   for (i = 0; i < num_samples; i++) {
   661     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   636     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   662     val = (gint) * data;
   637     val = (gint) * data;
   663     *data++ =
   638     *data++ =
   664         (gint8) ((this->current_vol_i8 * val) >> VOLUME_UNITY_INT8_BIT_SHIFT);
   639         (gint8) ((this->real_vol_i8 * val) >> VOLUME_UNITY_INT8_BIT_SHIFT);
   665   }
   640   }
   666 }
   641 }
   667 
   642 
   668 static void
   643 static void
   669 volume_process_int8_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
   644 volume_process_int8_clamp (GstVolume * this, gpointer bytes, guint n_bytes)
   676 
   651 
   677   for (i = 0; i < num_samples; i++) {
   652   for (i = 0; i < num_samples; i++) {
   678     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   653     /* we use bitshifting instead of dividing by UNITY_INT for speed */
   679     val = (gint) * data;
   654     val = (gint) * data;
   680     *data++ =
   655     *data++ =
   681         (gint8) CLAMP ((this->current_vol_i8 *
   656         (gint8) CLAMP ((this->real_vol_i8 *
   682             val) >> VOLUME_UNITY_INT8_BIT_SHIFT, VOLUME_MIN_INT8,
   657             val) >> VOLUME_UNITY_INT8_BIT_SHIFT, VOLUME_MIN_INT8,
   683         VOLUME_MAX_INT8);
   658         VOLUME_MAX_INT8);
   684   }
   659   }
   685 }
   660 }
   686 
   661 
   688 
   663 
   689 /* get notified of caps and plug in the correct process function */
   664 /* get notified of caps and plug in the correct process function */
   690 static gboolean
   665 static gboolean
   691 volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format)
   666 volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format)
   692 {
   667 {
   693   gboolean res;
       
   694   GstVolume *this = GST_VOLUME (filter);
   668   GstVolume *this = GST_VOLUME (filter);
   695   gfloat volume;
   669 
   696   gboolean mute;
   670   if (volume_choose_func (this)) {
   697 
   671     return TRUE;
   698   GST_OBJECT_LOCK (this);
   672   } else {
   699   volume = this->volume;
       
   700   mute = this->mute;
       
   701   GST_OBJECT_UNLOCK (this);
       
   702 
       
   703   res = volume_update_volume (this, volume, mute);
       
   704   if (!res) {
       
   705     GST_ELEMENT_ERROR (this, CORE, NEGOTIATION,
   673     GST_ELEMENT_ERROR (this, CORE, NEGOTIATION,
   706         ("Invalid incoming format"), (NULL));
   674         ("Invalid incoming format"), (NULL));
   707   }
   675     return FALSE;
   708   this->negotiated = res;
       
   709 
       
   710   return res;
       
   711 }
       
   712 
       
   713 static void
       
   714 volume_before_transform (GstBaseTransform * base, GstBuffer * buffer)
       
   715 {
       
   716   GstClockTime timestamp;
       
   717   GstVolume *this = GST_VOLUME (base);
       
   718   gfloat volume;
       
   719   gboolean mute;
       
   720 
       
   721   /* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small
       
   722    * chunks for smooth fades, what is small? 1/10th sec.
       
   723    */
       
   724   timestamp = GST_BUFFER_TIMESTAMP (buffer);
       
   725   timestamp =
       
   726       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
       
   727 
       
   728   GST_DEBUG_OBJECT (base, "sync to %" GST_TIME_FORMAT,
       
   729       GST_TIME_ARGS (timestamp));
       
   730 
       
   731   if (GST_CLOCK_TIME_IS_VALID (timestamp))
       
   732     gst_object_sync_values (G_OBJECT (this), timestamp);
       
   733 
       
   734   /* get latest values */
       
   735   GST_OBJECT_LOCK (this);
       
   736   volume = this->volume;
       
   737   mute = this->mute;
       
   738   GST_OBJECT_UNLOCK (this);
       
   739 
       
   740   if ((volume != this->current_volume) || (mute != this->current_mute)) {
       
   741     /* the volume or mute was updated, update our internal state before
       
   742      * we continue processing. */
       
   743     volume_update_volume (this, volume, mute);
       
   744   }
   676   }
   745 }
   677 }
   746 
   678 
   747 /* call the plugged-in process function for this instance
   679 /* call the plugged-in process function for this instance
   748  * needs to be done with this indirection since volume_transform is
   680  * needs to be done with this indirection since volume_transform is
   750  */
   682  */
   751 static GstFlowReturn
   683 static GstFlowReturn
   752 volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
   684 volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
   753 {
   685 {
   754   GstVolume *this = GST_VOLUME (base);
   686   GstVolume *this = GST_VOLUME (base);
   755   guint8 *data;
   687   GstClockTime timestamp;
   756   guint size;
   688 
   757 
   689   /* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small
   758   if (G_UNLIKELY (!this->negotiated))
   690    * chunks for smooth fades, what is small? 1/10th sec.
   759     goto not_negotiated;
   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);
   760 
   701 
   761   /* don't process data in passthrough-mode */
   702   /* don't process data in passthrough-mode */
   762   if (gst_base_transform_is_passthrough (base) ||
   703   if (gst_base_transform_is_passthrough (base) ||
   763       GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP))
   704       GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP))
   764     return GST_FLOW_OK;
   705     return GST_FLOW_OK;
   765 
   706 
   766   data = GST_BUFFER_DATA (outbuf);
   707   if (this->real_vol_f == 0.0)
   767   size = GST_BUFFER_SIZE (outbuf);
   708     this->silent_buffer = TRUE;
   768 
   709 
   769   if (this->current_volume == 0.0) {
   710   this->process (this, GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
   770     memset (data, 0, size);
   711 
       
   712   if (this->silent_buffer)
   771     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
   713     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
   772   } else if (this->current_volume != 1.0) {
   714   this->silent_buffer = FALSE;
   773     this->process (this, data, size);
       
   774   }
       
   775 
   715 
   776   return GST_FLOW_OK;
   716   return GST_FLOW_OK;
   777 
   717 }
   778   /* ERRORS */
   718 
   779 not_negotiated:
   719 static void
   780   {
   720 volume_update_mute (const GValue * value, gpointer data)
   781     GST_ELEMENT_ERROR (this, CORE, NEGOTIATION,
   721 {
   782         ("No format was negotiated"), (NULL));
   722   GstVolume *this = (GstVolume *) data;
   783     return GST_FLOW_NOT_NEGOTIATED;
   723 
   784   }
   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);
   785 }
   749 }
   786 
   750 
   787 static void
   751 static void
   788 volume_set_property (GObject * object, guint prop_id, const GValue * value,
   752 volume_set_property (GObject * object, guint prop_id, const GValue * value,
   789     GParamSpec * pspec)
   753     GParamSpec * pspec)
   790 {
   754 {
   791   GstVolume *this = GST_VOLUME (object);
   755   GstVolume *this = GST_VOLUME (object);
   792 
   756 
   793   switch (prop_id) {
   757   switch (prop_id) {
   794     case PROP_MUTE:
   758     case PROP_MUTE:
   795       GST_OBJECT_LOCK (this);
   759       volume_update_mute (value, this);
   796       this->mute = g_value_get_boolean (value);
       
   797       GST_OBJECT_UNLOCK (this);
       
   798       break;
   760       break;
   799     case PROP_VOLUME:
   761     case PROP_VOLUME:
   800       GST_OBJECT_LOCK (this);
   762       volume_update_volume (value, this);
   801       this->volume = g_value_get_double (value);
       
   802       GST_OBJECT_UNLOCK (this);
       
   803       break;
   763       break;
   804     default:
   764     default:
   805       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   765       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   806       break;
   766       break;
   807   }
   767   }
   813 {
   773 {
   814   GstVolume *this = GST_VOLUME (object);
   774   GstVolume *this = GST_VOLUME (object);
   815 
   775 
   816   switch (prop_id) {
   776   switch (prop_id) {
   817     case PROP_MUTE:
   777     case PROP_MUTE:
   818       GST_OBJECT_LOCK (this);
       
   819       g_value_set_boolean (value, this->mute);
   778       g_value_set_boolean (value, this->mute);
   820       GST_OBJECT_UNLOCK (this);
       
   821       break;
   779       break;
   822     case PROP_VOLUME:
   780     case PROP_VOLUME:
   823       GST_OBJECT_LOCK (this);
   781       g_value_set_double (value, this->volume_f);
   824       g_value_set_double (value, this->volume);
       
   825       GST_OBJECT_UNLOCK (this);
       
   826       break;
   782       break;
   827     default:
   783     default:
   828       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   784       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   829       break;
   785       break;
   830   }
   786   }
   831 }
   787 }
   832 
   788 
   833 static gboolean
   789 static gboolean
   834 plugin_init (GstPlugin * plugin)
   790 plugin_init (GstPlugin * plugin)
   835 {
   791 {
       
   792 #ifndef __SYMBIAN32__
   836   oil_init ();
   793   oil_init ();
       
   794 #endif  
   837 
   795 
   838   /* initialize gst controller library */
   796   /* initialize gst controller library */
   839   gst_controller_init (NULL, NULL);
   797   gst_controller_init (NULL, NULL);
   840 
       
   841   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain");
       
   842 
       
   843   /* ref class from a thread-safe context to work around missing bit of
       
   844    * thread-safety in GObject */
       
   845   g_type_class_ref (GST_TYPE_MIXER_TRACK);
       
   846 
   798 
   847   return gst_element_register (plugin, "volume", GST_RANK_NONE,
   799   return gst_element_register (plugin, "volume", GST_RANK_NONE,
   848       GST_TYPE_VOLUME);
   800       GST_TYPE_VOLUME);
   849 }
   801 }
   850 
   802