]> git.0d.be Git - empathy.git/blobdiff - src/empathy-audio-src.c
remove released flag
[empathy.git] / src / empathy-audio-src.c
index bd3c433fad5ced3dba01ad6dfe5851c7f7a5ca84..d77968b0d39dc62a2bf5ff0ccda0c5abdde4ceaa 100644 (file)
  */
 
 #include "config.h"
+#include "empathy-audio-src.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <libempathy/empathy-utils.h>
-#include <libempathy-gtk/empathy-call-utils.h>
+#include <tp-account-widgets/tpaw-utils.h>
 
-#include "empathy-audio-src.h"
+#include <gst/audio/streamvolume.h>
 
+#include "empathy-audio-utils.h"
 #include "empathy-mic-monitor.h"
+#include "empathy-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
-#include <libempathy/empathy-debug.h>
+#include "empathy-debug.h"
 
 G_DEFINE_TYPE(EmpathyGstAudioSrc, empathy_audio_src, GST_TYPE_BIN)
 
-/* signal enum */
-enum
-{
-    PEAK_LEVEL_CHANGED,
-    RMS_LEVEL_CHANGED,
-    LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = {0};
-
 enum {
     PROP_VOLUME = 1,
-    PROP_RMS_LEVEL,
-    PROP_PEAK_LEVEL,
+    PROP_MUTE,
     PROP_MICROPHONE,
 };
 
 /* private structure */
-typedef struct _EmpathyGstAudioSrcPrivate EmpathyGstAudioSrcPrivate;
-
 struct _EmpathyGstAudioSrcPrivate
 {
   gboolean dispose_has_run;
   GstElement *src;
-  GstElement *volume;
-  GstElement *level;
+  GstElement *volume_element;
 
   EmpathyMicMonitor *mic_monitor;
 
@@ -69,17 +54,73 @@ struct _EmpathyGstAudioSrcPrivate
   /* G_MAXUINT if not known yet */
   guint source_idx;
 
-  gdouble peak_level;
-  gdouble rms_level;
+  gdouble volume;
+  gboolean mute;
+  gboolean have_stream_volume;
 
-  GMutex *lock;
-  guint idle_id;
+  GMutex lock;
+  guint volume_idle_id;
 };
 
 #define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
   EmpathyGstAudioSrcPrivate))
 
+
+static gboolean
+empathy_audio_src_volume_changed (GObject *object,
+  GParamSpec *pspec,
+  gpointer user_data);
+
+static void
+empathy_audio_set_hw_mute (EmpathyGstAudioSrc *self, gboolean mute)
+{
+  if (mute == self->priv->mute)
+    return;
+
+  if (self->priv->have_stream_volume)
+    g_object_set (self->priv->src, "mute", mute, NULL);
+
+  /* Belt and braces: If for some reason the underlying src doesn't mute
+   * correctly or doesn't update us when it unmutes correctly enforce it using
+   * our own volume element. Our UI can in no circumstances be made to think
+   * the input is muted while it's not */
+  g_object_set (self->priv->volume_element, "mute", mute, NULL);
+
+  self->priv->mute = mute;
+}
+
+static gboolean
+empathy_audio_src_get_hw_mute (EmpathyGstAudioSrc *self)
+{
+  gboolean result;
+  g_object_get (self->priv->src, "mute", &result, NULL);
+
+  return result;
+}
+
+static void
+empathy_audio_src_set_hw_volume (EmpathyGstAudioSrc *self,
+    gdouble volume)
+{
+  if (volume == self->priv->volume)
+    return;
+
+  if (self->priv->have_stream_volume)
+    g_object_set (self->priv->src, "volume", volume, NULL);
+  self->priv->volume = volume;
+}
+
+static gdouble
+empathy_audio_src_get_hw_volume (EmpathyGstAudioSrc *self)
+{
+  gdouble result;
+  g_object_get (self->priv->src, "volume", &result, NULL);
+
+  return result;
+}
+
+
 gboolean
 empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
 {
@@ -106,7 +147,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,
@@ -205,9 +245,15 @@ create_src (void)
   /* Use pulsesrc as default */
   src = gst_element_factory_make ("pulsesrc", NULL);
   if (src == NULL)
-    return NULL;
+    {
+      g_warning ("Missing 'pulsesrc' element");
+      return NULL;
+    }
+
+  empathy_audio_set_stream_properties (src, TRUE);
 
-  empathy_call_set_stream_properties (src);
+  /* Set latency (buffering on the PulseAudio side) of 20ms */
+  g_object_set (src, "buffer-time", (gint64) 20000, NULL);
 
   return src;
 }
@@ -218,26 +264,75 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (obj);
   GstPad *ghost, *src;
 
-  priv->peak_level = -G_MAXDOUBLE;
-  priv->lock = g_mutex_new ();
+  obj->priv = priv;
+  g_mutex_init (&priv->lock);
+
+  priv->volume = 1.0;
 
   priv->src = create_src ();
   if (priv->src == NULL)
     return;
 
-  gst_bin_add (GST_BIN (obj), priv->src);
-
-  priv->volume = gst_element_factory_make ("volume", NULL);
-  g_object_ref (priv->volume);
+  if (GST_IS_STREAM_VOLUME (priv->src))
+    {
+      gdouble volume;
+      gboolean mute;
+
+      priv->have_stream_volume = TRUE;
+      /* We can't do a bidirection bind as the ::notify comes from another
+       * thread, for other bits of empathy it's most simpler if it comes from
+       * the main thread */
+      g_object_bind_property (obj, "volume", priv->src, "volume",
+        G_BINDING_DEFAULT);
+      g_object_bind_property (obj, "mute", priv->src, "mute",
+        G_BINDING_DEFAULT);
+
+      /* sync and callback for bouncing */
+      g_object_get (priv->src, "volume", &volume, NULL);
+      g_object_set (obj, "volume", volume, NULL);
+
+      g_object_get (priv->src, "mute", &mute, NULL);
+      g_object_set (obj, "mute", mute, NULL);
+
+      g_signal_connect (priv->src, "notify::volume",
+        G_CALLBACK (empathy_audio_src_volume_changed), obj);
+      g_signal_connect (priv->src, "notify::mute",
+        G_CALLBACK (empathy_audio_src_volume_changed), obj);
+    }
+  else
+    {
+      g_message ("No stream volume available :(, mute will work though");
+      priv->have_stream_volume = FALSE;
+    }
 
-  gst_bin_add (GST_BIN (obj), priv->volume);
-  gst_element_link (priv->src, priv->volume);
+  gst_bin_add (GST_BIN (obj), priv->src);
 
-  priv->level = gst_element_factory_make ("level", NULL);
-  gst_bin_add (GST_BIN (obj), priv->level);
-  gst_element_link (priv->volume, priv->level);
+  priv->volume_element = gst_element_factory_make ("volume", NULL);
+  gst_bin_add (GST_BIN (obj), priv->volume_element);
+
+  {
+    GstElement *capsfilter;
+    GstCaps *caps;
+
+    /* Explicitly state what format we want from pulsesrc. This pushes resampling
+     * and format conversion as early as possible, lowering the amount of data
+     * transferred and thus improving performance. When moving to GStreamer
+     * 0.11/1.0, this should change so that we actually request what the encoder
+     * wants downstream. */
+    caps = gst_caps_new_simple ("audio/x-raw",
+        "channels", G_TYPE_INT, 1,
+        "width", G_TYPE_INT, 16,
+        "depth", G_TYPE_INT, 16,
+        "rate", G_TYPE_INT, 32000,
+        NULL);
+    capsfilter = gst_element_factory_make ("capsfilter", NULL);
+    g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
+    gst_bin_add (GST_BIN (obj), capsfilter);
+    gst_element_link (priv->src, capsfilter);
+    gst_element_link (capsfilter, priv->volume_element);
+  }
 
-  src = gst_element_get_static_pad (priv->level, "src");
+  src = gst_element_get_static_pad (priv->volume_element, "src");
 
   ghost = gst_ghost_pad_new ("src", src);
   gst_element_add_pad (GST_ELEMENT (obj), ghost);
@@ -260,10 +355,6 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
 
 static void empathy_audio_src_dispose (GObject *object);
 static void empathy_audio_src_finalize (GObject *object);
-static void empathy_audio_src_handle_message (GstBin *bin,
-  GstMessage *message);
-
-static gboolean empathy_audio_src_levels_updated (gpointer user_data);
 
 static void
 empathy_audio_src_set_property (GObject *object,
@@ -272,9 +363,13 @@ 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;
+      case PROP_MUTE:
+        empathy_audio_set_hw_mute (EMPATHY_GST_AUDIO_SRC (object),
+          g_value_get_boolean (value));
+        break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -290,18 +385,10 @@ empathy_audio_src_get_property (GObject *object,
   switch (property_id)
     {
       case PROP_VOLUME:
-        g_value_set_double (value,
-          empathy_audio_src_get_volume (self));
-        break;
-      case PROP_PEAK_LEVEL:
-        g_mutex_lock (priv->lock);
-        g_value_set_double (value, priv->peak_level);
-        g_mutex_unlock (priv->lock);
+        g_value_set_double (value, priv->volume);
         break;
-      case PROP_RMS_LEVEL:
-        g_mutex_lock (priv->lock);
-        g_value_set_double (value, priv->rms_level);
-        g_mutex_unlock (priv->lock);
+      case PROP_MUTE:
+        g_value_set_boolean (value, priv->mute);
         break;
       case PROP_MICROPHONE:
         g_value_set_uint (value, priv->source_idx);
@@ -316,7 +403,6 @@ empathy_audio_src_class_init (EmpathyGstAudioSrcClass
   *empathy_audio_src_class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_src_class);
-  GstBinClass *gstbin_class = GST_BIN_CLASS (empathy_audio_src_class);
   GParamSpec *param_spec;
 
   g_type_class_add_private (empathy_audio_src_class,
@@ -328,44 +414,20 @@ empathy_audio_src_class_init (EmpathyGstAudioSrcClass
   object_class->set_property = empathy_audio_src_set_property;
   object_class->get_property = empathy_audio_src_get_property;
 
-  gstbin_class->handle_message =
-    GST_DEBUG_FUNCPTR (empathy_audio_src_handle_message);
-
   param_spec = g_param_spec_double ("volume", "Volume", "volume contol",
     0.0, 5.0, 1.0,
     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
 
-  param_spec = g_param_spec_double ("peak-level", "peak level", "peak level",
-    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
-    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-  g_object_class_install_property (object_class, PROP_PEAK_LEVEL, param_spec);
+  param_spec = g_param_spec_boolean ("mute", "Mute", "mute contol",
+    FALSE,
+    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_MUTE, param_spec);
 
   param_spec = g_param_spec_uint ("microphone", "microphone", "microphone",
     0, G_MAXUINT, G_MAXUINT,
     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_MICROPHONE, param_spec);
-
-  signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed",
-    G_TYPE_FROM_CLASS (empathy_audio_src_class),
-    G_SIGNAL_RUN_LAST,
-    0,
-    NULL, NULL,
-    g_cclosure_marshal_generic,
-    G_TYPE_NONE, 1, G_TYPE_DOUBLE);
-
-  param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level",
-    -G_MAXDOUBLE, G_MAXDOUBLE, 0,
-    G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-  g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
-
-  signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
-    G_TYPE_FROM_CLASS (empathy_audio_src_class),
-    G_SIGNAL_RUN_LAST,
-    0,
-    NULL, NULL,
-    g_cclosure_marshal_generic,
-    G_TYPE_NONE, 1, G_TYPE_DOUBLE);
 }
 
 void
@@ -379,10 +441,9 @@ empathy_audio_src_dispose (GObject *object)
 
   priv->dispose_has_run = TRUE;
 
-  if (priv->idle_id != 0)
-    g_source_remove (priv->idle_id);
-
-  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);
 
@@ -399,89 +460,57 @@ empathy_audio_src_finalize (GObject *object)
   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
 
   /* free any data held directly by the object here */
-  g_mutex_free (priv->lock);
+  g_mutex_clear (&priv->lock);
 
   G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
 }
 
 static gboolean
-empathy_audio_src_levels_updated (gpointer user_data)
+empathy_audio_src_volume_changed_idle (gpointer user_data)
 {
   EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
   EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+  gdouble volume;
+  gboolean mute;
 
-  g_mutex_lock (priv->lock);
+  g_mutex_lock (&priv->lock);
+  priv->volume_idle_id = 0;
+  g_mutex_unlock (&priv->lock);
 
-  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;
+  volume = empathy_audio_src_get_hw_volume (self);
 
-  g_mutex_unlock (priv->lock);
+  if (volume != priv->volume)
+    {
+      priv->volume = volume;
+      g_object_notify (G_OBJECT (self), "volume");
+    }
+
+  mute = empathy_audio_src_get_hw_mute (self);
+  if (mute != priv->mute)
+    {
+      priv->mute = mute;
+      /* hw mute changed, follow with own volume */
+      g_object_set (self->priv->volume_element, "mute", mute, NULL);
+      g_object_notify (G_OBJECT (self), "mute");
+    }
 
   return FALSE;
 }
 
-static void
-empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
+static gboolean
+empathy_audio_src_volume_changed (GObject *object,
+  GParamSpec *pspec,
+  gpointer user_data)
 {
-  EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (bin);
-  EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
-
-  if  (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
-        GST_MESSAGE_SRC (message) == GST_OBJECT (priv->level))
-    {
-      const GstStructure *s;
-      const gchar *name;
-      const GValue *list;
-      guint i, len;
-      gdouble peak = -G_MAXDOUBLE;
-      gdouble rms = -G_MAXDOUBLE;
-
-      s = gst_message_get_structure (message);
-      name = gst_structure_get_name (s);
-
-      if (g_strcmp0 ("level", name) != 0)
-        goto out;
-
-      list = gst_structure_get_value (s, "peak");
-      len = gst_value_list_get_size (list);
-
-      for (i =0 ; i < len; i++)
-        {
-          const GValue *value;
-          gdouble db;
-
-          value = gst_value_list_get_value (list, i);
-          db = g_value_get_double (value);
-          peak = MAX (db, peak);
-        }
-
-      list = gst_structure_get_value (s, "rms");
-      len = gst_value_list_get_size (list);
-
-      for (i =0 ; i < len; i++)
-        {
-          const GValue *value;
-          gdouble db;
-
-          value = gst_value_list_get_value (list, i);
-          db = g_value_get_double (value);
-          rms = MAX (db, rms);
-        }
-
-      g_mutex_lock (priv->lock);
-
-      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);
+  EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
 
-      g_mutex_unlock (priv->lock);
-    }
+  g_mutex_lock (&self->priv->lock);
+  if (self->priv->volume_idle_id == 0)
+    self->priv->volume_idle_id = g_idle_add (
+      empathy_audio_src_volume_changed_idle, self);
+  g_mutex_unlock (&self->priv->lock);
 
-out:
-   GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin,
-    message);
+  return FALSE;
 }
 
 GstElement *
@@ -499,33 +528,23 @@ empathy_audio_src_new (void)
 }
 
 void
-empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
+empathy_audio_src_set_echo_cancel (EmpathyGstAudioSrc *src,
+  gboolean enable)
 {
-  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);
+  DEBUG ("Src echo cancellation setting: %s", enable ? "on" : "off");
+  empathy_audio_set_stream_properties (src->priv->src, enable);
+}
 
-  g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL);
+void
+empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
+{
+  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
@@ -598,6 +617,15 @@ empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
     GAsyncResult *result,
     GError **error)
 {
-  empathy_implement_finish_void (src,
+  tpaw_implement_finish_void (src,
       empathy_audio_src_change_microphone_async);
 }
+
+void
+empathy_audio_src_set_mute (EmpathyGstAudioSrc *self,
+    gboolean mute)
+{
+  empathy_audio_set_hw_mute (self, mute);
+
+  g_object_notify (G_OBJECT (self), "mute");
+}