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