view src/miscplay.c @ 1314:15a91d7ae2d1

[xemacs-hg @ 2003-02-20 08:16:21 by ben] check in makefile fixes et al Makefile.in.in: Major surgery. Move all stuff related to building anything in the src/ directory into src/. Simplify the dependencies -- everything in src/ is dependent on the single entry `src' in MAKE_SUBDIRS. Remove weirdo targets like `all-elc[s]', dump-elc[s], etc. mule/mule-msw-init.el: Removed. Delete this file. mule/mule-win32-init.el: New file, with stuff from mule-msw-init.el -- not just for MS Windows native, boys and girls! bytecomp.el: Change code inserted to catch trying to load a Mule-only .elc file in a non-Mule XEmacs. Formerly you got the rather cryptic "The required feature `mule' cannot be provided". Now you get "Loading this file requires Mule support". finder.el: Remove dependency on which directory this function is invoked from. update-elc.el: Don't mess around with ../src/BYTECOMPILE_CHANGE. Now that Makefile.in.in and xemacs.mak are in sync, both of them use NEEDTODUMP and the other one isn't used. dumped-lisp.el: Rewrite in terms of `list' and `nconc' instead of assemble-list, so we can have arbitrary forms, not just `when-feature'. very-early-lisp.el: Nuke this file. finder-inf.el, packages.el, update-elc.el, update-elc-2.el, loadup.el, make-docfile.el: Eliminate references to very-early-lisp. msw-glyphs.el: Comment clarification. xemacs.mak: Add macros DO_TEMACS, DO_XEMACS, and a few others; this macro section is now completely in sync with src/Makefile.in.in. Copy check-features, load-shadows, and rebuilding finder-inf.el from src/Makefile.in.in. The main build/dump/recompile process is now synchronized with src/Makefile.in.in. Change `WARNING' to `NOTE' and `error checking' to `error-checking' TO avoid tripping faux warnings and errors in the VC++ IDE. Makefile.in.in: Major surgery. Move all stuff related to building anything in the src/ directory from top-level Makefile.in.in to here. Simplify the dependencies. Rearrange into logical subsections. Synchronize the main compile/dump/build-elcs section with xemacs.mak, which is already clean and in good working order. Remove weirdo targets like `all-elc[s]', dump-elc[s], etc. Add additional levels of macros \(e.g. DO_TEMACS, DO_XEMACS, TEMACS_BATCH, XEMACS_BATCH, XEMACS_BATCH_PACKAGES) to factor out duplicated stuff. Clean up handling of "HEAP_IN_DATA" (Cygwin) so it doesn't need to ignore the return value from dumping. Add .NO_PARALLEL since various aspects of building and dumping must be serialized but do not always have dependencies between them (this is impossible in some cases). Everything related to src/ now gets built in one pass in this directory by just running `make' (except the Makefiles themselves and config.h, paths.h, Emacs.ad.h, and other generated .h files). console.c: Update list of possibly valid console types. emacs.c: Rationalize the specifying and handling of the type of the first frame. This was originally prompted by a workspace in which I got GTK to compile under C++ and in the process fixed it so it could coexist with X in the same build -- hence, a combined TTY/X/MS-Windows/GTK build is now possible under Cygwin. (However, you can't simultaneously *display* more than one kind of device connection -- but getting that to work is not that difficult. Perhaps a project for a bored grad student. I (ben) would do it but don't see the use.) To make sense of this, I added new switches that can be used to specifically indicate the window system: -x [aka --use-x], -tty \[aka --use-tty], -msw [aka --use-ms-windows], -gtk [aka --use-gtk], and -gnome [aka --use-gnome, same as --use-gtk]. -nw continues as an alias for -tty. When none have been given, XEmacs checks for other parameters implying particular device types (-t -> tty, -display -> x [or should it have same treatment as DISPLAY below?]), and has ad-hoc logic afterwards: if env var DISPLAY is set, use x (or gtk? perhaps should check whether gnome is running), else MS Windows if it exsits, else TTY if it exists, else stream, and you must be running in batch mode. This also fixes an existing bug whereby compiling with no x, no mswin, no tty, when running non- interactively (e.g. to dump) I get "sorry, must have TTY support". emacs.c: Turn on Vstack_trace_on_error so that errors are debuggable even when occurring extremely early in reinitialization. emacs.c: Try to make sure that the user can see message output under Windows (i.e. it doesn't just disappear right away) regardless of when it occurs, e.g. in the middle of creating the first frame. emacs.c: Define new function `emacs-run-status', indicating whether XEmacs is noninteractive or interactive, whether raw, post-dump/pdump-load or run-temacs, whether we are dumping, whether pdump is in effect. event-stream.c: It's "mommas are fat", not "momas are fat". Fix other typo. event-stream.c: Conditionalize in_menu_callback check on HAVE_MENUBARS, because it won't exist on w/o menubar support, lisp.h: More hackery on RETURN_NOT_REACHED. Cygwin v3.2 DOES complain here if RETURN_NOT_REACHED() is blank, as it is for GCC 2.5+. So make it blank only for GCC 2.5 through 2.999999999999999. Declare Vstack_trace_on_error. profile.c: Need to include "profile.h" to fix warnings. sheap.c: Don't fatal() when need to rerun Make, just stderr_out() and exit(0). That way we can distinguish between a dumping failing expectedly (due to lack of stack space, triggering another dump) and unexpectedly, in which case, we want to stop building. (or go on, if -K is given) syntax.c, syntax.h: Use ints where they belong, and enum syntaxcode's where they belong, and fix warnings thereby. syntax.h: Fix crash caused by an edge condition in the syntax-cache macros. text.h: Spacing fixes. xmotif.h: New file, to get around shadowing warnings. EmacsManager.c, event-Xt.c, glyphs-x.c, gui-x.c, input-method-motif.c, xmmanagerp.h, xmprimitivep.h: Include xmotif.h. alloc.c: Conditionalize in_malloc on ERROR_CHECK_MALLOC. config.h.in, file-coding.h, fileio.c, getloadavg.c, select-x.c, signal.c, sysdep.c, sysfile.h, systime.h, text.c, unicode.c: Eliminate HAVE_WIN32_CODING_SYSTEMS, use WIN32_ANY instead. Replace defined (WIN32_NATIVE) || defined (CYGWIN) with WIN32_ANY. lisp.h: More futile attempts to walk and chew gum at the same time when dealing with subr's that don't return.
author ben
date Thu, 20 Feb 2003 08:16:21 +0000
parents 943eaba38521
children 04bc9d2f42c7
line wrap: on
line source

/* miscplay.c - general routines related to playing sounds
 **
 ** Copyright (C) 1995,96 by Markus Gutschke (gutschk@math.uni-muenster.de)
 ** This was sawed out from version 1.3 of linuxplay.c by
 ** Robert Bihlmeyer <robbe@orcus.priv.at>.
 **
 ** Parts of this code were inspired by sunplay.c, which is copyright 1989 by
 ** Jef Poskanzer and 1991,92 by Jamie Zawinski; c.f. sunplay.c for further
 ** information.
 **
 ** Permission to use, copy, modify, and distribute this software and its
 ** documentation for any purpose and without fee is hereby granted, provided
 ** that the above copyright notice appear in all copies and that both that
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
 */

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

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

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

#include "miscplay.h"
#include "sound.h"

#include "syssignal.h"
#include "sysfile.h"

#ifdef __GNUC__
#define UNUSED(x) ((void)(x))
#else
#define UNUSED(x)
#endif

/* Maintain global variable for keeping parser state information; this struct
   is set to zero before the first invocation of the parser. The use of a
   global variable prevents multiple concurrent executions of this code, but
   this does not happen anyways... */
enum wvState
{ wvMain,
  wvSubchunk,
  wvOutOfBlock,
  wvSkipChunk,
  wvSoundChunk,
  wvFatal,
  wvFatalNotify
};

static union {
  struct {
    int           align;
    enum wvState state;
    size_t        left;
    UChar_Binary leftover[HEADERSZ];
    signed long   chunklength;
  } wave;
  struct {
    int           align;
    int           isdata;
    int           skipping;
    size_t        left;
    UChar_Binary leftover[HEADERSZ];
  } audio;
} parsestate;

/* Use a global buffer as scratch-pad for possible conversions of the
   sampling format */
UChar_Binary miscplay_sndbuf[SNDBUFSZ];

/* Initialize global parser state information to zero */
void reset_parsestate()
{
  memset(&parsestate,0,sizeof(parsestate));
}

/* Verify that we could fully parse the entire soundfile; this is needed
   only for files in WAVE format */
int parse_wave_complete()
{
  if (parsestate.wave.state != wvOutOfBlock &&
      parsestate.wave.state != wvFatal) {
    sound_warn("Unexpected end of WAVE file");
    return 0;
  } else
    return 1;
}

/* There is no special treatment required for parsing raw data files; we
   assume that these files contain data in 8bit unsigned format that
   has been sampled at 8kHz; there is no extra header */
static size_t parseraw(void **data,size_t *sz,void **outbuf)
{
  int rc = *sz;

  *outbuf = *data;
  *sz = 0;
  return(rc);
}

/* Currently we cannot cope with files in VOC format; if you really need
   to play these files, they should be converted by using SOX */
static size_t parsevoc(void **data,size_t *sz,void **outbuf)
{
  UNUSED(data);
  UNUSED(sz);
  UNUSED(outbuf);
  return(0);
}

/* We need to perform some look-ahead in order to parse files in WAVE format;
   this might require re-partioning of the data segments if headers cross the
   boundaries between two read operations. This is done in a two-step way:
   first we request a certain amount of bytes... */
static int waverequire(void **data,size_t *sz,size_t rq)
{
  int rc = 1;

  if (rq > HEADERSZ) {
    sound_warn("Header size exceeded while parsing WAVE file");
    parsestate.wave.state = wvFatal;
    *sz = 0;
    return(0); }
  if ((rq -= parsestate.wave.left) <= 0)
    return(rc);
  if (rq > *sz) {rq = *sz; rc = 0;}
  memcpy(parsestate.wave.leftover+parsestate.wave.left,
        *data,rq);
  parsestate.wave.left      += rq;
  (*(UChar_Binary **)data) += rq;
  *sz                       -= rq;
  return(rc);
}

/* ...and next we remove this many bytes from the buffer */
static inline void waveremove(size_t rq)
{
  if (parsestate.wave.left <= rq)
    parsestate.wave.left = 0;
  else {
    parsestate.wave.left -= rq;
    memmove(parsestate.wave.leftover,
           parsestate.wave.leftover+rq,
           parsestate.wave.left); }
  return;
}

/* Sound files in WAVE format can contain an arbitrary amount of tagged
   chunks; this requires quite some effort for parsing the data */
static size_t parsewave(void **data,size_t *sz,void **outbuf)
{
  for (;;)
    switch (parsestate.wave.state) {
    case wvMain:
      if (!waverequire(data,sz,20))
       return(0);
      /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
      parsestate.wave.chunklength = parsestate.wave.leftover[16] +
       256*(parsestate.wave.leftover[17] +
            256*(parsestate.wave.leftover[18] +
                 256*parsestate.wave.leftover[19]));
      waveremove(20);
      parsestate.wave.state = wvSubchunk;
      break;
    case wvSubchunk:
      if (!waverequire(data,sz,parsestate.wave.chunklength))
       return(0);
      parsestate.wave.align = parsestate.wave.chunklength < 14 ? 1
       : parsestate.wave.leftover[12];
      if (parsestate.wave.align != 1 &&
         parsestate.wave.align != 2 &&
         parsestate.wave.align != 4) {
       sound_warn("Illegal datawidth detected while parsing WAVE file");
       parsestate.wave.state = wvFatal; }
      else
       parsestate.wave.state = wvOutOfBlock;
      waveremove(parsestate.wave.chunklength);
      break;
    case wvOutOfBlock:
      if (!waverequire(data,sz,8))
       return(0);
      /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
      parsestate.wave.chunklength = parsestate.wave.leftover[4] +
       256*(parsestate.wave.leftover[5] +
            256*(parsestate.wave.leftover[6] +
                 256*(parsestate.wave.leftover[7] & 0x7F)));
      if (memcmp(parsestate.wave.leftover,"data",4))
       parsestate.wave.state = wvSkipChunk;
      else
       parsestate.wave.state = wvSoundChunk;
      waveremove(8);
      break;
    case wvSkipChunk:
      if (parsestate.wave.chunklength > 0 && *sz > 0 &&
         (signed long)*sz < (signed long)parsestate.wave.chunklength) {
       parsestate.wave.chunklength -= *sz;
       *sz = 0; }
      else {
       if (parsestate.wave.chunklength > 0 && *sz > 0) {
         *sz -= parsestate.wave.chunklength;
         (*(UChar_Binary **)data) += parsestate.wave.chunklength; }
       parsestate.wave.state = wvOutOfBlock; }
      break;
    case wvSoundChunk: {
      size_t count,rq;
      if (parsestate.wave.left) { /* handle leftover bytes from last
                                    alignment operation */
       count = parsestate.wave.left;
       rq    = HEADERSZ-count;
       if (rq > (size_t) parsestate.wave.chunklength)
         rq = parsestate.wave.chunklength;
       if (!waverequire(data,sz,rq)) {
         parsestate.wave.chunklength -= parsestate.wave.left - count;
         return(0); }
       parsestate.wave.chunklength -= rq;
       *outbuf                      = parsestate.wave.leftover;
       parsestate.wave.left         = 0;
       return(rq); }
      if (*sz >= (size_t) parsestate.wave.chunklength) {
       count  = parsestate.wave.chunklength;
       rq     = 0; }
      else {
       count  = *sz;
       count -= rq = count % parsestate.wave.align; }
      *outbuf                   = *data;
      (*(UChar_Binary **)data) += count;
      *sz                       -= count;
      if ((parsestate.wave.chunklength -= count) < parsestate.wave.align) {
       parsestate.wave.state = wvOutOfBlock;
       /* Some broken software (e.g. SOX) attaches junk to the end of a sound
          chunk; so, let's ignore this... */
       if (parsestate.wave.chunklength)
         parsestate.wave.state = wvSkipChunk; }
      else if (rq)
       /* align data length to a multiple of datasize; keep additional data
          in "leftover" buffer --- this is necessary to ensure proper
          functioning of the sndcnv... routines */
       waverequire(data,sz,rq);
      return(count); }
    case wvFatalNotify:
      sound_warn("Irrecoverable error while parsing WAVE file");
      parsestate.wave.state = wvFatal;
      break;
    case wvFatal:
    default:
      *sz = 0;
      return(0); }
}

/* Strip the header from files in Sun/DEC audio format; this requires some
   extra processing as the header can be an arbitrary size and it might
   result in alignment errors for subsequent conversions --- thus we do
   some buffering, where needed */
static size_t parsesundecaudio(void **data,size_t *sz,void **outbuf)
{
  /* There is data left over from the last invocation of this function; join
     it with the new data and return a sound chunk that is as big as a
     single entry */
  if (parsestate.audio.left) {
    if (parsestate.audio.left + *sz > (size_t) parsestate.audio.align) {
      int  count;
      memmove(parsestate.audio.leftover + parsestate.audio.left,
             *data,
             count = parsestate.audio.align - parsestate.audio.left);
      *outbuf = parsestate.audio.leftover;
      *sz    -= count;
      *data   = (*(Char_Binary **)data) + count;
      parsestate.audio.left = 0;
      return(parsestate.audio.align); }
    else {
      /* We need even more data in order to get one complete single entry! */
      memmove(parsestate.audio.leftover + parsestate.audio.left,
             *data,
             *sz);
      *data = (*(Char_Binary **)data) + *sz;
      parsestate.audio.left += *sz;
      *sz   = 0;
      return(0); } }

  /* This is the main sound chunk, strip of any extra data that does not fit
     the alignment requirements and move these bytes into the leftover buffer*/
  if (parsestate.audio.isdata) {
    int rc = *sz;
    *outbuf = *data;
    if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) {
      memmove(parsestate.audio.leftover,
             (Char_Binary *)*outbuf + rc - parsestate.audio.left,
             parsestate.audio.left);
      rc -= parsestate.audio.left; }
    *sz = 0;
    return(rc); }

  /* This is the first invocation of this function; we need to parse the
     header information and determine how many bytes we need to skip until
     the start of the sound chunk */
  if (!parsestate.audio.skipping) {
    UChar_Binary *header = (UChar_Binary *) *data;
    if (*sz < 8) {
      sound_warn("Irrecoverable error while parsing Sun/DEC audio file");
      return(0); }
    /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
    if (header[3]) { /* Sun audio (big endian) */
      parsestate.audio.align = ((header[15] > 2)+1)*header[23];
      parsestate.audio.skipping = header[7]+256*(header[6]+256*
                                                (header[5]+256*header[4])); }
    else { /* DEC audio (little endian) */
      parsestate.audio.align = ((header[12] > 2)+1)*header[20];
      parsestate.audio.skipping = header[4]+256*(header[5]+256*
                                                (header[6]+256*header[7])); }}

  /* We are skipping extra data that has been attached to header; most usually
     this will be just a comment, such as the original filename and/or the
     creation date. Make sure that we do not return less than one single sound
     sample entry to the caller; if this happens, rather decide to move those
     few bytes into the leftover buffer and deal with it later */
  if (*sz >= (size_t) parsestate.audio.skipping) {
    /* Skip just the header information and return the sound chunk */
    int rc = *sz - parsestate.audio.skipping;
    *outbuf = (Char_Binary *)*data + parsestate.audio.skipping;
    if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) {
      memmove(parsestate.audio.leftover,
             (Char_Binary *)*outbuf + rc - parsestate.audio.left,
             parsestate.audio.left);
      rc -= parsestate.audio.left; }
    *sz = 0;
    parsestate.audio.skipping = 0;
    parsestate.audio.isdata++;
    return(rc); }
  else {
    /* Skip everything */
    parsestate.audio.skipping -= *sz;
    return(0); }
}

/* If the soundcard could not be set to natively support the data format, we
   try to do some limited on-the-fly conversion to a different format; if
   no conversion is needed, though, we can output directly */
size_t sndcnvnop(void **data,size_t *sz,void **outbuf)
{
  int rc = *sz;

  *outbuf = *data;
  *sz = 0;
  return(rc);
}

/* Convert 8 bit unsigned stereo data to 8 bit unsigned mono data */
size_t sndcnv8U_2mono(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (UChar_Binary)(((int)*(src) +
				 (int)*(src+1)) / 2);
      src += 2;
    }
  *data   = src;
  return(rc);
}

/* Convert 8 bit signed stereo data to 8 bit signed mono data */
size_t sndcnv8S_2mono(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc, count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (UChar_Binary)(((int)*((SChar_Binary *)(src)) +
				 (int)*((SChar_Binary *)(src+1))) / 2);
      src  += 2;
    }
  *data   = src;
  return(rc);
}

/* Convert 8 bit signed stereo data to 8 bit unsigned mono data */
size_t sndcnv2monounsigned(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (UChar_Binary)(((int)*((SChar_Binary *)(src)) +
				 (int)*((SChar_Binary *)(src+1))) / 2) ^ 0x80;
      src += 2;
    }
  *data   = src;
  return(rc);
}

/* Convert 8 bit signed mono data to 8 bit unsigned mono data */
size_t sndcnv2unsigned(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz;
  if (count > SNDBUFSZ) { *sz  -= SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    *dest++ = *(src)++ ^ 0x80;
  *data   = src;
  return(rc);
}

/* Convert a number in the range -32768..32767 to an 8 bit ulaw encoded
   number --- I hope, I got this conversion right :-) */
static inline SChar_Binary int2ulaw(int i)
{
    /* Lookup table for fast calculation of number of bits that need shifting*/
    static short int t_bits[128] = {
      0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
      6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
      7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
      7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
    REGISTER int bits,logi;

    /* unrolling this condition (hopefully) improves execution speed */
    if (i < 0) {
      if ((i = (132-i)) > 0x7FFF) i = 0x7FFF;
      logi = (i >> ((bits = t_bits[i/256])+4));
      return((bits << 4 | logi) ^ 0x7F); }
    else {
      if ((i = 132+i) > 0x7FFF) i = 0x7FFF;
      logi = (i >> ((bits = t_bits[i/256])+4));
      return(~(bits << 4 | logi)); }
}

/* Convert from 8 bit ulaw mono to 8 bit linear mono */
size_t sndcnvULaw_2linear(void **data,size_t *sz,void **outbuf)
{
  /* conversion table stolen from Linux's ulaw.h */
  static UChar_Binary ulaw_dsp[] = {
     3,    7,   11,   15,   19,   23,   27,   31,
    35,   39,   43,   47,   51,   55,   59,   63,
    66,   68,   70,   72,   74,   76,   78,   80,
    82,   84,   86,   88,   90,   92,   94,   96,
    98,   99,  100,  101,  102,  103,  104,  105,
   106,  107,  108,  109,  110,  111,  112,  113,
   113,  114,  114,  115,  115,  116,  116,  117,
   117,  118,  118,  119,  119,  120,  120,  121,
   121,  121,  122,  122,  122,  122,  123,  123,
   123,  123,  124,  124,  124,  124,  125,  125,
   125,  125,  125,  125,  126,  126,  126,  126,
   126,  126,  126,  126,  127,  127,  127,  127,
   127,  127,  127,  127,  127,  127,  127,  127,
   128,  128,  128,  128,  128,  128,  128,  128,
   128,  128,  128,  128,  128,  128,  128,  128,
   128,  128,  128,  128,  128,  128,  128,  128,
   253,  249,  245,  241,  237,  233,  229,  225,
   221,  217,  213,  209,  205,  201,  197,  193,
   190,  188,  186,  184,  182,  180,  178,  176,
   174,  172,  170,  168,  166,  164,  162,  160,
   158,  157,  156,  155,  154,  153,  152,  151,
   150,  149,  148,  147,  146,  145,  144,  143,
   143,  142,  142,  141,  141,  140,  140,  139,
   139,  138,  138,  137,  137,  136,  136,  135,
   135,  135,  134,  134,  134,  134,  133,  133,
   133,  133,  132,  132,  132,  132,  131,  131,
   131,  131,  131,  131,  130,  130,  130,  130,
   130,  130,  130,  130,  129,  129,  129,  129,
   129,  129,  129,  129,  129,  129,  129,  129,
   128,  128,  128,  128,  128,  128,  128,  128,
   128,  128,  128,  128,  128,  128,  128,  128,
   128,  128,  128,  128,  128,  128,  128,  128,
  };
  UChar_Binary *p=(UChar_Binary *)*data;

  *outbuf = *data;
  while ((*sz)--)
    {
      *p = ulaw_dsp[*p];
      p++;
    }
  *sz = 0;
  *data = p;
  return p - (UChar_Binary *)*outbuf;
}

/* Convert 8 bit ulaw stereo data to 8 bit ulaw mono data */
size_t sndcnvULaw_2mono(void **data,size_t *sz,void **outbuf)
{

  static short int ulaw2int[256] = {
    /* Precomputed lookup table for conversion from ulaw to 15 bit signed */
    -16062,-15550,-15038,-14526,-14014,-13502,-12990,-12478,
    -11966,-11454,-10942,-10430, -9918, -9406, -8894, -8382,
     -7998, -7742, -7486, -7230, -6974, -6718, -6462, -6206,
     -5950, -5694, -5438, -5182, -4926, -4670, -4414, -4158,
     -3966, -3838, -3710, -3582, -3454, -3326, -3198, -3070,
     -2942, -2814, -2686, -2558, -2430, -2302, -2174, -2046,
     -1950, -1886, -1822, -1758, -1694, -1630, -1566, -1502,
     -1438, -1374, -1310, -1246, -1182, -1118, -1054,  -990,
      -942,  -910,  -878,  -846,  -814,  -782,  -750,  -718,
      -686,  -654,  -622,  -590,  -558,  -526,  -494,  -462,
      -438,  -422,  -406,  -390,  -374,  -358,  -342,  -326,
      -310,  -294,  -278,  -262,  -246,  -230,  -214,  -198,
      -186,  -178,  -170,  -162,  -154,  -146,  -138,  -130,
      -122,  -114,  -106,   -98,   -90,   -82,   -74,   -66,
       -60,   -56,   -52,   -48,   -44,   -40,   -36,   -32,
       -28,   -24,   -20,   -16,   -12,    -8,    -4,    +0,
    +16062,+15550,+15038,+14526,+14014,+13502,+12990,+12478,
    +11966,+11454,+10942,+10430, +9918, +9406, +8894, +8382,
     +7998, +7742, +7486, +7230, +6974, +6718, +6462, +6206,
     +5950, +5694, +5438, +5182, +4926, +4670, +4414, +4158,
     +3966, +3838, +3710, +3582, +3454, +3326, +3198, +3070,
     +2942, +2814, +2686, +2558, +2430, +2302, +2174, +2046,
     +1950, +1886, +1822, +1758, +1694, +1630, +1566, +1502,
     +1438, +1374, +1310, +1246, +1182, +1118, +1054,  +990,
      +942,  +910,  +878,  +846,  +814,  +782,  +750,  +718,
      +686,  +654,  +622,  +590,  +558,  +526,  +494,  +462,
      +438,  +422,  +406,  +390,  +374,  +358,  +342,  +326,
      +310,  +294,  +278,  +262,  +246,  +230,  +214,  +198,
      +186,  +178,  +170,  +162,  +154,  +146,  +138,  +130,
      +122,  +114,  +106,   +98,   +90,   +82,   +74,   +66,
       +60,   +56,   +52,   +48,   +44,   +40,   +36,   +32,
       +28,   +24,   +20,   +16,   +12,    +8,    +4,    +0};

  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      /* it is not possible to directly interpolate between two ulaw encoded
	 data bytes, thus we need to convert to linear format first and later
	 we convert back to ulaw format */
      *dest++ = int2ulaw(ulaw2int[*src] +
			 ulaw2int[*(src+1)]);
      src  += 2;
    }
  *data = src;
  return(rc);
}

size_t sndcnv16swap(void **data,size_t *sz,void **outbuf)
{
  size_t cnt = *sz / 2;
  unsigned short *p;

  *outbuf = *data;
  p = (unsigned short *) *outbuf;
  while (cnt--)
    {
      *p = ((*p & 0x00ff) << 8) | (*p >> 8);
      p++;
    }
  *data = p;
  cnt = *sz;
  *sz = 0;
  return cnt;
}

/* Convert 16 bit little endian signed stereo data to 16 bit little endian
   signed mono data */
size_t sndcnv16_2monoLE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;
  signed short i;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  for (count /= 2; count--; ) {
    i = ((int)(src[0]) +
        256*(int)(src[1]) +
       (int)(src[2]) +
       256*(int)(src[3])) / 2;
    src += 4;
    *dest++ = (UChar_Binary)(i & 0xFF);
    *dest++ = (UChar_Binary)((i / 256) & 0xFF); }
  *data = src;
  return(rc);
}

/* Convert 16 bit big endian signed stereo data to 16 bit big endian
   signed mono data */
size_t sndcnv16_2monoBE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;
  signed short i;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  for (count /= 2; count--; ) {
    i = ((int)(src[1]) +
        256*(int)(src[0]) +
       (int)(src[3]) +
       256*(int)(src[2])) / 2;
    src += 4;
    *dest++ = (UChar_Binary)((i / 256) & 0xFF);
    *dest++ = (UChar_Binary)(i & 0xFF); }
  *data = src;
  return(rc);
}

/* Convert 16 bit little endian signed data to 8 bit unsigned data */
size_t sndcnv2byteLE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (UChar_Binary)(((SChar_Binary *)src)[1] ^ (SChar_Binary)0x80);
    src += 2;
  }
  *data = src;
  return(rc);
}

/* Convert 16 bit big endian signed data to 8 bit unsigned data */
size_t sndcnv2byteBE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (UChar_Binary)(((SChar_Binary *)src)[0] ^ (SChar_Binary)0x80);
    src += 2;
  }
  *data = src;
  return(rc);
}

/* Convert 16 bit little endian signed stereo data to 8 bit unsigned
   mono data */
size_t sndcnv2monobyteLE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 4;
  if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (UChar_Binary)(((int)((SChar_Binary *)src)[1] +
                              (int)((SChar_Binary *)src)[3]) / 2 ^ 0x80);
    src += 4;
  }
  *data = src;
  return(rc);
}

/* Convert 16 bit big endian signed stereo data to 8 bit unsigned
   mono data */
size_t sndcnv2monobyteBE(void **data,size_t *sz,void **outbuf)
{
  REGISTER UChar_Binary *src;
  REGISTER UChar_Binary *dest;
  int rc,count;

  count = *sz / 4;
  if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (UChar_Binary *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (UChar_Binary)(((int)((SChar_Binary *)src)[0] +
                              (int)((SChar_Binary *)src)[2]) / 2 ^ 0x80);
    src += 4;
  }
  *data = src;
  return(rc);
}

/* Look at the header of the sound file and try to determine the format;
   we can recognize files in VOC, WAVE, and, Sun/DEC-audio format--- everything
   else is assumed to be raw 8 bit unsigned data sampled at 8kHz */
fmtType analyze_format(UChar_Binary *format,int *fmt,int *speed,
                             int *tracks,
                             size_t (**parsesndfile)(void **,size_t *sz,
                                                     void **))
{
  /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
  if (!memcmp(format,"Creative Voice File\x1A\x1A\x00",22) &&
              (format[22]+256*format[23]) ==
      ((0x1233-format[24]-256*format[25])&0xFFFF)) { /* VOC */
    *fmt          = AFMT_U8;
    *speed        = 8000;
    *tracks       = 2;
    *parsesndfile = parsevoc;
    return(fmtVoc); }
  else if (!memcmp(format,"RIFF",4) &&
          !memcmp(format+8,"WAVEfmt ",8)) { /* WAVE */
    if (memcmp(format+20,"\001\000\001"/* PCM mono */,4) &&
       memcmp(format+20,"\001\000\002"/* PCM stereo */,4))
      return(fmtIllegal);
    *fmt          = (format[32]/(*tracks = format[22])) == 1 ?
                    AFMT_U8 : AFMT_S16_LE;
    /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
    *speed        = format[24]+256*(format[25]+256*
                                   (format[26]+256*format[27]));
    *parsesndfile = parsewave;
    return(fmtWave); }
  else if (!memcmp(format,".snd",4)) { /* Sun Audio (big endian) */
    if (format[7]+256*(format[6]+256*(format[5]+256*format[4])) < 24) {
      *fmt          = AFMT_MU_LAW;
      *speed        = 8000;
      *tracks       = 1;
      *parsesndfile = parsesundecaudio;
      return(fmtSunAudio); }
    if      (!memcmp(format+12,"\000\000\000\001",4)) *fmt = AFMT_MU_LAW;
    else if (!memcmp(format+12,"\000\000\000\002",4)) *fmt = AFMT_S8;
    else if (!memcmp(format+12,"\000\000\000\003",4)) *fmt = AFMT_S16_BE;
    else return(fmtIllegal);
    /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
    *speed        = format[19]+256*(format[18]+256*
                                   (format[17]+256*format[16]));
    *tracks       = format[23];
    *parsesndfile = parsesundecaudio;
    return(fmtSunAudio); }
  else if (!memcmp(format,".sd",4)) { /* DEC Audio (little endian) */
    if (format[4]+256*(format[5]+256*(format[6]+256*format[7])) < 24) {
      *fmt          = AFMT_MU_LAW;
      *speed        = 8000;
      *tracks       = 1;
      *parsesndfile = parsesundecaudio;
      return(fmtSunAudio); }
    if      (!memcmp(format+12,"\001\000\000",4)) *fmt = AFMT_MU_LAW;
    else if (!memcmp(format+12,"\002\000\000",4)) *fmt = AFMT_S8;
    else if (!memcmp(format+12,"\003\000\000",4)) *fmt = AFMT_S16_LE;
    else return(fmtIllegal);
    /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
    *speed        = format[16]+256*(format[17]+256*
                                   (format[18]+256*format[19]));
    *tracks       = format[20];
    *parsesndfile = parsesundecaudio;
    return(fmtSunAudio); }
  else {
    *fmt          = AFMT_U8;
    *speed        = 8000;
    *tracks       = 1;
    *parsesndfile = parseraw;
    return(fmtRaw); }
}