view src/miscplay.c @ 5146:88bd4f3ef8e4

make lrecord UID's have a separate UID space for each object, resurrect debug SOE code in extents.c -------------------- ChangeLog entries follow: -------------------- src/ChangeLog addition: 2010-03-15 Ben Wing <ben@xemacs.org> * alloc.c: * alloc.c (c_readonly): * alloc.c (deadbeef_memory): * alloc.c (make_compiled_function): * alloc.c (make_button_data): * alloc.c (make_motion_data): * alloc.c (make_process_data): * alloc.c (make_timeout_data): * alloc.c (make_magic_data): * alloc.c (make_magic_eval_data): * alloc.c (make_eval_data): * alloc.c (make_misc_user_data): * alloc.c (noseeum_make_marker): * alloc.c (ADDITIONAL_FREE_string): * alloc.c (common_init_alloc_early): * alloc.c (init_alloc_once_early): * bytecode.c (print_compiled_function): * bytecode.c (mark_compiled_function): * casetab.c: * casetab.c (print_case_table): * console.c: * console.c (print_console): * database.c (print_database): * database.c (finalize_database): * device-msw.c (sync_printer_with_devmode): * device-msw.c (print_devmode): * device-msw.c (finalize_devmode): * device.c: * device.c (print_device): * elhash.c: * elhash.c (print_hash_table): * eval.c (print_multiple_value): * eval.c (mark_multiple_value): * events.c (deinitialize_event): * events.c (print_event): * events.c (event_equal): * extents.c: * extents.c (soe_dump): * extents.c (soe_insert): * extents.c (soe_delete): * extents.c (soe_move): * extents.c (extent_fragment_update): * extents.c (print_extent_1): * extents.c (print_extent): * extents.c (vars_of_extents): * frame.c: * frame.c (print_frame): * free-hook.c: * free-hook.c (check_free): * glyphs.c: * glyphs.c (print_image_instance): * glyphs.c (print_glyph): * gui.c: * gui.c (copy_gui_item): * hash.c: * hash.c (NULL_ENTRY): * hash.c (KEYS_DIFFER_P): * keymap.c (print_keymap): * keymap.c (MARKED_SLOT): * lisp.h: * lrecord.h: * lrecord.h (LISP_OBJECT_UID): * lrecord.h (set_lheader_implementation): * lrecord.h (struct old_lcrecord_header): * lstream.c (print_lstream): * lstream.c (finalize_lstream): * marker.c (print_marker): * marker.c (marker_equal): * mc-alloc.c (visit_all_used_page_headers): * mule-charset.c: * mule-charset.c (print_charset): * objects.c (print_color_instance): * objects.c (print_font_instance): * objects.c (finalize_font_instance): * opaque.c (print_opaque): * opaque.c (print_opaque_ptr): * opaque.c (equal_opaque_ptr): * print.c (internal_object_printer): * print.c (enum printing_badness): * rangetab.c (print_range_table): * rangetab.c (range_table_equal): * specifier.c (print_specifier): * specifier.c (finalize_specifier): * symbols.c: * symbols.c (print_symbol_value_magic): * tooltalk.c: * tooltalk.c (print_tooltalk_message): * tooltalk.c (print_tooltalk_pattern): * window.c (print_window): * window.c (debug_print_window): (1) Make lrecord UID's have a separate UID space for each object. Otherwise, with 20-bit UID's, we rapidly wrap around, especially when common objects like conses and strings increment the UID value for every object created. (Originally I tried making two UID spaces, one for objects that always print readably and hence don't display the UID, and one for other objects. But certain objects like markers for which a UID is displayed are still generated rapidly enough that UID overflow is a serious issue.) This also has the advantage of making UID values smaller, hence easier to remember -- their main purpose is to make it easier to keep track of different objects of the same type when debugging code. Make sure we dump lrecord UID's so that we don't have problems with pdumped and non-dumped objects having the same UID. (2) Display UID's consistently whenever an object (a) doesn't consistently print readably (objects like cons and string, which always print readably, can't display a UID), and (b) doesn't otherwise have a unique property that makes objects of a particular type distinguishable. (E.g. buffers didn't and still don't print an ID, but the buffer name uniquely identifies the buffer.) Some types, such as event, extent, compiled-function, didn't always (or didn't ever) display an ID; others (such as marker, extent, lstream, opaque, opaque-ptr, any object using internal_object_printer()) used to display the actual machine pointer instead. (3) Rename NORMAL_LISP_OBJECT_UID to LISP_OBJECT_UID; make it work over all Lisp objects and take a Lisp object, not a struct pointer. (4) Some misc cleanups in alloc.c, elhash.c. (5) Change code in events.c that "deinitializes" an event so that it doesn't increment the event UID counter in the process. Also use deadbeef_memory() to overwrite memory instead of doing the same with custom code. In the process, make deadbeef_memory() in alloc.c always available, and delete extraneous copy in mc-alloc.c. Also capitalize all uses of 0xDEADBEEF. Similarly in elhash.c call deadbeef_memory(). (6) Resurrect "debug SOE" code in extents.c. Make it conditional on DEBUG_XEMACS and on a `debug-soe' variable, rather than on SOE_DEBUG. Make it output to stderr, not stdout. (7) Delete some custom print methods that were identical to external_object_printer().
author Ben Wing <ben@xemacs.org>
date Mon, 15 Mar 2010 16:35:38 -0500
parents ecf1ebac70d8
children
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"

/* 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;
    Binbyte leftover[HEADERSZ];
    signed long   chunklength;
  } wave;
  struct {
    int           align;
    int           isdata;
    int           skipping;
    size_t        left;
    Binbyte leftover[HEADERSZ];
  } audio;
} parsestate;

/* Use a global buffer as scratch-pad for possible conversions of the
   sampling format */
Binbyte 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 **UNUSED (data), size_t *UNUSED (sz),
		       void **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;
  (*(Binbyte **)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;
         (*(Binbyte **)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;
      (*(Binbyte **)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   = (*(CBinbyte **)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 = (*(CBinbyte **)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,
             (CBinbyte *)*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) {
    Binbyte *header = (Binbyte *) *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 = (CBinbyte *)*data + parsestate.audio.skipping;
    if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) {
      memmove(parsestate.audio.leftover,
             (CBinbyte *)*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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (Binbyte)(((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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc, count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (Binbyte)(((int)*((SBinbyte *)(src)) +
				 (int)*((SBinbyte *)(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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--)
    {
      *dest++ = (Binbyte)(((int)*((SBinbyte *)(src)) +
				 (int)*((SBinbyte *)(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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz;
  if (count > SNDBUFSZ) { *sz  -= SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *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 SBinbyte 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 Binbyte 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,
  };
  Binbyte *p=(Binbyte *)*data;

  *outbuf = *data;
  while ((*sz)--)
    {
      *p = ulaw_dsp[*p];
      p++;
    }
  *sz = 0;
  *data = p;
  return p - (Binbyte *)*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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;
  signed short i;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *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++ = (Binbyte)(i & 0xFF);
    *dest++ = (Binbyte)((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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;
  signed short i;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *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++ = (Binbyte)((i / 256) & 0xFF);
    *dest++ = (Binbyte)(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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (Binbyte)(((SBinbyte *)src)[1] ^ (SBinbyte)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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 2;
  if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (Binbyte)(((SBinbyte *)src)[0] ^ (SBinbyte)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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 4;
  if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (Binbyte)(((int)((SBinbyte *)src)[1] +
                              (int)((SBinbyte *)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 Binbyte *src;
  REGISTER Binbyte *dest;
  int rc,count;

  count = *sz / 4;
  if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
  else                    *sz   = 0;
  rc      = count;
  src     = (Binbyte *) *data;
  *outbuf =
  dest    = miscplay_sndbuf;
  while (count--) {
    *dest++ = (Binbyte)(((int)((SBinbyte *)src)[0] +
                              (int)((SBinbyte *)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(Binbyte *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); }
}