XEmacs 21.2.28 "Hermes".
[chise/xemacs-chise.git.1] / src / miscplay.c
1 /* miscplay.c - general routines related to playing sounds
2  **
3  ** Copyright (C) 1995,96 by Markus Gutschke (gutschk@math.uni-muenster.de)
4  ** This was sawed out from version 1.3 of linuxplay.c by
5  ** Robert Bihlmeyer <robbe@orcus.priv.at>.
6  **
7  ** Parts of this code were inspired by sunplay.c, which is copyright 1989 by
8  ** Jef Poskanzer and 1991,92 by Jamie Zawinski; c.f. sunplay.c for further
9  ** information.
10  **
11  ** Permission to use, copy, modify, and distribute this software and its
12  ** documentation for any purpose and without fee is hereby granted, provided
13  ** that the above copyright notice appear in all copies and that both that
14  ** copyright notice and this permission notice appear in supporting
15  ** documentation.  This software is provided "as is" without express or
16  ** implied warranty.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "miscplay.h"
24 #include "lisp.h"
25 #include "syssignal.h"
26 #include "sysfile.h"
27 #define warn(str)   message("audio: %s ",GETTEXT(str))
28
29 #include <stdlib.h>
30
31 #ifdef __GNUC__
32 #define UNUSED(x) ((void)(x))
33 #else
34 #define UNUSED(x)
35 #define __inline__
36 #endif
37
38 /* Maintain global variable for keeping parser state information; this struct
39    is set to zero before the first invocation of the parser. The use of a
40    global variable prevents multiple concurrent executions of this code, but
41    this does not happen anyways... */
42 enum wvState
43 { wvMain,
44   wvSubchunk,
45   wvOutOfBlock,
46   wvSkipChunk,
47   wvSoundChunk,
48   wvFatal,
49   wvFatalNotify
50 };
51
52 static union {
53   struct {
54     int           align;
55     enum wvState state;
56     size_t        left;
57     unsigned char leftover[HEADERSZ];
58     signed long   chunklength;
59   } wave;
60   struct {
61     int           align;
62     int           isdata;
63     int           skipping;
64     size_t        left;
65     unsigned char leftover[HEADERSZ];
66   } audio;
67 } parsestate;
68
69 /* Use a global buffer as scratch-pad for possible conversions of the
70    sampling format */
71 unsigned char miscplay_sndbuf[SNDBUFSZ];
72
73 /* Initialize global parser state information to zero */
74 void reset_parsestate()
75 {
76   memset(&parsestate,0,sizeof(parsestate));
77 }
78
79 /* Verify that we could fully parse the entire soundfile; this is needed
80    only for files in WAVE format */
81 int parse_wave_complete()
82 {
83   if (parsestate.wave.state != wvOutOfBlock &&
84       parsestate.wave.state != wvFatal) {
85     warn("Unexpected end of WAVE file");
86     return 0;
87   } else
88     return 1;
89 }
90
91 /* There is no special treatment required for parsing raw data files; we
92    assume that these files contain data in 8bit unsigned format that
93    has been sampled at 8kHz; there is no extra header */
94 static size_t parseraw(void **data,size_t *sz,void **outbuf)
95 {
96   int rc = *sz;
97
98   *outbuf = *data;
99   *sz = 0;
100   return(rc);
101 }
102
103 /* Currently we cannot cope with files in VOC format; if you really need
104    to play these files, they should be converted by using SOX */
105 static size_t parsevoc(void **data,size_t *sz,void **outbuf)
106 {
107   UNUSED(data);
108   UNUSED(sz);
109   UNUSED(outbuf);
110   return(0);
111 }
112
113 /* We need to perform some look-ahead in order to parse files in WAVE format;
114    this might require re-partioning of the data segments if headers cross the
115    boundaries between two read operations. This is done in a two-step way:
116    first we request a certain amount of bytes... */
117 static __inline__ int waverequire(void **data,size_t *sz,size_t rq)
118 {
119   int rc = 1;
120
121   if (rq > HEADERSZ) {
122     warn("Header size exceeded while parsing WAVE file");
123     parsestate.wave.state = wvFatal;
124     *sz = 0;
125     return(0); }
126   if ((rq -= parsestate.wave.left) <= 0)
127     return(rc);
128   if (rq > *sz) {rq = *sz; rc = 0;}
129   memcpy(parsestate.wave.leftover+parsestate.wave.left,
130         *data,rq);
131   parsestate.wave.left      += rq;
132   (*(unsigned char **)data) += rq;
133   *sz                       -= rq;
134   return(rc);
135 }
136
137 /* ...and next we remove this many bytes from the buffer */
138 static __inline__ void waveremove(size_t rq)
139 {
140   if (parsestate.wave.left <= rq)
141     parsestate.wave.left = 0;
142   else {
143     parsestate.wave.left -= rq;
144     memmove(parsestate.wave.leftover,
145            parsestate.wave.leftover+rq,
146            parsestate.wave.left); }
147   return;
148 }
149
150 /* Sound files in WAVE format can contain an arbitrary amount of tagged
151    chunks; this requires quite some effort for parsing the data */
152 static size_t parsewave(void **data,size_t *sz,void **outbuf)
153 {
154   for (;;)
155     switch (parsestate.wave.state) {
156     case wvMain:
157       if (!waverequire(data,sz,20))
158        return(0);
159       /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
160       parsestate.wave.chunklength = parsestate.wave.leftover[16] +
161        256*(parsestate.wave.leftover[17] +
162             256*(parsestate.wave.leftover[18] +
163                  256*parsestate.wave.leftover[19]));
164       waveremove(20);
165       parsestate.wave.state = wvSubchunk;
166       break;
167     case wvSubchunk:
168       if (!waverequire(data,sz,parsestate.wave.chunklength))
169        return(0);
170       parsestate.wave.align = parsestate.wave.chunklength < 14 ? 1
171        : parsestate.wave.leftover[12];
172       if (parsestate.wave.align != 1 &&
173          parsestate.wave.align != 2 &&
174          parsestate.wave.align != 4) {
175        warn("Illegal datawidth detected while parsing WAVE file");
176        parsestate.wave.state = wvFatal; }
177       else
178        parsestate.wave.state = wvOutOfBlock;
179       waveremove(parsestate.wave.chunklength);
180       break;
181     case wvOutOfBlock:
182       if (!waverequire(data,sz,8))
183        return(0);
184       /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
185       parsestate.wave.chunklength = parsestate.wave.leftover[4] +
186        256*(parsestate.wave.leftover[5] +
187             256*(parsestate.wave.leftover[6] +
188                  256*(parsestate.wave.leftover[7] & 0x7F)));
189       if (memcmp(parsestate.wave.leftover,"data",4))
190        parsestate.wave.state = wvSkipChunk;
191       else
192        parsestate.wave.state = wvSoundChunk;
193       waveremove(8);
194       break;
195     case wvSkipChunk:
196       if (parsestate.wave.chunklength > 0 && *sz > 0 &&
197          (signed long)*sz < (signed long)parsestate.wave.chunklength) {
198        parsestate.wave.chunklength -= *sz;
199        *sz = 0; }
200       else {
201        if (parsestate.wave.chunklength > 0 && *sz > 0) {
202          *sz -= parsestate.wave.chunklength;
203          (*(unsigned char **)data) += parsestate.wave.chunklength; }
204        parsestate.wave.state = wvOutOfBlock; }
205       break;
206     case wvSoundChunk: {
207       size_t count,rq;
208       if (parsestate.wave.left) { /* handle leftover bytes from last
209                                     alignment operation */
210        count = parsestate.wave.left;
211        rq    = HEADERSZ-count;
212        if (rq > (size_t) parsestate.wave.chunklength)
213          rq = parsestate.wave.chunklength;
214        if (!waverequire(data,sz,rq)) {
215          parsestate.wave.chunklength -= parsestate.wave.left - count;
216          return(0); }
217        parsestate.wave.chunklength -= rq;
218        *outbuf                      = parsestate.wave.leftover;
219        parsestate.wave.left         = 0;
220        return(rq); }
221       if (*sz >= (size_t) parsestate.wave.chunklength) {
222        count  = parsestate.wave.chunklength;
223        rq     = 0; }
224       else {
225        count  = *sz;
226        count -= rq = count % parsestate.wave.align; }
227       *outbuf                   = *data;
228       (*(unsigned char **)data) += count;
229       *sz                       -= count;
230       if ((parsestate.wave.chunklength -= count) < parsestate.wave.align) {
231        parsestate.wave.state = wvOutOfBlock;
232        /* Some broken software (e.g. SOX) attaches junk to the end of a sound
233           chunk; so, let's ignore this... */
234        if (parsestate.wave.chunklength)
235          parsestate.wave.state = wvSkipChunk; }
236       else if (rq)
237        /* align data length to a multiple of datasize; keep additional data
238           in "leftover" buffer --- this is necessary to ensure proper
239           functioning of the sndcnv... routines */
240        waverequire(data,sz,rq);
241       return(count); }
242     case wvFatalNotify:
243       warn("Irrecoverable error while parsing WAVE file");
244       parsestate.wave.state = wvFatal;
245       break;
246     case wvFatal:
247     default:
248       *sz = 0;
249       return(0); }
250 }
251
252 /* Strip the header from files in Sun/DEC audio format; this requires some
253    extra processing as the header can be an arbitrary size and it might
254    result in alignment errors for subsequent conversions --- thus we do
255    some buffering, where needed */
256 static size_t parsesundecaudio(void **data,size_t *sz,void **outbuf)
257 {
258   /* There is data left over from the last invocation of this function; join
259      it with the new data and return a sound chunk that is as big as a
260      single entry */
261   if (parsestate.audio.left) {
262     if (parsestate.audio.left + *sz > (size_t) parsestate.audio.align) {
263       int  count;
264       memmove(parsestate.audio.leftover + parsestate.audio.left,
265              *data,
266              count = parsestate.audio.align - parsestate.audio.left);
267       *outbuf = parsestate.audio.leftover;
268       *sz    -= count;
269       *data   = (*(char **)data) + count;
270       parsestate.audio.left = 0;
271       return(parsestate.audio.align); }
272     else {
273       /* We need even more data in order to get one complete single entry! */
274       memmove(parsestate.audio.leftover + parsestate.audio.left,
275              *data,
276              *sz);
277       *data = (*(char **)data) + *sz;
278       parsestate.audio.left += *sz;
279       *sz   = 0;
280       return(0); } }
281
282   /* This is the main sound chunk, strip of any extra data that does not fit
283      the alignment requirements and move these bytes into the leftover buffer*/
284   if (parsestate.audio.isdata) {
285     int rc = *sz;
286     *outbuf = *data;
287     if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) {
288       memmove(parsestate.audio.leftover,
289              (char *)*outbuf + rc - parsestate.audio.left,
290              parsestate.audio.left);
291       rc -= parsestate.audio.left; }
292     *sz = 0;
293     return(rc); }
294
295   /* This is the first invocation of this function; we need to parse the
296      header information and determine how many bytes we need to skip until
297      the start of the sound chunk */
298   if (!parsestate.audio.skipping) {
299     unsigned char *header = (unsigned char *) *data;
300     if (*sz < 8) {
301       warn("Irrecoverable error while parsing Sun/DEC audio file");
302       return(0); }
303     /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
304     if (header[3]) { /* Sun audio (big endian) */
305       parsestate.audio.align = ((header[15] > 2)+1)*header[23];
306       parsestate.audio.skipping = header[7]+256*(header[6]+256*
307                                                 (header[5]+256*header[4])); }
308     else { /* DEC audio (little endian) */
309       parsestate.audio.align = ((header[12] > 2)+1)*header[20];
310       parsestate.audio.skipping = header[4]+256*(header[5]+256*
311                                                 (header[6]+256*header[7])); }}
312
313   /* We are skipping extra data that has been attached to header; most usually
314      this will be just a comment, such as the original filename and/or the
315      creation date. Make sure that we do not return less than one single sound
316      sample entry to the caller; if this happens, rather decide to move those
317      few bytes into the leftover buffer and deal with it later */
318   if (*sz >= (size_t) parsestate.audio.skipping) {
319     /* Skip just the header information and return the sound chunk */
320     int rc = *sz - parsestate.audio.skipping;
321     *outbuf = (char *)*data + parsestate.audio.skipping;
322     if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) {
323       memmove(parsestate.audio.leftover,
324              (char *)*outbuf + rc - parsestate.audio.left,
325              parsestate.audio.left);
326       rc -= parsestate.audio.left; }
327     *sz = 0;
328     parsestate.audio.skipping = 0;
329     parsestate.audio.isdata++;
330     return(rc); }
331   else {
332     /* Skip everything */
333     parsestate.audio.skipping -= *sz;
334     return(0); }
335 }
336
337 /* If the soundcard could not be set to natively support the data format, we
338    try to do some limited on-the-fly conversion to a different format; if
339    no conversion is needed, though, we can output directly */
340 size_t sndcnvnop(void **data,size_t *sz,void **outbuf)
341 {
342   int rc = *sz;
343
344   *outbuf = *data;
345   *sz = 0;
346   return(rc);
347 }
348
349 /* Convert 8 bit unsigned stereo data to 8 bit unsigned mono data */
350 size_t sndcnv8U_2mono(void **data,size_t *sz,void **outbuf)
351 {
352   REGISTER unsigned char *src;
353   REGISTER unsigned char *dest;
354   int rc,count;
355
356   count = *sz / 2;
357   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
358   else                    *sz   = 0;
359   rc      = count;
360   src     = (unsigned char *) *data;
361   *outbuf =
362   dest    = miscplay_sndbuf;
363   while (count--)
364     *dest++ = (unsigned char)(((int)*(src)++ +
365                               (int)*(src)++) / 2);
366   *data   = src;
367   return(rc);
368 }
369
370 /* Convert 8 bit signed stereo data to 8 bit signed mono data */
371 size_t sndcnv8S_2mono(void **data,size_t *sz,void **outbuf)
372 {
373   REGISTER unsigned char *src;
374   REGISTER unsigned char *dest;
375   int rc, count;
376
377   count = *sz / 2;
378   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
379   else                    *sz   = 0;
380   rc      = count;
381   src     = (unsigned char *) *data;
382   *outbuf =
383   dest    = miscplay_sndbuf;
384   while (count--)
385     *dest++ = (unsigned char)(((int)*((signed char *)(src++)) +
386                               (int)*((signed char *)(src++))) / 2);
387   *data   = src;
388   return(rc);
389 }
390
391 /* Convert 8 bit signed stereo data to 8 bit unsigned mono data */
392 size_t sndcnv2monounsigned(void **data,size_t *sz,void **outbuf)
393 {
394   REGISTER unsigned char *src;
395   REGISTER unsigned char *dest;
396   int rc,count;
397
398   count = *sz / 2;
399   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
400   else                    *sz   = 0;
401   rc      = count;
402   src     = (unsigned char *) *data;
403   *outbuf =
404   dest    = miscplay_sndbuf;
405   while (count--)
406     *dest++ = (unsigned char)(((int)*((signed char *)(src++)) +
407                               (int)*((signed char *)(src++))) / 2) ^ 0x80;
408   *data   = src;
409   return(rc);
410 }
411
412 /* Convert 8 bit signed mono data to 8 bit unsigned mono data */
413 size_t sndcnv2unsigned(void **data,size_t *sz,void **outbuf)
414 {
415   REGISTER unsigned char *src;
416   REGISTER unsigned char *dest;
417   int rc,count;
418
419   count = *sz;
420   if (count > SNDBUFSZ) { *sz  -= SNDBUFSZ; count = SNDBUFSZ; }
421   else                    *sz   = 0;
422   rc      = count;
423   src     = (unsigned char *) *data;
424   *outbuf =
425   dest    = miscplay_sndbuf;
426   while (count--)
427     *dest++ = *(src)++ ^ 0x80;
428   *data   = src;
429   return(rc);
430 }
431
432 /* Convert a number in the range -32768..32767 to an 8 bit ulaw encoded
433    number --- I hope, I got this conversion right :-) */
434 static __inline__ signed char int2ulaw(int i)
435 {
436     /* Lookup table for fast calculation of number of bits that need shifting*/
437     static short int t_bits[128] = {
438       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,
439       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,
440       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,
441       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};
442     REGISTER int bits,logi;
443
444     /* unrolling this condition (hopefully) improves execution speed */
445     if (i < 0) {
446       if ((i = (132-i)) > 0x7FFF) i = 0x7FFF;
447       logi = (i >> ((bits = t_bits[i/256])+4));
448       return((bits << 4 | logi) ^ 0x7F); }
449     else {
450       if ((i = 132+i) > 0x7FFF) i = 0x7FFF;
451       logi = (i >> ((bits = t_bits[i/256])+4));
452       return(~(bits << 4 | logi)); }
453 }
454
455 /* Convert from 8 bit ulaw mono to 8 bit linear mono */
456 size_t sndcnvULaw_2linear(void **data,size_t *sz,void **outbuf)
457 {
458   /* conversion table stolen from Linux's ulaw.h */
459   static unsigned char ulaw_dsp[] = {
460      3,    7,   11,   15,   19,   23,   27,   31,
461     35,   39,   43,   47,   51,   55,   59,   63,
462     66,   68,   70,   72,   74,   76,   78,   80,
463     82,   84,   86,   88,   90,   92,   94,   96,
464     98,   99,  100,  101,  102,  103,  104,  105,
465    106,  107,  108,  109,  110,  111,  112,  113,
466    113,  114,  114,  115,  115,  116,  116,  117,
467    117,  118,  118,  119,  119,  120,  120,  121,
468    121,  121,  122,  122,  122,  122,  123,  123,
469    123,  123,  124,  124,  124,  124,  125,  125,
470    125,  125,  125,  125,  126,  126,  126,  126,
471    126,  126,  126,  126,  127,  127,  127,  127,
472    127,  127,  127,  127,  127,  127,  127,  127,
473    128,  128,  128,  128,  128,  128,  128,  128,
474    128,  128,  128,  128,  128,  128,  128,  128,
475    128,  128,  128,  128,  128,  128,  128,  128,
476    253,  249,  245,  241,  237,  233,  229,  225,
477    221,  217,  213,  209,  205,  201,  197,  193,
478    190,  188,  186,  184,  182,  180,  178,  176,
479    174,  172,  170,  168,  166,  164,  162,  160,
480    158,  157,  156,  155,  154,  153,  152,  151,
481    150,  149,  148,  147,  146,  145,  144,  143,
482    143,  142,  142,  141,  141,  140,  140,  139,
483    139,  138,  138,  137,  137,  136,  136,  135,
484    135,  135,  134,  134,  134,  134,  133,  133,
485    133,  133,  132,  132,  132,  132,  131,  131,
486    131,  131,  131,  131,  130,  130,  130,  130,
487    130,  130,  130,  130,  129,  129,  129,  129,
488    129,  129,  129,  129,  129,  129,  129,  129,
489    128,  128,  128,  128,  128,  128,  128,  128,
490    128,  128,  128,  128,  128,  128,  128,  128,
491    128,  128,  128,  128,  128,  128,  128,  128,
492   };
493   unsigned char *p=(unsigned char *)*data;
494
495   *outbuf = *data;
496   while ((*sz)--)
497     *p++ = ulaw_dsp[*p];
498   *sz = 0;
499   *data = p;
500   return p - (unsigned char *)*outbuf;
501 }
502
503 /* Convert 8 bit ulaw stereo data to 8 bit ulaw mono data */
504 size_t sndcnvULaw_2mono(void **data,size_t *sz,void **outbuf)
505 {
506
507   static short int ulaw2int[256] = {
508     /* Precomputed lookup table for conversion from ulaw to 15 bit signed */
509     -16062,-15550,-15038,-14526,-14014,-13502,-12990,-12478,
510     -11966,-11454,-10942,-10430, -9918, -9406, -8894, -8382,
511      -7998, -7742, -7486, -7230, -6974, -6718, -6462, -6206,
512      -5950, -5694, -5438, -5182, -4926, -4670, -4414, -4158,
513      -3966, -3838, -3710, -3582, -3454, -3326, -3198, -3070,
514      -2942, -2814, -2686, -2558, -2430, -2302, -2174, -2046,
515      -1950, -1886, -1822, -1758, -1694, -1630, -1566, -1502,
516      -1438, -1374, -1310, -1246, -1182, -1118, -1054,  -990,
517       -942,  -910,  -878,  -846,  -814,  -782,  -750,  -718,
518       -686,  -654,  -622,  -590,  -558,  -526,  -494,  -462,
519       -438,  -422,  -406,  -390,  -374,  -358,  -342,  -326,
520       -310,  -294,  -278,  -262,  -246,  -230,  -214,  -198,
521       -186,  -178,  -170,  -162,  -154,  -146,  -138,  -130,
522       -122,  -114,  -106,   -98,   -90,   -82,   -74,   -66,
523        -60,   -56,   -52,   -48,   -44,   -40,   -36,   -32,
524        -28,   -24,   -20,   -16,   -12,    -8,    -4,    +0,
525     +16062,+15550,+15038,+14526,+14014,+13502,+12990,+12478,
526     +11966,+11454,+10942,+10430, +9918, +9406, +8894, +8382,
527      +7998, +7742, +7486, +7230, +6974, +6718, +6462, +6206,
528      +5950, +5694, +5438, +5182, +4926, +4670, +4414, +4158,
529      +3966, +3838, +3710, +3582, +3454, +3326, +3198, +3070,
530      +2942, +2814, +2686, +2558, +2430, +2302, +2174, +2046,
531      +1950, +1886, +1822, +1758, +1694, +1630, +1566, +1502,
532      +1438, +1374, +1310, +1246, +1182, +1118, +1054,  +990,
533       +942,  +910,  +878,  +846,  +814,  +782,  +750,  +718,
534       +686,  +654,  +622,  +590,  +558,  +526,  +494,  +462,
535       +438,  +422,  +406,  +390,  +374,  +358,  +342,  +326,
536       +310,  +294,  +278,  +262,  +246,  +230,  +214,  +198,
537       +186,  +178,  +170,  +162,  +154,  +146,  +138,  +130,
538       +122,  +114,  +106,   +98,   +90,   +82,   +74,   +66,
539        +60,   +56,   +52,   +48,   +44,   +40,   +36,   +32,
540        +28,   +24,   +20,   +16,   +12,    +8,    +4,    +0};
541
542   REGISTER unsigned char *src;
543   REGISTER unsigned char *dest;
544   int rc,count;
545
546   count = *sz / 2;
547   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
548   else                    *sz   = 0;
549   rc      = count;
550   src     = (unsigned char *) *data;
551   *outbuf =
552   dest    = miscplay_sndbuf;
553   while (count--)
554     /* it is not possible to directly interpolate between two ulaw encoded
555        data bytes, thus we need to convert to linear format first and later
556        we convert back to ulaw format */
557     *dest++ = int2ulaw(ulaw2int[*(src)++] +
558                       ulaw2int[*(src)++]);
559   *data = src;
560   return(rc);
561 }
562
563 size_t sndcnv16swap(void **data,size_t *sz,void **outbuf)
564 {
565   size_t cnt = *sz / 2;
566   unsigned short *p;
567
568   *outbuf = *data;
569   p = (unsigned short *) *outbuf;
570   while (cnt--) {
571     *p++ = ((*p & 0x00ff) << 8) | (*p >> 8);
572   }
573   *data = p;
574   cnt = *sz;
575   *sz = 0;
576   return cnt;
577 }
578
579 /* Convert 16 bit little endian signed stereo data to 16 bit little endian
580    signed mono data */
581 size_t sndcnv16_2monoLE(void **data,size_t *sz,void **outbuf)
582 {
583   REGISTER unsigned char *src;
584   REGISTER unsigned char *dest;
585   int rc,count;
586   signed short i;
587
588   count = *sz / 2;
589   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
590   else                    *sz   = 0;
591   rc      = count;
592   src     = (unsigned char *) *data;
593   *outbuf =
594   dest    = miscplay_sndbuf;
595   for (count /= 2; count--; ) {
596     i = ((int)(src[0]) +
597         256*(int)(src[1]) +
598        (int)(src[2]) +
599        256*(int)(src[3])) / 2;
600     src += 4;
601     *dest++ = (unsigned char)(i & 0xFF);
602     *dest++ = (unsigned char)((i / 256) & 0xFF); }
603   *data = src;
604   return(rc);
605 }
606
607 /* Convert 16 bit big endian signed stereo data to 16 bit big endian
608    signed mono data */
609 size_t sndcnv16_2monoBE(void **data,size_t *sz,void **outbuf)
610 {
611   REGISTER unsigned char *src;
612   REGISTER unsigned char *dest;
613   int rc,count;
614   signed short i;
615
616   count = *sz / 2;
617   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
618   else                    *sz   = 0;
619   rc      = count;
620   src     = (unsigned char *) *data;
621   *outbuf =
622   dest    = miscplay_sndbuf;
623   for (count /= 2; count--; ) {
624     i = ((int)(src[1]) +
625         256*(int)(src[0]) +
626        (int)(src[3]) +
627        256*(int)(src[2])) / 2;
628     src += 4;
629     *dest++ = (unsigned char)((i / 256) & 0xFF);
630     *dest++ = (unsigned char)(i & 0xFF); }
631   *data = src;
632   return(rc);
633 }
634
635 /* Convert 16 bit little endian signed data to 8 bit unsigned data */
636 size_t sndcnv2byteLE(void **data,size_t *sz,void **outbuf)
637 {
638   REGISTER unsigned char *src;
639   REGISTER unsigned char *dest;
640   int rc,count;
641
642   count = *sz / 2;
643   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
644   else                    *sz   = 0;
645   rc      = count;
646   src     = (unsigned char *) *data;
647   *outbuf =
648   dest    = miscplay_sndbuf;
649   while (count--) {
650     *dest++ = (unsigned char)(((signed char *)src)[1] ^ (signed char)0x80);
651     src += 2;
652   }
653   *data = src;
654   return(rc);
655 }
656
657 /* Convert 16 bit big endian signed data to 8 bit unsigned data */
658 size_t sndcnv2byteBE(void **data,size_t *sz,void **outbuf)
659 {
660   REGISTER unsigned char *src;
661   REGISTER unsigned char *dest;
662   int rc,count;
663
664   count = *sz / 2;
665   if (count > SNDBUFSZ) { *sz  -= 2*SNDBUFSZ; count = SNDBUFSZ; }
666   else                    *sz   = 0;
667   rc      = count;
668   src     = (unsigned char *) *data;
669   *outbuf =
670   dest    = miscplay_sndbuf;
671   while (count--) {
672     *dest++ = (unsigned char)(((signed char *)src)[0] ^ (signed char)0x80);
673     src += 2;
674   }
675   *data = src;
676   return(rc);
677 }
678
679 /* Convert 16 bit little endian signed stereo data to 8 bit unsigned
680    mono data */
681 size_t sndcnv2monobyteLE(void **data,size_t *sz,void **outbuf)
682 {
683   REGISTER unsigned char *src;
684   REGISTER unsigned char *dest;
685   int rc,count;
686
687   count = *sz / 4;
688   if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
689   else                    *sz   = 0;
690   rc      = count;
691   src     = (unsigned char *) *data;
692   *outbuf =
693   dest    = miscplay_sndbuf;
694   while (count--) {
695     *dest++ = (unsigned char)(((int)((signed char *)src)[1] +
696                               (int)((signed char *)src)[3]) / 2 ^ 0x80);
697     src += 4;
698   }
699   *data = src;
700   return(rc);
701 }
702
703 /* Convert 16 bit big endian signed stereo data to 8 bit unsigned
704    mono data */
705 size_t sndcnv2monobyteBE(void **data,size_t *sz,void **outbuf)
706 {
707   REGISTER unsigned char *src;
708   REGISTER unsigned char *dest;
709   int rc,count;
710
711   count = *sz / 4;
712   if (count > SNDBUFSZ) { *sz  -= 4*SNDBUFSZ; count = SNDBUFSZ; }
713   else                    *sz   = 0;
714   rc      = count;
715   src     = (unsigned char *) *data;
716   *outbuf =
717   dest    = miscplay_sndbuf;
718   while (count--) {
719     *dest++ = (unsigned char)(((int)((signed char *)src)[0] +
720                               (int)((signed char *)src)[2]) / 2 ^ 0x80);
721     src += 4;
722   }
723   *data = src;
724   return(rc);
725 }
726
727 /* Look at the header of the sound file and try to determine the format;
728    we can recognize files in VOC, WAVE, and, Sun/DEC-audio format--- everything
729    else is assumed to be raw 8 bit unsigned data sampled at 8kHz */
730 fmtType analyze_format(unsigned char *format,int *fmt,int *speed,
731                              int *tracks,
732                              size_t (**parsesndfile)(void **,size_t *sz,
733                                                      void **))
734 {
735   /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
736   if (!memcmp(format,"Creative Voice File\x1A\x1A\x00",22) &&
737               (format[22]+256*format[23]) ==
738       ((0x1233-format[24]-256*format[25])&0xFFFF)) { /* VOC */
739     *fmt          = AFMT_U8;
740     *speed        = 8000;
741     *tracks       = 2;
742     *parsesndfile = parsevoc;
743     return(fmtVoc); }
744   else if (!memcmp(format,"RIFF",4) &&
745           !memcmp(format+8,"WAVEfmt ",8)) { /* WAVE */
746     if (memcmp(format+20,"\001\000\001"/* PCM mono */,4) &&
747        memcmp(format+20,"\001\000\002"/* PCM stereo */,4))
748       return(fmtIllegal);
749     *fmt          = (format[32]/(*tracks = format[22])) == 1 ?
750                     AFMT_U8 : AFMT_S16_LE;
751     /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
752     *speed        = format[24]+256*(format[25]+256*
753                                    (format[26]+256*format[27]));
754     *parsesndfile = parsewave;
755     return(fmtWave); }
756   else if (!memcmp(format,".snd",4)) { /* Sun Audio (big endian) */
757     if (format[7]+256*(format[6]+256*(format[5]+256*format[4])) < 24) {
758       *fmt          = AFMT_MU_LAW;
759       *speed        = 8000;
760       *tracks       = 1;
761       *parsesndfile = parsesundecaudio;
762       return(fmtSunAudio); }
763     if      (!memcmp(format+12,"\000\000\000\001",4)) *fmt = AFMT_MU_LAW;
764     else if (!memcmp(format+12,"\000\000\000\002",4)) *fmt = AFMT_S8;
765     else if (!memcmp(format+12,"\000\000\000\003",4)) *fmt = AFMT_S16_BE;
766     else return(fmtIllegal);
767     /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
768     *speed        = format[19]+256*(format[18]+256*
769                                    (format[17]+256*format[16]));
770     *tracks       = format[23];
771     *parsesndfile = parsesundecaudio;
772     return(fmtSunAudio); }
773   else if (!memcmp(format,".sd",4)) { /* DEC Audio (little endian) */
774     if (format[4]+256*(format[5]+256*(format[6]+256*format[7])) < 24) {
775       *fmt          = AFMT_MU_LAW;
776       *speed        = 8000;
777       *tracks       = 1;
778       *parsesndfile = parsesundecaudio;
779       return(fmtSunAudio); }
780     if      (!memcmp(format+12,"\001\000\000",4)) *fmt = AFMT_MU_LAW;
781     else if (!memcmp(format+12,"\002\000\000",4)) *fmt = AFMT_S8;
782     else if (!memcmp(format+12,"\003\000\000",4)) *fmt = AFMT_S16_LE;
783     else return(fmtIllegal);
784     /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex  */
785     *speed        = format[16]+256*(format[17]+256*
786                                    (format[18]+256*format[19]));
787     *tracks       = format[20];
788     *parsesndfile = parsesundecaudio;
789     return(fmtSunAudio); }
790   else {
791     *fmt          = AFMT_U8;
792     *speed        = 8000;
793     *tracks       = 1;
794     *parsesndfile = parseraw;
795     return(fmtRaw); }
796 }