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