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