2 * empathy-sound-manager.c - Various sound related utility functions.
3 * Copyright (C) 2009-2010 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "empathy-sound-manager.h"
24 #include <glib/gi18n-lib.h>
26 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
27 #include <libempathy/empathy-debug.h>
28 #include <libempathy/empathy-gsettings.h>
29 #include <libempathy/empathy-utils.h>
32 EmpathySound sound_id;
33 const char * event_ca_id;
34 const char * event_ca_description;
42 guint replay_timeout_id;
43 EmpathySoundManager *self;
44 } EmpathyRepeatableSound;
46 /* NOTE: these entries MUST be in the same order than EmpathySound enum */
47 static EmpathySoundEntry sound_entries[LAST_EMPATHY_SOUND] = {
48 { EMPATHY_SOUND_MESSAGE_INCOMING, "message-new-instant",
49 N_("Received an instant message"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE } ,
50 { EMPATHY_SOUND_MESSAGE_OUTGOING, "message-sent-instant",
51 N_("Sent an instant message"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE } ,
52 { EMPATHY_SOUND_CONVERSATION_NEW, "message-new-instant",
53 N_("Incoming chat request"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
54 { EMPATHY_SOUND_CONTACT_CONNECTED, "service-login",
55 N_("Contact connected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
56 { EMPATHY_SOUND_CONTACT_DISCONNECTED, "service-logout",
57 N_("Contact disconnected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
58 { EMPATHY_SOUND_ACCOUNT_CONNECTED, "service-login",
59 N_("Connected to server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
60 { EMPATHY_SOUND_ACCOUNT_DISCONNECTED, "service-logout",
61 N_("Disconnected from server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT },
62 { EMPATHY_SOUND_PHONE_INCOMING, "phone-incoming-call",
63 N_("Incoming voice call"), NULL },
64 { EMPATHY_SOUND_PHONE_OUTGOING, "phone-outgoing-calling",
65 N_("Outgoing voice call"), NULL },
66 { EMPATHY_SOUND_PHONE_HANGUP, "phone-hangup",
67 N_("Voice call ended"), NULL },
70 G_DEFINE_TYPE (EmpathySoundManager, empathy_sound_manager, G_TYPE_OBJECT)
72 struct _EmpathySoundManagerPrivate
74 /* A hash table containing currently repeating sounds. The format is the
76 * Key: An EmpathySound
77 * Value : The EmpathyRepeatableSound associated with that EmpathySound. */
78 GHashTable *repeating_sounds;
79 GSettings *gsettings_sound;
83 empathy_sound_manager_dispose (GObject *object)
85 EmpathySoundManager *self = (EmpathySoundManager *) object;
87 tp_clear_pointer (&self->priv->repeating_sounds, g_hash_table_unref);
88 tp_clear_object (&self->priv->gsettings_sound);
90 G_OBJECT_CLASS (empathy_sound_manager_parent_class)->dispose (object);
94 empathy_sound_manager_class_init (EmpathySoundManagerClass *cls)
96 GObjectClass *object_class = G_OBJECT_CLASS (cls);
98 object_class->dispose = empathy_sound_manager_dispose;
100 g_type_class_add_private (cls, sizeof (EmpathySoundManagerPrivate));
104 empathy_sound_widget_destroyed_cb (GtkWidget *widget,
107 EmpathyRepeatableSound *repeatable_sound = user_data;
109 /* The sound must be stopped... If it is waiting for replay, remove
110 * it from hash table to cancel. Otherwise playing_finished_cb will be
111 * called with an error. */
112 if (repeatable_sound->replay_timeout_id != 0)
114 g_hash_table_remove (repeatable_sound->self->priv->repeating_sounds,
115 GINT_TO_POINTER (repeatable_sound->sound_id));
120 repeating_sounds_item_delete (gpointer data)
122 EmpathyRepeatableSound *repeatable_sound = data;
124 if (repeatable_sound->replay_timeout_id != 0)
125 g_source_remove (repeatable_sound->replay_timeout_id);
127 if (repeatable_sound->widget != NULL)
129 g_signal_handlers_disconnect_by_func (repeatable_sound->widget,
130 empathy_sound_widget_destroyed_cb, repeatable_sound);
133 g_object_unref (repeatable_sound->self);
135 g_slice_free (EmpathyRepeatableSound, repeatable_sound);
139 empathy_sound_manager_init (EmpathySoundManager *self)
141 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
142 EMPATHY_TYPE_SOUND_MANAGER, EmpathySoundManagerPrivate);
144 self->priv->repeating_sounds = g_hash_table_new_full (NULL, NULL,
145 NULL, repeating_sounds_item_delete);
147 self->priv->gsettings_sound = g_settings_new (EMPATHY_PREFS_SOUNDS_SCHEMA);
150 EmpathySoundManager *
151 empathy_sound_manager_dup_singleton (void)
153 static EmpathySoundManager *manager = NULL;
155 if (G_LIKELY (manager != NULL))
156 return g_object_ref (manager);
158 manager = g_object_new (EMPATHY_TYPE_SOUND_MANAGER, NULL);
160 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer *) &manager);
165 empathy_sound_pref_is_enabled (EmpathySoundManager *self,
166 EmpathySound sound_id)
168 EmpathySoundEntry *entry;
170 entry = &(sound_entries[sound_id]);
171 g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
173 if (entry->key == NULL)
176 if (! g_settings_get_boolean (self->priv->gsettings_sound,
177 EMPATHY_PREFS_SOUNDS_ENABLED))
180 if (!empathy_check_available_state ())
182 if (g_settings_get_boolean (self->priv->gsettings_sound,
183 EMPATHY_PREFS_SOUNDS_DISABLED_AWAY))
187 return g_settings_get_boolean (self->priv->gsettings_sound, entry->key);
191 * empathy_sound_manager_stop:
192 * @self: a #EmpathySoundManager
193 * @sound_id: The #EmpathySound to stop playing.
195 * Stop playing a sound. If it has been stated in loop with
196 * empathy_sound_start_playing(), it will also stop replaying.
199 empathy_sound_manager_stop (EmpathySoundManager *self,
200 EmpathySound sound_id)
202 EmpathySoundEntry *entry;
203 EmpathyRepeatableSound *repeatable_sound;
205 g_return_if_fail (sound_id < LAST_EMPATHY_SOUND);
207 entry = &(sound_entries[sound_id]);
208 g_return_if_fail (entry->sound_id == sound_id);
210 repeatable_sound = g_hash_table_lookup (self->priv->repeating_sounds,
211 GINT_TO_POINTER (sound_id));
212 if (repeatable_sound != NULL)
214 /* The sound must be stopped... If it is waiting for replay, remove
215 * it from hash table to cancel. Otherwise we'll cancel the sound
217 if (repeatable_sound->replay_timeout_id != 0)
219 g_hash_table_remove (self->priv->repeating_sounds,
220 GINT_TO_POINTER (sound_id));
225 ca_context_cancel (ca_gtk_context_get (), entry->sound_id);
229 empathy_sound_play_internal (GtkWidget *widget, EmpathySound sound_id,
230 ca_finish_callback_t callback, gpointer user_data)
232 EmpathySoundEntry *entry;
234 ca_proplist *p = NULL;
236 entry = &(sound_entries[sound_id]);
237 g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
239 c = ca_gtk_context_get ();
240 ca_context_cancel (c, entry->sound_id);
242 DEBUG ("Play sound \"%s\" (%s)",
244 entry->event_ca_description);
246 if (ca_proplist_create (&p) < 0)
249 if (ca_proplist_sets (p, CA_PROP_EVENT_ID, entry->event_ca_id) < 0)
252 if (ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION,
253 gettext (entry->event_ca_description)) < 0)
258 if (ca_gtk_proplist_set_for_widget (p, widget) < 0)
262 ca_context_play_full (ca_gtk_context_get (), entry->sound_id, p, callback,
265 ca_proplist_destroy (p);
271 ca_proplist_destroy (p);
277 * empathy_sound_manager_play_full:
278 * @self: a #EmpathySoundManager
279 * @widget: The #GtkWidget from which the sound is originating.
280 * @sound_id: The #EmpathySound to play.
281 * @callback: The #ca_finish_callback_t function that will be called when the
282 * sound has stopped playing.
283 * @user_data: user data to pass to the function.
287 * Returns %TRUE if the sound has successfully started playing, otherwise
288 * returning %FALSE and @callback won't be called.
290 * This function returns %FALSE if the sound is already playing in loop using
291 * %empathy_sound_start_playing.
293 * This function returns %FALSE if the sound is disabled in empathy preferences.
295 * Return value: %TRUE if the sound has successfully started playing, %FALSE
299 empathy_sound_manager_play_full (EmpathySoundManager *self,
301 EmpathySound sound_id,
302 ca_finish_callback_t callback,
305 g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), FALSE);
306 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
308 if (!empathy_sound_pref_is_enabled (self, sound_id))
311 /* The sound might already be playing repeatedly. If it's the case, we
312 * immediadely return since there's no need to make it play again */
313 if (g_hash_table_lookup (self->priv->repeating_sounds,
314 GINT_TO_POINTER (sound_id)) != NULL)
317 return empathy_sound_play_internal (widget, sound_id, callback, user_data);
321 * empathy_sound_manager_play:
322 * @self: a #EmpathySoundManager
323 * @widget: The #GtkWidget from which the sound is originating.
324 * @sound_id: The #EmpathySound to play.
326 * Plays a sound. See %empathy_sound_manager_play_full for details.'
328 * Return value: %TRUE if the sound has successfully started playing, %FALSE
332 empathy_sound_manager_play (EmpathySoundManager *self,
334 EmpathySound sound_id)
336 g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), FALSE);
337 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
339 return empathy_sound_manager_play_full (self, widget, sound_id, NULL, NULL);
342 static void playing_finished_cb (ca_context *c, guint id, int error_code,
346 playing_timeout_cb (gpointer data)
348 EmpathyRepeatableSound *repeatable_sound = data;
351 repeatable_sound->replay_timeout_id = 0;
353 playing = empathy_sound_play_internal (repeatable_sound->widget,
354 repeatable_sound->sound_id, playing_finished_cb, data);
358 DEBUG ("Failed to replay sound, stop repeating");
359 g_hash_table_remove (repeatable_sound->self->priv->repeating_sounds,
360 GINT_TO_POINTER (repeatable_sound->sound_id));
367 playing_finished_cb (ca_context *c, guint id, int error_code,
370 EmpathyRepeatableSound *repeatable_sound = user_data;
372 if (error_code != CA_SUCCESS)
374 DEBUG ("Error: %s", ca_strerror (error_code));
375 g_hash_table_remove (repeatable_sound->self->priv->repeating_sounds,
376 GINT_TO_POINTER (repeatable_sound->sound_id));
380 repeatable_sound->replay_timeout_id = g_timeout_add (
381 repeatable_sound->play_interval, playing_timeout_cb, user_data);
385 * empathy_sound_manager_start_playing:
386 * @self: a #EmpathySoundManager
387 * @widget: The #GtkWidget from which the sound is originating.
388 * @sound_id: The #EmpathySound to play.
389 * @timeout_before_replay: The amount of time, in milliseconds, between two
392 * Start playing a sound in loop. To stop the sound, call empathy_call_stop ()
393 * by passing it the same @sound_id. Note that if you start playing a sound
394 * multiple times, you'll have to call %empathy_sound_stop the same number of
397 * Return value: %TRUE if the sound has successfully started playing.
400 empathy_sound_manager_start_playing (EmpathySoundManager *self,
402 EmpathySound sound_id,
403 guint timeout_before_replay)
405 EmpathyRepeatableSound *repeatable_sound;
406 gboolean playing = FALSE;
408 g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), FALSE);
409 g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
411 if (!empathy_sound_pref_is_enabled (self, sound_id))
414 if (g_hash_table_lookup (self->priv->repeating_sounds,
415 GINT_TO_POINTER (sound_id)) != NULL)
417 /* The sound is already playing in loop. No need to continue. */
421 repeatable_sound = g_slice_new0 (EmpathyRepeatableSound);
422 repeatable_sound->widget = widget;
423 repeatable_sound->sound_id = sound_id;
424 repeatable_sound->play_interval = timeout_before_replay;
425 repeatable_sound->replay_timeout_id = 0;
426 repeatable_sound->self = g_object_ref (self);
428 g_hash_table_insert (self->priv->repeating_sounds, GINT_TO_POINTER (sound_id),
433 g_signal_connect (G_OBJECT (widget), "destroy",
434 G_CALLBACK (empathy_sound_widget_destroyed_cb),
438 playing = empathy_sound_play_internal (widget, sound_id, playing_finished_cb,
442 g_hash_table_remove (self->priv->repeating_sounds,
443 GINT_TO_POINTER (sound_id));