Mercurial > hg > xemacs-beta
comparison src/nas.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 0293115a14e9 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 /* nas.c --- XEmacs support for the Network Audio System server. | |
2 * | |
3 * Author: Richard Caley <R.Caley@ed.ac.uk> | |
4 * | |
5 * Copyright 1994 Free Software Foundation, Inc. | |
6 * Copyright 1993 Network Computing Devices, Inc. | |
7 * | |
8 * Permission to use, copy, modify, distribute, and sell this software and | |
9 * its documentation for any purpose is hereby granted without fee, provided | |
10 * that the above copyright notice appear in all copies and that both that | |
11 * copyright notice and this permission notice appear in supporting | |
12 * documentation, and that the name Network Computing Devices, Inc. not be | |
13 * used in advertising or publicity pertaining to distribution of this | |
14 * software without specific, written prior permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED 'AS-IS'. NETWORK COMPUTING DEVICES, INC., | |
17 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT | |
18 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
19 * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL NETWORK | |
20 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING | |
21 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, | |
22 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF | |
23 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN | |
24 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
25 */ | |
26 | |
27 /* Synched up with: Not in FSF. */ | |
28 | |
29 /* There are four compile-time options. | |
30 * | |
31 * XTOOLKIT This will be part of an Xt program. | |
32 * | |
33 * XTEVENTS The playing will be supervised asynchronously by the Xt event | |
34 * loop. If not set, playing will be completed within the call | |
35 * to play_file etc. | |
36 * | |
37 * ROBUST_PLAY Causes errors in nas to be caught. This means that the | |
38 * program will attempt not to die if the nas server does. | |
39 * | |
40 * CACHE_SOUNDS Causes the sounds to be played in buckets in the NAS | |
41 * server. They are named by their comment field, or if that is | |
42 * empty by the filename, or for play_sound_data by a name made up | |
43 * from the sample itself. | |
44 */ | |
45 | |
46 /* CHANGES: | |
47 * 10/8/94, rjc Changed names from netaudio to nas | |
48 * Added back asynchronous play if nas library has | |
49 * correct error facilities. | |
50 * 4/11/94, rjc Added wait_for_sounds to be called when user wants to | |
51 * be sure all play has finished. | |
52 */ | |
53 | |
54 #if __STDC__ || defined (STDC_HEADERS) | |
55 | |
56 # include <stdlib.h> | |
57 # include <unistd.h> | |
58 # include <stdarg.h> | |
59 | |
60 #endif | |
61 | |
62 #include <stdio.h> | |
63 #include <string.h> | |
64 #include <config.h> /* for CONST in syssignal.h (neal@ctd.comsat.com) */ | |
65 #include "syssignal.h" | |
66 | |
67 #include <audio/audiolib.h> | |
68 #include <audio/soundlib.h> | |
69 #include <audio/snd.h> | |
70 #include <audio/fileutil.h> | |
71 | |
72 #ifdef emacs | |
73 | |
74 # include <config.h> | |
75 # include "lisp.h" | |
76 | |
77 # define XTOOLKIT | |
78 # define XTEVENTS | |
79 # define ROBUST_PLAY | |
80 # define CACHE_SOUNDS | |
81 | |
82 /* | |
83 * For old NAS libraries, force playing to be synchronous | |
84 * and declare the long jump point locally. | |
85 */ | |
86 | |
87 # if defined (NAS_NO_ERROR_JUMP) | |
88 | |
89 # undef XTEVENTS | |
90 | |
91 # include <setjmp.h> | |
92 jmp_buf AuXtErrorJump; | |
93 # endif | |
94 | |
95 /* The GETTEXT is correct. --ben */ | |
96 # define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str)) | |
97 | |
98 # define play_sound_file nas_play_sound_file | |
99 # define play_sound_data nas_play_sound_data | |
100 # define wait_for_sounds nas_wait_for_sounds | |
101 # define init_play nas_init_play | |
102 # define close_down_play nas_close_down_play | |
103 | |
104 #else /* !emacs */ | |
105 # define warn(str) fprintf (stderr, "%s\n", (str)) | |
106 # define CONST const | |
107 #endif /* emacs */ | |
108 | |
109 #ifdef XTOOLKIT | |
110 # include <X11/Intrinsic.h> | |
111 # include <audio/Xtutil.h> | |
112 #endif | |
113 | |
114 #if defined (ROBUST_PLAY) | |
115 static AuBool CatchIoErrorAndJump (AuServer *aud); | |
116 static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event); | |
117 SIGTYPE sigpipe_handle (int signo); | |
118 #endif | |
119 | |
120 extern Lisp_Object Vsynchronous_sounds; | |
121 | |
122 static Sound SoundOpenDataForReading (unsigned char *data, int length); | |
123 | |
124 static AuServer *aud; | |
125 | |
126 /* count of sounds currently being played. */ | |
127 static int sounds_in_play; | |
128 | |
129 | |
130 #ifdef XTOOLKIT | |
131 static Display *aud_server; | |
132 static XtInputId input_id; | |
133 #else | |
134 static char *aud_server; | |
135 #endif /* XTOOLKIT */ | |
136 | |
137 char * | |
138 init_play ( | |
139 #ifdef XTOOLKIT | |
140 Display *display | |
141 #else | |
142 char *server | |
143 #endif | |
144 ) | |
145 { | |
146 char *err_message; | |
147 SIGTYPE (*old_sigpipe) (); | |
148 | |
149 #ifdef XTOOLKIT | |
150 char * server = DisplayString (display); | |
151 XtAppContext app_context = XtDisplayToApplicationContext (display); | |
152 | |
153 aud_server = display; | |
154 #else | |
155 | |
156 aud_server = server; | |
157 #endif | |
158 | |
159 #ifdef ROBUST_PLAY | |
160 old_sigpipe = signal (SIGPIPE, sigpipe_handle); | |
161 if (setjmp (AuXtErrorJump)) | |
162 { | |
163 signal (SIGPIPE, old_sigpipe); | |
164 #ifdef emacs | |
165 start_interrupts (); | |
166 #endif | |
167 return "error in NAS"; | |
168 } | |
169 #endif | |
170 | |
171 #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP) | |
172 AuDefaultIOErrorHandler = CatchIoErrorAndJump; | |
173 AuDefaultErrorHandler = CatchErrorAndJump; | |
174 #endif | |
175 | |
176 #ifdef emacs | |
177 stop_interrupts (); | |
178 #endif | |
179 aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message); | |
180 #ifdef emacs | |
181 start_interrupts (); | |
182 #endif | |
183 if (!aud) | |
184 { | |
185 #ifdef ROBUST_PLAY | |
186 signal (SIGPIPE, old_sigpipe); | |
187 #endif | |
188 if (err_message == NULL) | |
189 return "Can't connect to audio server"; | |
190 else | |
191 return err_message; | |
192 } | |
193 | |
194 #if defined (ROBUST_PLAY) | |
195 # if defined (NAS_NO_ERROR_JUMP) | |
196 aud->funcs.ioerror_handler = CatchIoErrorAndJump; | |
197 aud->funcs.error_handler = CatchErrorAndJump; | |
198 # else /* !NAS_NO_ERROR_JUMP */ | |
199 AuDefaultIOErrorHandler = NULL; | |
200 AuDefaultErrorHandler = NULL; | |
201 # endif | |
202 #endif | |
203 | |
204 #ifdef XTEVENTS | |
205 input_id = AuXtAppAddAudioHandler (app_context, aud); | |
206 #endif | |
207 | |
208 #ifdef CACHE_SOUNDS | |
209 AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL); | |
210 #endif | |
211 | |
212 #ifdef ROBUST_PLAY | |
213 signal (SIGPIPE, old_sigpipe); | |
214 #endif | |
215 | |
216 sounds_in_play = 0; | |
217 | |
218 return NULL; | |
219 } | |
220 | |
221 void | |
222 close_down_play (void) | |
223 | |
224 { | |
225 AuCloseServer (aud); | |
226 warn ("disconnected from audio server"); | |
227 } | |
228 | |
229 /********************************************************************\ | |
230 * * | |
231 * Callback which is run when the sound finishes playing. * | |
232 * * | |
233 \********************************************************************/ | |
234 | |
235 static void | |
236 doneCB (AuServer *aud, | |
237 AuEventHandlerRec *handler, | |
238 AuEvent *ev, | |
239 AuPointer data) | |
240 { | |
241 int *in_play_p = (int *) data; | |
242 | |
243 (*in_play_p) --; | |
244 } | |
245 | |
246 #ifdef CACHE_SOUNDS | |
247 | |
248 /********************************************************************\ | |
249 * * | |
250 * Play a sound by playing the relevant bucket, if any or * | |
251 * downloading it if not. * | |
252 * * | |
253 \********************************************************************/ | |
254 | |
255 static void | |
256 do_caching_play (Sound s, | |
257 int volume, | |
258 unsigned char *buf) | |
259 | |
260 { | |
261 AuBucketAttributes *list, b; | |
262 AuBucketID id; | |
263 int n; | |
264 | |
265 AuSetString (AuBucketDescription (&b), | |
266 AuStringLatin1, strlen (SoundComment (s)), SoundComment (s)); | |
267 | |
268 list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL); | |
269 | |
270 if (list == NULL) | |
271 { | |
272 unsigned char *my_buf; | |
273 | |
274 if (buf==NULL) | |
275 { | |
276 if ((my_buf=malloc (SoundNumBytes (s)))==NULL) | |
277 { | |
278 return; | |
279 } | |
280 | |
281 if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s)) | |
282 { | |
283 free (my_buf); | |
284 return; | |
285 } | |
286 } | |
287 else | |
288 my_buf=buf; | |
289 | |
290 id = AuSoundCreateBucketFromData (aud, | |
291 s, | |
292 my_buf, | |
293 AuAccessAllMasks, | |
294 NULL, | |
295 NULL); | |
296 if (buf == NULL) | |
297 free (my_buf); | |
298 } | |
299 else /* found cached sound */ | |
300 { | |
301 id = AuBucketIdentifier (list); | |
302 AuFreeBucketAttributes (aud, n, list); | |
303 } | |
304 | |
305 sounds_in_play++; | |
306 | |
307 AuSoundPlayFromBucket (aud, | |
308 id, | |
309 AuNone, | |
310 AuFixedPointFromFraction (volume, 100), | |
311 doneCB, (AuPointer) &sounds_in_play, | |
312 1, | |
313 NULL, NULL, | |
314 NULL, NULL); | |
315 | |
316 } | |
317 #endif /* CACHE_SOUNDS */ | |
318 | |
319 | |
320 void | |
321 wait_for_sounds (void) | |
322 | |
323 { | |
324 AuEvent ev; | |
325 | |
326 while (sounds_in_play>0) | |
327 { | |
328 AuNextEvent (aud, AuTrue, &ev); | |
329 AuDispatchEvent (aud, &ev); | |
330 } | |
331 } | |
332 | |
333 int | |
334 play_sound_file (char *sound_file, | |
335 int volume) | |
336 { | |
337 SIGTYPE (*old_sigpipe) (); | |
338 | |
339 #ifdef ROBUST_PLAY | |
340 old_sigpipe=signal (SIGPIPE, sigpipe_handle); | |
341 if (setjmp (AuXtErrorJump)) | |
342 { | |
343 signal (SIGPIPE, old_sigpipe); | |
344 return 0; | |
345 } | |
346 #endif | |
347 | |
348 if (aud==NULL) | |
349 if (aud_server != NULL) | |
350 { | |
351 char *m; | |
352 /* attempt to reconect */ | |
353 if ((m=init_play (aud_server))!= NULL) | |
354 { | |
355 | |
356 #ifdef ROBUST_PLAY | |
357 signal (SIGPIPE, old_sigpipe); | |
358 #endif | |
359 return 0; | |
360 } | |
361 } | |
362 else | |
363 { | |
364 warn ("Attempt to play with no audio init\n"); | |
365 #ifdef ROBUST_PLAY | |
366 signal (SIGPIPE, old_sigpipe); | |
367 #endif | |
368 return 0; | |
369 } | |
370 | |
371 #ifndef CACHE_SOUNDS | |
372 sounds_in_play++; | |
373 AuSoundPlayFromFile (aud, | |
374 sound_file, | |
375 AuNone, | |
376 AuFixedPointFromFraction (volume,100), | |
377 doneCB, (AuPointer) &sounds_in_play, | |
378 NULL, | |
379 NULL, | |
380 NULL, | |
381 NULL); | |
382 #else | |
383 /* Cache the sounds in buckets on the server */ | |
384 | |
385 { | |
386 Sound s; | |
387 | |
388 if ((s = SoundOpenFileForReading (sound_file))==NULL) | |
389 { | |
390 #ifdef ROBUST_PLAY | |
391 signal (SIGPIPE, old_sigpipe); | |
392 #endif | |
393 return 0; | |
394 } | |
395 | |
396 if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0') | |
397 { | |
398 SoundComment (s) = FileCommentFromFilename (sound_file); | |
399 } | |
400 | |
401 do_caching_play (s, volume, NULL); | |
402 | |
403 SoundCloseFile (s); | |
404 | |
405 } | |
406 #endif /* CACHE_SOUNDS */ | |
407 | |
408 #ifndef XTEVENTS | |
409 wait_for_sounds (); | |
410 #else | |
411 if (!NILP (Vsynchronous_sounds)) | |
412 { | |
413 wait_for_sounds (); | |
414 } | |
415 #endif | |
416 | |
417 #ifdef ROBUST_PLAY | |
418 signal (SIGPIPE, old_sigpipe); | |
419 #endif | |
420 | |
421 return 1; | |
422 } | |
423 | |
424 int | |
425 play_sound_data (unsigned char *data, | |
426 int length, | |
427 int volume) | |
428 { | |
429 Sound s; | |
430 int offset; | |
431 SIGTYPE (*old_sigpipe) (); | |
432 | |
433 #if !defined (XTEVENTS) | |
434 AuEvent ev; | |
435 #endif | |
436 | |
437 #ifdef ROBUST_PLAY | |
438 old_sigpipe = signal (SIGPIPE, sigpipe_handle); | |
439 if (setjmp (AuXtErrorJump) !=0) | |
440 { | |
441 signal (SIGPIPE, old_sigpipe); | |
442 return 0; | |
443 } | |
444 #endif | |
445 | |
446 | |
447 if (aud == NULL) | |
448 if (aud_server != NULL) | |
449 { | |
450 char *m; | |
451 /* attempt to reconect */ | |
452 if ((m = init_play (aud_server)) != NULL) | |
453 { | |
454 #ifdef ROBUST_PLAY | |
455 signal (SIGPIPE, old_sigpipe); | |
456 #endif | |
457 return 0; | |
458 } | |
459 } | |
460 else | |
461 { | |
462 warn ("Attempt to play with no audio init\n"); | |
463 #ifdef ROBUST_PLAY | |
464 signal (SIGPIPE, old_sigpipe); | |
465 #endif | |
466 return 0; | |
467 } | |
468 | |
469 if ((s=SoundOpenDataForReading (data, length))==NULL) | |
470 { | |
471 warn ("unknown sound type"); | |
472 #ifdef ROBUST_PLAY | |
473 signal (SIGPIPE, old_sigpipe); | |
474 #endif | |
475 return 0; | |
476 } | |
477 | |
478 if (SoundFileFormat (s) == SoundFileFormatSnd) | |
479 { | |
480 /* hack, hack */ | |
481 offset = ((SndInfo *) (s->formatInfo))->h.dataOffset; | |
482 } | |
483 else | |
484 { | |
485 warn ("only understand snd files at the moment"); | |
486 SoundCloseFile (s); | |
487 #ifdef ROBUST_PLAY | |
488 signal (SIGPIPE, old_sigpipe); | |
489 #endif | |
490 return 0; | |
491 } | |
492 | |
493 #ifndef CACHE_SOUNDS | |
494 sounds_in_play++; | |
495 AuSoundPlayFromData (aud, | |
496 s, | |
497 data+offset, | |
498 AuNone, | |
499 AuFixedPointFromFraction (volume,100), | |
500 doneCB, (AuPointer) &sounds_in_play, | |
501 NULL, | |
502 NULL, | |
503 NULL, | |
504 NULL); | |
505 #else | |
506 /* Cache the sounds in buckets on the server */ | |
507 | |
508 { | |
509 do_caching_play (s, volume, data+offset); | |
510 } | |
511 #endif /* CACHE_SOUNDS */ | |
512 | |
513 | |
514 #ifndef XTEVENTS | |
515 wait_for_sounds (); | |
516 #else | |
517 if (!NILP (Vsynchronous_sounds)) | |
518 { | |
519 wait_for_sounds (); | |
520 } | |
521 #endif | |
522 | |
523 SoundCloseFile (s); | |
524 | |
525 #ifdef ROBUST_PLAY | |
526 signal (SIGPIPE, old_sigpipe); | |
527 #endif | |
528 | |
529 return 1; | |
530 } | |
531 | |
532 #if defined (ROBUST_PLAY) | |
533 | |
534 /********************************************************************\ | |
535 * * | |
536 * Code to protect the client from server shutdowns. * | |
537 * * | |
538 * This is unbelievably horrible. * | |
539 * * | |
540 \********************************************************************/ | |
541 | |
542 static AuBool | |
543 CatchIoErrorAndJump (AuServer *old_aud) | |
544 { | |
545 if (old_aud) | |
546 warn ("Audio Server connection broken"); | |
547 else | |
548 warn ("Audio Server connection broken because of signal"); | |
549 | |
550 #ifdef XTEVENTS | |
551 #ifdef XTOOLKIT | |
552 { | |
553 AuXtAppRemoveAudioHandler (aud, input_id); | |
554 } | |
555 #endif | |
556 | |
557 if (aud) | |
558 AuCloseServer (aud); | |
559 aud = NULL; | |
560 sounds_in_play = 0; | |
561 | |
562 longjmp (AuXtErrorJump, 1); | |
563 | |
564 #else /* not XTEVENTS */ | |
565 | |
566 if (aud) | |
567 AuCloseServer (aud); | |
568 aud = NULL; | |
569 sounds_in_play = 0; | |
570 longjmp (AuXtErrorJump, 1); | |
571 | |
572 #endif /* XTEVENTS */ | |
573 } | |
574 | |
575 SIGTYPE | |
576 sigpipe_handle (int signo) | |
577 { | |
578 CatchIoErrorAndJump (NULL); | |
579 } | |
580 | |
581 static AuBool | |
582 CatchErrorAndJump (AuServer *old_aud, | |
583 AuErrorEvent *event) | |
584 { | |
585 return CatchIoErrorAndJump (old_aud); | |
586 } | |
587 | |
588 #endif /* ROBUST_PLAY */ | |
589 | |
590 /********************************************************************\ | |
591 * * | |
592 * This code is here because the nas Sound library doesn't * | |
593 * support playing from a file buffered in memory. It's a fairly * | |
594 * direct translation of the file-based equivalent. * | |
595 * * | |
596 * Since we don't have a filename, samples with no comment field * | |
597 * are named by a section of their content. * | |
598 * * | |
599 \********************************************************************/ | |
600 | |
601 /* Create a name from the sound. */ | |
602 | |
603 static char * | |
604 NameFromData (CONST unsigned char *buf, | |
605 int len) | |
606 | |
607 { | |
608 unsigned char name[9]; | |
609 int i; | |
610 char *s; | |
611 | |
612 buf+=len/2; | |
613 len -= len/2; | |
614 | |
615 i=0; | |
616 while (i<8 && len >0) | |
617 { | |
618 while (*buf < 32 && len>0) | |
619 { | |
620 buf++; | |
621 len--; | |
622 } | |
623 name[i]= *buf; | |
624 i++; | |
625 buf++; | |
626 len--; | |
627 } | |
628 | |
629 name[i]='\0'; | |
630 | |
631 if (i==8) | |
632 { | |
633 strcpy (s=malloc (10), name); | |
634 } | |
635 else | |
636 { | |
637 strcpy (s=malloc (15), "short sound"); | |
638 } | |
639 | |
640 return s; | |
641 } | |
642 | |
643 /* Code to do a pseudo-open on a data buffer. Only for snd files at the | |
644 moment. | |
645 */ | |
646 | |
647 static SndInfo * | |
648 SndOpenDataForReading (CONST char *data, | |
649 int length) | |
650 | |
651 { | |
652 SndInfo *si; | |
653 int size; | |
654 | |
655 if (!(si = (SndInfo *) malloc (sizeof (SndInfo)))) | |
656 return NULL; | |
657 | |
658 si->comment = NULL; | |
659 si->writing = 0; | |
660 | |
661 memcpy (&si->h, data, sizeof (SndHeader)); | |
662 | |
663 if (LITTLE_ENDIAN) | |
664 { | |
665 char n; | |
666 | |
667 swapl (&si->h.magic, n); | |
668 swapl (&si->h.dataOffset, n); | |
669 swapl (&si->h.dataSize, n); | |
670 swapl (&si->h.format, n); | |
671 swapl (&si->h.sampleRate, n); | |
672 swapl (&si->h.tracks, n); | |
673 } | |
674 | |
675 if (si->h.magic != SND_MAGIC_NUM) | |
676 { | |
677 free (si); | |
678 return NULL; | |
679 } | |
680 | |
681 size = si->h.dataOffset - sizeof (SndHeader); | |
682 | |
683 if (size) | |
684 { | |
685 if (!(si->comment = (char *) malloc (size + 1))) | |
686 { | |
687 free (si); | |
688 return NULL; | |
689 } | |
690 | |
691 memcpy (si->comment, data+sizeof (SndHeader), size); | |
692 | |
693 *(si->comment + size) = 0; | |
694 if (*si->comment == '\0') | |
695 si->comment = | |
696 NameFromData (data+si->h.dataOffset, length-si->h.dataOffset); | |
697 } | |
698 else | |
699 si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset); | |
700 | |
701 si->h.dataSize = length-si->h.dataOffset; | |
702 | |
703 si->fp=NULL; | |
704 | |
705 return si; | |
706 } | |
707 | |
708 static Sound | |
709 SoundOpenDataForReading (unsigned char *data, | |
710 int length) | |
711 | |
712 { | |
713 Sound s; | |
714 | |
715 if (!(s = (Sound) malloc (sizeof (SoundRec)))) | |
716 return NULL; | |
717 | |
718 if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL) | |
719 { | |
720 free (s); | |
721 return NULL; | |
722 } | |
723 | |
724 | |
725 if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s)) | |
726 { | |
727 SndCloseFile (s->formatInfo); | |
728 free (s); | |
729 return NULL; | |
730 } | |
731 | |
732 return s; | |
733 } | |
734 |