XEmacs 21.2.36 "Notos"
[chise/xemacs-chise.git.1] / src / nas.c
index d62b30b..bc473aa 100644 (file)
--- a/src/nas.c
+++ b/src/nas.c
  *                     correct error facilities.
  *      4/11/94, rjc    Added wait_for_sounds to be called when user wants to
  *                     be sure all play has finished.
+ *      1998-10-01 rlt  Added support for WAVE files.
  */
 
 #ifdef emacs
 #include <config.h>
 #include "lisp.h"
+#include "sysdep.h"
+#include "syssignal.h"
 #endif
 
-#if __STDC__ || defined (STDC_HEADERS)
-#    include <stdlib.h>
-#    include <stdarg.h>
-#    include <string.h>
-#endif
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#include <stdio.h>
-#include "syssignal.h"
+/* NAS <= 1.2p5 defines {BIG,LITTLE}_ENDIAN in <audio/fileutil.h>,
+   conflicting with GNU libc (at least); newer versions avoid this
+   name space pollution.
+
+   DO NOT USE THOSE MACROS in this file.  Use NAS_{BIG,LITTLE}_ENDIAN.
 
+   It would be slightly more reliable to do this via configure, but that
+   seems unnecessarily complex.
+*/
 #undef LITTLE_ENDIAN
 #undef BIG_ENDIAN
+
 #include <audio/audiolib.h>
 #include <audio/soundlib.h>
 #include <audio/snd.h>
+#include <audio/wave.h>
 #include <audio/fileutil.h>
 
+/* NAS <= 1.2p5 <audio/fileutil.h> doesn't define the NAS_ versions */
+#ifndef NAS_LITTLE_ENDIAN
+#define NAS_LITTLE_ENDIAN LITTLE_ENDIAN
+#define NAS_BIG_ENDIAN BIG_ENDIAN
+#endif
+
 #ifdef emacs
 
 #    define XTOOLKIT
 
 #else /* !emacs */
 #    define warn(str) fprintf (stderr, "%s\n", (str))
-#    define CONST const
 #endif /* emacs */
 
 #ifdef XTOOLKIT
@@ -145,10 +160,18 @@ init_play (
 #else
           char *server
 #endif
+          );
+char *
+init_play (
+#ifdef XTOOLKIT
+          Display *display
+#else
+          char *server
+#endif
           )
 {
   char *err_message;
-  SIGTYPE (*old_sigpipe) ();
+  SIGTYPE (*old_sigpipe) (int);
 
 #ifdef XTOOLKIT
   char * server = DisplayString (display);
@@ -222,7 +245,7 @@ init_play (
   return NULL;
 }
 
-void
+static void
 close_down_play (void)
 
 {
@@ -237,7 +260,7 @@ close_down_play (void)
  \********************************************************************/
 
 static void
-doneCB (AuServer       *aud,
+doneCB (AuServer       *auserver,
        AuEventHandlerRec *handler,
        AuEvent        *ev,
        AuPointer       data)
@@ -273,23 +296,23 @@ do_caching_play (Sound s,
 
   if (list == NULL)
     {
-      unsigned char *my_buf;
+      AuPointer my_buf;
 
       if (buf==NULL)
        {
-         if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
+         if ((my_buf= (AuPointer) malloc (SoundNumBytes (s)))==NULL)
            {
              return;
            }
 
-         if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
+         if (SoundReadFile ((char *) my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
            {
              free (my_buf);
              return;
            }
        }
       else
-       my_buf=buf;
+       my_buf = (AuPointer) buf;
 
       id = AuSoundCreateBucketFromData (aud, 
                                        s,
@@ -321,6 +344,7 @@ do_caching_play (Sound s,
 #endif /* CACHE_SOUNDS */
 
 
+void wait_for_sounds (void);
 void 
 wait_for_sounds (void)
 
@@ -334,11 +358,12 @@ wait_for_sounds (void)
     }
 }
 
+int play_sound_file (char *sound_file, int volume);
 int
 play_sound_file (char *sound_file,
                 int volume)
 {
-  SIGTYPE (*old_sigpipe) ();
+  SIGTYPE (*old_sigpipe) (int);
 
 #ifdef ROBUST_PLAY
   old_sigpipe=signal (SIGPIPE, sigpipe_handle);
@@ -426,6 +451,7 @@ play_sound_file (char *sound_file,
   return 1;
 }
 
+int play_sound_data (unsigned char *data, int length, int volume);
 int
 play_sound_data (unsigned char *data,
                 int length, 
@@ -433,7 +459,7 @@ play_sound_data (unsigned char *data,
 {
   Sound s;
   int offset;
-  SIGTYPE (*old_sigpipe) ();
+  SIGTYPE (*old_sigpipe) (int);
 
 #if !defined (XTEVENTS)
   AuEvent         ev;
@@ -486,9 +512,13 @@ play_sound_data (unsigned char *data,
       /* hack, hack */
       offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
     }
+  else if (SoundFileFormat (s) == SoundFileFormatWave)
+    {
+      offset = ((WaveInfo *) (s->formatInfo))->dataOffset;
+    }
   else
     {
-      warn ("only understand snd files at the moment");
+      warn ("only understand snd and wave files at the moment");
       SoundCloseFile (s);
 #ifdef ROBUST_PLAY
       signal (SIGPIPE, old_sigpipe);
@@ -576,6 +606,7 @@ CatchIoErrorAndJump (AuServer *old_aud)
   longjmp (AuXtErrorJump, 1);
  
 #endif /* XTEVENTS */
+  return 0;
 }
 
 SIGTYPE
@@ -607,11 +638,11 @@ CatchErrorAndJump (AuServer *old_aud,
 /* Create a name from the sound. */
 
 static char *
-NameFromData (CONST unsigned char *buf,
+NameFromData (const char *buf,
              int len)
 
 {
-  unsigned char name[9];
+  char name[9];
   int i;
   char *s;
 
@@ -636,11 +667,11 @@ NameFromData (CONST unsigned char *buf,
 
   if (i==8)
     {
-      strcpy (s=malloc (10), name);
+      strcpy (s = (char *) malloc (10), name);
     }
   else 
     {
-      strcpy (s=malloc (15), "short sound");
+      strcpy (s = (char *) malloc (15), "short sound");
     }
 
   return s;
@@ -651,7 +682,7 @@ NameFromData (CONST unsigned char *buf,
  */
 
 static SndInfo *
-SndOpenDataForReading (CONST char *data,
+SndOpenDataForReading (const char *data,
                       int length)
 
 {
@@ -666,7 +697,7 @@ SndOpenDataForReading (CONST char *data,
 
   memcpy (&si->h, data, sizeof (SndHeader));
 
-  if (LITTLE_ENDIAN)
+  if (NAS_LITTLE_ENDIAN)
     {
       char            n;
     
@@ -711,6 +742,256 @@ SndOpenDataForReading (CONST char *data,
   return si;
 }
 
+/* Stuff taken from wave.c from NAS.  Just like snd files, NAS can't
+   read wave data from memory, so these functions do that for us. */
+
+#define Err()          { return NULL; }
+#define readFourcc(_f) dread(_f, sizeof(RIFF_FOURCC), 1)
+#define cmpID(_x, _y)                                                        \
+    strncmp((char *) (_x), (char *) (_y), sizeof(RIFF_FOURCC))
+#define PAD2(_x)       (((_x) + 1) & ~1)
+
+/* These functions here are for faking file I/O from buffer. */
+
+/* The "file" position */
+static size_t file_posn;
+/* The length of the "file" */
+static size_t file_len;
+/* The actual "file" data. */
+static const void* file_data;
+
+/* Like fopen, but for a buffer in memory */
+static void
+dopen (const void* data, size_t length)
+{
+   file_data = data;
+   file_len = length;
+   file_posn = 0;
+}
+
+/* Like fread, but for a buffer in memory */
+static int
+dread (void* buf, size_t size, size_t nitems)
+{
+  size_t nread = size * nitems;
+  
+  if (file_posn + nread <= file_len)
+    {
+      memcpy(buf, (char *) file_data + file_posn, size * nitems);
+      file_posn += nread;
+      return nitems;
+    }
+  else
+    {
+      return EOF;
+    }
+}
+
+/* Like fgetc, but for a buffer in memory */
+static int
+dgetc (void)
+{
+  if (file_posn < file_len)
+    return ((char *)file_data)[file_posn++];
+  else
+    return -1;
+}
+
+/* Like fseek, but for a buffer in memory */
+static int
+dseek (long offset, int from)
+{
+  if (from == 0)
+    file_posn = offset;
+  else if (from == 1)
+    file_posn += offset;
+  else if (from == 2)
+    file_posn = file_len + offset;
+
+  return 0;
+}
+
+/* Like ftell, but for a buffer in memory */
+static long
+dtell (void)
+{
+  return file_posn;
+}
+
+/* Data buffer analogs for FileReadS and FileReadL in NAS. */
+
+static unsigned short
+DataReadS (int swapit)
+{
+    unsigned short us;
+
+    dread(&us, 2, 1);
+    if (swapit)
+       us = FileSwapS(us);
+    return us;
+}
+
+static AuUint32
+DataReadL (int swapit)
+{
+    AuUint32 ul;
+
+    dread(&ul, 4, 1);
+    if (swapit)
+       ul = FileSwapL(ul);
+    return ul;
+}
+
+static int
+readChunk (RiffChunk *c)
+{
+    int             status;
+    char            n;
+
+    if ((status = dread(c, sizeof(RiffChunk), 1)))
+       if (NAS_BIG_ENDIAN)
+           swapl(&c->ckSize, n);
+
+    return status;
+}
+
+/* A very straight-forward translation of WaveOpenFileForReading to
+   read the wave data from a buffer in memory. */
+
+static WaveInfo *
+WaveOpenDataForReading (const char *data,
+                       int length)
+{
+    RiffChunk       ck;
+    RIFF_FOURCC     fourcc;
+    AuInt32            fileSize;
+    WaveInfo       *wi;
+
+    
+    if (!(wi = (WaveInfo *) malloc(sizeof(WaveInfo))))
+       return NULL;
+
+    wi->comment = NULL;
+    wi->dataOffset = wi->format = wi->writing = 0;
+
+    dopen(data, length);
+    
+    if (!readChunk(&ck) ||
+       cmpID(&ck.ckID, RIFF_RiffID) ||
+       !readFourcc(&fourcc) ||
+       cmpID(&fourcc, RIFF_WaveID))
+       Err();
+
+    fileSize = PAD2(ck.ckSize) - sizeof(RIFF_FOURCC);
+
+    while (fileSize >= sizeof(RiffChunk))
+    {
+       if (!readChunk(&ck))
+           Err();
+
+       fileSize -= sizeof(RiffChunk) + PAD2(ck.ckSize);
+
+       /* LIST chunk */
+       if (!cmpID(&ck.ckID, RIFF_ListID))
+       {
+           if (!readFourcc(&fourcc))
+               Err();
+
+           /* INFO chunk */
+           if (!cmpID(&fourcc, RIFF_ListInfoID))
+           {
+               ck.ckSize -= sizeof(RIFF_FOURCC);
+
+               while (ck.ckSize)
+               {
+                   RiffChunk       c;
+
+                   if (!readChunk(&c))
+                       Err();
+
+                   /* ICMT chunk */
+                   if (!cmpID(&c.ckID, RIFF_InfoIcmtID))
+                   {
+                       if (!(wi->comment = (char *) malloc(c.ckSize)) ||
+                           !dread(wi->comment, c.ckSize, 1))
+                           Err();
+
+                       if (c.ckSize & 1)
+                           dgetc();    /* eat the pad byte */
+                   }
+                   else
+                       /* skip unknown chunk */
+                       dseek(PAD2(c.ckSize), 1);
+
+                   ck.ckSize -= sizeof(RiffChunk) + PAD2(c.ckSize);
+               }
+           }
+           else
+               /* skip unknown chunk */
+               dseek(PAD2(ck.ckSize) - sizeof(RIFF_FOURCC), 1);
+       }
+       /* wave format chunk */
+       else if (!cmpID(&ck.ckID, RIFF_WaveFmtID) && !wi->format)
+       {
+           AuInt32            dummy;
+
+           wi->format = DataReadS(NAS_BIG_ENDIAN);
+           wi->channels = DataReadS(NAS_BIG_ENDIAN);
+           wi->sampleRate = DataReadL(NAS_BIG_ENDIAN);
+
+           /* we don't care about the next two fields */
+           dummy = DataReadL(NAS_BIG_ENDIAN);
+           dummy = DataReadS(NAS_BIG_ENDIAN);
+
+           if (wi->format != RIFF_WAVE_FORMAT_PCM)
+               Err();
+
+           wi->bitsPerSample = DataReadS(NAS_BIG_ENDIAN);
+
+           /* skip any other format specific fields */
+           dseek(PAD2(ck.ckSize - 16), 1);
+       }
+       /* wave data chunk */
+       else if (!cmpID(&ck.ckID, RIFF_WaveDataID) && !wi->dataOffset)
+       {
+           long endOfFile;
+
+           wi->dataOffset = dtell();
+           wi->dataSize = ck.ckSize;
+           dseek(0, 2);
+           endOfFile = dtell();
+
+           /* seek past the data */
+           if (dseek(wi->dataOffset + PAD2(ck.ckSize), 0) ||
+               dtell() > endOfFile)
+           {
+               /* the seek failed, assume the size is bogus */
+               dseek(0, 2);
+               wi->dataSize = dtell() - wi->dataOffset;
+           }
+
+           wi->dataOffset -= sizeof(long);
+       }
+       else
+           /* skip unknown chunk */
+           dseek(PAD2(ck.ckSize), 1);
+    }
+
+    if (!wi->dataOffset)
+       Err();
+
+    wi->numSamples = wi->dataSize / wi->channels / (wi->bitsPerSample >> 3);
+
+    if (!wi->comment)
+       wi->comment = NameFromData (data + wi->dataOffset,
+                                   length - wi->dataOffset);
+
+    wi->fp = NULL;
+    
+    return wi;
+}
+
+
 static Sound
 SoundOpenDataForReading (unsigned char *data,
                         int length)
@@ -721,20 +1002,24 @@ SoundOpenDataForReading (unsigned char *data,
   if (!(s = (Sound) malloc (sizeof (SoundRec))))
     return NULL;
 
-  if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
+  if ((s->formatInfo = SndOpenDataForReading ((char *) data, length)) != NULL)
     {
-      free (s);
-      return NULL;
+      if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatSnd].toSound)) (s))
+       {
+         SndCloseFile ((SndInfo *) (s->formatInfo));
+         free (s);
+         return NULL;
+       }
     }
-    
-
-  if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
+  else if ((s->formatInfo = WaveOpenDataForReading ((char *) data, length)) != NULL)
     {
-      SndCloseFile (s->formatInfo);
-      free (s);
-      return NULL;
+      if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatWave].toSound)) (s))
+       {
+         WaveCloseFile ((WaveInfo *) (s->formatInfo));
+         free (s);
+         return NULL;
+       }
     }
 
   return s;
 }
-