1 /*                             -*- Mode: C++-C -*-
2  *
3  *		 Copyright 1994 Christopher B. Liebman
4  *
5  *     Permission to use, copy, modify, distribute, and sell this software
6  *     and its documentation for any purpose is hereby granted without fee,
7  *     provided that the above copyright notice appear in all copies and that
8  *     both that copyright notice and this permission notice appear in
9  *     supporting documentation, and that the name Christopher B. Liebman not
10  *     be used in advertising or publicity pertaining to distribution of this
11  *     software without specific, written prior permission.
12  *
13  *    THIS SOFTWARE IS PROVIDED `AS-IS'.  CHRISTOPHER B. LIEBMAN, DISCLAIMS
14  *    ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
15  *    LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16  *    PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL CHRISTOPHER
17  *    B. LIEBMAN, BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING SPECIAL,
18  *    INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, OR
19  *    PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
20  *    WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF
21  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author          : Chris Liebman
24  * Created On      : Tue Jan 11 14:11:30 1994
25  * Last Modified By: Chris Liebman
26  * Last Modified On: Sat Feb 12 23:11:34 1994
27  * Update Count    : 40
28  * Status          : Released
29  *
30  * HISTORY
31  * 31-Jan-1994		Chris Liebman
32  *    Last Modified: Sat Jan 29 23:56:11 1994 #35 (Chris Liebman)
33  *    Major changes for new search stuff.
34  *
35  * 24-Jan-1994		Chris Liebman
36  *    Last Modified: Sun Jan 23 16:03:49 1994 #19 (Chris Liebman)
37  *    Added path support and moved all sound checking here.
38  *
39  * 14-Jan-1994		Chris Liebman
40  *    Last Modified: Wed Jan 12 11:58:51 1994 #4 (Chris Liebman)
41  *    added #if to use XtDatabase() for versions of Xt less than 5
42  *
43  *
44  * PURPOSE
45  * 	Sound support using NetAudio.
46 */
47 
48 #ifndef lint
49 static char *RCSid = "$Id: face_sound.c,v 1.17 1994/03/07 20:27:46 liebman Exp $";
50 #endif
51 
52 #include "faces.h"
53 #include <audio/audiolib.h>
54 #include <audio/Xtutil.h>
55 #include <audio/soundlib.h>
56 #include "face_sound.h"
57 #include "face_search.h"
58 #include <sys/stat.h>
59 
60 static AuServer*	audio = NULL;
61 
62 /*
63  *  Here is the play list.
64 */
65 
66 typedef struct play_list
67 {
68     FaceSound*	sound;
69     struct play_list* next;
70 } PlayList;
71 
72 static PlayList* PlayListHead;
73 static PlayList* PlayListTail;
74 
75 /*
76  *   Here we store a complete list of sounds.
77 */
78 
79 static FaceSound	*TheSounds = NULL;
80 
81 /*
82  *    Create face sound data for face.
83 */
84 
85 FaceSound *
FaceSoundCreate(file)86 FaceSoundCreate(file)
87 char	*file;
88 {
89     struct stat buf;
90     FaceSound	*fs;
91 
92     /*
93      *    First see if we already have this sound.
94     */
95 
96     for (fs = TheSounds; fs != NULL; fs = fs->next)
97     {
98 	if (strcmp(fs->file, file) == 0)
99 	{
100 	    /*
101 	     * Yep!
102 	    */
103 
104 	    fs->refs += 1;
105 
106 	    return(fs);
107 	}
108     }
109 
110     if (!audio)
111     {
112 	return NULL;
113     }
114 
115     /*
116      * Fail if file is a directory or does not exist.
117     */
118 
119     if ((stat(file, &buf) == -1) || (buf.st_mode & S_IFDIR))
120     {
121 	return NULL;
122     }
123 
124     /*
125      *   Ok, create a face sound struct.
126     */
127 
128     fs = (FaceSound *)XtCalloc(1, sizeof(FaceSound));
129     fs->file  = XtNewString(file);
130 #ifdef USE_BUCKETS
131     fs->bucket = AuSoundCreateBucketFromFile(audio, file, (AuAccessImportMask |
132 							   AuAccessExportMask |
133 							   AuAccessListMask),
134 					     NULL, NULL);
135 
136     if (fs->bucket == AuNone)
137     {
138 	XtFree((char *)fs);
139 	return NULL;
140     }
141 #else
142     fs->sound = SoundOpenFileForReading(file);
143     if (!fs->sound)
144     {
145 	XtFree((void*)fs);
146 	return NULL;
147     }
148 #endif /* USE_BUCKETS */
149 
150     fs->refs  = 1;
151 
152     /*
153      *  Put the new sound on the list.
154     */
155 
156     fs->next = TheSounds;
157     fs->prev = NULL;
158 
159     if (fs->next != NULL)
160     {
161 	fs->next->prev = fs;
162     }
163 
164     TheSounds = fs;
165 
166     /*
167      * and return it.
168     */
169 
170     return(fs);
171 }
172 
173 /*
174  *    Free an sound.
175 */
176 
177 void
FaceSoundFree(fs)178 FaceSoundFree(fs)
179 FaceSound	*fs;
180 {
181     if (!fs)
182     {
183 	return;
184     }
185 
186     /*
187      *   First remove one reference.  If there are still more refs just
188      * return.
189     */
190 
191     fs->refs -= 1;
192     if (fs->refs != 0)
193     {
194 	return;
195     }
196 
197     /*
198      *   Destroy the attacked sound.
199     */
200 
201     if (audio)
202     {
203 #ifdef USE_BUCKETS
204 	AuDestroyBucket(audio, fs->bucket, NULL);
205 #else
206 	SoundCloseFile(fs->sound);
207 #endif /* USE_BUCKETS */
208     }
209 
210     /*
211      * The previous sound is now previous to the next sound.
212     */
213 
214     if (fs->next != NULL)
215     {
216 	fs->next->prev = fs->prev;
217     }
218 
219     /*
220      * The next face is now next from the previous face.
221     */
222 
223     if (fs->prev != NULL)
224     {
225 	fs->prev->next = fs->next;
226     }
227 
228     /*
229      * If this was the first sound then the next sound is
230      * first.
231     */
232 
233     if (fs == TheSounds)
234     {
235 	TheSounds = fs->next;
236     }
237 
238     /*
239      *    Ok, free the name.
240     */
241 
242     XtFree(fs->file);
243 
244     /*
245      *    Free the struct.
246     */
247 
248     XtFree((void *)fs);
249 }
250 
251 static int
FaceSoundLoadWork(file,path,item)252 FaceSoundLoadWork(file, path, item)
253 char*		file;
254 char*		path;
255 MailItem*	item;
256 {
257     FaceSound*	fs;
258     char**	p;
259     int		length;
260     static char *filename = NULL;
261     static int  filename_length;
262     static char *extensions[] =
263     {
264 	".au", ".snd", ".voc", ".wav", ".wave", 0
265     };
266 
267     /*
268      * First try the file as given.
269     */
270 
271     if ((fs = FaceSoundCreate(file)) != NULL)
272     {
273 	item->sound = fs;
274 	return 1;
275     }
276 
277     /*
278      * Make a buffer to try various names in.
279     */
280 
281     length = strlen(file) + 20;
282 
283     if (filename_length < length)
284     {
285 	filename_length = length;
286 
287 	if (filename)
288 	{
289 	    XtFree(filename);
290 	}
291 
292 	filename = XtMalloc(filename_length);
293     }
294 
295     for(p = extensions; *p; ++p)
296     {
297 	sprintf(filename, "%s%s", file, *p);
298 	if ((fs = FaceSoundCreate(filename)) != NULL)
299 	{
300 	    item->sound = fs;
301 	    return 1;
302 	}
303     }
304 
305     return 0;
306 }
307 
308 int
FaceSoundLoad(file,item,data)309 FaceSoundLoad(file, item, data)
310 char*		file;
311 MailItem*	item;
312 FaceSearchData*	data;
313 {
314     int			found = 0;
315     char**		paths = TheFacesResources.sound_paths;
316 
317     if (data != NULL)
318     {
319 	/*
320 	 * Fixup paths.
321 	*/
322 
323 	if (data->paths != NULL)
324 	{
325 	    paths = data->paths;
326 	}
327     }
328 
329     /*
330      *   enumerate thru paths if needed.
331      */
332 
333     if ((*file != '/') &&
334 	strncmp(file, "./", 2) &&
335 	strncmp(file, "../", 3))
336     {
337 	found = PathEnumerate(file, paths, FaceSoundLoadWork, item);
338     }
339     else
340     {
341 	found = FaceSoundLoadWork(file, ".", item);
342     }
343 
344     return found;
345 }
346 
347 /*
348  * Shamlessly coppied from net audio and modified to play from bucket
349  * instead of file.
350 */
351 
352 #define	VOL(volume)		((1 << 16) * (volume) / 100)
353 
354 /* ARGSUSED */
355 static void
play_cb(aud,handler,ev,data)356 play_cb(aud, handler, ev, data)
357 AuServer       *aud;
358 AuEventHandlerRec *handler;
359 AuEvent        *ev;
360 AuPointer       data;
361 {
362     AuStatus	ret;
363     PlayList	*this;
364 
365     /*
366      * Ok, one item finished, remove it from the list and start the next.
367     */
368 
369 
370     this = PlayListHead;
371     PlayListHead = this->next;
372     XtFree((char *)this);
373 
374     /*
375      *   If there is another sound to play then start it, else that was the
376      * last sound so NULL the tail pointer.
377     */
378 
379     if (PlayListHead != NULL)
380     {
381 #ifdef USE_BUCKETS
382 	AuSoundPlayFromBucket(audio, PlayListHead->sound->bucket, AuNone,
383 			    VOL(TheFacesResources.volume),
384 			    play_cb, NULL, 1,
385 			    NULL, NULL, NULL,
386 			    &ret);
387 #else
388 	AuSoundPlayFromFile(audio, PlayListHead->sound->file, AuNone,
389 			    VOL(TheFacesResources.volume),
390 			    play_cb, NULL,
391 			    NULL, NULL, NULL,
392 			    &ret);
393 #endif
394     }
395     else
396     {
397 	PlayListTail = NULL;
398     }
399 }
400 
401 /*
402  *  Play a sound.
403 */
404 
405 void
FaceSoundPlay(fs)406 FaceSoundPlay(fs)
407 FaceSound	*fs;
408 {
409     PlayList *this;
410     AuStatus ret;
411 
412     /*
413      *  No audio device!
414     */
415 
416     if (!audio)
417     {
418 	return;
419     }
420 
421     /*
422      *  No sound!
423     */
424 
425     if (!fs)
426     {
427 	return;
428     }
429 
430     /*
431      *  make a play list for this guy.
432     */
433 
434     this = (PlayList*)XtMalloc(sizeof(PlayList));
435     this->sound = fs;
436     this->next = NULL;
437 
438     /*
439      * First item on play list starts the playing.
440     */
441 
442     if (!PlayListHead)
443     {
444 	PlayListHead = PlayListTail = this;
445 #ifdef USE_BUCKETS
446 	AuSoundPlayFromBucket(audio, PlayListHead->sound->bucket, AuNone,
447 			    VOL(TheFacesResources.volume),
448 			    play_cb, NULL, 1,
449 			    NULL, NULL, NULL,
450 			    &ret);
451 #else
452 	AuSoundPlayFromFile(audio, PlayListHead->sound->file, AuNone,
453 			    VOL(TheFacesResources.volume),
454 			    play_cb, NULL,
455 			    NULL, NULL, NULL,
456 			    &ret);
457 #endif
458 	return;
459     }
460 
461     /*
462      *  Just add this item to the tail of the list.
463     */
464 
465     PlayListTail->next = this;
466     PlayListTail = this;
467 }
468 
469 void
FaceSoundFind(item)470 FaceSoundFind(item)
471 MailItem*	item;
472 {
473     if (TheFacesResources.use_sound)
474     {
475 	FaceSearch(item, TheFacesResources.sound_search);
476     }
477 }
478 
479 
480 void
FaceSoundInit()481 FaceSoundInit()
482 {
483     static int init = 0;
484 
485     if (!init)
486     {
487 	String	dpy_name = DisplayString(XtDisplay(TheTopLevel));
488 	audio = AuOpenServer(dpy_name, 0, NULL, 0, NULL, NULL);
489 
490 	if (audio)
491 	{
492 	    AuXtAppAddAudioHandler(XtWidgetToApplicationContext(TheTopLevel),
493 				   audio);
494 	}
495 
496 	init = 1;
497     }
498 }
499