diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nas.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,734 @@
+/* nas.c --- XEmacs support for the Network Audio System server.
+ *
+ * Author: Richard Caley <R.Caley@ed.ac.uk>
+ *
+ * Copyright 1994 Free Software Foundation, Inc.
+ * Copyright 1993 Network Computing Devices, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name Network Computing Devices, Inc. not be
+ * used in advertising or publicity pertaining to distribution of this 
+ * software without specific, written prior permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED 'AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
+ * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
+ * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
+ * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
+ * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
+ * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Synched up with: Not in FSF. */
+
+/* There are four compile-time options.
+ *
+ * XTOOLKIT	This will be part of an Xt program.
+ * 
+ * XTEVENTS	The playing will be supervised asynchronously by the Xt event
+ *		loop.  If not set, playing will be completed within the call
+ *		to play_file etc. 
+ *
+ * ROBUST_PLAY	Causes errors in nas to be caught.  This means that the
+ *		program will attempt not to die if the nas server does.
+ *
+ * CACHE_SOUNDS	Causes the sounds to be played in buckets in the NAS
+ *		server.  They are named by their comment field, or if that is
+ *		empty by the filename, or for play_sound_data by a name made up
+ *		from the sample itself.
+ */
+
+/* CHANGES:
+ *	10/8/94, rjc	Changed names from netaudio to nas
+ *			Added back asynchronous play if nas library has
+ *			correct error facilities.
+ *      4/11/94, rjc    Added wait_for_sounds to be called when user wants to
+ *			be sure all play has finished.
+ */
+
+#if __STDC__ || defined (STDC_HEADERS)
+
+#    include <stdlib.h>
+#    include <unistd.h>
+#    include <stdarg.h>
+
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <config.h> /* for CONST in syssignal.h (neal@ctd.comsat.com) */
+#include "syssignal.h"
+
+#include <audio/audiolib.h>
+#include <audio/soundlib.h>
+#include <audio/snd.h>
+#include <audio/fileutil.h>
+
+#ifdef emacs
+
+#    include <config.h>
+#    include "lisp.h"
+
+#    define XTOOLKIT
+#    define XTEVENTS
+#    define ROBUST_PLAY
+#    define CACHE_SOUNDS
+
+    /*
+     * For old NAS libraries, force playing to be synchronous
+     * and declare the long jump point locally.
+     */
+
+#    if defined (NAS_NO_ERROR_JUMP)
+
+#	undef XTEVENTS
+
+#	include <setjmp.h>
+	jmp_buf AuXtErrorJump;
+#    endif
+
+     /* The GETTEXT is correct. --ben */
+#    define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str))
+
+#    define play_sound_file nas_play_sound_file
+#    define play_sound_data nas_play_sound_data
+#    define wait_for_sounds nas_wait_for_sounds
+#    define init_play       nas_init_play
+#    define close_down_play nas_close_down_play
+
+#else /* !emacs */
+#    define warn(str) fprintf (stderr, "%s\n", (str))
+#    define CONST const
+#endif /* emacs */
+
+#ifdef XTOOLKIT
+#    include <X11/Intrinsic.h>
+#    include <audio/Xtutil.h>
+#endif
+
+#if defined (ROBUST_PLAY)
+static AuBool CatchIoErrorAndJump (AuServer *aud);
+static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event);
+SIGTYPE sigpipe_handle (int signo);
+#endif
+
+extern Lisp_Object Vsynchronous_sounds;
+
+static Sound SoundOpenDataForReading (unsigned char *data, int length);
+
+static AuServer       *aud;
+
+/* count of sounds currently being played. */
+static int sounds_in_play;
+
+
+#ifdef XTOOLKIT
+static Display *aud_server;
+static XtInputId input_id;
+#else
+static char *aud_server;
+#endif /* XTOOLKIT */
+
+char *
+init_play (
+#ifdef XTOOLKIT
+	   Display *display
+#else
+	   char *server
+#endif
+	   )
+{
+  char *err_message;
+  SIGTYPE (*old_sigpipe) ();
+
+#ifdef XTOOLKIT
+  char * server = DisplayString (display);
+  XtAppContext app_context = XtDisplayToApplicationContext (display);
+
+  aud_server = display;
+#else
+
+  aud_server = server;
+#endif
+
+#ifdef ROBUST_PLAY
+  old_sigpipe = signal (SIGPIPE, sigpipe_handle);
+  if (setjmp (AuXtErrorJump))
+    {
+      signal (SIGPIPE, old_sigpipe);
+#ifdef emacs
+      start_interrupts ();
+#endif  
+      return "error in NAS";
+    }
+#endif
+
+#if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP)
+  AuDefaultIOErrorHandler = CatchIoErrorAndJump;
+  AuDefaultErrorHandler = CatchErrorAndJump;
+#endif
+
+#ifdef emacs
+  stop_interrupts ();
+#endif  
+  aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message);
+#ifdef emacs
+  start_interrupts ();
+#endif  
+  if (!aud)
+    {
+#ifdef ROBUST_PLAY
+      signal (SIGPIPE, old_sigpipe);
+#endif
+      if (err_message == NULL)
+	return "Can't connect to audio server";
+      else
+	return err_message;
+    }
+
+#if defined (ROBUST_PLAY)
+# if defined (NAS_NO_ERROR_JUMP)
+  aud->funcs.ioerror_handler = CatchIoErrorAndJump;
+  aud->funcs.error_handler = CatchErrorAndJump;
+# else /* !NAS_NO_ERROR_JUMP */
+  AuDefaultIOErrorHandler = NULL;
+  AuDefaultErrorHandler = NULL;
+# endif
+#endif
+
+#ifdef XTEVENTS
+  input_id = AuXtAppAddAudioHandler (app_context, aud); 
+#endif
+
+#ifdef CACHE_SOUNDS
+  AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL);
+#endif
+
+#ifdef ROBUST_PLAY
+  signal (SIGPIPE, old_sigpipe);
+#endif
+
+  sounds_in_play = 0;
+
+  return NULL;
+}
+
+void
+close_down_play (void)
+
+{
+  AuCloseServer (aud);
+  warn ("disconnected from audio server");
+}
+
+ /********************************************************************\
+ *                                                                    *
+ * Callback which is run when the sound finishes playing.             *
+ *                                                                    *
+ \********************************************************************/
+
+static void
+doneCB (AuServer       *aud,
+	AuEventHandlerRec *handler,
+	AuEvent        *ev,
+	AuPointer       data)
+{
+  int         *in_play_p = (int *) data;
+
+  (*in_play_p) --;
+}
+
+#ifdef CACHE_SOUNDS
+
+ /********************************************************************\
+ *                                                                    *
+ * Play a sound by playing the relevant bucket, if any or             *
+ * downloading it if not.                                             *
+ *                                                                    *
+ \********************************************************************/
+
+static void
+do_caching_play (Sound s,
+		 int volume,
+		 unsigned char *buf)
+
+{
+  AuBucketAttributes *list, b;
+  AuBucketID      id;
+  int n;
+
+  AuSetString (AuBucketDescription (&b),
+	       AuStringLatin1, strlen (SoundComment (s)), SoundComment (s));
+
+  list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL);
+
+  if (list == NULL)
+    {
+      unsigned char *my_buf;
+
+      if (buf==NULL)
+	{
+	  if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
+	    {
+	      return;
+	    }
+
+	  if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
+	    {
+	      free (my_buf);
+	      return;
+	    }
+	}
+      else
+	my_buf=buf;
+
+      id = AuSoundCreateBucketFromData (aud, 
+					s,
+					my_buf,
+					AuAccessAllMasks, 
+					NULL,
+					NULL);
+      if (buf == NULL)
+	free (my_buf);
+    }
+  else /* found cached sound */
+    {
+      id = AuBucketIdentifier (list);
+      AuFreeBucketAttributes (aud, n, list);
+    }
+
+  sounds_in_play++;
+
+  AuSoundPlayFromBucket (aud, 
+			 id, 
+			 AuNone,
+			 AuFixedPointFromFraction (volume, 100), 
+			 doneCB, (AuPointer) &sounds_in_play,
+			 1,
+			 NULL, NULL,
+			 NULL, NULL);
+
+}
+#endif /* CACHE_SOUNDS */
+
+
+void 
+wait_for_sounds (void)
+
+{
+  AuEvent         ev;
+
+  while (sounds_in_play>0)
+    {
+      AuNextEvent (aud, AuTrue, &ev);
+      AuDispatchEvent (aud, &ev);
+    }
+}
+
+int
+play_sound_file (char *sound_file,
+		 int volume)
+{
+  SIGTYPE (*old_sigpipe) ();
+
+#ifdef ROBUST_PLAY
+  old_sigpipe=signal (SIGPIPE, sigpipe_handle);
+  if (setjmp (AuXtErrorJump))
+    {
+      signal (SIGPIPE, old_sigpipe);
+      return 0;
+    }
+#endif
+
+  if (aud==NULL)
+    if (aud_server != NULL)
+      {
+	char *m;
+	/* attempt to reconect */
+	if ((m=init_play (aud_server))!= NULL)
+	  {
+
+#ifdef ROBUST_PLAY
+	    signal (SIGPIPE, old_sigpipe);
+#endif
+	    return 0;
+	  }
+      }
+    else
+      {
+	warn ("Attempt to play with no audio init\n");
+#ifdef ROBUST_PLAY
+	signal (SIGPIPE, old_sigpipe);
+#endif
+	return 0;
+      }
+
+#ifndef CACHE_SOUNDS
+  sounds_in_play++;
+  AuSoundPlayFromFile (aud,
+		       sound_file,
+		       AuNone,
+		       AuFixedPointFromFraction (volume,100),
+		       doneCB, (AuPointer) &sounds_in_play,
+		       NULL,
+		       NULL,
+		       NULL,
+		       NULL);
+#else
+  /* Cache the sounds in buckets on the server */
+
+  {
+    Sound s;
+
+    if ((s = SoundOpenFileForReading (sound_file))==NULL)
+      {
+#ifdef ROBUST_PLAY
+	signal (SIGPIPE, old_sigpipe);
+#endif
+	return 0;
+      }
+
+    if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0')
+      {
+	SoundComment (s) = FileCommentFromFilename (sound_file);
+      }
+
+    do_caching_play (s, volume, NULL);
+
+    SoundCloseFile (s);
+
+  }
+#endif /* CACHE_SOUNDS */
+
+#ifndef XTEVENTS
+  wait_for_sounds ();
+#else
+  if (!NILP (Vsynchronous_sounds))
+    {
+      wait_for_sounds ();
+    }
+#endif
+
+#ifdef ROBUST_PLAY
+  signal (SIGPIPE, old_sigpipe);
+#endif
+
+  return 1;
+}
+
+int
+play_sound_data (unsigned char *data,
+		 int length, 
+		 int volume)
+{
+  Sound s;
+  int offset;
+  SIGTYPE (*old_sigpipe) ();
+
+#if !defined (XTEVENTS)
+  AuEvent         ev;
+#endif
+
+#ifdef ROBUST_PLAY
+  old_sigpipe = signal (SIGPIPE, sigpipe_handle);
+  if (setjmp (AuXtErrorJump) !=0)
+    {
+      signal (SIGPIPE, old_sigpipe);
+      return 0;
+    }
+#endif
+
+
+  if (aud == NULL)
+    if (aud_server != NULL)
+      {
+	char *m;
+	/* attempt to reconect */
+	if ((m = init_play (aud_server)) != NULL)
+	  {
+#ifdef ROBUST_PLAY
+	    signal (SIGPIPE, old_sigpipe);
+#endif
+	    return 0;
+	  }
+      }
+    else
+      {
+	warn ("Attempt to play with no audio init\n");
+#ifdef ROBUST_PLAY
+	signal (SIGPIPE, old_sigpipe);
+#endif
+	return 0;
+      }
+
+  if ((s=SoundOpenDataForReading (data, length))==NULL)
+    {
+      warn ("unknown sound type");
+#ifdef ROBUST_PLAY
+      signal (SIGPIPE, old_sigpipe);
+#endif
+      return 0;
+    }
+
+  if (SoundFileFormat (s) == SoundFileFormatSnd)
+    {
+      /* hack, hack */
+      offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
+    }
+  else
+    {
+      warn ("only understand snd files at the moment");
+      SoundCloseFile (s);
+#ifdef ROBUST_PLAY
+      signal (SIGPIPE, old_sigpipe);
+#endif
+      return 0;
+    }
+
+#ifndef CACHE_SOUNDS
+  sounds_in_play++;
+  AuSoundPlayFromData (aud,
+		       s,
+		       data+offset,
+		       AuNone,
+		       AuFixedPointFromFraction (volume,100),
+		       doneCB, (AuPointer) &sounds_in_play,
+		       NULL,
+		       NULL,
+		       NULL,
+		       NULL);
+#else
+  /* Cache the sounds in buckets on the server */
+
+  {
+    do_caching_play (s, volume, data+offset);
+  }
+#endif /* CACHE_SOUNDS */
+
+
+#ifndef XTEVENTS
+  wait_for_sounds ();
+#else
+  if (!NILP (Vsynchronous_sounds))
+    {
+      wait_for_sounds ();
+    }
+#endif
+
+  SoundCloseFile (s); 
+
+#ifdef ROBUST_PLAY
+  signal (SIGPIPE, old_sigpipe);
+#endif
+
+  return 1;
+}
+
+#if defined (ROBUST_PLAY)
+
+ /********************************************************************\
+ *                                                                    *
+ * Code to protect the client from server shutdowns.                  *
+ *                                                                    *
+ * This is unbelievably horrible.                                     *
+ *                                                                    *
+ \********************************************************************/
+
+static AuBool
+CatchIoErrorAndJump (AuServer *old_aud)
+{
+  if (old_aud)
+    warn ("Audio Server connection broken"); 
+  else
+    warn ("Audio Server connection broken because of signal");
+
+#ifdef XTEVENTS
+#ifdef XTOOLKIT
+  {
+    AuXtAppRemoveAudioHandler (aud, input_id); 
+  }
+#endif
+
+  if (aud)
+    AuCloseServer (aud);
+  aud = NULL;
+  sounds_in_play = 0;
+
+  longjmp (AuXtErrorJump, 1);
+
+#else /* not XTEVENTS */
+
+  if (aud)
+    AuCloseServer (aud);
+  aud = NULL;
+  sounds_in_play = 0;
+  longjmp (AuXtErrorJump, 1);
+ 
+#endif /* XTEVENTS */
+}
+
+SIGTYPE
+sigpipe_handle (int signo)
+{
+  CatchIoErrorAndJump (NULL);
+}
+
+static AuBool
+CatchErrorAndJump (AuServer *old_aud,
+		   AuErrorEvent *event)
+{
+  return CatchIoErrorAndJump (old_aud);
+}
+
+#endif /* ROBUST_PLAY */
+
+ /********************************************************************\
+ *                                                                    *
+ * This code is here because the nas Sound library doesn't            *
+ * support playing from a file buffered in memory. It's a fairly      *
+ * direct translation of the file-based equivalent.                   *
+ *                                                                    *
+ * Since we don't have a filename, samples with no comment field      *
+ * are named by a section of their content.                           *
+ *                                                                    *
+ \********************************************************************/
+
+/* Create a name from the sound. */
+
+static char *
+NameFromData (CONST unsigned char *buf,
+	      int len)
+
+{
+  unsigned char name[9];
+  int i;
+  char *s;
+
+  buf+=len/2;
+  len -= len/2;
+
+  i=0;
+  while (i<8 && len >0)
+    {
+      while (*buf < 32 && len>0)
+	{
+	  buf++;
+	  len--;
+	}
+      name[i]= *buf;
+      i++;
+      buf++;
+      len--;
+    }
+
+  name[i]='\0';
+
+  if (i==8)
+    {
+      strcpy (s=malloc (10), name);
+    }
+  else 
+    {
+      strcpy (s=malloc (15), "short sound");
+    }
+
+  return s;
+}
+
+/* Code to do a pseudo-open on a data buffer. Only for snd files at the
+   moment. 
+ */
+
+static SndInfo *
+SndOpenDataForReading (CONST char *data,
+		       int length)
+
+{
+  SndInfo        *si;
+  int             size;
+
+  if (!(si = (SndInfo *) malloc (sizeof (SndInfo))))
+    return NULL;
+
+  si->comment = NULL;
+  si->writing = 0;
+
+  memcpy (&si->h, data, sizeof (SndHeader));
+
+  if (LITTLE_ENDIAN)
+    {
+      char            n;
+    
+      swapl (&si->h.magic, n);
+      swapl (&si->h.dataOffset, n);
+      swapl (&si->h.dataSize, n);
+      swapl (&si->h.format, n);
+      swapl (&si->h.sampleRate, n);
+      swapl (&si->h.tracks, n);
+    }
+
+  if (si->h.magic != SND_MAGIC_NUM)
+    {
+      free (si);
+      return NULL;
+    }
+
+  size = si->h.dataOffset - sizeof (SndHeader);
+
+  if (size)
+    {
+      if (!(si->comment = (char *) malloc (size + 1)))
+	{
+	  free (si);
+	  return NULL;
+	}
+
+      memcpy (si->comment,  data+sizeof (SndHeader), size);
+
+      *(si->comment + size) = 0;
+      if (*si->comment == '\0')
+	si->comment =
+	  NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
+    }
+  else
+    si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
+
+  si->h.dataSize = length-si->h.dataOffset;
+
+  si->fp=NULL;
+
+  return si;
+}
+
+static Sound
+SoundOpenDataForReading (unsigned char *data,
+			 int length)
+
+{
+  Sound s;
+
+  if (!(s = (Sound) malloc (sizeof (SoundRec))))
+    return NULL;
+
+  if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
+    {
+      free (s);
+      return NULL;
+    }
+    
+
+  if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
+    {
+      SndCloseFile (s->formatInfo);
+      free (s);
+      return NULL;
+    }
+
+  return s;
+}
+