XEmacs 21.2.29 "Hestia".
[chise/xemacs-chise.git.1] / src / nas.c
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  *      1998-10-01 rlt  Added support for WAVE files.
53  */
54
55 #ifdef emacs
56 #include <config.h>
57 #include "lisp.h"
58 #include "sysdep.h"
59 #include "syssignal.h"
60 #endif
61
62 #include <stdlib.h>
63 #include <stdarg.h>
64 #include <string.h>
65 #include <stdio.h>
66
67 #ifdef HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif
70
71
72 #undef LITTLE_ENDIAN
73 #undef BIG_ENDIAN
74 #include <audio/audiolib.h>
75 #include <audio/soundlib.h>
76 #include <audio/snd.h>
77 #include <audio/wave.h>
78 #include <audio/fileutil.h>
79
80 #ifdef emacs
81
82 #    define XTOOLKIT
83 #    define XTEVENTS
84 #    define ROBUST_PLAY
85 #    define CACHE_SOUNDS
86
87     /*
88      * For old NAS libraries, force playing to be synchronous
89      * and declare the long jump point locally.
90      */
91
92 #    if defined (NAS_NO_ERROR_JUMP)
93
94 #       undef XTEVENTS
95
96 #       include <setjmp.h>
97         jmp_buf AuXtErrorJump;
98 #    endif
99
100      /* The GETTEXT is correct. --ben */
101 #    define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str))
102
103 #    define play_sound_file nas_play_sound_file
104 #    define play_sound_data nas_play_sound_data
105 #    define wait_for_sounds nas_wait_for_sounds
106 #    define init_play       nas_init_play
107 #    define close_down_play nas_close_down_play
108
109 #else /* !emacs */
110 #    define warn(str) fprintf (stderr, "%s\n", (str))
111 #endif /* emacs */
112
113 #ifdef XTOOLKIT
114 #    include <X11/Intrinsic.h>
115 #    include <audio/Xtutil.h>
116 #endif
117
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);
122 #endif
123
124 extern Lisp_Object Vsynchronous_sounds;
125
126 static Sound SoundOpenDataForReading (unsigned char *data, int length);
127
128 static AuServer       *aud;
129
130 /* count of sounds currently being played. */
131 static int sounds_in_play;
132
133
134 #ifdef XTOOLKIT
135 static Display *aud_server;
136 static XtInputId input_id;
137 #else
138 static char *aud_server;
139 #endif /* XTOOLKIT */
140
141 char *
142 init_play (
143 #ifdef XTOOLKIT
144            Display *display
145 #else
146            char *server
147 #endif
148            );
149 char *
150 init_play (
151 #ifdef XTOOLKIT
152            Display *display
153 #else
154            char *server
155 #endif
156            )
157 {
158   char *err_message;
159   SIGTYPE (*old_sigpipe) (int);
160
161 #ifdef XTOOLKIT
162   char * server = DisplayString (display);
163   XtAppContext app_context = XtDisplayToApplicationContext (display);
164
165   aud_server = display;
166 #else
167
168   aud_server = server;
169 #endif
170
171 #ifdef ROBUST_PLAY
172   old_sigpipe = signal (SIGPIPE, sigpipe_handle);
173   if (setjmp (AuXtErrorJump))
174     {
175       signal (SIGPIPE, old_sigpipe);
176 #ifdef emacs
177       start_interrupts ();
178 #endif  
179       return "error in NAS";
180     }
181 #endif
182
183 #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP)
184   AuDefaultIOErrorHandler = CatchIoErrorAndJump;
185   AuDefaultErrorHandler = CatchErrorAndJump;
186 #endif
187
188 #ifdef emacs
189   stop_interrupts ();
190 #endif  
191   aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message);
192 #ifdef emacs
193   start_interrupts ();
194 #endif  
195   if (!aud)
196     {
197 #ifdef ROBUST_PLAY
198       signal (SIGPIPE, old_sigpipe);
199 #endif
200       if (err_message == NULL)
201         return "Can't connect to audio server";
202       else
203         return err_message;
204     }
205
206 #if defined (ROBUST_PLAY)
207 # if defined (NAS_NO_ERROR_JUMP)
208   aud->funcs.ioerror_handler = CatchIoErrorAndJump;
209   aud->funcs.error_handler = CatchErrorAndJump;
210 # else /* !NAS_NO_ERROR_JUMP */
211   AuDefaultIOErrorHandler = NULL;
212   AuDefaultErrorHandler = NULL;
213 # endif
214 #endif
215
216 #ifdef XTEVENTS
217   input_id = AuXtAppAddAudioHandler (app_context, aud); 
218 #endif
219
220 #ifdef CACHE_SOUNDS
221   AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL);
222 #endif
223
224 #ifdef ROBUST_PLAY
225   signal (SIGPIPE, old_sigpipe);
226 #endif
227
228   sounds_in_play = 0;
229
230   return NULL;
231 }
232
233 static void
234 close_down_play (void)
235
236 {
237   AuCloseServer (aud);
238   warn ("disconnected from audio server");
239 }
240
241  /********************************************************************\
242  *                                                                    *
243  * Callback which is run when the sound finishes playing.             *
244  *                                                                    *
245  \********************************************************************/
246
247 static void
248 doneCB (AuServer       *auserver,
249         AuEventHandlerRec *handler,
250         AuEvent        *ev,
251         AuPointer       data)
252 {
253   int         *in_play_p = (int *) data;
254
255   (*in_play_p) --;
256 }
257
258 #ifdef CACHE_SOUNDS
259
260  /********************************************************************\
261  *                                                                    *
262  * Play a sound by playing the relevant bucket, if any or             *
263  * downloading it if not.                                             *
264  *                                                                    *
265  \********************************************************************/
266
267 static void
268 do_caching_play (Sound s,
269                  int volume,
270                  unsigned char *buf)
271
272 {
273   AuBucketAttributes *list, b;
274   AuBucketID      id;
275   int n;
276
277   AuSetString (AuBucketDescription (&b),
278                AuStringLatin1, strlen (SoundComment (s)), SoundComment (s));
279
280   list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL);
281
282   if (list == NULL)
283     {
284       AuPointer my_buf;
285
286       if (buf==NULL)
287         {
288           if ((my_buf= (AuPointer) malloc (SoundNumBytes (s)))==NULL)
289             {
290               return;
291             }
292
293           if (SoundReadFile ((char *) my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
294             {
295               free (my_buf);
296               return;
297             }
298         }
299       else
300         my_buf = (AuPointer) buf;
301
302       id = AuSoundCreateBucketFromData (aud, 
303                                         s,
304                                         my_buf,
305                                         AuAccessAllMasks, 
306                                         NULL,
307                                         NULL);
308       if (buf == NULL)
309         free (my_buf);
310     }
311   else /* found cached sound */
312     {
313       id = AuBucketIdentifier (list);
314       AuFreeBucketAttributes (aud, n, list);
315     }
316
317   sounds_in_play++;
318
319   AuSoundPlayFromBucket (aud, 
320                          id, 
321                          AuNone,
322                          AuFixedPointFromFraction (volume, 100), 
323                          doneCB, (AuPointer) &sounds_in_play,
324                          1,
325                          NULL, NULL,
326                          NULL, NULL);
327
328 }
329 #endif /* CACHE_SOUNDS */
330
331
332 void wait_for_sounds (void);
333 void 
334 wait_for_sounds (void)
335
336 {
337   AuEvent         ev;
338
339   while (sounds_in_play>0)
340     {
341       AuNextEvent (aud, AuTrue, &ev);
342       AuDispatchEvent (aud, &ev);
343     }
344 }
345
346 int play_sound_file (char *sound_file, int volume);
347 int
348 play_sound_file (char *sound_file,
349                  int volume)
350 {
351   SIGTYPE (*old_sigpipe) (int);
352
353 #ifdef ROBUST_PLAY
354   old_sigpipe=signal (SIGPIPE, sigpipe_handle);
355   if (setjmp (AuXtErrorJump))
356     {
357       signal (SIGPIPE, old_sigpipe);
358       return 0;
359     }
360 #endif
361
362   if (aud==NULL) {
363     if (aud_server != NULL)
364       {
365         char *m;
366         /* attempt to reconect */
367         if ((m=init_play (aud_server))!= NULL)
368           {
369
370 #ifdef ROBUST_PLAY
371             signal (SIGPIPE, old_sigpipe);
372 #endif
373             return 0;
374           }
375       }
376     else
377       {
378         warn ("Attempt to play with no audio init\n");
379 #ifdef ROBUST_PLAY
380         signal (SIGPIPE, old_sigpipe);
381 #endif
382         return 0;
383       }
384   }
385
386 #ifndef CACHE_SOUNDS
387   sounds_in_play++;
388   AuSoundPlayFromFile (aud,
389                        sound_file,
390                        AuNone,
391                        AuFixedPointFromFraction (volume,100),
392                        doneCB, (AuPointer) &sounds_in_play,
393                        NULL,
394                        NULL,
395                        NULL,
396                        NULL);
397 #else
398   /* Cache the sounds in buckets on the server */
399
400   {
401     Sound s;
402
403     if ((s = SoundOpenFileForReading (sound_file))==NULL)
404       {
405 #ifdef ROBUST_PLAY
406         signal (SIGPIPE, old_sigpipe);
407 #endif
408         return 0;
409       }
410
411     if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0')
412       {
413         SoundComment (s) = FileCommentFromFilename (sound_file);
414       }
415
416     do_caching_play (s, volume, NULL);
417
418     SoundCloseFile (s);
419
420   }
421 #endif /* CACHE_SOUNDS */
422
423 #ifndef XTEVENTS
424   wait_for_sounds ();
425 #else
426   if (!NILP (Vsynchronous_sounds))
427     {
428       wait_for_sounds ();
429     }
430 #endif
431
432 #ifdef ROBUST_PLAY
433   signal (SIGPIPE, old_sigpipe);
434 #endif
435
436   return 1;
437 }
438
439 int play_sound_data (unsigned char *data, int length, int volume);
440 int
441 play_sound_data (unsigned char *data,
442                  int length, 
443                  int volume)
444 {
445   Sound s;
446   int offset;
447   SIGTYPE (*old_sigpipe) (int);
448
449 #if !defined (XTEVENTS)
450   AuEvent         ev;
451 #endif
452
453 #ifdef ROBUST_PLAY
454   old_sigpipe = signal (SIGPIPE, sigpipe_handle);
455   if (setjmp (AuXtErrorJump) !=0)
456     {
457       signal (SIGPIPE, old_sigpipe);
458       return 0;
459     }
460 #endif
461
462
463   if (aud == NULL) {
464     if (aud_server != NULL)
465       {
466         char *m;
467         /* attempt to reconect */
468         if ((m = init_play (aud_server)) != NULL)
469           {
470 #ifdef ROBUST_PLAY
471             signal (SIGPIPE, old_sigpipe);
472 #endif
473             return 0;
474           }
475       }
476     else
477       {
478         warn ("Attempt to play with no audio init\n");
479 #ifdef ROBUST_PLAY
480         signal (SIGPIPE, old_sigpipe);
481 #endif
482         return 0;
483       }
484   }
485
486   if ((s=SoundOpenDataForReading (data, length))==NULL)
487     {
488       warn ("unknown sound type");
489 #ifdef ROBUST_PLAY
490       signal (SIGPIPE, old_sigpipe);
491 #endif
492       return 0;
493     }
494
495   if (SoundFileFormat (s) == SoundFileFormatSnd)
496     {
497       /* hack, hack */
498       offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
499     }
500   else if (SoundFileFormat (s) == SoundFileFormatWave)
501     {
502       offset = ((WaveInfo *) (s->formatInfo))->dataOffset;
503     }
504   else
505     {
506       warn ("only understand snd and wave files at the moment");
507       SoundCloseFile (s);
508 #ifdef ROBUST_PLAY
509       signal (SIGPIPE, old_sigpipe);
510 #endif
511       return 0;
512     }
513
514 #ifndef CACHE_SOUNDS
515   sounds_in_play++;
516   AuSoundPlayFromData (aud,
517                        s,
518                        data+offset,
519                        AuNone,
520                        AuFixedPointFromFraction (volume,100),
521                        doneCB, (AuPointer) &sounds_in_play,
522                        NULL,
523                        NULL,
524                        NULL,
525                        NULL);
526 #else
527   /* Cache the sounds in buckets on the server */
528
529   {
530     do_caching_play (s, volume, data+offset);
531   }
532 #endif /* CACHE_SOUNDS */
533
534
535 #ifndef XTEVENTS
536   wait_for_sounds ();
537 #else
538   if (!NILP (Vsynchronous_sounds))
539     {
540       wait_for_sounds ();
541     }
542 #endif
543
544   SoundCloseFile (s); 
545
546 #ifdef ROBUST_PLAY
547   signal (SIGPIPE, old_sigpipe);
548 #endif
549
550   return 1;
551 }
552
553 #if defined (ROBUST_PLAY)
554
555  /********************************************************************\
556  *                                                                    *
557  * Code to protect the client from server shutdowns.                  *
558  *                                                                    *
559  * This is unbelievably horrible.                                     *
560  *                                                                    *
561  \********************************************************************/
562
563 static AuBool
564 CatchIoErrorAndJump (AuServer *old_aud)
565 {
566   if (old_aud)
567     warn ("Audio Server connection broken"); 
568   else
569     warn ("Audio Server connection broken because of signal");
570
571 #ifdef XTEVENTS
572 #ifdef XTOOLKIT
573   {
574     AuXtAppRemoveAudioHandler (aud, input_id); 
575   }
576 #endif
577
578   if (aud)
579     AuCloseServer (aud);
580   aud = NULL;
581   sounds_in_play = 0;
582
583   longjmp (AuXtErrorJump, 1);
584
585 #else /* not XTEVENTS */
586
587   if (aud)
588     AuCloseServer (aud);
589   aud = NULL;
590   sounds_in_play = 0;
591   longjmp (AuXtErrorJump, 1);
592  
593 #endif /* XTEVENTS */
594   return 0;
595 }
596
597 SIGTYPE
598 sigpipe_handle (int signo)
599 {
600   CatchIoErrorAndJump (NULL);
601 }
602
603 static AuBool
604 CatchErrorAndJump (AuServer *old_aud,
605                    AuErrorEvent *event)
606 {
607   return CatchIoErrorAndJump (old_aud);
608 }
609
610 #endif /* ROBUST_PLAY */
611
612  /********************************************************************\
613  *                                                                    *
614  * This code is here because the nas Sound library doesn't            *
615  * support playing from a file buffered in memory. It's a fairly      *
616  * direct translation of the file-based equivalent.                   *
617  *                                                                    *
618  * Since we don't have a filename, samples with no comment field      *
619  * are named by a section of their content.                           *
620  *                                                                    *
621  \********************************************************************/
622
623 /* Create a name from the sound. */
624
625 static char *
626 NameFromData (const char *buf,
627               int len)
628
629 {
630   char name[9];
631   int i;
632   char *s;
633
634   buf+=len/2;
635   len -= len/2;
636
637   i=0;
638   while (i<8 && len >0)
639     {
640       while (*buf < 32 && len>0)
641         {
642           buf++;
643           len--;
644         }
645       name[i]= *buf;
646       i++;
647       buf++;
648       len--;
649     }
650
651   name[i]='\0';
652
653   if (i==8)
654     {
655       strcpy (s = (char *) malloc (10), name);
656     }
657   else 
658     {
659       strcpy (s = (char *) malloc (15), "short sound");
660     }
661
662   return s;
663 }
664
665 /* Code to do a pseudo-open on a data buffer. Only for snd files at the
666    moment. 
667  */
668
669 static SndInfo *
670 SndOpenDataForReading (const char *data,
671                        int length)
672
673 {
674   SndInfo        *si;
675   int             size;
676
677   if (!(si = (SndInfo *) malloc (sizeof (SndInfo))))
678     return NULL;
679
680   si->comment = NULL;
681   si->writing = 0;
682
683   memcpy (&si->h, data, sizeof (SndHeader));
684
685   if (LITTLE_ENDIAN)
686     {
687       char            n;
688     
689       swapl (&si->h.magic, n);
690       swapl (&si->h.dataOffset, n);
691       swapl (&si->h.dataSize, n);
692       swapl (&si->h.format, n);
693       swapl (&si->h.sampleRate, n);
694       swapl (&si->h.tracks, n);
695     }
696
697   if (si->h.magic != SND_MAGIC_NUM)
698     {
699       free (si);
700       return NULL;
701     }
702
703   size = si->h.dataOffset - sizeof (SndHeader);
704
705   if (size)
706     {
707       if (!(si->comment = (char *) malloc (size + 1)))
708         {
709           free (si);
710           return NULL;
711         }
712
713       memcpy (si->comment,  data+sizeof (SndHeader), size);
714
715       *(si->comment + size) = 0;
716       if (*si->comment == '\0')
717         si->comment =
718           NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
719     }
720   else
721     si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
722
723   si->h.dataSize = length-si->h.dataOffset;
724
725   si->fp=NULL;
726
727   return si;
728 }
729
730 /* Stuff taken from wave.c from NAS.  Just like snd files, NAS can't
731    read wave data from memory, so these functions do that for us. */
732
733 #define Err()           { return NULL; }
734 #define readFourcc(_f)  dread(_f, sizeof(RIFF_FOURCC), 1)
735 #define cmpID(_x, _y)                                                         \
736     strncmp((char *) (_x), (char *) (_y), sizeof(RIFF_FOURCC))
737 #define PAD2(_x)        (((_x) + 1) & ~1)
738
739 /* These functions here are for faking file I/O from buffer. */
740
741 /* The "file" position */
742 static size_t file_posn;
743 /* The length of the "file" */
744 static size_t file_len;
745 /* The actual "file" data. */
746 static const void* file_data;
747
748 /* Like fopen, but for a buffer in memory */
749 static void
750 dopen (const void* data, size_t length)
751 {
752    file_data = data;
753    file_len = length;
754    file_posn = 0;
755 }
756
757 /* Like fread, but for a buffer in memory */
758 static int
759 dread (void* buf, size_t size, size_t nitems)
760 {
761   size_t nread = size * nitems;
762   
763   if (file_posn + nread <= file_len)
764     {
765       memcpy(buf, (char *) file_data + file_posn, size * nitems);
766       file_posn += nread;
767       return nitems;
768     }
769   else
770     {
771       return EOF;
772     }
773 }
774
775 /* Like fgetc, but for a buffer in memory */
776 static int
777 dgetc (void)
778 {
779   if (file_posn < file_len)
780     return ((char *)file_data)[file_posn++];
781   else
782     return -1;
783 }
784
785 /* Like fseek, but for a buffer in memory */
786 static int
787 dseek (long offset, int from)
788 {
789   if (from == 0)
790     file_posn = offset;
791   else if (from == 1)
792     file_posn += offset;
793   else if (from == 2)
794     file_posn = file_len + offset;
795
796   return 0;
797 }
798
799 /* Like ftell, but for a buffer in memory */
800 static long
801 dtell (void)
802 {
803   return file_posn;
804 }
805
806 /* Data buffer analogs for FileReadS and FileReadL in NAS. */
807
808 static unsigned short
809 DataReadS (int swapit)
810 {
811     unsigned short us;
812
813     dread(&us, 2, 1);
814     if (swapit)
815         us = FileSwapS(us);
816     return us;
817 }
818
819 static AuUint32
820 DataReadL (int swapit)
821 {
822     AuUint32 ul;
823
824     dread(&ul, 4, 1);
825     if (swapit)
826         ul = FileSwapL(ul);
827     return ul;
828 }
829
830 static int
831 readChunk (RiffChunk *c)
832 {
833     int             status;
834     char            n;
835
836     if ((status = dread(c, sizeof(RiffChunk), 1)))
837         if (BIG_ENDIAN)
838             swapl(&c->ckSize, n);
839
840     return status;
841 }
842
843 /* A very straight-forward translation of WaveOpenFileForReading to
844    read the wave data from a buffer in memory. */
845
846 static WaveInfo *
847 WaveOpenDataForReading (const char *data,
848                         int length)
849 {
850     RiffChunk       ck;
851     RIFF_FOURCC     fourcc;
852     AuInt32            fileSize;
853     WaveInfo       *wi;
854
855     
856     if (!(wi = (WaveInfo *) malloc(sizeof(WaveInfo))))
857         return NULL;
858
859     wi->comment = NULL;
860     wi->dataOffset = wi->format = wi->writing = 0;
861
862     dopen(data, length);
863     
864     if (!readChunk(&ck) ||
865         cmpID(&ck.ckID, RIFF_RiffID) ||
866         !readFourcc(&fourcc) ||
867         cmpID(&fourcc, RIFF_WaveID))
868         Err();
869
870     fileSize = PAD2(ck.ckSize) - sizeof(RIFF_FOURCC);
871
872     while (fileSize >= sizeof(RiffChunk))
873     {
874         if (!readChunk(&ck))
875             Err();
876
877         fileSize -= sizeof(RiffChunk) + PAD2(ck.ckSize);
878
879         /* LIST chunk */
880         if (!cmpID(&ck.ckID, RIFF_ListID))
881         {
882             if (!readFourcc(&fourcc))
883                 Err();
884
885             /* INFO chunk */
886             if (!cmpID(&fourcc, RIFF_ListInfoID))
887             {
888                 ck.ckSize -= sizeof(RIFF_FOURCC);
889
890                 while (ck.ckSize)
891                 {
892                     RiffChunk       c;
893
894                     if (!readChunk(&c))
895                         Err();
896
897                     /* ICMT chunk */
898                     if (!cmpID(&c.ckID, RIFF_InfoIcmtID))
899                     {
900                         if (!(wi->comment = (char *) malloc(c.ckSize)) ||
901                             !dread(wi->comment, c.ckSize, 1))
902                             Err();
903
904                         if (c.ckSize & 1)
905                             dgetc();    /* eat the pad byte */
906                     }
907                     else
908                         /* skip unknown chunk */
909                         dseek(PAD2(c.ckSize), 1);
910
911                     ck.ckSize -= sizeof(RiffChunk) + PAD2(c.ckSize);
912                 }
913             }
914             else
915                 /* skip unknown chunk */
916                 dseek(PAD2(ck.ckSize) - sizeof(RIFF_FOURCC), 1);
917         }
918         /* wave format chunk */
919         else if (!cmpID(&ck.ckID, RIFF_WaveFmtID) && !wi->format)
920         {
921             AuInt32            dummy;
922
923             wi->format = DataReadS(BIG_ENDIAN);
924             wi->channels = DataReadS(BIG_ENDIAN);
925             wi->sampleRate = DataReadL(BIG_ENDIAN);
926
927             /* we don't care about the next two fields */
928             dummy = DataReadL(BIG_ENDIAN);
929             dummy = DataReadS(BIG_ENDIAN);
930
931             if (wi->format != RIFF_WAVE_FORMAT_PCM)
932                 Err();
933
934             wi->bitsPerSample = DataReadS(BIG_ENDIAN);
935
936             /* skip any other format specific fields */
937             dseek(PAD2(ck.ckSize - 16), 1);
938         }
939         /* wave data chunk */
940         else if (!cmpID(&ck.ckID, RIFF_WaveDataID) && !wi->dataOffset)
941         {
942             long endOfFile;
943
944             wi->dataOffset = dtell();
945             wi->dataSize = ck.ckSize;
946             dseek(0, 2);
947             endOfFile = dtell();
948
949             /* seek past the data */
950             if (dseek(wi->dataOffset + PAD2(ck.ckSize), 0) ||
951                 dtell() > endOfFile)
952             {
953                 /* the seek failed, assume the size is bogus */
954                 dseek(0, 2);
955                 wi->dataSize = dtell() - wi->dataOffset;
956             }
957
958             wi->dataOffset -= sizeof(long);
959         }
960         else
961             /* skip unknown chunk */
962             dseek(PAD2(ck.ckSize), 1);
963     }
964
965     if (!wi->dataOffset)
966         Err();
967
968     wi->numSamples = wi->dataSize / wi->channels / (wi->bitsPerSample >> 3);
969
970     if (!wi->comment)
971        wi->comment = NameFromData (data + wi->dataOffset,
972                                    length - wi->dataOffset);
973
974     wi->fp = NULL;
975     
976     return wi;
977 }
978
979
980 static Sound
981 SoundOpenDataForReading (unsigned char *data,
982                          int length)
983
984 {
985   Sound s;
986
987   if (!(s = (Sound) malloc (sizeof (SoundRec))))
988     return NULL;
989
990   if ((s->formatInfo = SndOpenDataForReading ((char *) data, length)) != NULL)
991     {
992       if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatSnd].toSound)) (s))
993         {
994           SndCloseFile ((SndInfo *) (s->formatInfo));
995           free (s);
996           return NULL;
997         }
998     }
999   else if ((s->formatInfo = WaveOpenDataForReading ((char *) data, length)) != NULL)
1000     {
1001       if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatWave].toSound)) (s))
1002         {
1003           WaveCloseFile ((WaveInfo *) (s->formatInfo));
1004           free (s);
1005           return NULL;
1006         }
1007     }
1008
1009   return s;
1010 }