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