comparison src/nas.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 3a7e78e1142d
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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