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