[empathy] Let the audio source control the volume, not a software volume element



commit 3db517442865a03c1d303774c9a36fda1d8ea51d
Author: Sjoerd Simons <sjoerd simons collabora co uk>
Date:   Tue Nov 15 17:57:20 2011 +0100

    Let the audio source control the volume, not a software volume element
    
    There is no point in amplifying the mic level is it's too low or too
    high as it either doesn't have enough information or it is clipping.
    Instead tell pulsesrc what it should do, so it can adjust the hardware
    volume for us.
    
    Also listen to changed done by pulsesrc so we can track volume updates
    and feed back the settings properly

 src/empathy-audio-src.c |  218 ++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 179 insertions(+), 39 deletions(-)
---
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c
index 19af887..a91d3b2 100644
--- a/src/empathy-audio-src.c
+++ b/src/empathy-audio-src.c
@@ -23,6 +23,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <gst/interfaces/mixer.h>
+
 #include <libempathy/empathy-utils.h>
 #include <libempathy-gtk/empathy-call-utils.h>
 
@@ -57,7 +59,6 @@ struct _EmpathyGstAudioSrcPrivate
 {
   gboolean dispose_has_run;
   GstElement *src;
-  GstElement *volume;
   GstElement *level;
 
   EmpathyMicMonitor *mic_monitor;
@@ -70,14 +71,67 @@ struct _EmpathyGstAudioSrcPrivate
   gdouble peak_level;
   gdouble rms_level;
 
+  gdouble volume;
+  /* the mixer track on src we follow and adjust */
+  GstMixerTrack *track;
+
   GMutex *lock;
-  guint idle_id;
+  guint level_idle_id;
+  guint volume_idle_id;
 };
 
 #define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
   EmpathyGstAudioSrcPrivate))
 
+/* There is no predefined maximum channels by gstreamer, just pick 32, which is
+ * the same as the pulseaudio maximum */
+#define MAX_MIC_CHANNELS 32
+
+static void
+empathy_audio_src_set_hw_volume (EmpathyGstAudioSrc *self, gdouble volume)
+{
+  gint volumes[MAX_MIC_CHANNELS];
+  int i;
+
+  g_mutex_lock (self->priv->lock);
+  /* If there is no mixer available ignore the setting */
+  if (self->priv->track == NULL)
+    goto out;
+
+  for (i = 0; i < MAX_MIC_CHANNELS; i++)
+    volumes[i] = self->priv->track->max_volume * volume;
+
+  gst_mixer_set_volume (GST_MIXER (self->priv->src),
+    self->priv->track, volumes);
+
+out:
+   g_mutex_unlock (self->priv->lock);
+
+  self->priv->volume = volume;
+}
+
+static gdouble
+empathy_audio_src_get_hw_volume (EmpathyGstAudioSrc *self)
+{
+  gint volumes[MAX_MIC_CHANNELS];
+  gdouble result = self->priv->volume;
+
+  g_mutex_lock (self->priv->lock);
+  if (self->priv->track == NULL)
+    goto out;
+
+  gst_mixer_get_volume (GST_MIXER (self->priv->src),
+    self->priv->track, volumes);
+  result = volumes[0]/(gdouble)self->priv->track->max_volume;
+
+out:
+  g_mutex_unlock (self->priv->lock);
+
+  return result;
+}
+
+
 gboolean
 empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
 {
@@ -104,7 +158,6 @@ empathy_audio_src_get_mic_index (EmpathyGstAudioSrc *self)
   return audio_src_idx;
 }
 
-
 static void
 empathy_audio_src_microphone_changed_cb (EmpathyMicMonitor *monitor,
     guint source_output_idx,
@@ -178,6 +231,43 @@ empathy_audio_src_source_output_index_notify (GObject *object,
       source_output_idx, empathy_audio_src_get_current_mic_cb, self);
 }
 
+static GstMixerTrack *
+empathy_audio_src_get_track (GstElement *src)
+{
+  const GList *t;
+  GstMixerTrack *track = NULL;
+
+  if (!gst_element_implements_interface (src, GST_TYPE_MIXER))
+    {
+      g_warning ("No mixer interface implementation, can't control volume");
+      return NULL;
+    }
+
+  for (t = gst_mixer_list_tracks (GST_MIXER (src));
+      t != NULL; t = g_list_next (t))
+    {
+      GstMixerTrack *tr = t->data;
+      if (!tp_strdiff (tr->label, "Master"))
+        {
+          track = tr;
+          break;
+        }
+    }
+
+  if (track == NULL)
+    {
+      g_warning ("No suitable track found");
+    }
+  else if (track->num_channels > MAX_MIC_CHANNELS)
+    {
+      g_warning ("Microphones with more then %d channels not supported ",
+        MAX_MIC_CHANNELS);
+      track = NULL;
+    }
+
+  return track;
+}
+
 static GstElement *
 create_src (void)
 {
@@ -221,6 +311,7 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
   obj->priv = priv;
   priv->peak_level = -G_MAXDOUBLE;
   priv->lock = g_mutex_new ();
+  priv->volume = 1.0;
 
   priv->src = create_src ();
   if (priv->src == NULL)
@@ -244,15 +335,9 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
   gst_bin_add (GST_BIN (obj), capsfilter);
   gst_element_link (priv->src, capsfilter);
 
-  priv->volume = gst_element_factory_make ("volume", NULL);
-  g_object_ref (priv->volume);
-
-  gst_bin_add (GST_BIN (obj), priv->volume);
-  gst_element_link (capsfilter, priv->volume);
-
   priv->level = gst_element_factory_make ("level", NULL);
   gst_bin_add (GST_BIN (obj), priv->level);
-  gst_element_link (priv->volume, priv->level);
+  gst_element_link (capsfilter, priv->level);
 
   src = gst_element_get_static_pad (priv->level, "src");
 
@@ -289,7 +374,7 @@ empathy_audio_src_set_property (GObject *object,
   switch (property_id)
     {
       case PROP_VOLUME:
-        empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (object),
+        empathy_audio_src_set_hw_volume (EMPATHY_GST_AUDIO_SRC (object),
           g_value_get_double (value));
         break;
       default:
@@ -307,8 +392,7 @@ empathy_audio_src_get_property (GObject *object,
   switch (property_id)
     {
       case PROP_VOLUME:
-        g_value_set_double (value,
-          empathy_audio_src_get_volume (self));
+        g_value_set_double (value, priv->volume);
         break;
       case PROP_PEAK_LEVEL:
         g_mutex_lock (priv->lock);
@@ -396,10 +480,13 @@ empathy_audio_src_dispose (GObject *object)
 
   priv->dispose_has_run = TRUE;
 
-  if (priv->idle_id != 0)
-    g_source_remove (priv->idle_id);
+  if (priv->level_idle_id != 0)
+    g_source_remove (priv->level_idle_id);
+  priv->level_idle_id = 0;
 
-  priv->idle_id = 0;
+  if (priv->volume_idle_id != 0)
+    g_source_remove (priv->volume_idle_id);
+  priv->volume_idle_id = 0;
 
   tp_clear_object (&priv->mic_monitor);
 
@@ -431,13 +518,35 @@ empathy_audio_src_levels_updated (gpointer user_data)
 
   g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level);
   g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level);
-  priv->idle_id = 0;
+  priv->level_idle_id = 0;
 
   g_mutex_unlock (priv->lock);
 
   return FALSE;
 }
 
+static gboolean
+empathy_audio_src_volume_changed (gpointer user_data)
+{
+  EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
+  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+  gdouble volume;
+
+  g_mutex_lock (priv->lock);
+  priv->volume_idle_id = 0;
+  g_mutex_unlock (priv->lock);
+
+  volume = empathy_audio_src_get_hw_volume (self);
+
+  if (volume != priv->volume)
+    {
+      priv->volume = volume;
+      g_object_notify (G_OBJECT (self), "volume");
+    }
+
+  return FALSE;
+}
+
 static void
 empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
 {
@@ -490,11 +599,60 @@ empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
 
       priv->peak_level = peak;
       priv->rms_level = rms;
-      if (priv->idle_id == 0)
-        priv->idle_id = g_idle_add (empathy_audio_src_levels_updated, self);
+      if (priv->level_idle_id == 0)
+        priv->level_idle_id = g_idle_add (
+          empathy_audio_src_levels_updated, self);
 
       g_mutex_unlock (priv->lock);
     }
+  else if  (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
+        GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
+    {
+      /* Listen for volume changes on the src element */
+      if (gst_mixer_message_get_type (message) ==
+          GST_MIXER_MESSAGE_VOLUME_CHANGED)
+        {
+          GstMixerTrack *track;
+
+          gst_mixer_message_parse_volume_changed (message, &track,
+            NULL, NULL);
+
+          g_mutex_lock (priv->lock);
+
+          if (track == priv->track && priv->volume_idle_id == 0)
+            priv->volume_idle_id = g_idle_add (
+                empathy_audio_src_volume_changed, self);
+          g_mutex_unlock (priv->lock);
+        }
+    }
+  else if  (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED &&
+      GST_MESSAGE_SRC (message) == GST_OBJECT (priv->src))
+    {
+      GstState old, new;
+
+      gst_message_parse_state_changed (message, &old, &new, NULL);
+
+      /* GstMixer is only available in state >= READY, so only start
+       * controlling the source element when going to ready state and stop
+       * doing so when going below ready. Furthermore once we have mixer read
+       * the current volume level from it and remove the settings done by
+       * Empathy. We want to pick up the level pulseaudio saved */
+      if (old == GST_STATE_NULL && new == GST_STATE_READY)
+        {
+          g_mutex_lock (priv->lock);
+          priv->track = empathy_audio_src_get_track (priv->src);
+          if (priv->track != NULL)
+            priv->volume_idle_id = g_idle_add (
+              empathy_audio_src_volume_changed, self);
+          g_mutex_unlock (priv->lock);
+        }
+      else if (old == GST_STATE_READY && new == GST_STATE_NULL)
+        {
+          g_mutex_lock (priv->lock);
+          priv->track = NULL;
+          g_mutex_unlock (priv->lock);
+        }
+    }
 
 out:
    GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin,
@@ -526,31 +684,13 @@ empathy_audio_src_set_echo_cancel (EmpathyGstAudioSrc *src,
 void
 empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
 {
-  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
-  GParamSpec *pspec;
-  GParamSpecDouble *pspec_double;
-
-  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->volume),
-    "volume");
-
-  g_assert (pspec != NULL);
-
-  pspec_double = G_PARAM_SPEC_DOUBLE (pspec);
-
-  volume = CLAMP (volume, pspec_double->minimum, pspec_double->maximum);
-
-  g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL);
+  g_object_set (src, "volume", volume, NULL);
 }
 
 gdouble
 empathy_audio_src_get_volume (EmpathyGstAudioSrc *src)
 {
-  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
-  gdouble volume;
-
-  g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL);
-
-  return volume;
+  return src->priv->volume;
 }
 
 guint



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]