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