1 /* nas.c --- XEmacs support for the Network Audio System server.
3 * Author: Richard Caley <R.Caley@ed.ac.uk>
5 * Copyright 1994 Free Software Foundation, Inc.
6 * Copyright 1993 Network Computing Devices, Inc.
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.
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.
27 /* Synched up with: Not in FSF. */
29 /* There are four compile-time options.
31 * XTOOLKIT This will be part of an Xt program.
33 * XTEVENTS The playing will be supervised asynchronously by the Xt event
34 * loop. If not set, playing will be completed within the call
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.
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.
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.
59 #if __STDC__ || defined (STDC_HEADERS)
70 #include "syssignal.h"
74 #include <audio/audiolib.h>
75 #include <audio/soundlib.h>
76 #include <audio/snd.h>
77 #include <audio/fileutil.h>
87 * For old NAS libraries, force playing to be synchronous
88 * and declare the long jump point locally.
91 # if defined (NAS_NO_ERROR_JUMP)
96 jmp_buf AuXtErrorJump;
99 /* The GETTEXT is correct. --ben */
100 # define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str))
102 # define play_sound_file nas_play_sound_file
103 # define play_sound_data nas_play_sound_data
104 # define wait_for_sounds nas_wait_for_sounds
105 # define init_play nas_init_play
106 # define close_down_play nas_close_down_play
109 # define warn(str) fprintf (stderr, "%s\n", (str))
114 # include <X11/Intrinsic.h>
115 # include <audio/Xtutil.h>
118 #if defined (ROBUST_PLAY)
119 static AuBool CatchIoErrorAndJump (AuServer *aud);
120 static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event);
121 SIGTYPE sigpipe_handle (int signo);
124 extern Lisp_Object Vsynchronous_sounds;
126 static Sound SoundOpenDataForReading (unsigned char *data, int length);
128 static AuServer *aud;
130 /* count of sounds currently being played. */
131 static int sounds_in_play;
135 static Display *aud_server;
136 static XtInputId input_id;
138 static char *aud_server;
139 #endif /* XTOOLKIT */
151 SIGTYPE (*old_sigpipe) ();
154 char * server = DisplayString (display);
155 XtAppContext app_context = XtDisplayToApplicationContext (display);
157 aud_server = display;
164 old_sigpipe = signal (SIGPIPE, sigpipe_handle);
165 if (setjmp (AuXtErrorJump))
167 signal (SIGPIPE, old_sigpipe);
171 return "error in NAS";
175 #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP)
176 AuDefaultIOErrorHandler = CatchIoErrorAndJump;
177 AuDefaultErrorHandler = CatchErrorAndJump;
183 aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message);
190 signal (SIGPIPE, old_sigpipe);
192 if (err_message == NULL)
193 return "Can't connect to audio server";
198 #if defined (ROBUST_PLAY)
199 # if defined (NAS_NO_ERROR_JUMP)
200 aud->funcs.ioerror_handler = CatchIoErrorAndJump;
201 aud->funcs.error_handler = CatchErrorAndJump;
202 # else /* !NAS_NO_ERROR_JUMP */
203 AuDefaultIOErrorHandler = NULL;
204 AuDefaultErrorHandler = NULL;
209 input_id = AuXtAppAddAudioHandler (app_context, aud);
213 AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL);
217 signal (SIGPIPE, old_sigpipe);
226 close_down_play (void)
230 warn ("disconnected from audio server");
233 /********************************************************************\
235 * Callback which is run when the sound finishes playing. *
237 \********************************************************************/
240 doneCB (AuServer *aud,
241 AuEventHandlerRec *handler,
245 int *in_play_p = (int *) data;
252 /********************************************************************\
254 * Play a sound by playing the relevant bucket, if any or *
255 * downloading it if not. *
257 \********************************************************************/
260 do_caching_play (Sound s,
265 AuBucketAttributes *list, b;
269 AuSetString (AuBucketDescription (&b),
270 AuStringLatin1, strlen (SoundComment (s)), SoundComment (s));
272 list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL);
276 unsigned char *my_buf;
280 if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
285 if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
294 id = AuSoundCreateBucketFromData (aud,
303 else /* found cached sound */
305 id = AuBucketIdentifier (list);
306 AuFreeBucketAttributes (aud, n, list);
311 AuSoundPlayFromBucket (aud,
314 AuFixedPointFromFraction (volume, 100),
315 doneCB, (AuPointer) &sounds_in_play,
321 #endif /* CACHE_SOUNDS */
325 wait_for_sounds (void)
330 while (sounds_in_play>0)
332 AuNextEvent (aud, AuTrue, &ev);
333 AuDispatchEvent (aud, &ev);
338 play_sound_file (char *sound_file,
341 SIGTYPE (*old_sigpipe) ();
344 old_sigpipe=signal (SIGPIPE, sigpipe_handle);
345 if (setjmp (AuXtErrorJump))
347 signal (SIGPIPE, old_sigpipe);
353 if (aud_server != NULL)
356 /* attempt to reconect */
357 if ((m=init_play (aud_server))!= NULL)
361 signal (SIGPIPE, old_sigpipe);
368 warn ("Attempt to play with no audio init\n");
370 signal (SIGPIPE, old_sigpipe);
378 AuSoundPlayFromFile (aud,
381 AuFixedPointFromFraction (volume,100),
382 doneCB, (AuPointer) &sounds_in_play,
388 /* Cache the sounds in buckets on the server */
393 if ((s = SoundOpenFileForReading (sound_file))==NULL)
396 signal (SIGPIPE, old_sigpipe);
401 if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0')
403 SoundComment (s) = FileCommentFromFilename (sound_file);
406 do_caching_play (s, volume, NULL);
411 #endif /* CACHE_SOUNDS */
416 if (!NILP (Vsynchronous_sounds))
423 signal (SIGPIPE, old_sigpipe);
430 play_sound_data (unsigned char *data,
436 SIGTYPE (*old_sigpipe) ();
438 #if !defined (XTEVENTS)
443 old_sigpipe = signal (SIGPIPE, sigpipe_handle);
444 if (setjmp (AuXtErrorJump) !=0)
446 signal (SIGPIPE, old_sigpipe);
453 if (aud_server != NULL)
456 /* attempt to reconect */
457 if ((m = init_play (aud_server)) != NULL)
460 signal (SIGPIPE, old_sigpipe);
467 warn ("Attempt to play with no audio init\n");
469 signal (SIGPIPE, old_sigpipe);
475 if ((s=SoundOpenDataForReading (data, length))==NULL)
477 warn ("unknown sound type");
479 signal (SIGPIPE, old_sigpipe);
484 if (SoundFileFormat (s) == SoundFileFormatSnd)
487 offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
491 warn ("only understand snd files at the moment");
494 signal (SIGPIPE, old_sigpipe);
501 AuSoundPlayFromData (aud,
505 AuFixedPointFromFraction (volume,100),
506 doneCB, (AuPointer) &sounds_in_play,
512 /* Cache the sounds in buckets on the server */
515 do_caching_play (s, volume, data+offset);
517 #endif /* CACHE_SOUNDS */
523 if (!NILP (Vsynchronous_sounds))
532 signal (SIGPIPE, old_sigpipe);
538 #if defined (ROBUST_PLAY)
540 /********************************************************************\
542 * Code to protect the client from server shutdowns. *
544 * This is unbelievably horrible. *
546 \********************************************************************/
549 CatchIoErrorAndJump (AuServer *old_aud)
552 warn ("Audio Server connection broken");
554 warn ("Audio Server connection broken because of signal");
559 AuXtAppRemoveAudioHandler (aud, input_id);
568 longjmp (AuXtErrorJump, 1);
570 #else /* not XTEVENTS */
576 longjmp (AuXtErrorJump, 1);
578 #endif /* XTEVENTS */
582 sigpipe_handle (int signo)
584 CatchIoErrorAndJump (NULL);
588 CatchErrorAndJump (AuServer *old_aud,
591 return CatchIoErrorAndJump (old_aud);
594 #endif /* ROBUST_PLAY */
596 /********************************************************************\
598 * This code is here because the nas Sound library doesn't *
599 * support playing from a file buffered in memory. It's a fairly *
600 * direct translation of the file-based equivalent. *
602 * Since we don't have a filename, samples with no comment field *
603 * are named by a section of their content. *
605 \********************************************************************/
607 /* Create a name from the sound. */
610 NameFromData (CONST unsigned char *buf,
614 unsigned char name[9];
622 while (i<8 && len >0)
624 while (*buf < 32 && len>0)
639 strcpy (s=malloc (10), name);
643 strcpy (s=malloc (15), "short sound");
649 /* Code to do a pseudo-open on a data buffer. Only for snd files at the
654 SndOpenDataForReading (CONST char *data,
661 if (!(si = (SndInfo *) malloc (sizeof (SndInfo))))
667 memcpy (&si->h, data, sizeof (SndHeader));
673 swapl (&si->h.magic, n);
674 swapl (&si->h.dataOffset, n);
675 swapl (&si->h.dataSize, n);
676 swapl (&si->h.format, n);
677 swapl (&si->h.sampleRate, n);
678 swapl (&si->h.tracks, n);
681 if (si->h.magic != SND_MAGIC_NUM)
687 size = si->h.dataOffset - sizeof (SndHeader);
691 if (!(si->comment = (char *) malloc (size + 1)))
697 memcpy (si->comment, data+sizeof (SndHeader), size);
699 *(si->comment + size) = 0;
700 if (*si->comment == '\0')
702 NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
705 si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
707 si->h.dataSize = length-si->h.dataOffset;
715 SoundOpenDataForReading (unsigned char *data,
721 if (!(s = (Sound) malloc (sizeof (SoundRec))))
724 if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
731 if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
733 SndCloseFile (s->formatInfo);