diff options
author | Sjoerd Simons <sjoerd.simons@collabora.co.uk> | 2011-11-16 00:57:20 +0800 |
---|---|---|
committer | Sjoerd Simons <sjoerd.simons@collabora.co.uk> | 2011-11-18 06:45:57 +0800 |
commit | 3db517442865a03c1d303774c9a36fda1d8ea51d (patch) | |
tree | f975594e3e57dc89f3c037e2ba4673dcb6341fcf | |
parent | 5bb2c1c62a06e682ab48d530cae5f9614a90a75e (diff) | |
download | gsoc2013-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.c | 218 |
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 |