comparison src/sound.c @ 5684:b61520e71679

[gaim-migrate @ 6104] sound is now really core/ui split. committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Tue, 03 Jun 2003 03:33:20 +0000
parents 9cd94a5bec8e
children 059d95c67cda
comparison
equal deleted inserted replaced
5683:9befba33f7c8 5684:b61520e71679
1 /* 1 /*
2 * gaim 2 * gaim
3 * 3 *
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> 4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
6 * 5 *
7 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or 8 * the Free Software Foundation; either version 2 of the License, or
21 */ 20 */
22 21
23 #ifdef HAVE_CONFIG_H 22 #ifdef HAVE_CONFIG_H
24 #include <config.h> 23 #include <config.h>
25 #endif 24 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 25
30 #ifndef _WIN32
31 #include <unistd.h>
32 #else
33 #include <windows.h>
34 #include <mmsystem.h>
35 #endif
36
37 #ifdef HAVE_ENDIAN_H
38 #include <endian.h>
39 #endif
40
41 #ifdef USE_AO
42 #include <ao/ao.h>
43 #include <audiofile.h>
44 #endif /* USE_AO */
45 #ifdef USE_NAS_AUDIO
46 #include <audio/audiolib.h>
47 #include <audio/soundlib.h>
48 #endif /* USE_NAS_AUDIO */
49
50 #include "gaim.h"
51 #include "sound.h" 26 #include "sound.h"
52 #include "notify.h"
53 #include "prefs.h" 27 #include "prefs.h"
28 #include "gaim.h" /* XXX: this goes away when away messages become sane */
54 29
55 #ifdef _WIN32 30 #ifdef _WIN32
56 #include "win32dep.h" 31 #include "win32dep.h"
57 #endif 32 #endif
58 33
59 struct gaim_sound_event { 34 static GaimSoundUiOps *sound_ui_ops = NULL;
60 char *label;
61 char *pref;
62 char *def;
63 };
64 35
65 #ifdef USE_AO 36 void gaim_set_sound_ui_ops(GaimSoundUiOps *ops)
66 static gboolean ao_initialized=FALSE;
67 static int ao_driver = -1;
68 #endif /* USE_AO */
69
70
71 static gboolean mute_login_sounds = FALSE;
72 static gboolean mute_sounds = FALSE;
73 static char *sound_cmd = NULL;
74
75 /* description, option bit, default sound file *
76 * set the option bit to 0 to have it not display in prefs *
77 * the order here has to match the defines in gaim.h. *
78 * -Robot101 */
79 static struct gaim_sound_event sounds[GAIM_NUM_SOUNDS] = {
80 {N_("Buddy logs in"), "/gaim/gtk/sound/login", "arrive.wav"},
81 {N_("Buddy logs out"), "/gaim/gtk/sound/logout", "leave.wav"},
82 {N_("Message received"), "/gaim/gtk/sound/im_recv", "receive.wav"},
83 {N_("Message received begins conversation"), "/gaim/gtk/sound/first_im_recv", "receive.wav"},
84 {N_("Message sent"), "/gaim/gtk/sound/send_im", "send.wav"},
85 {N_("Person enters chat"), "/gaim/gtk/sound/join_chat", "arrive.wav"},
86 {N_("Person leaves chat"), "/gaim/gtk/sound/left_chat", "leave.wav"},
87 {N_("You talk in chat"), "/gaim/gtk/sound/send_chat_msg", "send.wav"},
88 {N_("Others talk in chat"), "/gaim/gtk/sound/chat_msg_recv", "receive.wav"},
89 /* this isn't a terminator, it's the buddy pounce default sound event ;-) */
90 {NULL, 0, "redalert.wav"},
91 {N_("Someone says your name in chat"), "/gaim/gtk/sound/nick_said", "redalert.wav"}
92 };
93
94 static char *sound_file[GAIM_NUM_SOUNDS];
95
96
97 #ifdef USE_AO
98 static void check_ao_init()
99 { 37 {
100 if(!ao_initialized) { 38 if(sound_ui_ops && sound_ui_ops->shutdown)
101 gaim_debug(GAIM_DEBUG_INFO, "sound", 39 sound_ui_ops->shutdown();
102 "Initializing sound output drivers.\n"); 40 sound_ui_ops = ops;
103 ao_initialize(); 41 if(sound_ui_ops && sound_ui_ops->init)
104 ao_initialized = TRUE; 42 sound_ui_ops->init();
105 }
106 }
107 #endif /* USE_AO */
108
109 void gaim_sound_change_output_method() {
110 #ifdef USE_AO
111 ao_driver = -1;
112
113 if (gaim_prefs_get_bool("/core/sound/use_esd") ||
114 gaim_prefs_get_bool("/core/sound/use_arts") ||
115 gaim_prefs_get_bool("/core/sound/use_sys_default")) {
116
117 check_ao_init();
118
119 if (ao_driver == -1 && gaim_prefs_get_bool("/core/sound/use_esd"))
120 ao_driver = ao_driver_id("esd");
121
122 if (ao_driver == -1 && gaim_prefs_get_bool("/core/sound/use_arts"))
123 ao_driver = ao_driver_id("arts");
124
125 if (ao_driver == -1)
126 ao_driver = ao_default_driver_id();
127 }
128
129 if(ao_driver != -1) {
130 ao_info *info = ao_driver_info(ao_driver);
131 gaim_debug(GAIM_DEBUG_INFO, "sound",
132 "Sound output driver loaded: %s\n", info->name);
133 }
134 #endif /* USE_AO */
135 #ifdef USE_NAS
136 if (gaim_prefs_get_bool("/core/sound/use_nas"))
137 gaim_debug(GAIM_DEBUG_INFO, "sound",
138 "Sound output driver loaded: NAS output\n");
139 #endif /* USE_NAS */
140 } 43 }
141 44
142 void gaim_sound_quit() 45 GaimSoundUiOps *gaim_get_sound_ui_ops(void)
143 { 46 {
144 #ifdef USE_AO 47 return sound_ui_ops;
145 if(ao_initialized)
146 ao_shutdown();
147 #endif
148 } 48 }
149 49
150 50 void gaim_sound_init()
151 #ifdef USE_NAS_AUDIO
152 static gboolean play_file_nas(const char *filename)
153 { 51 {
154 AuServer *nas_serv; 52 gaim_prefs_add_none("/core/sound");
155 gboolean ret = FALSE; 53 gaim_prefs_add_bool("/core/sound/while_away", FALSE);
156
157 if((nas_serv = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL))) {
158 ret = AuSoundPlaySynchronousFromFile(nas_serv, filename, 100);
159 AuCloseServer(nas_serv);
160 }
161
162 return ret;
163 } 54 }
164 55
165 #endif /* USE_NAS_AUDIO */ 56 void gaim_sound_shutdown()
57 {
58 if(sound_ui_ops && sound_ui_ops->shutdown)
59 sound_ui_ops->shutdown();
60 }
166 61
167 void gaim_sound_play_file(char *filename) 62 void gaim_sound_play_file(const char *filename)
168 { 63 {
169 #if defined(USE_NAS_AUDIO) || defined(USE_AO) 64 if(awaymessage && !gaim_prefs_get_bool("/core/sound/while_away"))
170 pid_t pid;
171 #ifdef USE_AO
172 AFfilehandle file;
173 #endif
174 #endif
175
176 if (mute_sounds)
177 return; 65 return;
178 66
179 if (awaymessage && !gaim_prefs_get_bool("/core/sound/while_away")) 67 if(sound_ui_ops && sound_ui_ops->play_file)
180 return; /* check here in case a buddy pounce plays a file while away */ 68 sound_ui_ops->play_file(filename);
181
182 if (gaim_prefs_get_bool("/core/sound/use_beep")) {
183 gdk_beep();
184 return;
185 }
186
187 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
188 char *tmp = g_strdup_printf(_("Unable to play sound because the chosen file (%s) does not exist."), filename);
189 gaim_notify_error(NULL, NULL, tmp, NULL);
190 g_free(tmp);
191 return;
192 }
193
194 #ifndef _WIN32
195 if (gaim_prefs_get_bool("/core/sound/use_custom")) {
196 const char *sound_cmd;
197 char *command;
198 GError *error = NULL;
199
200 sound_cmd = gaim_prefs_get_string("/core/sound/command");
201
202 if (!sound_cmd || *sound_cmd == '\0') {
203 gaim_notify_error(NULL, NULL,
204 _("Unable to play sound because the "
205 "'Command' sound method has been chosen, "
206 "but no command has been set."), NULL);
207 return;
208 }
209
210 command = g_strdup_printf(sound_cmd, filename);
211
212 if(!g_spawn_command_line_async(command, &error)) {
213 char *tmp = g_strdup_printf(_("Unable to play sound because the configured sound command could not be launched: %s"), error->message);
214 gaim_notify_error(NULL, NULL, tmp, NULL);
215 g_free(tmp);
216 g_error_free(error);
217 }
218
219 g_free(command);
220 return;
221 }
222 #if defined(USE_NAS_AUDIO) || defined(USE_AO)
223 pid = fork();
224 if (pid < 0)
225 return;
226 else if (pid == 0) {
227 #ifdef USE_NAS_AUDIO
228 if (gaim_prefs_get_bool("/core/sound/use_nas")) {
229 if (play_file_nas(filename))
230 _exit(0);
231 }
232 #endif /* USE_NAS_AUDIO */
233
234 #ifdef USE_AO
235 file = afOpenFile(filename, "rb", NULL);
236 if(file) {
237 ao_device *device;
238 ao_sample_format format;
239 int in_fmt;
240 int bytes_per_frame;
241
242 format.rate = afGetRate(file, AF_DEFAULT_TRACK);
243 format.channels = afGetChannels(file, AF_DEFAULT_TRACK);
244 afGetSampleFormat(file, AF_DEFAULT_TRACK, &in_fmt,
245 &format.bits);
246
247 /* XXX: libao doesn't seem to like 8-bit sounds, so we'll
248 * let libaudiofile make them a bit better for us */
249 if(format.bits == 8)
250 format.bits = 16;
251
252 afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK,
253 AF_SAMPFMT_TWOSCOMP, format.bits);
254
255 #if __BYTE_ORDER == __BIG_ENDIAN
256 format.byte_format = AO_FMT_BIG;
257 afSetVirtualByteOrder(file, AF_DEFAULT_TRACK,
258 AF_BYTEORDER_BIGENDIAN);
259 #elif __BYTE_ORDER == __LITTLE_ENDIAN
260 format.byte_format = AO_FMT_LITTLE;
261 afSetVirtualByteOrder(file, AF_DEFAULT_TRACK,
262 AF_BYTEORDER_LITTLEENDIAN);
263 #endif
264
265 bytes_per_frame = format.bits * format.channels / 8;
266
267 device = ao_open_live(ao_driver, &format, NULL);
268
269 if(device) {
270 int frames_read;
271 char buf[4096];
272 int buf_frames = sizeof(buf) / bytes_per_frame;
273
274 while((frames_read = afReadFrames(file, AF_DEFAULT_TRACK,
275 buf, buf_frames))) {
276 if(!ao_play(device, buf, frames_read * bytes_per_frame))
277 break;
278 }
279 ao_close(device);
280 }
281 afCloseFile(file);
282 }
283 ao_shutdown();
284 #endif /* USE_AO */
285 _exit(0);
286 }
287 #else /* USE_NAS_AUDIO || USE_AO */
288 gdk_beep();
289 return;
290 #endif /* USE_NAS_AUDIO || USE_AO */
291 #else /* _WIN32 */
292 gaim_debug(GAIM_DEBUG_INFO, "sound", "Playing %s\n", filename);
293
294 if (!PlaySound(filename, 0, SND_ASYNC | SND_FILENAME))
295 gaim_debug(GAIM_DEBUG_ERROR, "sound", "Error playing sound.\n");
296 #endif /* _WIN32 */
297 } 69 }
298 70
299 void gaim_sound_play_event(GaimSoundEventID event) 71 void gaim_sound_play_event(GaimSoundEventID event)
300 { 72 {
301 if ((event == GAIM_SOUND_BUDDY_ARRIVE) && mute_login_sounds) 73 if(awaymessage && !gaim_prefs_get_bool("/core/sound/while_away"))
302 return; 74 return;
303 75
304 if (event >= GAIM_NUM_SOUNDS) { 76 if(sound_ui_ops && sound_ui_ops->play_event)
305 gaim_debug(GAIM_DEBUG_MISC, "sound", 77 sound_ui_ops->play_event(event);
306 "got request for unknown sound: %d\n", event);
307 return;
308 }
309
310 /* check NULL for sounds that don't have an option, ie buddy pounce */
311 if (sounds[event].pref == NULL || gaim_prefs_get_bool(sounds[event].pref)) {
312 if (sound_file[event]) {
313 gaim_sound_play_file(sound_file[event]);
314 } else {
315 gchar *filename = NULL;
316
317 filename = g_build_filename(DATADIR, "sounds", "gaim", sounds[event].def, NULL);
318 gaim_sound_play_file(filename);
319 g_free(filename);
320 }
321 }
322 } 78 }
323
324 void gaim_sound_set_mute(gboolean mute)
325 {
326 mute_sounds = mute;
327 }
328
329 gboolean gaim_sound_get_mute()
330 {
331 return mute_sounds;
332 }
333
334 void gaim_sound_set_login_mute(gboolean mute)
335 {
336 mute_login_sounds = mute;
337 }
338
339 void gaim_sound_set_event_file(GaimSoundEventID event, const char *filename)
340 {
341 if(event >= GAIM_NUM_SOUNDS)
342 return;
343
344 if(sound_file[event])
345 g_free(sound_file[event]);
346
347 sound_file[event] = g_strdup(filename);
348 }
349
350
351 char *gaim_sound_get_event_file(GaimSoundEventID event)
352 {
353 if(event >= GAIM_NUM_SOUNDS)
354 return NULL;
355
356 return sound_file[event];
357 }
358
359 const char *gaim_sound_get_event_option(GaimSoundEventID event)
360 {
361 if(event >= GAIM_NUM_SOUNDS)
362 return 0;
363
364 return sounds[event].pref;
365 }
366
367 char *gaim_sound_get_event_label(GaimSoundEventID event)
368 {
369 if(event >= GAIM_NUM_SOUNDS)
370 return NULL;
371
372 return sounds[event].label;
373 }
374
375
376 void gaim_sound_set_command(const char *cmd)
377 {
378 if(sound_cmd)
379 g_free(sound_cmd);
380 if(strlen(cmd) > 0)
381 sound_cmd = g_strdup(cmd);
382 else
383 sound_cmd = NULL;
384 }
385
386 char *gaim_sound_get_command()
387 {
388 return sound_cmd;
389 }
390