2
* empathy-sound.c - Various sound related utility functions.
3
* Copyright (C) 2009 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.h"
24
#include <canberra-gtk.h>
25
#include <glib/gi18n-lib.h>
28
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
29
#include <libempathy/empathy-debug.h>
30
#include <libempathy/empathy-gsettings.h>
31
#include <libempathy/empathy-utils.h>
34
EmpathySound sound_id;
35
const char * event_ca_id;
36
const char * event_ca_description;
44
guint replay_timeout_id;
45
} EmpathyRepeatableSound;
47
/* NOTE: these entries MUST be in the same order than EmpathySound enum */
48
static EmpathySoundEntry sound_entries[LAST_EMPATHY_SOUND] = {
49
{ EMPATHY_SOUND_MESSAGE_INCOMING, "message-new-instant",
50
N_("Received an instant message"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE } ,
51
{ EMPATHY_SOUND_MESSAGE_OUTGOING, "message-sent-instant",
52
N_("Sent an instant message"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE } ,
53
{ EMPATHY_SOUND_CONVERSATION_NEW, "message-new-instant",
54
N_("Incoming chat request"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION },
55
{ EMPATHY_SOUND_CONTACT_CONNECTED, "service-login",
56
N_("Contact connected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN },
57
{ EMPATHY_SOUND_CONTACT_DISCONNECTED, "service-logout",
58
N_("Contact disconnected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT },
59
{ EMPATHY_SOUND_ACCOUNT_CONNECTED, "service-login",
60
N_("Connected to server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN },
61
{ EMPATHY_SOUND_ACCOUNT_DISCONNECTED, "service-logout",
62
N_("Disconnected from server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT },
63
{ EMPATHY_SOUND_PHONE_INCOMING, "phone-incoming-call",
64
N_("Incoming voice call"), NULL },
65
{ EMPATHY_SOUND_PHONE_OUTGOING, "phone-outgoing-calling",
66
N_("Outgoing voice call"), NULL },
67
{ EMPATHY_SOUND_PHONE_HANGUP, "phone-hangup",
68
N_("Voice call ended"), NULL },
71
/* An hash table containing currently repeating sounds. The format is the
73
* Key: An EmpathySound
74
* Value : The EmpathyRepeatableSound associated with that EmpathySound. */
75
static GHashTable *repeating_sounds;
78
empathy_sound_pref_is_enabled (EmpathySound sound_id)
80
EmpathySoundEntry *entry;
81
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_SOUNDS_SCHEMA);
84
entry = &(sound_entries[sound_id]);
85
g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
87
if (entry->key == NULL)
93
res = g_settings_get_boolean (gsettings, EMPATHY_PREFS_SOUNDS_ENABLED);
98
if (!empathy_check_available_state ())
100
if (g_settings_get_boolean (gsettings,
101
EMPATHY_PREFS_SOUNDS_DISABLED_AWAY))
108
res = g_settings_get_boolean (gsettings, entry->key);
111
g_object_unref (gsettings);
117
* empathy_sound_stop:
118
* @sound_id: The #EmpathySound to stop playing.
120
* Stop playing a sound. If it has been stated in loop with
121
* empathy_sound_start_playing(), it will also stop replaying.
124
empathy_sound_stop (EmpathySound sound_id)
126
EmpathySoundEntry *entry;
128
g_return_if_fail (sound_id < LAST_EMPATHY_SOUND);
130
entry = &(sound_entries[sound_id]);
131
g_return_if_fail (entry->sound_id == sound_id);
133
if (repeating_sounds != NULL)
135
EmpathyRepeatableSound *repeatable_sound;
137
repeatable_sound = g_hash_table_lookup (repeating_sounds,
138
GINT_TO_POINTER (sound_id));
139
if (repeatable_sound != NULL)
141
/* The sound must be stopped... If it is waiting for replay, remove
142
* it from hash table to cancel. Otherwise we'll cancel the sound
144
if (repeatable_sound->replay_timeout_id != 0)
146
g_hash_table_remove (repeating_sounds, GINT_TO_POINTER (sound_id));
152
ca_context_cancel (ca_gtk_context_get (), entry->sound_id);
156
empathy_sound_play_internal (GtkWidget *widget, EmpathySound sound_id,
157
ca_finish_callback_t callback, gpointer user_data)
159
EmpathySoundEntry *entry;
161
ca_proplist *p = NULL;
163
entry = &(sound_entries[sound_id]);
164
g_return_val_if_fail (entry->sound_id == sound_id, FALSE);
166
c = ca_gtk_context_get ();
167
ca_context_cancel (c, entry->sound_id);
169
DEBUG ("Play sound \"%s\" (%s)",
171
entry->event_ca_description);
173
if (ca_proplist_create (&p) < 0)
176
if (ca_proplist_sets (p, CA_PROP_EVENT_ID, entry->event_ca_id) < 0)
179
if (ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION,
180
gettext (entry->event_ca_description)) < 0)
183
if (ca_gtk_proplist_set_for_widget (p, widget) < 0)
186
ca_context_play_full (ca_gtk_context_get (), entry->sound_id, p, callback,
189
ca_proplist_destroy (p);
195
ca_proplist_destroy (p);
201
* empathy_sound_play_full:
202
* @widget: The #GtkWidget from which the sound is originating.
203
* @sound_id: The #EmpathySound to play.
204
* @callback: The #ca_finish_callback_t function that will be called when the
205
* sound has stopped playing.
206
* @user_data: user data to pass to the function.
210
* Returns %TRUE if the sound has successfully started playing, otherwise
211
* returning %FALSE and @callback won't be called.
213
* This function returns %FALSE if the sound is already playing in loop using
214
* %empathy_sound_start_playing.
216
* This function returns %FALSE if the sound is disabled in empathy preferences.
218
* Return value: %TRUE if the sound has successfully started playing, %FALSE
222
empathy_sound_play_full (GtkWidget *widget, EmpathySound sound_id,
223
ca_finish_callback_t callback, gpointer user_data)
225
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
226
g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
228
if (!empathy_sound_pref_is_enabled (sound_id))
231
/* The sound might already be playing repeatedly. If it's the case, we
232
* immediadely return since there's no need to make it play again */
233
if (repeating_sounds != NULL &&
234
g_hash_table_lookup (repeating_sounds, GINT_TO_POINTER (sound_id)) != NULL)
237
return empathy_sound_play_internal (widget, sound_id, callback, user_data);
241
* empathy_sound_play:
242
* @widget: The #GtkWidget from which the sound is originating.
243
* @sound_id: The #EmpathySound to play.
245
* Plays a sound. See %empathy_sound_play_full for details.'
247
* Return value: %TRUE if the sound has successfully started playing, %FALSE
251
empathy_sound_play (GtkWidget *widget, EmpathySound sound_id)
253
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
254
g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
256
return empathy_sound_play_full (widget, sound_id, NULL, NULL);
259
static void playing_finished_cb (ca_context *c, guint id, int error_code,
263
playing_timeout_cb (gpointer data)
265
EmpathyRepeatableSound *repeatable_sound = data;
268
repeatable_sound->replay_timeout_id = 0;
270
playing = empathy_sound_play_internal (repeatable_sound->widget,
271
repeatable_sound->sound_id, playing_finished_cb, data);
275
DEBUG ("Failed to replay sound, stop repeating");
276
g_hash_table_remove (repeating_sounds,
277
GINT_TO_POINTER (repeatable_sound->sound_id));
284
playing_finished_cb (ca_context *c, guint id, int error_code,
287
EmpathyRepeatableSound *repeatable_sound = user_data;
289
if (error_code != CA_SUCCESS)
291
DEBUG ("Error: %s", ca_strerror (error_code));
292
g_hash_table_remove (repeating_sounds,
293
GINT_TO_POINTER (repeatable_sound->sound_id));
297
repeatable_sound->replay_timeout_id = g_timeout_add (
298
repeatable_sound->play_interval, playing_timeout_cb, user_data);
302
empathy_sound_widget_destroyed_cb (GtkWidget *widget, gpointer user_data)
304
EmpathyRepeatableSound *repeatable_sound = user_data;
306
/* The sound must be stopped... If it is waiting for replay, remove
307
* it from hash table to cancel. Otherwise playing_finished_cb will be
308
* called with an error. */
309
if (repeatable_sound->replay_timeout_id != 0)
311
g_hash_table_remove (repeating_sounds,
312
GINT_TO_POINTER (repeatable_sound->sound_id));
317
repeating_sounds_item_delete (gpointer data)
319
EmpathyRepeatableSound *repeatable_sound = data;
321
if (repeatable_sound->replay_timeout_id != 0)
322
g_source_remove (repeatable_sound->replay_timeout_id);
324
g_signal_handlers_disconnect_by_func (repeatable_sound->widget,
325
empathy_sound_widget_destroyed_cb, repeatable_sound);
327
g_slice_free (EmpathyRepeatableSound, repeatable_sound);
331
* empathy_sound_start_playing:
332
* @widget: The #GtkWidget from which the sound is originating.
333
* @sound_id: The #EmpathySound to play.
334
* @timeout_before_replay: The amount of time, in milliseconds, between two
337
* Start playing a sound in loop. To stop the sound, call empathy_call_stop ()
338
* by passing it the same @sound_id. Note that if you start playing a sound
339
* multiple times, you'll have to call %empathy_sound_stop the same number of
342
* Return value: %TRUE if the sound has successfully started playing.
345
empathy_sound_start_playing (GtkWidget *widget, EmpathySound sound_id,
346
guint timeout_before_replay)
348
EmpathyRepeatableSound *repeatable_sound;
349
gboolean playing = FALSE;
351
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
352
g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE);
354
if (!empathy_sound_pref_is_enabled (sound_id))
357
if (repeating_sounds == NULL)
359
repeating_sounds = g_hash_table_new_full (g_direct_hash, g_direct_equal,
360
NULL, repeating_sounds_item_delete);
362
else if (g_hash_table_lookup (repeating_sounds,
363
GINT_TO_POINTER (sound_id)) != NULL)
365
/* The sound is already playing in loop. No need to continue. */
369
repeatable_sound = g_slice_new0 (EmpathyRepeatableSound);
370
repeatable_sound->widget = widget;
371
repeatable_sound->sound_id = sound_id;
372
repeatable_sound->play_interval = timeout_before_replay;
373
repeatable_sound->replay_timeout_id = 0;
375
g_hash_table_insert (repeating_sounds, GINT_TO_POINTER (sound_id),
378
g_signal_connect (G_OBJECT (widget), "destroy",
379
G_CALLBACK (empathy_sound_widget_destroyed_cb),
382
playing = empathy_sound_play_internal (widget, sound_id, playing_finished_cb,
386
g_hash_table_remove (repeating_sounds, GINT_TO_POINTER (sound_id));