aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSjoerd Simons <sjoerd.simons@collabora.co.uk>2011-11-16 00:57:20 +0800
committerSjoerd Simons <sjoerd.simons@collabora.co.uk>2011-11-18 06:45:57 +0800
commit3db517442865a03c1d303774c9a36fda1d8ea51d (patch)
treef975594e3e57dc89f3c037e2ba4673dcb6341fcf
parent5bb2c1c62a06e682ab48d530cae5f9614a90a75e (diff)
downloadgsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar.gz
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar.bz2
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar.lz
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar.xz
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.tar.zst
gsoc2013-empathy-3db517442865a03c1d303774c9a36fda1d8ea51d.zip
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
-rw-r--r--src/empathy-audio-src.c218
1 files changed, 179 insertions, 39 deletions
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c
index 19af8877a..a91d3b286 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