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