This is nas.c in view mode; [Download] [Up]
/* 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.
*/
#if __STDC__ || defined (STDC_HEADERS)
# include <stdlib.h>
# include <unistd.h>
# include <stdarg.h>
#endif
#include <stdio.h>
#include <string.h>
#include <config.h> /* for CONST in syssignal.h (neal@ctd.comsat.com) */
#include "syssignal.h"
#include <audio/audiolib.h>
#include <audio/soundlib.h>
#include <audio/snd.h>
#include <audio/fileutil.h>
#ifdef emacs
# include <config.h>
# include "lisp.h"
# 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))
# define CONST const
#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 *err_message;
SIGTYPE (*old_sigpipe) ();
#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;
}
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 *aud,
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)
{
unsigned char *my_buf;
if (buf==NULL)
{
if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
{
return;
}
if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
{
free (my_buf);
return;
}
}
else
my_buf=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)
{
AuEvent ev;
while (sounds_in_play>0)
{
AuNextEvent (aud, AuTrue, &ev);
AuDispatchEvent (aud, &ev);
}
}
int
play_sound_file (char *sound_file,
int volume)
{
SIGTYPE (*old_sigpipe) ();
#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)
{
Sound s;
int offset;
SIGTYPE (*old_sigpipe) ();
#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
{
warn ("only understand snd 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 */
}
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 unsigned char *buf,
int len)
{
unsigned 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=malloc (10), name);
}
else
{
strcpy (s=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 (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;
}
static Sound
SoundOpenDataForReading (unsigned char *data,
int length)
{
Sound s;
if (!(s = (Sound) malloc (sizeof (SoundRec))))
return NULL;
if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
{
free (s);
return NULL;
}
if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
{
SndCloseFile (s->formatInfo);
free (s);
return NULL;
}
return s;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.