Mercurial > hg > xemacs-beta
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 |