view src/sound.c @ 2367:ecf1ebac70d8

[xemacs-hg @ 2004-11-04 23:05:23 by ben] commit mega-patch configure.in: Turn off -Winline and -Wchar-subscripts. Use the right set of cflags when compiling modules. Rewrite ldap configuration to separate the inclusion of lber (needed in recent Cygwin) from the basic checks for the needed libraries. add a function for MAKE_JUNK_C; initially code was added to generate xemacs.def using this, but it will need to be rewritten. add an rm -f for junk.c to avoid weird Cygwin bug with cp -f onto an existing file. Sort list of auto-detected functions and eliminate unused checks for stpcpy, setlocale and getwd. Add autodetection of Cygwin scanf problems BETA: Rewrite section on configure to indicate what flags are important and what not. digest-doc.c, make-dump-id.c, profile.c, sorted-doc.c: Add proper decls for main(). make-msgfile.c: Document that this is old junk. Move proposal to text.c. make-msgfile.lex: Move proposal to text.c. make-mswin-unicode.pl: Convert error-generating code so that the entire message will be seen as a single unrecognized token. mule/mule-ccl.el: Update docs. lispref/mule.texi: Update CCL docs. ldap/eldap.c: Mule-ize. Use EXTERNAL_LIST_LOOP_2 instead of deleted EXTERNAL_LIST_LOOP. * XEmacs 21.5.18 "chestnut" is released. --------------------------------------------------------------- MULE-RELATED WORK: --------------------------------------------------------------- --------------------------- byte-char conversion --------------------------- buffer.c, buffer.h, insdel.c, text.c: Port FSF algorithm for byte-char conversion, replacing broken previous version. Track the char position of the gap. Add functions to do char-byte conversion downwards as well as upwards. Move comments about algorithm workings to internals manual. --------------------------- work on types --------------------------- alloc.c, console-x-impl.h, dump-data.c, dump-data.h, dumper.c, dialog-msw.c, dired-msw.c, doc.c, editfns.c, esd.c, event-gtk.h, event-msw.c, events.c, file-coding.c, file-coding.h, fns.c, glyphs-eimage.c, glyphs-gtk.c, glyphs-msw.c, glyphs-shared.c, glyphs-x.c, glyphs.c, glyphs.h, gui.c, hpplay.c, imgproc.c, intl-win32.c, lrecord.h, lstream.c, keymap.c, lisp.h, libsst.c, linuxplay.c, miscplay.c, miscplay.h, mule-coding.c, nas.c, nt.c, ntheap.c, ntplay.c, objects-msw.c, objects-tty.c, objects-x.c, print.c, process-nt.c, process.c, redisplay.h, select-common.h, select-gtk.c, select-x.c, sgiplay.c, sound.c, sound.h, sunplay.c, sysfile.h, sysdep.c, syswindows.h, text.c, unexnt.c, win32.c, xgccache.c: Further work on types. This creates a full set of types for all the basic semantics of `char' that I have so far identified, so that its semantics can always be identified for the purposes of proper Mule-safe code, and the raw use of `char' always avoided. (1) More type renaming, for consistency of naming. Char_ASCII -> Ascbyte UChar_ASCII -> UAscbyte Char_Binary -> CBinbyte UChar_Binary -> Binbyte SChar_Binary -> SBinbyte (2) Introduce Rawbyte, CRawbyte, Boolbyte, Chbyte, UChbyte, and Bitbyte and use them. (3) New types Itext, Wexttext and Textcount for separating out the concepts of bytes and textual units (different under UTF-16 and UTF-32, which are potential internal encodings). (4) qxestr*_c -> qxestr*_ascii. lisp.h: New; goes with other qxe() functions. #### Maybe goes in a different section. lisp.h: Group generic int-type defs together with EMACS_INT defs. lisp.h: * lisp.h (WEXTTEXT_IS_WIDE) New defns. lisp.h: New type to replace places where int occurs as a boolean. It's signed because occasionally people may want to use -1 as an error value, and because unsigned ints are viral -- see comments in the internals manual against using them. dynarr.c: int -> Bytecount. --------------------------- Mule-izing --------------------------- device-x.c: Partially Mule-ize. dumper.c, dumper.h: Mule-ize. Use Rawbyte. Use stderr_out not printf. Use wext_*(). sysdep.c, syswindows.h, text.c: New Wexttext API for manipulation of external text that may be Unicode (e.g. startup code under Windows). emacs.c: Mule-ize. Properly deal with argv in external encoding. Use wext_*() and Wexttext. Use Rawbyte. #if 0 some old junk on SCO that is unlikely to be correct. Rewrite allocation code in run-temacs. emacs.c, symsinit.h, win32.c: Rename win32 init function and call it even earlier, to initialize mswindows_9x_p even earlier, for use in startup code (XEUNICODE_P). process.c: Use _wenviron not environ under Windows, to get Unicode environment variables. event-Xt.c: Mule-ize drag-n-drop related stuff. dragdrop.c, dragdrop.h, frame-x.c: Mule-ize. text.h: Add some more stand-in defines for particular kinds of conversion; use in Mule-ization work in frame-x.c etc. --------------------------- Freshening --------------------------- intl-auto-encap-win32.c, intl-auto-encap-win32.h: Regenerate. --------------------------- Unicode-work --------------------------- intl-win32.c, syswindows.h: Factor out common options to MultiByteToWideChar and WideCharToMultiByte. Add convert_unicode_to_multibyte_malloc() and convert_unicode_to_multibyte_dynarr() and use. Add stuff for alloca() conversion of multibyte/unicode. alloc.c: Use dfc_external_data_len() in case of unicode coding system. alloc.c, mule-charset.c: Don't zero out and reinit charset Unicode tables. This fucks up dump-time loading. Anyway, either we load them at dump time or run time, never both. unicode.c: Dump the blank tables as well. --------------------------------------------------------------- DOCUMENTATION, MOSTLY MULE-RELATED: --------------------------------------------------------------- EmacsFrame.c, emodules.c, event-Xt.c, fileio.c, input-method-xlib.c, mule-wnnfns.c, redisplay-gtk.c, redisplay-tty.c, redisplay-x.c, regex.c, sysdep.c: Add comment about Mule work needed. text.h: Add more documentation describing why DFC routines were not written to return their value. Add some other DFC documentation. console-msw.c, console-msw.h: Add pointer to docs in win32.c. emacs.c: Add comments on sources of doc info. text.c, charset.h, unicode.c, intl-win32.c, intl-encap-win32.c, text.h, file-coding.c, mule-coding.c: Collect background comments and related to text matters and internationalization, and proposals for work to be done, in text.c or Internals manual, stuff related to specific textual API's in text.h, and stuff related to internal implementation of Unicode conversion in unicode.c. Put lots of pointers to the comments to make them easier to find. s/mingw32.h, s/win32-common.h, s/win32-native.h, s/windowsnt.h, win32.c: Add bunches of new documentation on the different kinds of builds and environments under Windows and how they work. Collect this info in win32.c. Add pointers to these docs in the relevant s/* files. emacs.c: Document places with long comments. Remove comment about exiting, move to internals manual, put in pointer. event-stream.c: Move docs about event queues and focus to internals manual, put in pointer. events.h: Move docs about event stream callbacks to internals manual, put in pointer. profile.c, redisplay.c, signal.c: Move documentation to the Internals manual. process-nt.c: Add pointer to comment in win32-native.el. lisp.h: Add comments about some comment conventions. lisp.h: Add comment about the second argument. device-msw.c, redisplay-msw.c: @@#### comments are out-of-date. --------------------------------------------------------------- PDUMP WORK (MOTIVATED BY UNICODE CHANGES) --------------------------------------------------------------- alloc.c, buffer.c, bytecode.c, console-impl.h, console.c, device.c, dumper.c, lrecord.h, elhash.c, emodules.h, events.c, extents.c, frame.c, glyphs.c, glyphs.h, mule-charset.c, mule-coding.c, objects.c, profile.c, rangetab.c, redisplay.c, specifier.c, specifier.h, window.c, lstream.c, file-coding.h, file-coding.c: PDUMP: Properly implement dump_add_root_block(), which never worked before, and is necessary for dumping Unicode tables. Pdump name changes for accuracy: XD_STRUCT_PTR -> XD_BLOCK_PTR. XD_STRUCT_ARRAY -> XD_BLOCK_ARRAY. XD_C_STRING -> XD_ASCII_STRING. *_structure_* -> *_block_*. lrecord.h: some comments added about dump_add_root_block() vs dump_add_root_block_ptr(). extents.c: remove incorrect comment about pdump problems with gap array. --------------------------------------------------------------- ALLOCATION --------------------------------------------------------------- abbrev.c, alloc.c, bytecode.c, casefiddle.c, device-msw.c, device-x.c, dired-msw.c, doc.c, doprnt.c, dragdrop.c, editfns.c, emodules.c, file-coding.c, fileio.c, filelock.c, fns.c, glyphs-eimage.c, glyphs-gtk.c, glyphs-msw.c, glyphs-x.c, gui-msw.c, gui-x.c, imgproc.c, intl-win32.c, lread.c, menubar-gtk.c, menubar.c, nt.c, objects-msw.c, objects-x.c, print.c, process-nt.c, process-unix.c, process.c, realpath.c, redisplay.c, search.c, select-common.c, symbols.c, sysdep.c, syswindows.h, text.c, text.h, ui-byhand.c: New macros {alloca,xnew}_{itext,{i,ext,raw,bin,asc}bytes} for more convenient allocation of these commonly requested items. Modify functions to use alloca_ibytes, alloca_array, alloca_extbytes, xnew_ibytes, etc. also XREALLOC_ARRAY, xnew. alloc.c: Rewrite the allocation functions to factor out repeated code. Add assertions for freeing dumped data. lisp.h: Moved down and consolidated with other allocation stuff. lisp.h, dynarr.c: New functions for allocation that's very efficient when mostly in LIFO order. lisp.h, text.c, text.h: Factor out some stuff for general use by alloca()-conversion funs. text.h, lisp.h: Fill out convenience routines for allocating various kinds of bytes and put them in lisp.h. Use them in place of xmalloc(), ALLOCA(). text.h: Fill out the convenience functions so the _MALLOC() kinds match the alloca() kinds. --------------------------------------------------------------- ERROR-CHECKING --------------------------------------------------------------- text.h: Create ASSERT_ASCTEXT_ASCII() and ASSERT_ASCTEXT_ASCII_LEN() from similar Eistring checkers and change the Eistring checkers to use them instead. --------------------------------------------------------------- MACROS IN LISP.H --------------------------------------------------------------- lisp.h: Redo GCPRO declarations. Create a "base" set of functions that can be used to generate any kind of gcpro sets -- regular, ngcpro, nngcpro, private ones used in GC_EXTERNAL_LIST_LOOP_2. buffer.c, callint.c, chartab.c, console-msw.c, device-x.c, dialog-msw.c, dired.c, extents.c, ui-gtk.c, rangetab.c, nt.c, mule-coding.c, minibuf.c, menubar-msw.c, menubar.c, menubar-gtk.c, lread.c, lisp.h, gutter.c, glyphs.c, glyphs-widget.c, fns.c, fileio.c, file-coding.c, specifier.c: Eliminate EXTERNAL_LIST_LOOP, which does not check for circularities. Use EXTERNAL_LIST_LOOP_2 instead or EXTERNAL_LIST_LOOP_3 or EXTERNAL_PROPERTY_LIST_LOOP_3 or GC_EXTERNAL_LIST_LOOP_2 (new macro). Removed/redid comments on EXTERNAL_LIST_LOOP. --------------------------------------------------------------- SPACING FIXES --------------------------------------------------------------- callint.c, hftctl.c, number-gmp.c, process-unix.c: Spacing fixes. --------------------------------------------------------------- FIX FOR GEOMETRY PROBLEM IN FIRST FRAME --------------------------------------------------------------- unicode.c: Add workaround for newlib bug in sscanf() [should be fixed by release 1.5.12 of Cygwin]. toolbar.c: bug fix for problem of initial frame being 77 chars wide on Windows. will be overridden by my other ws. --------------------------------------------------------------- FIX FOR LEAKING PROCESS HANDLES: --------------------------------------------------------------- process-nt.c: Fixes for leaking handles. Inspired by work done by Adrian Aichner <adrian@xemacs.org>. --------------------------------------------------------------- FIX FOR CYGWIN BUG (Unicode-related): --------------------------------------------------------------- unicode.c: Add workaround for newlib bug in sscanf() [should be fixed by release 1.5.12 of Cygwin]. --------------------------------------------------------------- WARNING FIXES: --------------------------------------------------------------- console-stream.c: `reinit' is unused. compiler.h, event-msw.c, frame-msw.c, intl-encap-win32.c, text.h: Add stuff to deal with ANSI-aliasing warnings I got. regex.c: Gather includes together to avoid warning. --------------------------------------------------------------- CHANGES TO INITIALIZATION ROUTINES: --------------------------------------------------------------- buffer.c, emacs.c, console.c, debug.c, device-x.c, device.c, dragdrop.c, emodules.c, eval.c, event-Xt.c, event-gtk.c, event-msw.c, event-stream.c, event-tty.c, events.c, extents.c, faces.c, file-coding.c, fileio.c, font-lock.c, frame-msw.c, glyphs-widget.c, glyphs.c, gui-x.c, insdel.c, lread.c, lstream.c, menubar-gtk.c, menubar-x.c, minibuf.c, mule-wnnfns.c, objects-msw.c, objects.c, print.c, scrollbar-x.c, search.c, select-x.c, text.c, undo.c, unicode.c, window.c, symsinit.h: Call reinit_*() functions directly from emacs.c, for clarity. Factor out some redundant init code. Move disallowed stuff that had crept into vars_of_glyphs() into complex_vars_of_glyphs(). Call init_eval_semi_early() from eval.c not in the middle of vars_of_() in emacs.c since there should be no order dependency in the latter calls. --------------------------------------------------------------- ARMAGEDDON: --------------------------------------------------------------- alloc.c, emacs.c, lisp.h, print.c: Rename inhibit_non_essential_printing_operations to inhibit_non_essential_conversion_operations. text.c: Assert on !inhibit_non_essential_conversion_operations. console-msw.c, print.c: Don't do conversion in SetConsoleTitle or FindWindow to avoid problems during armageddon. Put #errors for NON_ASCII_INTERNAL_FORMAT in places where problems would arise. --------------------------------------------------------------- CHANGES TO THE BUILD PROCEDURE: --------------------------------------------------------------- config.h.in, s/cxux.h, s/usg5-4-2.h, m/powerpc.h: Add comment about correct ordering of this file. Rearrange everything to follow this -- put all #undefs together and before the s&m files. Add undefs for HAVE_ALLOCA, C_ALLOCA, BROKEN_ALLOCA_IN_FUNCTION_CALLS, STACK_DIRECTION. Remove unused HAVE_STPCPY, HAVE_GETWD, HAVE_SETLOCALE. m/gec63.h: Deleted; totally broken, not used at all, not in FSF. m/7300.h, m/acorn.h, m/alliant-2800.h, m/alliant.h, m/altos.h, m/amdahl.h, m/apollo.h, m/att3b.h, m/aviion.h, m/celerity.h, m/clipper.h, m/cnvrgnt.h, m/convex.h, m/cydra5.h, m/delta.h, m/delta88k.h, m/dpx2.h, m/elxsi.h, m/ews4800r.h, m/gould.h, m/hp300bsd.h, m/hp800.h, m/hp9000s300.h, m/i860.h, m/ibmps2-aix.h, m/ibmrs6000.h, m/ibmrt-aix.h, m/ibmrt.h, m/intel386.h, m/iris4d.h, m/iris5d.h, m/iris6d.h, m/irist.h, m/isi-ov.h, m/luna88k.h, m/m68k.h, m/masscomp.h, m/mg1.h, m/mips-nec.h, m/mips-siemens.h, m/mips.h, m/news.h, m/nh3000.h, m/nh4000.h, m/ns32000.h, m/orion105.h, m/pfa50.h, m/plexus.h, m/pmax.h, m/powerpc.h, m/pyrmips.h, m/sequent-ptx.h, m/sequent.h, m/sgi-challenge.h, m/symmetry.h, m/tad68k.h, m/tahoe.h, m/targon31.h, m/tekxd88.h, m/template.h, m/tower32.h, m/tower32v3.h, m/ustation.h, m/vax.h, m/wicat.h, m/xps100.h: Delete C_ALLOCA, HAVE_ALLOCA, STACK_DIRECTION, BROKEN_ALLOCA_IN_FUNCTION_CALLS. All of this is auto-detected. When in doubt, I followed recent FSF sources, which also have these things deleted.
author ben
date Thu, 04 Nov 2004 23:08:28 +0000
parents d5131a81147e
children 902d5bd9b75c
line wrap: on
line source

/* Sound functions.
   Copyright (C) 1992, 1993, 1994 Lucid Inc.
   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
   Copyright (C) 2002 Ben Wing.

This file is part of XEmacs.

XEmacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

XEmacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/* Synched up with: Not in FSF. */

/* This file Mule-ized by Ben Wing, 5-15-01. */

/* Originally written by Jamie Zawinski.
   Hacked on quite a bit by various others. */

#include <config.h>
#include <time.h>
#include "lisp.h"

#include "buffer.h"
#ifdef HAVE_X_WINDOWS
#include "console-x-impl.h"
#endif
#include "device-impl.h"
#include "redisplay.h"
#include "sound.h"

#include "sysdep.h"

#include "sysfile.h"

#ifdef HAVE_NATIVE_SOUND
# include "sysproc.h"
#endif

#ifdef WIN32_NATIVE
#include "syswindows.h"
#endif

#ifdef HAVE_ESD_SOUND
extern int esd_play_sound_file (Extbyte *file, int vol);
extern int esd_play_sound_data (Binbyte *data, size_t length, int vol);
# define DEVICE_CONNECTED_TO_ESD_P(x) 1 /* #### better check */
#endif

Fixnum bell_volume;
Fixnum bell_inhibit_time;
Lisp_Object Vsound_alist;
Lisp_Object Vsynchronous_sounds;
Lisp_Object Vnative_sound_only_on_console;
Lisp_Object Q_volume, Q_pitch, Q_duration, Q_sound;
Lisp_Object Qsound_error;


#ifdef HAVE_NAS_SOUND
extern int nas_play_sound_file (Extbyte *name, int volume);
extern int nas_play_sound_data (Binbyte *data, int length, int volume);
extern int nas_wait_for_sounds (void);
extern Extbyte *nas_init_play (Display *);
#endif

DOESNT_RETURN
report_sound_error (const Ascbyte *string, Lisp_Object data)
{
  report_error_with_errno (Qsound_error, string, data);
}

DEFUN ("play-sound-file", Fplay_sound_file, 1, 3, "fSound file name: ", /*
Play the named sound file on DEVICE's speaker at the specified volume
\(0-100, default specified by the `bell-volume' variable).
On Unix machines the sound file must be in the Sun/NeXT U-LAW format
except under Linux where WAV files are also supported.  On Microsoft
Windows the sound file must be in WAV format.
  DEVICE defaults to the selected device.
*/
     (file, volume, device))
{
  /* This function can call lisp */
  int vol;
#if defined (HAVE_NATIVE_SOUND) || defined (HAVE_NAS_SOUND) \
       || defined (HAVE_ESD_SOUND)
  struct device *d = decode_device (device);
#endif
  struct gcpro gcpro1;

  CHECK_STRING (file);
  if (NILP (volume))
    vol = bell_volume;
  else
    {
      CHECK_INT (volume);
      vol = XINT (volume);
    }

  GCPRO1 (file);
  while (1)
    {
      file = Fexpand_file_name (file, Qnil);
      if (!NILP(Ffile_readable_p (file)))
	break;
      else
	{
	  /* #### This is crockish.  It might be a better idea to try
	     to open the file, and use report_file_error() if it
	     fails.  --hniksic */
	  if (NILP (Ffile_exists_p (file)))
	    file =
	      signal_continuable_error (Qfile_error,
					"File does not exist", file);
	  else
	    file =
	      signal_continuable_error (Qfile_error,
					"File is unreadable", file);
	}
    }
  UNGCPRO;

#ifdef HAVE_NAS_SOUND
  if (DEVICE_CONNECTED_TO_NAS_P (d))
    {
      Extbyte *fileext;

      LISP_STRING_TO_EXTERNAL (file, fileext, Qfile_name);
      /* #### NAS code should allow specification of a device. */
      if (nas_play_sound_file (fileext, vol))
	return Qnil;
    }
#endif /* HAVE_NAS_SOUND */

#ifdef HAVE_ESD_SOUND
  if (DEVICE_CONNECTED_TO_ESD_P (d))
    {
      Extbyte *fileext;
      int result;

      LISP_STRING_TO_EXTERNAL (file, fileext, Qfile_name);

      /* #### ESD uses alarm(). But why should we also stop SIGIO? */
      stop_interrupts ();
      result = esd_play_sound_file (fileext, vol);
      start_interrupts ();
      if (result)
       return Qnil;
    }
#endif /* HAVE_ESD_SOUND */

#ifdef HAVE_NATIVE_SOUND
  if (NILP (Vnative_sound_only_on_console) || DEVICE_ON_CONSOLE_P (d))
    {
      Extbyte *fileext;

#ifdef WIN32_NATIVE
      /* #### more garbage.  we should be passing the internal file name
	 to play_sound_file. */
      LISP_STRING_TO_EXTERNAL (file, fileext, Qmswindows_tstr);
#else
      LISP_STRING_TO_EXTERNAL (file, fileext, Qfile_name);
#endif
      /* The sound code doesn't like getting SIGIO interrupts.
	 Unix sucks! */
      stop_interrupts ();
      play_sound_file (fileext, vol);
      start_interrupts ();
      QUIT;
    }
#endif /* HAVE_NATIVE_SOUND */

  return Qnil;
}

static void
parse_sound_alist_elt (Lisp_Object elt,
		       Lisp_Object *volume,
		       Lisp_Object *pitch,
		       Lisp_Object *duration,
		       Lisp_Object *sound)
{
  *volume = Qnil;
  *pitch = Qnil;
  *duration = Qnil;
  *sound = Qnil;
  if (! CONSP (elt))
    return;

  /* The things we do for backward compatibility...
     I wish I had just forced this to be a plist to begin with.
   */

  if (SYMBOLP (elt) || STRINGP (elt))		/* ( name . <sound> ) */
    {
      *sound = elt;
    }
  else if (!CONSP (elt))
    {
      return;
    }
  else if (NILP (XCDR (elt)) &&		/* ( name <sound> ) */
	   (SYMBOLP (XCAR (elt)) ||
	    STRINGP (XCAR (elt))))
    {
      *sound = XCAR (elt);
    }
  else if (INT_OR_FLOATP (XCAR (elt)) &&	/* ( name <vol> . <sound> ) */
	   (SYMBOLP (XCDR (elt)) ||
	    STRINGP (XCDR (elt))))
    {
      *volume = XCAR (elt);
      *sound = XCDR (elt);
    }
  else if (INT_OR_FLOATP (XCAR (elt)) &&	/* ( name <vol> <sound> ) */
	   CONSP (XCDR (elt)) &&
	   NILP (XCDR (XCDR (elt))) &&
	   (SYMBOLP (XCAR (XCDR (elt))) ||
	    STRINGP (XCAR (XCDR (elt)))))
    {
      *volume = XCAR (elt);
      *sound = XCAR (XCDR (elt));
    }
  else if ((SYMBOLP (XCAR (elt)) ||	/* ( name <sound> . <vol> ) */
	    STRINGP (XCAR (elt))) &&
	   INT_OR_FLOATP (XCDR (elt)))
    {
      *sound = XCAR (elt);
      *volume = XCDR (elt);
    }
#if 0 /* this one is ambiguous with the plist form */
  else if ((SYMBOLP (XCAR (elt)) ||	/* ( name <sound> <vol> ) */
	    STRINGP (XCAR (elt))) &&
	   CONSP (XCDR (elt)) &&
	   NILP (XCDR (XCDR (elt))) &&
	   INT_OR_FLOATP (XCAR (XCDR (elt))))
    {
      *sound = XCAR (elt);
      *volume = XCAR (XCDR (elt));
    }
#endif /* 0 */
  else					/* ( name [ keyword <value> ]* ) */
    {
      while (CONSP (elt))
	{
	  Lisp_Object key, val;
	  key = XCAR (elt);
	  val = XCDR (elt);
	  if (!CONSP (val))
	    return;
	  elt = XCDR (val);
	  val = XCAR (val);
	  if (EQ (key, Q_volume))
	    {
	      if (INT_OR_FLOATP (val)) *volume = val;
	    }
	  else if (EQ (key, Q_pitch))
	    {
	      if (INT_OR_FLOATP (val)) *pitch = val;
	      if (NILP (*sound)) *sound = Qt;
	    }
	  else if (EQ (key, Q_duration))
	    {
	      if (INT_OR_FLOATP (val)) *duration = val;
	      if (NILP (*sound)) *sound = Qt;
	    }
	  else if (EQ (key, Q_sound))
	    {
	      if (SYMBOLP (val) || STRINGP (val)) *sound = val;
	    }
	}
    }
}

DEFUN ("play-sound", Fplay_sound, 1, 3, 0, /*
Play a sound of the provided type.

SOUND can a symbol, specifying a sound to be looked up in `sound-alist'
\(generally, either the symbol directly maps to a sound or is an "abstract"
symbol that maps to another symbol and is used to specify the sound that is
played when a particular behavior occurs.  `ding' lists the built-in
abstract sounds and their intended purpose.

SOUND can also be a string, which directly encodes the sound data to be played.

If SOUND is nil, the abstract sound `default' will be used.

VOLUME controls the volume (max is around 150? not sure).

DEVICE is the device to play the sound on (defaults to the selected device).

If the sound cannot be played in any other way, the standard "bell" will sound.
*/
       (sound, volume, device))
{
  int looking_for_default = 0;
  /* variable `sound' is anything that can be a cdr in sound-alist */
  Lisp_Object new_volume, pitch, duration, data;
  int loop_count = 0;
  int vol, pit, dur;
  struct device *d = decode_device (device);

  /* NOTE!  You'd better not signal an error in here. */


 try_it_again:
  while (1)
    {
      if (SYMBOLP (sound))
	sound = Fcdr (Fassq (sound, Vsound_alist));
      parse_sound_alist_elt (sound, &new_volume, &pitch, &duration, &data);
      sound = data;
      if (NILP (volume)) volume = new_volume;
      if (EQ (sound, Qt) || EQ (sound, Qnil) || STRINGP (sound))
	break;
      if (loop_count++ > 500)	/* much bogosity has occurred */
	break;
    }

  if (NILP (sound) && !looking_for_default)
    {
      looking_for_default = 1;
      loop_count = 0;
      sound = Qdefault;
      goto try_it_again;
    }


  vol = (INT_OR_FLOATP (volume)   ? (int) XFLOATINT (volume)   : bell_volume);
  pit = (INT_OR_FLOATP (pitch)    ? (int) XFLOATINT (pitch)    : -1);
  dur = (INT_OR_FLOATP (duration) ? (int) XFLOATINT (duration) : -1);

  /* If the sound is a string, and we're connected to Nas, do that.
     Else if the sound is a string, and we're on console, play it natively.
     Else just beep.
   */
#ifdef HAVE_NAS_SOUND
  if (DEVICE_CONNECTED_TO_NAS_P (d) && STRINGP (sound))
    {
      Binbyte *soundext;
      Bytecount soundextlen;

      TO_EXTERNAL_FORMAT (LISP_STRING, sound,
			  ALLOCA, (soundext, soundextlen),
			  Qbinary);
      if (nas_play_sound_data (soundext, soundextlen, vol))
	return Qnil;
    }
#endif /* HAVE_NAS_SOUND */

#ifdef HAVE_ESD_SOUND
  if (DEVICE_CONNECTED_TO_ESD_P (d) && STRINGP (sound))
    {
      Binbyte *soundext;
      Bytecount soundextlen;
      int succes;

      TO_EXTERNAL_FORMAT (LISP_STRING, sound, ALLOCA, (soundext, soundextlen),
			  Qbinary);
      
      /* #### ESD uses alarm(). But why should we also stop SIGIO? */
      stop_interrupts ();
      succes = esd_play_sound_data (soundext, soundextlen, vol);
      start_interrupts ();
      QUIT;
      if(succes)
        return Qnil;
    }
#endif /* HAVE_ESD_SOUND */

#ifdef HAVE_NATIVE_SOUND
  if ((NILP (Vnative_sound_only_on_console) || DEVICE_ON_CONSOLE_P (d))
      && STRINGP (sound))
    {
      Binbyte *soundext;
      Bytecount soundextlen;
      int succes;

      TO_EXTERNAL_FORMAT (LISP_STRING, sound,
			  ALLOCA, (soundext, soundextlen),
			  Qbinary);
      /* The sound code doesn't like getting SIGIO interrupts. Unix sucks! */
      stop_interrupts ();
      succes = play_sound_data (soundext, soundextlen, vol);
      start_interrupts ();
      QUIT;
      if (succes)
        return Qnil;
    }
#endif  /* HAVE_NATIVE_SOUND */

  DEVMETH (d, ring_bell, (d, vol, pit, dur));
  return Qnil;
}

DEFUN ("device-sound-enabled-p", Fdevice_sound_enabled_p, 0, 1, 0, /*
Return t if DEVICE is able to play sound.  Defaults to selected device.
*/
       (device))
{
#ifdef HAVE_NAS_SOUND
  if (DEVICE_CONNECTED_TO_NAS_P (decode_device (device)))
    return Qt;
#endif
#ifdef HAVE_NATIVE_SOUND
  if (DEVICE_ON_CONSOLE_P (decode_device (device)))
    return Qt;
#endif
  return Qnil;
}

DEFUN ("ding", Fding, 0, 3, 0, /*
Beep, or flash the frame.

\(See `visible-bell'; setting this makes the frame flash instead of
beeping.)  Also, unless NO-TERMINATE is given, terminate any keyboard macro
currently executing.  SOUND specifies the sound to make and DEVICE the
device to make it on (defaults to the selected device).

SOUND is either a string (raw data to be played directly), a symbol, or
`nil' (equivalent to the symbol `default').  Sound symbols are looked up in
`sound-alist', and resolve either to strings of data or to other symbols.
Sound symbols that map directly to data should be considered named sounds;
sound symbols that map to other sounds should be considered abstract
sounds, and are used when a particular behavior or state occurs.

Rremember that the sound symbol is the *second* argument to `ding', not the
first.

The following abstract sounds are used by XEmacs itself:

    alarm		when a package wants to remind the user
    auto-save-error	when an auto-save does not succeed
    buffer-bound	when you attempt to move off the end of a buffer
    command-error	any uncaught error (i.e. any error that the user
                        sees) except those handled by undefined-click,
			undefined-key, buffer-bound, or read-only
    default		used when nothing else is appropriate.
    isearch-failed      unable to locate search text during incremental search
    isearch-quit        when you delete chars past the beginning of the search
                        text in isearch
    no-completion	during completing-read
    quit		when C-g is typed
    read-only           when you try to modify a read-only buffer
    ready		when a compile or other time-consuming task is done
    undefined-click	when you use an undefined mouse-click combination
    undefined-key	when you type a key that is undefined
    warp		XEmacs has changed the selected-window or frame
			asynchronously -- e.g. a debugger breakpoint is hit
			in an asynchronous process filter
    y-or-n-p		when you type something other than 'y' or 'n'
    yes-or-no-p  	when you type something other than 'yes' or 'no'
    
Other lisp packages may use other beep types, but these are the ones that
the C kernel of Emacs uses.

*/
       (no_terminate, sound, device))
{
  static time_t last_bell_time;
  static struct device *last_bell_device;
  time_t now;
  struct device *d = decode_device (device);     

  device = wrap_device (d);
  now = time (0);

  if (NILP (no_terminate) && !NILP (Vexecuting_macro))
    /* Stop executing a keyboard macro. */
    invalid_operation ("Keyboard macro terminated by a command ringing the bell", Qunbound);
  
  if (d == last_bell_device && now-last_bell_time < bell_inhibit_time)
    return Qnil;
  else if (!NILP (Vvisible_bell) && DEVMETH (d, flash, (d)))
    ;
  else
    Fplay_sound (sound, Qnil, device);
  
  last_bell_time = now;
  last_bell_device = d;
  return Qnil;    
}

#ifdef HAVE_NAS_SOUND
#define USED_IF_HAVE_NAS(decl) decl
#else
#define USED_IF_HAVE_NAS(decl) UNUSED (decl)
#endif

DEFUN ("wait-for-sounds", Fwait_for_sounds, 0, 1, 0, /*
Wait for all sounds to finish playing on DEVICE.
*/
       (USED_IF_HAVE_NAS (device)))
{
#ifdef HAVE_NAS_SOUND
  struct device *d = decode_device (device);
  if (DEVICE_CONNECTED_TO_NAS_P (d))
    {
      /* #### somebody fix this to be device-dependent. */
      nas_wait_for_sounds ();
    }
#endif
  return Qnil;
}

DEFUN ("connected-to-nas-p", Fconnected_to_nas_p, 0, 1, 0, /*
Return t if connected to NAS server for sounds on DEVICE.
*/
       (USED_IF_HAVE_NAS (device)))
{
#ifdef HAVE_NAS_SOUND
  return DEVICE_CONNECTED_TO_NAS_P (decode_device (device)) ? Qt : Qnil;
#else
  return Qnil;
#endif
}
#ifdef HAVE_NAS_SOUND

static void
init_nas_sound (struct device *d)
{
#ifdef HAVE_X_WINDOWS
  if (DEVICE_X_P (d))
    {
      Extbyte *err_message = nas_init_play (DEVICE_X_DISPLAY (d));
      DEVICE_CONNECTED_TO_NAS_P (d) = !err_message;
      /* Print out the message? */
    }
#endif /* HAVE_X_WINDOWS */
}

#endif /* HAVE_NAS_SOUND */

#ifdef HAVE_NATIVE_SOUND

static void
init_native_sound (struct device *d)
{
  if (DEVICE_TTY_P (d) || DEVICE_STREAM_P (d) || DEVICE_MSWINDOWS_P(d))
    DEVICE_ON_CONSOLE_P (d) = 1;
#ifdef HAVE_X_WINDOWS
  else
    {
      /* When running on a machine with native sound support, we cannot use
	 digitized sounds as beeps unless emacs is running on the same machine
	 that $DISPLAY points to, and $DISPLAY points to frame 0 of that
	 machine.
	 */

      Display *display = DEVICE_X_DISPLAY (d);
      Extbyte *dpy = DisplayString (display);
      Extbyte *tail = strchr (dpy, ':');
      if (! tail ||
	  strncmp (tail, ":0", 2))
	DEVICE_ON_CONSOLE_P (d) = 0;
      else
	{
	  Extbyte dpyname[255], localname[255];

	  /* some systems can't handle SIGIO or SIGALARM in gethostbyname. */
	  stop_interrupts ();
	  strncpy (dpyname, dpy, tail-dpy);
	  dpyname [tail-dpy] = 0;
	  if (!*dpyname ||
	      !strcmp (dpyname, "unix") ||
	      !strcmp (dpyname, "localhost"))
	    DEVICE_ON_CONSOLE_P (d) = 1;
	  else if (gethostname (localname, sizeof (localname)))
	    DEVICE_ON_CONSOLE_P (d) = 0;	/* can't find hostname? */
	  else
	    {
	      /* We have to call gethostbyname() on the result of gethostname()
		 because the two aren't guaranteed to be the same name for the
		 same host: on some losing systems, one is a FQDN and the other
		 is not.  Here in the wide wonderful world of Unix it's rocket
		 science to obtain the local hostname in a portable fashion.

		 And don't forget, gethostbyname() reuses the structure it
		 returns, so we have to copy the fucker before calling it
		 again.

		 Thank you master, may I have another.
		 */
	      struct hostent *h = gethostbyname (dpyname);
	      if (!h)
		DEVICE_ON_CONSOLE_P (d) = 0;
	      else
		{
		  Extbyte hn [255];
		  struct hostent *l;
		  strcpy (hn, h->h_name);
		  l = gethostbyname (localname);
		  DEVICE_ON_CONSOLE_P (d) = (l && !(strcmp (l->h_name, hn)));
		}
	    }
	  start_interrupts ();
	}
    }
#endif /* HAVE_X_WINDOWS */
}

#endif /* HAVE_NATIVE_SOUND */

void
init_device_sound (struct device *d)
{
#ifdef HAVE_NAS_SOUND
  init_nas_sound (d);
#endif

#ifdef HAVE_NATIVE_SOUND
  init_native_sound (d);
#endif
}

void
syms_of_sound (void)
{
  DEFKEYWORD (Q_volume);
  DEFKEYWORD (Q_pitch);
  DEFKEYWORD (Q_duration);
  DEFKEYWORD (Q_sound);

  DEFERROR_STANDARD (Qsound_error, Qio_error);

  DEFSUBR (Fplay_sound_file);
  DEFSUBR (Fplay_sound);
  DEFSUBR (Fding);
  DEFSUBR (Fwait_for_sounds);
  DEFSUBR (Fconnected_to_nas_p);
  DEFSUBR (Fdevice_sound_enabled_p);
}


void
vars_of_sound (void)
{
#ifdef HAVE_NATIVE_SOUND
  Fprovide (intern ("native-sound"));
#endif
#ifdef HAVE_NAS_SOUND
  Fprovide (intern ("nas-sound"));
#endif
#ifdef HAVE_ESD_SOUND
  Fprovide (intern ("esd-sound"));
#endif

  DEFVAR_INT ("bell-volume", &bell_volume /*
*How loud to be, from 0 to 100.
*/ );
  bell_volume = 50;
  
  DEFVAR_INT ("bell-inhibit-time", &bell_inhibit_time /*
*Don't ring the bell on the same device more than once within this many seconds.
*/ );
  bell_inhibit_time = 0;

  DEFVAR_LISP ("sound-alist", &Vsound_alist /*
An alist associating names with sounds.
When `beep' or `ding' is called with one of the name symbols, the associated
sound will be generated instead of the standard beep.

Each element of `sound-alist' is a list describing a sound.
The first element of the list is the name of the sound being defined.
Subsequent elements of the list are alternating keyword/value pairs:

   Keyword:	Value:
   -------	-----
   sound	A string of raw sound data, or the name of another sound to
		play.   The symbol `t' here means use the default X beep.
   volume	An integer from 0-100, defaulting to `bell-volume'
   pitch	If using the default X beep, the pitch (Hz) to generate.
   duration	If using the default X beep, the duration (milliseconds).

For compatibility, elements of `sound-alist' may also be:

   ( sound-name . <sound> )
   ( sound-name <volume> <sound> )

You should probably add things to this list by calling the function
load-sound-file.

Caveats:
 - XEmacs must be built with sound support for your system.  Not all
   systems support sound. 

 - The pitch, duration, and volume options are available everywhere, but
   many X servers ignore the `pitch' option.

Sound symbols that map directly to data should be considered named sounds;
sound symbols that map to other sounds should be considered abstract
sounds, and are used when a particular behavior or state occurs.  See
`ding' for a list of the standard abstract sounds.
*/ );
  Vsound_alist = Qnil;

  DEFVAR_LISP ("synchronous-sounds", &Vsynchronous_sounds /*
Play sounds synchronously, if non-nil.
Only applies if NAS is used and supports asynchronous playing
of sounds.  Otherwise, sounds are always played synchronously.
*/ );
  Vsynchronous_sounds = Qnil;

  DEFVAR_LISP ("native-sound-only-on-console", &Vnative_sound_only_on_console /*
Non-nil value means play sounds only if XEmacs is running
on the system console.
Nil means always play sounds, even if running on a non-console tty
or a secondary X display.

This variable only applies to native sound support.
*/ );
  Vnative_sound_only_on_console = Qt;

#if defined (HAVE_NATIVE_SOUND) && defined (hp9000s800)
  {
    void vars_of_hpplay (void);
    vars_of_hpplay ();
  }
#endif
}