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