Mercurial > hg > xemacs-beta
view src/nas.c @ 4677:8f1ee2d15784
Support full Common Lisp multiple values in C.
lisp/ChangeLog
2009-08-11 Aidan Kehoe <kehoea@parhasard.net>
* bytecomp.el :
Update this file to support full C-level multiple values. This
involves:
-- Four new bytecodes, and special compiler functions to compile
multiple-value-call, multiple-value-list-internal, values,
values-list, and, since it now needs to pass back multiple values
and is a special form, throw.
-- There's a new compiler variable, byte-compile-checks-on-load,
which is a list of forms that are evaluated at the very start of a
file, with an error thrown if any of them give nil.
-- The header is now inserted *after* compilation, giving a chance
for the compilation process to influence what those checks
are. There is still a check done before compilation for non-ASCII
characters, to try to turn off dynamic docstrings if appopriate,
in `byte-compile-maybe-reset-coding'.
Space is reserved for checks; comments describing the version of
the byte compiler generating the file are inserted if space
remains for them.
* bytecomp.el (byte-compile-version):
Update this, we're a newer version of the byte compiler.
* byte-optimize.el (byte-optimize-funcall):
Correct a comment.
* bytecomp.el (byte-compile-lapcode):
Discard the arg with byte-multiple-value-call.
* bytecomp.el (byte-compile-checks-and-comments-space):
New variable, describe how many octets to reserve for checks at
the start of byte-compiled files.
* cl-compat.el:
Remove the fake multiple-value implementation. Have the functions
that use it use the real multiple-value implementation instead.
* cl-macs.el (cl-block-wrapper, cl-block-throw):
Revise the byte-compile properties of these symbols to work now
we've made throw into a special form; keep the byte-compile
properties as anonymous lambdas, since we don't have docstrings
for them.
* cl-macs.el (multiple-value-bind, multiple-value-setq)
(multiple-value-list, nth-value):
Update these functions to work with the C support for multiple
values.
* cl-macs.el (values):
Modify the setf handler for this to call
#'multiple-value-list-internal appropriately.
* cl-macs.el (cl-setf-do-store):
If the store form is a cons, treat it specially as wrapping the
store value.
* cl.el (cl-block-wrapper):
Make this an alias of #'and, not #'identity, since it needs to
pass back multiple values.
* cl.el (multiple-value-apply):
We no longer support this, mark it obsolete.
* lisp-mode.el (eval-interactive-verbose):
Remove a useless space in the docstring.
* lisp-mode.el (eval-interactive):
Update this function and its docstring. It now passes back a list,
basically wrapping any eval calls with multiple-value-list. This
allows multiple values to be printed by default in *scratch*.
* lisp-mode.el (prin1-list-as-multiple-values):
New function, printing a list as multiple values in the manner of
Bruno Haible's clisp, separating each entry with " ;\n".
* lisp-mode.el (eval-last-sexp):
Call #'prin1-list-as-multiple-values on the return value of
#'eval-interactive.
* lisp-mode.el (eval-defun):
Call #'prin1-list-as-multiple-values on the return value of
#'eval-interactive.
* mouse.el (mouse-eval-sexp):
Deal with lists corresponding to multiple values from
#'eval-interactive. Call #'cl-prettyprint, which is always
available, instead of sometimes calling #'pprint and sometimes
falling back to prin1.
* obsolete.el (obsolete-throw):
New function, called from eval.c when #'funcall encounters an
attempt to call #'throw (now a special form) as a function. Only
needed for compatibility with 21.4 byte-code.
man/ChangeLog addition:
2009-08-11 Aidan Kehoe <kehoea@parhasard.net>
* cl.texi (Organization):
Remove references to the obsolete multiple-value emulating code.
src/ChangeLog addition:
2009-08-11 Aidan Kehoe <kehoea@parhasard.net>
* bytecode.c (enum Opcode /* Byte codes */):
Add four new bytecodes, to deal with multiple values.
(POP_WITH_MULTIPLE_VALUES): New macro.
(POP): Modify this macro to ignore multiple values.
(DISCARD_PRESERVING_MULTIPLE_VALUES): New macro.
(DISCARD): Modify this macro to ignore multiple values.
(TOP_WITH_MULTIPLE_VALUES): New macro.
(TOP_ADDRESS): New macro.
(TOP): Modify this macro to ignore multiple values.
(TOP_LVALUE): New macro.
(Bcall): Ignore multiple values where appropriate.
(Breturn): Pass back multiple values.
(Bdup): Preserve multiple values.
Use TOP_LVALUE with most bytecodes that assign anything to
anything.
(Bbind_multiple_value_limits, Bmultiple_value_call,
Bmultiple_value_list_internal, Bthrow): Implement the new
bytecodes.
(Bgotoifnilelsepop, Bgotoifnonnilelsepop, BRgotoifnilelsepop,
BRgotoifnonnilelsepop):
Discard any multiple values.
* callint.c (Fcall_interactively):
Ignore multiple values when calling #'eval, in two places.
* device-x.c (x_IO_error_handler):
* macros.c (pop_kbd_macro_event):
* eval.c (Fsignal):
* eval.c (flagged_a_squirmer):
Call throw_or_bomb_out, not Fthrow, now that the latter is a
special form.
* eval.c:
Make Qthrow, Qobsolete_throw available as symbols.
Provide multiple_value_current_limit, multiple-values-limit (the
latter as specified by Common Lisp.
* eval.c (For):
Ignore multiple values when comparing with Qnil, but pass any
multiple values back for the last arg.
* eval.c (Fand):
Ditto.
* eval.c (Fif):
Ignore multiple values when examining the result of the
condition.
* eval.c (Fcond):
Ignore multiple values when comparing what the clauses give, but
pass them back if a clause gave non-nil.
* eval.c (Fprog2):
Never pass back multiple values.
* eval.c (FletX, Flet):
Ignore multiple when evaluating what exactly symbols should be
bound to.
* eval.c (Fwhile):
Ignore multiple values when evaluating the test.
* eval.c (Fsetq, Fdefvar, Fdefconst):
Ignore multiple values.
* eval.c (Fthrow):
Declare this as a special form; ignore multiple values for TAG,
preserve them for VALUE.
* eval.c (throw_or_bomb_out):
Make this available to other files, now Fthrow is a special form.
* eval.c (Feval):
Ignore multiple values when calling a compiled function, a
non-special-form subr, or a lambda expression.
* eval.c (Ffuncall):
If we attempt to call #'throw (now a special form) as a function,
don't error, call #'obsolete-throw instead.
* eval.c (make_multiple_value, multiple_value_aset)
(multiple_value_aref, print_multiple_value, mark_multiple_value)
(size_multiple_value):
Implement the multiple_value type. Add a long comment describing
our implementation.
* eval.c (bind_multiple_value_limits):
New function, used by the bytecode and by #'multiple-value-call,
#'multiple-value-list-internal.
* eval.c (multiple_value_call):
New function, used by the bytecode and #'multiple-value-call.
* eval.c (Fmultiple_value_call):
New special form.
* eval.c (multiple_value_list_internal):
New function, used by the byte code and
#'multiple-value-list-internal.
* eval.c (Fmultiple_value_list_internal, Fmultiple_value_prog1):
New special forms.
* eval.c (Fvalues, Fvalues_list):
New Lisp functions.
* eval.c (values2):
New function, for C code returning multiple values.
* eval.c (syms_of_eval):
Make our new Lisp functions and symbols available.
* eval.c (multiple-values-limit):
Make this available to Lisp.
* event-msw.c (dde_eval_string):
* event-stream.c (execute_help_form):
* glade.c (connector):
* glyphs-widget.c (glyph_instantiator_to_glyph):
* glyphs.c (evaluate_xpm_color_symbols):
* gui-x.c (wv_set_evalable_slot, button_item_to_widget_value):
* gui.c (gui_item_value, gui_item_display_flush_left):
* lread.c (check_if_suppressed):
* menubar-gtk.c (menu_convert, menu_descriptor_to_widget_1):
* menubar-msw.c (populate_menu_add_item):
* print.c (Fwith_output_to_temp_buffer):
* symbols.c (Fsetq_default):
Ignore multiple values when calling Feval.
* symeval.h:
Add the header declarations necessary for the multiple-values
implementation.
* inline.c:
#include symeval.h, now that it has some inline functions.
* lisp.h:
Update Fthrow's declaration. Make throw_or_bomb_out available to
all files.
* lrecord.h (enum lrecord_type):
Add the multiple_value type here.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sun, 16 Aug 2009 20:55:49 +0100 |
parents | 6c7605dfcf07 |
children | c12b646d84ee |
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. */ /* This file Mule-ized by Ben Wing, 5-15-01. */ /* 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. * 2002-10-16 Jon Trulson modifed this to work with NAS releases * 1.5f and higher. We were using the private variable * SoundFileInfo that doesn't exist anymore. But preserve * backward compatibility. This will not work for some * versions of NAS around 1.5b to 1.5f or so. Known to * work on 1.2p5 and 1.6. */ #include <config.h> #include "lisp.h" #include "sound.h" #include "sysdep.h" #include "syssignal.h" /* 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 BEGIN_C_DECLS #include <audio/audiolib.h> #include <audio/soundlib.h> #include <audio/snd.h> #include <audio/wave.h> #include <audio/fileutil.h> END_C_DECLS /* 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 #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 #ifdef XTOOLKIT # include <X11/Intrinsic.h> # include <audio/Xtutil.h> #endif /* audiolib <= some version doesn't define major and minor versions */ /* Do NOT use AudioLibraryVersion in this file. */ #ifndef AudioLibraryVersion #define AudioLibraryVersion 1 #endif #ifndef AudioLibraryVersionMajor #define AudioLibraryVersionMajor AudioLibraryVersion #endif #ifndef AudioLibraryVersionMinor #define AudioLibraryVersionMinor 0 #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 (Binbyte *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 Extbyte *aud_server; #endif /* XTOOLKIT */ Extbyte * nas_init_play ( #ifdef XTOOLKIT Display *display #else Extbyte *server #endif ); Extbyte * nas_init_play ( #ifdef XTOOLKIT Display *display #else Extbyte *server #endif ) { Extbyte *err_message; SIGTYPE (*old_sigpipe) (int); #ifdef XTOOLKIT Extbyte * server = DisplayString (display); XtAppContext app_context = XtDisplayToApplicationContext (display); aud_server = display; #else aud_server = server; #endif #ifdef ROBUST_PLAY old_sigpipe = EMACS_SIGNAL (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump)) { EMACS_SIGNAL (SIGPIPE, old_sigpipe); start_interrupts (); return "error in NAS"; } #endif #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP) AuDefaultIOErrorHandler = CatchIoErrorAndJump; AuDefaultErrorHandler = CatchErrorAndJump; #endif stop_interrupts (); aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message); start_interrupts (); if (!aud) { #ifdef ROBUST_PLAY EMACS_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 EMACS_SIGNAL (SIGPIPE, old_sigpipe); #endif sounds_in_play = 0; return NULL; } static void nas_close_down_play (void) { AuCloseServer (aud); sound_warn ("disconnected from audio server"); } /********************************************************************\ * * * Callback which is run when the sound finishes playing. * * * \********************************************************************/ static void doneCB (AuServer *UNUSED (auserver), AuEventHandlerRec *UNUSED (handler), AuEvent *UNUSED (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, Binbyte *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 ((Extbyte *) 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 nas_wait_for_sounds (void); void nas_wait_for_sounds (void) { AuEvent ev; while (sounds_in_play>0) { AuNextEvent (aud, AuTrue, &ev); AuDispatchEvent (aud, &ev); } } int nas_play_sound_file (Extbyte *sound_file, int volume); int nas_play_sound_file (Extbyte *sound_file, int volume) { SIGTYPE (*old_sigpipe) (int); #ifdef ROBUST_PLAY old_sigpipe = EMACS_SIGNAL (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump)) { EMACS_SIGNAL (SIGPIPE, old_sigpipe); return 0; } #endif if (aud==NULL) { if (aud_server != NULL) { Extbyte *m; /* attempt to reconect */ if ((m = nas_init_play (aud_server)) != NULL) { #ifdef ROBUST_PLAY EMACS_SIGNAL (SIGPIPE, old_sigpipe); #endif return 0; } } else { sound_warn ("Attempt to play with no audio init\n"); #ifdef ROBUST_PLAY EMACS_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 EMACS_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 nas_wait_for_sounds (); #else if (!NILP (Vsynchronous_sounds)) nas_wait_for_sounds (); #endif #ifdef ROBUST_PLAY EMACS_SIGNAL (SIGPIPE, old_sigpipe); #endif return 1; } int nas_play_sound_data (Binbyte *data, int length, int volume); int nas_play_sound_data (Binbyte *data, int length, int volume) { Sound s; int offset; SIGTYPE (*old_sigpipe) (int); #if !defined (XTEVENTS) AuEvent ev; #endif #ifdef ROBUST_PLAY old_sigpipe = EMACS_SIGNAL (SIGPIPE, sigpipe_handle); if (setjmp (AuXtErrorJump) != 0) { EMACS_SIGNAL (SIGPIPE, old_sigpipe); return 0; } #endif if (aud == NULL) { if (aud_server != NULL) { Extbyte *m; /* attempt to reconect */ if ((m = nas_init_play (aud_server)) != NULL) { #ifdef ROBUST_PLAY EMACS_SIGNAL (SIGPIPE, old_sigpipe); #endif return 0; } } else { sound_warn ("Attempt to play with no audio init\n"); #ifdef ROBUST_PLAY EMACS_SIGNAL (SIGPIPE, old_sigpipe); #endif return 0; } } if ((s=SoundOpenDataForReading (data, length))==NULL) { sound_warn ("unknown sound type"); #ifdef ROBUST_PLAY EMACS_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 { sound_warn ("only understand snd and wave files at the moment"); SoundCloseFile (s); #ifdef ROBUST_PLAY EMACS_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 nas_wait_for_sounds (); #else if (!NILP (Vsynchronous_sounds)) nas_wait_for_sounds (); #endif SoundCloseFile (s); #ifdef ROBUST_PLAY EMACS_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) sound_warn ("Audio Server connection broken"); else sound_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 UNUSED (signo)) { CatchIoErrorAndJump (NULL); } static AuBool CatchErrorAndJump (AuServer *old_aud, AuErrorEvent *UNUSED (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 Extbyte * NameFromData (const CBinbyte *buf, int len) { Extbyte name[9]; int i; Extbyte *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 = (Extbyte *) malloc (10), name); } else { strcpy (s = (Extbyte *) 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 CBinbyte *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) { CBinbyte 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 = (Extbyte *) 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() { free(wi); return NULL; } #define readFourcc(_f) dread(_f, sizeof(RIFF_FOURCC), 1) #define cmpID(_x, _y) \ strncmp((CBinbyte *) (_x), (CBinbyte *) (_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, (CBinbyte *) 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 ((CBinbyte *)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; CBinbyte 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 CBinbyte *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 >= (AuInt32) 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 = (Extbyte *) 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 (Binbyte *data, int length) { Sound s; #if (AudioLibraryVersionMajor >= 2 ) && (AudioLibraryVersionMinor >= 3) SoundFileInfoProc toProc; #endif if (!(s = (Sound) malloc (sizeof (SoundRec)))) return NULL; if ((s->formatInfo = SndOpenDataForReading ((CBinbyte *) data, length)) != NULL) { #if (AudioLibraryVersionMajor >= 2 ) && (AudioLibraryVersionMinor >= 3) if ((toProc = SoundFileGetProc(SoundFileFormatSnd, SoundFileInfoProcTo)) == NULL) { SndCloseFile ((SndInfo *) (s->formatInfo)); free (s); return NULL; } if (!((*toProc)(s))) #else if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatSnd].toSound)) (s)) #endif { SndCloseFile ((SndInfo *) (s->formatInfo)); free (s); return NULL; } } else if ((s->formatInfo = WaveOpenDataForReading ((CBinbyte *) data, length)) != NULL) { #if (AudioLibraryVersionMajor >= 2 ) && (AudioLibraryVersionMinor >= 3) if ((toProc = SoundFileGetProc(SoundFileFormatWave, SoundFileInfoProcTo)) == NULL) { WaveCloseFile ((WaveInfo *) (s->formatInfo)); free (s); return NULL; } if (!((*toProc)(s))) #else if (!((int(*)(Sound))(SoundFileInfo[SoundFileFormatWave].toSound)) (s)) #endif { WaveCloseFile ((WaveInfo *) (s->formatInfo)); free (s); return NULL; } } return s; }