2 * empathy-gst-audio-src.c - Source for EmpathyGstAudioSrc
3 * Copyright (C) 2008 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <libempathy/empathy-utils.h>
26 #include <libempathy-gtk/empathy-call-utils.h>
28 #include "empathy-audio-src.h"
30 #include "empathy-mic-monitor.h"
32 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
33 #include <libempathy/empathy-debug.h>
35 G_DEFINE_TYPE(EmpathyGstAudioSrc, empathy_audio_src, GST_TYPE_BIN)
45 static guint signals[LAST_SIGNAL] = {0};
54 /* private structure */
55 typedef struct _EmpathyGstAudioSrcPrivate EmpathyGstAudioSrcPrivate;
57 struct _EmpathyGstAudioSrcPrivate
59 gboolean dispose_has_run;
64 EmpathyMicMonitor *mic_monitor;
66 /* 0 if not known yet */
67 guint source_output_idx;
68 /* G_MAXUINT if not known yet */
78 #define EMPATHY_GST_AUDIO_SRC_GET_PRIVATE(o) \
79 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
80 EmpathyGstAudioSrcPrivate))
83 empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
85 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
86 GObjectClass *object_class;
88 object_class = G_OBJECT_GET_CLASS (priv->src);
90 return (g_object_class_find_property (object_class,
91 "source-output-index") != NULL);
95 empathy_audio_src_get_mic_index (EmpathyGstAudioSrc *self)
97 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
98 guint audio_src_idx = PA_INVALID_INDEX;
100 if (empathy_audio_src_supports_changing_mic (self))
101 g_object_get (priv->src,
102 "source-output-index", &audio_src_idx,
105 return audio_src_idx;
110 empathy_audio_src_microphone_changed_cb (EmpathyMicMonitor *monitor,
111 guint source_output_idx,
115 EmpathyGstAudioSrc *self = user_data;
116 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
119 audio_src_idx = empathy_audio_src_get_mic_index (self);
121 if (source_output_idx == PA_INVALID_INDEX
122 || source_output_idx != audio_src_idx)
125 if (priv->source_idx == source_idx)
128 priv->source_idx = source_idx;
129 g_object_notify (G_OBJECT (self), "microphone");
133 empathy_audio_src_get_current_mic_cb (GObject *source_object,
134 GAsyncResult *result,
137 EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
138 EmpathyGstAudioSrc *self = user_data;
139 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
141 GError *error = NULL;
143 source_idx = empathy_mic_monitor_get_current_mic_finish (monitor, result, &error);
147 DEBUG ("Failed to get current mic: %s", error->message);
148 g_clear_error (&error);
152 if (priv->source_idx == source_idx)
155 priv->source_idx = source_idx;
156 g_object_notify (G_OBJECT (self), "microphone");
160 empathy_audio_src_source_output_index_notify (GObject *object,
162 EmpathyGstAudioSrc *self)
164 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
165 guint source_output_idx;
167 source_output_idx = empathy_audio_src_get_mic_index (self);
169 if (source_output_idx == PA_INVALID_INDEX)
172 if (priv->source_output_idx == source_output_idx)
175 /* It's actually changed. */
176 priv->source_output_idx = source_output_idx;
178 empathy_mic_monitor_get_current_mic_async (priv->mic_monitor,
179 source_output_idx, empathy_audio_src_get_current_mic_cb, self);
186 const gchar *description;
188 description = g_getenv ("EMPATHY_AUDIO_SRC");
190 if (description != NULL)
192 GError *error = NULL;
194 src = gst_parse_bin_from_description (description, TRUE, &error);
197 DEBUG ("Failed to create bin %s: %s", description, error->message);
198 g_error_free (error);
204 /* Use pulsesrc as default */
205 src = gst_element_factory_make ("pulsesrc", NULL);
209 empathy_call_set_stream_properties (src);
215 empathy_audio_src_init (EmpathyGstAudioSrc *obj)
217 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (obj);
220 priv->peak_level = -G_MAXDOUBLE;
221 priv->lock = g_mutex_new ();
223 priv->src = create_src ();
224 if (priv->src == NULL)
227 gst_bin_add (GST_BIN (obj), priv->src);
229 priv->volume = gst_element_factory_make ("volume", NULL);
230 g_object_ref (priv->volume);
232 gst_bin_add (GST_BIN (obj), priv->volume);
233 gst_element_link (priv->src, priv->volume);
235 priv->level = gst_element_factory_make ("level", NULL);
236 gst_bin_add (GST_BIN (obj), priv->level);
237 gst_element_link (priv->volume, priv->level);
239 src = gst_element_get_static_pad (priv->level, "src");
241 ghost = gst_ghost_pad_new ("src", src);
242 gst_element_add_pad (GST_ELEMENT (obj), ghost);
244 gst_object_unref (G_OBJECT (src));
246 /* Listen to changes to GstPulseSrc:source-output-index so we know when
247 * it's no longer PA_INVALID_INDEX (starting for the first time) or if it
248 * changes (READY->NULL->READY...) */
249 g_signal_connect (priv->src, "notify::source-output-index",
250 G_CALLBACK (empathy_audio_src_source_output_index_notify),
253 priv->mic_monitor = empathy_mic_monitor_new ();
254 g_signal_connect (priv->mic_monitor, "microphone-changed",
255 G_CALLBACK (empathy_audio_src_microphone_changed_cb), obj);
257 priv->source_idx = PA_INVALID_INDEX;
260 static void empathy_audio_src_dispose (GObject *object);
261 static void empathy_audio_src_finalize (GObject *object);
262 static void empathy_audio_src_handle_message (GstBin *bin,
263 GstMessage *message);
265 static gboolean empathy_audio_src_levels_updated (gpointer user_data);
268 empathy_audio_src_set_property (GObject *object,
269 guint property_id, const GValue *value, GParamSpec *pspec)
274 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (object),
275 g_value_get_double (value));
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
283 empathy_audio_src_get_property (GObject *object,
284 guint property_id, GValue *value, GParamSpec *pspec)
286 EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
287 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
292 g_value_set_double (value,
293 empathy_audio_src_get_volume (self));
295 case PROP_PEAK_LEVEL:
296 g_mutex_lock (priv->lock);
297 g_value_set_double (value, priv->peak_level);
298 g_mutex_unlock (priv->lock);
301 g_mutex_lock (priv->lock);
302 g_value_set_double (value, priv->rms_level);
303 g_mutex_unlock (priv->lock);
305 case PROP_MICROPHONE:
306 g_value_set_uint (value, priv->source_idx);
309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
314 empathy_audio_src_class_init (EmpathyGstAudioSrcClass
315 *empathy_audio_src_class)
317 GObjectClass *object_class = G_OBJECT_CLASS (empathy_audio_src_class);
318 GstBinClass *gstbin_class = GST_BIN_CLASS (empathy_audio_src_class);
319 GParamSpec *param_spec;
321 g_type_class_add_private (empathy_audio_src_class,
322 sizeof (EmpathyGstAudioSrcPrivate));
324 object_class->dispose = empathy_audio_src_dispose;
325 object_class->finalize = empathy_audio_src_finalize;
327 object_class->set_property = empathy_audio_src_set_property;
328 object_class->get_property = empathy_audio_src_get_property;
330 gstbin_class->handle_message =
331 GST_DEBUG_FUNCPTR (empathy_audio_src_handle_message);
333 param_spec = g_param_spec_double ("volume", "Volume", "volume contol",
335 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
336 g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
338 param_spec = g_param_spec_double ("peak-level", "peak level", "peak level",
339 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
340 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
341 g_object_class_install_property (object_class, PROP_PEAK_LEVEL, param_spec);
343 param_spec = g_param_spec_uint ("microphone", "microphone", "microphone",
344 0, G_MAXUINT, G_MAXUINT,
345 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
346 g_object_class_install_property (object_class, PROP_MICROPHONE, param_spec);
348 signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed",
349 G_TYPE_FROM_CLASS (empathy_audio_src_class),
353 g_cclosure_marshal_generic,
354 G_TYPE_NONE, 1, G_TYPE_DOUBLE);
356 param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level",
357 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
358 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
359 g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
361 signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
362 G_TYPE_FROM_CLASS (empathy_audio_src_class),
366 g_cclosure_marshal_generic,
367 G_TYPE_NONE, 1, G_TYPE_DOUBLE);
371 empathy_audio_src_dispose (GObject *object)
373 EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
374 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
376 if (priv->dispose_has_run)
379 priv->dispose_has_run = TRUE;
381 if (priv->idle_id != 0)
382 g_source_remove (priv->idle_id);
386 tp_clear_object (&priv->mic_monitor);
388 /* release any references held by the object here */
390 if (G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose)
391 G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose (object);
395 empathy_audio_src_finalize (GObject *object)
397 EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (object);
398 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
400 /* free any data held directly by the object here */
401 g_mutex_free (priv->lock);
403 G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
407 empathy_audio_src_levels_updated (gpointer user_data)
409 EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (user_data);
410 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
412 g_mutex_lock (priv->lock);
414 g_signal_emit (self, signals[PEAK_LEVEL_CHANGED], 0, priv->peak_level);
415 g_signal_emit (self, signals[RMS_LEVEL_CHANGED], 0, priv->rms_level);
418 g_mutex_unlock (priv->lock);
424 empathy_audio_src_handle_message (GstBin *bin, GstMessage *message)
426 EmpathyGstAudioSrc *self = EMPATHY_GST_AUDIO_SRC (bin);
427 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
429 if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT &&
430 GST_MESSAGE_SRC (message) == GST_OBJECT (priv->level))
432 const GstStructure *s;
436 gdouble peak = -G_MAXDOUBLE;
437 gdouble rms = -G_MAXDOUBLE;
439 s = gst_message_get_structure (message);
440 name = gst_structure_get_name (s);
442 if (g_strcmp0 ("level", name) != 0)
445 list = gst_structure_get_value (s, "peak");
446 len = gst_value_list_get_size (list);
448 for (i =0 ; i < len; i++)
453 value = gst_value_list_get_value (list, i);
454 db = g_value_get_double (value);
455 peak = MAX (db, peak);
458 list = gst_structure_get_value (s, "rms");
459 len = gst_value_list_get_size (list);
461 for (i =0 ; i < len; i++)
466 value = gst_value_list_get_value (list, i);
467 db = g_value_get_double (value);
471 g_mutex_lock (priv->lock);
473 priv->peak_level = peak;
474 priv->rms_level = rms;
475 if (priv->idle_id == 0)
476 priv->idle_id = g_idle_add (empathy_audio_src_levels_updated, self);
478 g_mutex_unlock (priv->lock);
482 GST_BIN_CLASS (empathy_audio_src_parent_class)->handle_message (bin,
487 empathy_audio_src_new (void)
489 static gboolean registered = FALSE;
492 if (!gst_element_register (NULL, "empathyaudiosrc",
493 GST_RANK_NONE, EMPATHY_TYPE_GST_AUDIO_SRC))
497 return gst_element_factory_make ("empathyaudiosrc", NULL);
501 empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume)
503 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
505 GParamSpecDouble *pspec_double;
507 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->volume),
510 g_assert (pspec != NULL);
512 pspec_double = G_PARAM_SPEC_DOUBLE (pspec);
514 volume = CLAMP (volume, pspec_double->minimum, pspec_double->maximum);
516 g_object_set (G_OBJECT (priv->volume), "volume", volume, NULL);
520 empathy_audio_src_get_volume (EmpathyGstAudioSrc *src)
522 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
525 g_object_get (G_OBJECT (priv->volume), "volume", &volume, NULL);
531 empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src)
533 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
535 return priv->source_idx;
539 empathy_audio_src_change_microphone_cb (GObject *source_object,
540 GAsyncResult *result,
543 EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
544 GSimpleAsyncResult *simple = user_data;
545 GError *error = NULL;
547 if (!empathy_mic_monitor_change_microphone_finish (monitor,
550 g_simple_async_result_take_error (simple, error);
553 g_simple_async_result_complete (simple);
554 g_object_unref (simple);
558 empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
560 GAsyncReadyCallback callback,
563 EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
564 guint source_output_idx;
565 GSimpleAsyncResult *simple;
567 simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
568 empathy_audio_src_change_microphone_async);
570 if (!empathy_audio_src_supports_changing_mic (src))
572 g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
573 "pulsesrc is not new enough to support changing microphone");
574 g_simple_async_result_complete_in_idle (simple);
575 g_object_unref (simple);
579 source_output_idx = empathy_audio_src_get_mic_index (src);
581 if (source_output_idx == PA_INVALID_INDEX)
583 g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
584 "pulsesrc is not yet PLAYING");
585 g_simple_async_result_complete_in_idle (simple);
586 g_object_unref (simple);
590 empathy_mic_monitor_change_microphone_async (priv->mic_monitor,
591 source_output_idx, microphone, empathy_audio_src_change_microphone_cb,
596 empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
597 GAsyncResult *result,
600 empathy_implement_finish_void (src,
601 empathy_audio_src_change_microphone_async);