Mercurial > hg > xemacs-beta
view src/nas.c @ 444:576fb035e263 r21-2-37
Import from CVS: tag r21-2-37
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:36:19 +0200 |
parents | abe6d1db359e |
children | 183866b06e0b |
line wrap: on
line source
/* nas.c --- XEmacs support for the Network Audio System server. * * Author: Richard Caley <R.Caley@ed.ac.uk> * * Copyright 1994 Free Software Foundation, Inc. * Copyright 1993 Network Computing Devices, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name Network Computing Devices, Inc. not be * used in advertising or publicity pertaining to distribution of this * software without specific, written prior permission. * * THIS SOFTWARE IS PROVIDED 'AS-IS'. NETWORK COMPUTING DEVICES, INC., * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL NETWORK * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Synched up with: Not in FSF. */ /* There are four compile-time options. * * XTOOLKIT This will be part of an Xt program. * * XTEVENTS The playing will be supervised asynchronously by the Xt event * loop. If not set, playing will be completed within the call * to play_file etc. * * ROBUST_PLAY Causes errors in nas to be caught. This means that the * program will attempt not to die if the nas server does. * * CACHE_SOUNDS Causes the sounds to be played in buckets in the NAS * server. They are named by their comment field, or if that is * empty by the filename, or for play_sound_data by a name made up * from the sample itself. */ /* CHANGES: * 10/8/94, rjc Changed names from netaudio to nas * Added back asynchronous play if nas library has * correct error facilities. * 4/11/94, rjc Added wait_for_sounds to be called when user wants to * be sure all play has finished. * 1998-10-01 rlt Added support for WAVE files. */ #ifdef emacs #include <config.h> #include "lisp.h" #include "sysdep.h" #include "syssignal.h" #endif #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <stdio.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif /* NAS <= 1.2p5 defines {BIG,LITTLE}_ENDIAN in <audio/fileutil.h>, conflicting with GNU libc (at least); newer versions avoid this name space pollution. DO NOT USE THOSE MACROS in this file. Use NAS_{BIG,LITTLE}_ENDIAN. It would be slightly more reliable to do this via configure, but that seems unnecessarily complex. */ #undef LITTLE_ENDIAN #undef BIG_ENDIAN #include <audio/audiolib.h> #include <audio/soundlib.h> #include <audio/snd.h> #include <audio/wave.h> #include <audio/fileutil.h> /* NAS <= 1.2p5 <audio/fileutil.h> doesn't define the NAS_ versions */ #ifndef NAS_LITTLE_ENDIAN #define NAS_LITTLE_ENDIAN LITTLE_ENDIAN #define NAS_BIG_ENDIAN BIG_ENDIAN #endif #ifdef emacs # define XTOOLKIT # define XTEVENTS # define ROBUST_PLAY # define CACHE_SOUNDS /* * For old NAS libraries, force playing to be synchronous * and declare the long jump point locally. */ # if defined (NAS_NO_ERROR_JUMP) # undef XTEVENTS # include <setjmp.h> jmp_buf AuXtErrorJump; # endif /* The GETTEXT is correct. --ben */ # define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str)) # define play_sound_file nas_play_sound_file # define play_sound_data nas_play_sound_data # define wait_for_sounds nas_wait_for_sounds # define init_play nas_init_play # define close_down_play nas_close_down_play #else /* !emacs */ # define warn(str) fprintf (stderr, "%s\n", (str)) #endif /* emacs */ #ifdef XTOOLKIT # include <X11/Intrinsic.h> # include <audio/Xtutil.h> #endif #if defined (ROBUST_PLAY) static AuBool CatchIoErrorAndJump (AuServer *aud); static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event); SIGTYPE sigpipe_handle (int signo); #endif extern Lisp_Object Vsynchronous_sounds; static Sound SoundOpenDataForReading (unsigned char *data, int length); static AuServer *aud; /* count of sounds currently being played. */ static int sounds_in_play; #ifdef XTOOLKIT static Display *aud_server; static XtInputId input_id; #else static char *aud_server; #endif /* XTOOLKIT */ char * init_play ( #ifdef XTOOLKIT Display *display #else char *server #endif ); char * init_play ( #ifdef XTOOLKIT Display *display #else char *server #endif ) { char *err_message; SIGTYPE (*old_sigpipe) (int); #ifdef XTOOLKIT char * server = DisplayString (display); XtAppContext app_context = XtDisplayToApplicationContext (display); aud_server = display; #else aud_server = server; #endif #ifdef ROBUST_PLAY old_sigpipe = signal (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump)) { signal (SIGPIPE, old_sigpipe); #ifdef emacs start_interrupts (); #endif return "error in NAS"; } #endif #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP) AuDefaultIOErrorHandler = CatchIoErrorAndJump; AuDefaultErrorHandler = CatchErrorAndJump; #endif #ifdef emacs stop_interrupts (); #endif aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message); #ifdef emacs start_interrupts (); #endif if (!aud) { #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif if (err_message == NULL) return "Can't connect to audio server"; else return err_message; } #if defined (ROBUST_PLAY) # if defined (NAS_NO_ERROR_JUMP) aud->funcs.ioerror_handler = CatchIoErrorAndJump; aud->funcs.error_handler = CatchErrorAndJump; # else /* !NAS_NO_ERROR_JUMP */ AuDefaultIOErrorHandler = NULL; AuDefaultErrorHandler = NULL; # endif #endif #ifdef XTEVENTS input_id = AuXtAppAddAudioHandler (app_context, aud); #endif #ifdef CACHE_SOUNDS AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL); #endif #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif sounds_in_play = 0; return NULL; } static void close_down_play (void) { AuCloseServer (aud); warn ("disconnected from audio server"); } /********************************************************************\ * * * Callback which is run when the sound finishes playing. * * * \********************************************************************/ static void doneCB (AuServer *auserver, AuEventHandlerRec *handler, AuEvent *ev, AuPointer data) { int *in_play_p = (int *) data; (*in_play_p) --; } #ifdef CACHE_SOUNDS /********************************************************************\ * * * Play a sound by playing the relevant bucket, if any or * * downloading it if not. * * * \********************************************************************/ static void do_caching_play (Sound s, int volume, unsigned char *buf) { AuBucketAttributes *list, b; AuBucketID id; int n; AuSetString (AuBucketDescription (&b), AuStringLatin1, strlen (SoundComment (s)), SoundComment (s)); list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL); if (list == NULL) { AuPointer my_buf; if (buf==NULL) { if ((my_buf= (AuPointer) malloc (SoundNumBytes (s)))==NULL) { return; } if (SoundReadFile ((char *) my_buf, SoundNumBytes (s), s) != SoundNumBytes (s)) { free (my_buf); return; } } else my_buf = (AuPointer) buf; id = AuSoundCreateBucketFromData (aud, s, my_buf, AuAccessAllMasks, NULL, NULL); if (buf == NULL) free (my_buf); } else /* found cached sound */ { id = AuBucketIdentifier (list); AuFreeBucketAttributes (aud, n, list); } sounds_in_play++; AuSoundPlayFromBucket (aud, id, AuNone, AuFixedPointFromFraction (volume, 100), doneCB, (AuPointer) &sounds_in_play, 1, NULL, NULL, NULL, NULL); } #endif /* CACHE_SOUNDS */ void wait_for_sounds (void); void wait_for_sounds (void) { AuEvent ev; while (sounds_in_play>0) { AuNextEvent (aud, AuTrue, &ev); AuDispatchEvent (aud, &ev); } } int play_sound_file (char *sound_file, int volume); int play_sound_file (char *sound_file, int volume) { SIGTYPE (*old_sigpipe) (int); #ifdef ROBUST_PLAY old_sigpipe=signal (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump)) { signal (SIGPIPE, old_sigpipe); return 0; } #endif if (aud==NULL) { if (aud_server != NULL) { char *m; /* attempt to reconect */ if ((m=init_play (aud_server))!= NULL) { #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } } else { warn ("Attempt to play with no audio init\n"); #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } } #ifndef CACHE_SOUNDS sounds_in_play++; AuSoundPlayFromFile (aud, sound_file, AuNone, AuFixedPointFromFraction (volume,100), doneCB, (AuPointer) &sounds_in_play, NULL, NULL, NULL, NULL); #else /* Cache the sounds in buckets on the server */ { Sound s; if ((s = SoundOpenFileForReading (sound_file))==NULL) { #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0') { SoundComment (s) = FileCommentFromFilename (sound_file); } do_caching_play (s, volume, NULL); SoundCloseFile (s); } #endif /* CACHE_SOUNDS */ #ifndef XTEVENTS wait_for_sounds (); #else if (!NILP (Vsynchronous_sounds)) { wait_for_sounds (); } #endif #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 1; } int play_sound_data (unsigned char *data, int length, int volume); int play_sound_data (unsigned char *data, int length, int volume) { Sound s; int offset; SIGTYPE (*old_sigpipe) (int); #if !defined (XTEVENTS) AuEvent ev; #endif #ifdef ROBUST_PLAY old_sigpipe = signal (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump) !=0) { signal (SIGPIPE, old_sigpipe); return 0; } #endif if (aud == NULL) { if (aud_server != NULL) { char *m; /* attempt to reconect */ if ((m = init_play (aud_server)) != NULL) { #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } } else { warn ("Attempt to play with no audio init\n"); #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } } if ((s=SoundOpenDataForReading (data, length))==NULL) { warn ("unknown sound type"); #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } if (SoundFileFormat (s) == SoundFileFormatSnd) { /* hack, hack */ offset = ((SndInfo *) (s->formatInfo))->h.dataOffset; } else if (SoundFileFormat (s) == SoundFileFormatWave) { offset = ((WaveInfo *) (s->formatInfo))->dataOffset; } else { warn ("only understand snd and wave files at the moment"); SoundCloseFile (s); #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 0; } #ifndef CACHE_SOUNDS sounds_in_play++; AuSoundPlayFromData (aud, s, data+offset, AuNone, AuFixedPointFromFraction (volume,100), doneCB, (AuPointer) &sounds_in_play, NULL, NULL, NULL, NULL); #else /* Cache the sounds in buckets on the server */ { do_caching_play (s, volume, data+offset); } #endif /* CACHE_SOUNDS */ #ifndef XTEVENTS wait_for_sounds (); #else if (!NILP (Vsynchronous_sounds)) { wait_for_sounds (); } #endif SoundCloseFile (s); #ifdef ROBUST_PLAY signal (SIGPIPE, old_sigpipe); #endif return 1; } #if defined (ROBUST_PLAY) /********************************************************************\ * * * Code to protect the client from server shutdowns. * * * * This is unbelievably horrible. * * * \********************************************************************/ static AuBool CatchIoErrorAndJump (AuServer *old_aud) { if (old_aud) warn ("Audio Server connection broken"); else warn ("Audio Server connection broken because of signal"); #ifdef XTEVENTS #ifdef XTOOLKIT { AuXtAppRemoveAudioHandler (aud, input_id); } #endif if (aud) AuCloseServer (aud); aud = NULL; sounds_in_play = 0; longjmp (AuXtErrorJump, 1); #else /* not XTEVENTS */ if (aud) AuCloseServer (aud); aud = NULL; sounds_in_play = 0; longjmp (AuXtErrorJump, 1); #endif /* XTEVENTS */ return 0; } SIGTYPE sigpipe_handle (int signo) { CatchIoErrorAndJump (NULL); } static AuBool CatchErrorAndJump (AuServer *old_aud, AuErrorEvent *event) { return CatchIoErrorAndJump (old_aud); } #endif /* ROBUST_PLAY */ /********************************************************************\ * * * This code is here because the nas Sound library doesn't * * support playing from a file buffered in memory. It's a fairly * * direct translation of the file-based equivalent. * * * * Since we don't have a filename, samples with no comment field * * are named by a section of their content. * * * \********************************************************************/ /* Create a name from the sound. */ static char * NameFromData (const char *buf, int len) { char name[9]; int i; char *s; buf+=len/2; len -= len/2; i=0; while (i<8 && len >0) { while (*buf < 32 && len>0) { buf++; len--; } name[i]= *buf; i++; buf++; len--; } name[i]='\0'; if (i==8) { strcpy (s = (char *) malloc (10), name); } else { strcpy (s = (char *) malloc (15), "short sound"); } return s; } /* Code to do a pseudo-open on a data buffer. Only for snd files at the moment. */ static SndInfo * SndOpenDataForReading (const char *data, int length) { SndInfo *si; int size; if (!(si = (SndInfo *) malloc (sizeof (SndInfo)))) return NULL; si->comment = NULL; si->writing = 0; memcpy (&si->h, data, sizeof (SndHeader)); if (NAS_LITTLE_ENDIAN) { char n; swapl (&si->h.magic, n); swapl (&si->h.dataOffset, n); swapl (&si->h.dataSize, n); swapl (&si->h.format, n); swapl (&si->h.sampleRate, n); swapl (&si->h.tracks, n); } if (si->h.magic != SND_MAGIC_NUM) { free (si); return NULL; } size = si->h.dataOffset - sizeof (SndHeader); if (size) { if (!(si->comment = (char *) malloc (size + 1))) { free (si); return NULL; } memcpy (si->comment, data+sizeof (SndHeader), size); *(si->comment + size) = 0; if (*si->comment == '\0') si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset); } else si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset); si->h.dataSize = length-si->h.dataOffset; si->fp=NULL; return si; } /* Stuff taken from wave.c from NAS. Just like snd files, NAS can't read wave data from memory, so these functions do that for us. */ #define Err() { return NULL; } #define readFourcc(_f) dread(_f, sizeof(RIFF_FOURCC), 1) #define cmpID(_x, _y) \ strncmp((char *) (_x), (char *) (_y), sizeof(RIFF_FOURCC)) #define PAD2(_x) (((_x) + 1) & ~1) /* These functions here are for faking file I/O from buffer. */ /* The "file" position */ static size_t file_posn; /* The length of the "file" */ static size_t file_len; /* The actual "file" data. */ static const void* file_data; /* Like fopen, but for a buffer in memory */ static void dopen (const void* data, size_t length) { file_data = data; file_len = length; file_posn = 0; } /* Like fread, but for a buffer in memory */ static int dread (void* buf, size_t size, size_t nitems) { size_t nread = size * nitems; if (file_posn + nread <= file_len) { memcpy(buf, (char *) file_data + file_posn, size * nitems); file_posn += nread; return nitems; } else { return EOF; } } /* Like fgetc, but for a buffer in memory */ static int dgetc (void) { if (file_posn < file_len) return ((char *)file_data)[file_posn++]; else return -1; } /* Like fseek, but for a buffer in memory */ static int dseek (long offset, int from) { if (from == 0) file_posn = offset; else if (from == 1) file_posn += offset; else if (from == 2) file_posn = file_len + offset; return 0; } /* Like ftell, but for a buffer in memory */ static long dtell (void) { return file_posn; } /* Data buffer analogs for FileReadS and FileReadL in NAS. */ static unsigned short DataReadS (int swapit) { unsigned short us; dread(&us, 2, 1); if (swapit) us = FileSwapS(us); return us; } static AuUint32 DataReadL (int swapit) { AuUint32 ul; dread(&ul, 4, 1); if (swapit) ul = FileSwapL(ul); return ul; } static int readChunk (RiffChunk *c) { int status; char n; if ((status = dread(c, sizeof(RiffChunk), 1))) if (NAS_BIG_ENDIAN) swapl(&c->ckSize, n); return status; } /* A very straight-forward translation of WaveOpenFileForReading to read the wave data from a buffer in memory. */ static WaveInfo * WaveOpenDataForReading (const char *data, int length) { RiffChunk ck; RIFF_FOURCC fourcc; AuInt32 fileSize; WaveInfo *wi; if (!(wi = (WaveInfo *) malloc(sizeof(WaveInfo)))) return NULL; wi->comment = NULL; wi->dataOffset = wi->format = wi->writing = 0; dopen(data, length); if (!readChunk(&ck) || cmpID(&ck.ckID, RIFF_RiffID) || !readFourcc(&fourcc) || cmpID(&fourcc, RIFF_WaveID)) Err(); fileSize = PAD2(ck.ckSize) - sizeof(RIFF_FOURCC); while (fileSize >= sizeof(RiffChunk)) { if (!readChunk(&ck)) Err(); fileSize -= sizeof(RiffChunk) + PAD2(ck.ckSize); /* LIST chunk */ if (!cmpID(&ck.ckID, RIFF_ListID)) { if (!readFourcc(&fourcc)) Err(); /* INFO chunk */ if (!cmpID(&fourcc, RIFF_ListInfoID)) { ck.ckSize -= sizeof(RIFF_FOURCC); while (ck.ckSize) { RiffChunk c; if (!readChunk(&c)) Err(); /* ICMT chunk */ if (!cmpID(&c.ckID, RIFF_InfoIcmtID)) { if (!(wi->comment = (char *) malloc(c.ckSize)) || !dread(wi->comment, c.ckSize, 1)) Err(); if (c.ckSize & 1) dgetc(); /* eat the pad byte */ } else /* skip unknown chunk */ dseek(PAD2(c.ckSize), 1); ck.ckSize -= sizeof(RiffChunk) + PAD2(c.ckSize); } } else /* skip unknown chunk */ dseek(PAD2(ck.ckSize) - sizeof(RIFF_FOURCC), 1); } /* wave format chunk */ else if (!cmpID(&ck.ckID, RIFF_WaveFmtID) && !wi->format) { AuInt32 dummy; wi->format = DataReadS(NAS_BIG_ENDIAN); wi->channels = DataReadS(NAS_BIG_ENDIAN); wi->sampleRate = DataReadL(NAS_BIG_ENDIAN); /* we don't care about the next two fields */ dummy = DataReadL(NAS_BIG_ENDIAN); dummy = DataReadS(NAS_BIG_ENDIAN); if (wi->format != RIFF_WAVE_FORMAT_PCM) Err(); wi->bitsPerSample = DataReadS(NAS_BIG_ENDIAN); /* skip any other format specific fields */ dseek(PAD2(ck.ckSize - 16), 1); } /* wave data chunk */ else if (!cmpID(&ck.ckID, RIFF_WaveDataID) && !wi->dataOffset) { long endOfFile; wi->dataOffset = dtell(); wi->dataSize = ck.ckSize; dseek(0, 2); endOfFile = dtell(); /* seek past the data */ if (dseek(wi->dataOffset + PAD2(ck.ckSize), 0) || dtell() > endOfFile) { /* the seek failed, assume the size is bogus */ dseek(0, 2); wi->dataSize = dtell() - wi->dataOffset; } wi->dataOffset -= sizeof(long); } else /* skip unknown chunk */ dseek(PAD2(ck.ckSize), 1); } if (!wi->dataOffset) Err(); wi->numSamples = wi->dataSize / wi->channels / (wi->bitsPerSample >> 3); if (!wi->comment) wi->comment = NameFromData (data + wi->dataOffset, length - wi->dataOffset); wi->fp = NULL; return wi; } static Sound SoundOpenDataForReading (unsigned char *data, int length) { Sound s; if (!(s = (Sound) malloc (sizeof (SoundRec)))) return NULL; if ((s->formatInfo = SndOpenDataForReading ((char *) data, length)) != NULL) { if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatSnd].toSound)) (s)) { SndCloseFile ((SndInfo *) (s->formatInfo)); free (s); return NULL; } } else if ((s->formatInfo = WaveOpenDataForReading ((char *) data, length)) != NULL) { if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatWave].toSound)) (s)) { WaveCloseFile ((WaveInfo *) (s->formatInfo)); free (s); return NULL; } } return s; }