Mercurial > hg > xemacs-beta
comparison src/linuxplay.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 8fc7fe29b841 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 /* linuxplay.c - play a sound file on the speaker | |
2 ** | |
3 ** Copyright (C) 1995,96 by Markus Gutschke (gutschk@math.uni-muenster.de) | |
4 ** This is version 1.3 of linuxplay.c | |
5 ** | |
6 ** Parts of this code were inspired by sunplay.c, which is copyright 1989 by | |
7 ** Jef Poskanzer and 1991,92 by Jamie Zawinski; c.f. sunplay.c for further | |
8 ** information. | |
9 ** | |
10 ** Permission to use, copy, modify, and distribute this software and its | |
11 ** documentation for any purpose and without fee is hereby granted, provided | |
12 ** that the above copyright notice appear in all copies and that both that | |
13 ** copyright notice and this permission notice appear in supporting | |
14 ** documentation. This software is provided "as is" without express or | |
15 ** implied warranty. | |
16 ** | |
17 ** Changelog: | |
18 ** 1.0 -- first release; supports SunAudio, Wave and RAW file formats | |
19 ** detects (and rejects) VOC file format | |
20 ** tested with PC-Speaker driver only | |
21 ** 1.1 -- fixed bug with playback of stereo Wave files | |
22 ** fixed VOC file detection | |
23 ** fixed mono/8bit conversion | |
24 ** cleaned up mixer programming (c.f. VoxWare-SDK) | |
25 ** tested with PC-Speaker driver and with PAS16 soundcard | |
26 ** 1.2 -- first (incompatible) attempt at fixing reliable signal handling | |
27 ** 1.3 -- changed signal handling to use reliable signals; this is done | |
28 ** by including "syssignal.h"; it fixes nasty program crashes | |
29 ** when using native sound in TTY mode. | |
30 ** added support for DEC audio file format (this is basically the | |
31 ** same as Sun audio, but uses little endian format, instead). | |
32 ** strip the header from Sun audio and DEC audio files in order to | |
33 ** prevent noise at beginning of samples (thanks to Thomas Pundt | |
34 ** <pundtt@math.uni-muenster.de> for pointing out this bug and | |
35 ** providing information on the file format). | |
36 ** added a few more conversion routines. | |
37 ** made the code even more tolerant to the limits imposed by some | |
38 ** soundcards and try to accept soundfiles even if they are not | |
39 ** fully conformant to the standard. | |
40 ** 1.4 -- increased header size to 256; I hope there is no sample software | |
41 ** that requires this much. | |
42 ** added code for converting from signed to unsigned format as | |
43 ** some soundcards cannot handle signed 8bit data. | |
44 */ | |
45 | |
46 /* Synched up with: Not in FSF. */ | |
47 | |
48 #define HEADERSZ 256 /* has to be at least as big as the biggest header */ | |
49 #define SNDBUFSZ 2048 /* has to be at least as big as HEADERSZ */ | |
50 | |
51 /* XEmacs beta testers say: undef this by default. */ | |
52 #undef NOVOLUMECTRLFORMULAW /* Changing the volume for uLaw-encoded | |
53 samples sounds very poor; possibly, | |
54 this is true only for the PC-Snd | |
55 driver, so undefine this symbol at your | |
56 discretion */ | |
57 | |
58 #ifdef HAVE_CONFIG_H | |
59 #include <config.h> | |
60 #endif | |
61 | |
62 #include <errno.h> | |
63 #include <fcntl.h> | |
64 #include <linux/soundcard.h> | |
65 #include <stdio.h> | |
66 #include <stdlib.h> | |
67 #include <string.h> | |
68 #include <sys/fcntl.h> | |
69 #include <sys/file.h> | |
70 #include <sys/ioctl.h> | |
71 #include <sys/signal.h> | |
72 #include <unistd.h> | |
73 | |
74 #ifdef LINUXPLAYSTANDALONE | |
75 #define perror(str) fprintf(stderr,"audio: %s %s\n",str,strerror(errno)); | |
76 #define warn(str) fprintf(stderr,"audio: %s\n",str); | |
77 #else | |
78 #include "lisp.h" | |
79 #include "syssignal.h" | |
80 #define perror(str) message("audio: %s, %s ",str,strerror(errno)) | |
81 #define warn(str) message("audio: %s ",GETTEXT(str)) | |
82 #endif | |
83 | |
84 #ifdef __GNUC__ | |
85 #define UNUSED(x) ((void)(x)) | |
86 #else | |
87 #define UNUSED(x) | |
88 #define __inline__ | |
89 #endif | |
90 | |
91 static __sighandler_t sighup_handler; | |
92 static __sighandler_t sigint_handler; | |
93 | |
94 /* Maintain global variable for keeping parser state information; this struct | |
95 is set to zero before the first invocation of the parser. The use of a | |
96 global variable prevents multiple concurrent executions of this code, but | |
97 this does not happen anyways... */ | |
98 static union { | |
99 struct { | |
100 int align; | |
101 enum {wvMain,wvSubchunk,wvOutOfBlock,wvSkipChunk, | |
102 wvSoundChunk,wvFatal,wvFatalNotify} state; | |
103 size_t left; | |
104 unsigned char leftover[HEADERSZ]; | |
105 signed long chunklength; | |
106 } wave; | |
107 struct { | |
108 int align; | |
109 int isdata; | |
110 int skipping; | |
111 size_t left; | |
112 unsigned char leftover[HEADERSZ]; | |
113 } audio; | |
114 } parsestate; | |
115 | |
116 /* Use a global buffer as scratch-pad for possible conversions of the | |
117 sampling format */ | |
118 static unsigned char sndbuf[SNDBUFSZ]; | |
119 | |
120 static int mix_fd = -1; | |
121 static int audio_vol = -1; | |
122 static int audio_fd = -1; | |
123 static char *audio_dev = ""; | |
124 | |
125 typedef enum {fmtIllegal,fmtRaw,fmtVoc,fmtWave,fmtSunAudio} fmtType; | |
126 | |
127 /* Intercept SIGINT and SIGHUP in order to close the audio and mixer | |
128 devices before terminating sound output; this requires reliable | |
129 signals as provided by "syssignal.h" */ | |
130 static void sighandler(int sig) | |
131 { | |
132 if (mix_fd > 0) { | |
133 if (audio_vol >= 0) { | |
134 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol); | |
135 audio_vol = -1; } | |
136 if (mix_fd != audio_fd) | |
137 close(mix_fd); | |
138 mix_fd = -1; } | |
139 if (audio_fd > 0) { | |
140 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL); | |
141 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL); | |
142 close(audio_fd); | |
143 audio_fd = -1; } | |
144 if (sig == SIGHUP && sighup_handler) sighup_handler(sig); | |
145 else if (sig == SIGINT && sigint_handler) sigint_handler(sig); | |
146 else exit(1); | |
147 } | |
148 | |
149 /* There is no special treatment required for parsing raw data files; we | |
150 assume that these files contain data in 8bit unsigned format that | |
151 has been sampled at 8kHz; there is no extra header */ | |
152 static size_t parseraw(void **data,size_t *sz,void **outbuf) | |
153 { | |
154 int rc = *sz; | |
155 | |
156 *outbuf = *data; | |
157 *sz = 0; | |
158 return(rc); | |
159 } | |
160 | |
161 /* Currently we cannot cope with files in VOC format; if you really need | |
162 to play these files, they should be converted by using SOX */ | |
163 static size_t parsevoc(void **data,size_t *sz,void **outbuf) | |
164 { | |
165 UNUSED(data); | |
166 UNUSED(sz); | |
167 UNUSED(outbuf); | |
168 return(0); | |
169 } | |
170 | |
171 /* We need to perform some look-ahead in order to parse files in WAVE format; | |
172 this might require re-partioning of the data segments if headers cross the | |
173 boundaries between two read operations. This is done in a two-step way: | |
174 first we request a certain amount of bytes... */ | |
175 static __inline__ int waverequire(void **data,size_t *sz,size_t rq) | |
176 { | |
177 int rc = 1; | |
178 | |
179 if (rq > HEADERSZ) { | |
180 warn("Header size exceeded while parsing WAVE file"); | |
181 parsestate.wave.state = wvFatal; | |
182 *sz = 0; | |
183 return(0); } | |
184 if ((rq -= parsestate.wave.left) <= 0) | |
185 return(rc); | |
186 if (rq > *sz) {rq = *sz; rc = 0;} | |
187 memcpy(parsestate.wave.leftover+parsestate.wave.left, | |
188 *data,rq); | |
189 parsestate.wave.left += rq; | |
190 ((unsigned char *)*data) += rq; | |
191 *sz -= rq; | |
192 return(rc); | |
193 } | |
194 | |
195 /* ...and next we remove this many bytes from the buffer */ | |
196 static __inline__ void waveremove(size_t rq) | |
197 { | |
198 if (parsestate.wave.left <= rq) | |
199 parsestate.wave.left = 0; | |
200 else { | |
201 parsestate.wave.left -= rq; | |
202 memmove(parsestate.wave.leftover, | |
203 parsestate.wave.leftover+rq, | |
204 parsestate.wave.left); } | |
205 return; | |
206 } | |
207 | |
208 /* Sound files in WAVE format can contain an arbitrary amount of tagged | |
209 chunks; this requires quite some effort for parsing the data */ | |
210 static size_t parsewave(void **data,size_t *sz,void **outbuf) | |
211 { | |
212 for (;;) | |
213 switch (parsestate.wave.state) { | |
214 case wvMain: | |
215 if (!waverequire(data,sz,20)) | |
216 return(0); | |
217 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
218 parsestate.wave.chunklength = parsestate.wave.leftover[16] + | |
219 256*(parsestate.wave.leftover[17] + | |
220 256*(parsestate.wave.leftover[18] + | |
221 256*parsestate.wave.leftover[19])); | |
222 waveremove(20); | |
223 parsestate.wave.state = wvSubchunk; | |
224 break; | |
225 case wvSubchunk: | |
226 if (!waverequire(data,sz,parsestate.wave.chunklength)) | |
227 return(0); | |
228 parsestate.wave.align = parsestate.wave.chunklength < 14 ? 1 | |
229 : parsestate.wave.leftover[12]; | |
230 if (parsestate.wave.align != 1 && | |
231 parsestate.wave.align != 2 && | |
232 parsestate.wave.align != 4) { | |
233 warn("Illegal datawidth detected while parsing WAVE file"); | |
234 parsestate.wave.state = wvFatal; } | |
235 else | |
236 parsestate.wave.state = wvOutOfBlock; | |
237 waveremove(parsestate.wave.chunklength); | |
238 break; | |
239 case wvOutOfBlock: | |
240 if (!waverequire(data,sz,8)) | |
241 return(0); | |
242 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
243 parsestate.wave.chunklength = parsestate.wave.leftover[4] + | |
244 256*(parsestate.wave.leftover[5] + | |
245 256*(parsestate.wave.leftover[6] + | |
246 256*(parsestate.wave.leftover[7] & 0x7F))); | |
247 if (memcmp(parsestate.wave.leftover,"data",4)) | |
248 parsestate.wave.state = wvSkipChunk; | |
249 else | |
250 parsestate.wave.state = wvSoundChunk; | |
251 waveremove(8); | |
252 break; | |
253 case wvSkipChunk: | |
254 if (parsestate.wave.chunklength > 0 && *sz > 0 && | |
255 (signed long)*sz < (signed long)parsestate.wave.chunklength) { | |
256 parsestate.wave.chunklength -= *sz; | |
257 *sz = 0; } | |
258 else { | |
259 if (parsestate.wave.chunklength > 0 && *sz > 0) { | |
260 *sz -= parsestate.wave.chunklength; | |
261 ((unsigned char *)*data) += parsestate.wave.chunklength; } | |
262 parsestate.wave.state = wvOutOfBlock; } | |
263 break; | |
264 case wvSoundChunk: { | |
265 size_t count,rq; | |
266 if (parsestate.wave.left) { /* handle leftover bytes from last | |
267 alignment operation */ | |
268 count = parsestate.wave.left; | |
269 rq = HEADERSZ-count; | |
270 if (rq > parsestate.wave.chunklength) | |
271 rq = parsestate.wave.chunklength; | |
272 if (!waverequire(data,sz,rq)) { | |
273 parsestate.wave.chunklength -= parsestate.wave.left - count; | |
274 return(0); } | |
275 parsestate.wave.chunklength -= rq; | |
276 *outbuf = parsestate.wave.leftover; | |
277 parsestate.wave.left = 0; | |
278 return(rq); } | |
279 if (*sz >= parsestate.wave.chunklength) { | |
280 count = parsestate.wave.chunklength; | |
281 rq = 0; } | |
282 else { | |
283 count = *sz; | |
284 count -= rq = count % parsestate.wave.align; } | |
285 *outbuf = *data; | |
286 ((unsigned char *)*data) += count; | |
287 *sz -= count; | |
288 if ((parsestate.wave.chunklength -= count) < parsestate.wave.align) { | |
289 parsestate.wave.state = wvOutOfBlock; | |
290 /* Some broken software (e.g. SOX) attaches junk to the end of a sound | |
291 chunk; so, let's ignore this... */ | |
292 if (parsestate.wave.chunklength) | |
293 parsestate.wave.state = wvSkipChunk; } | |
294 else if (rq) | |
295 /* align data length to a multiple of datasize; keep additional data | |
296 in "leftover" buffer --- this is neccessary to ensure proper | |
297 functioning of the sndcnv... routines */ | |
298 waverequire(data,sz,rq); | |
299 return(count); } | |
300 case wvFatalNotify: | |
301 warn("Irrecoverable error while parsing WAVE file"); | |
302 parsestate.wave.state = wvFatal; | |
303 break; | |
304 case wvFatal: | |
305 default: | |
306 *sz = 0; | |
307 return(0); } | |
308 } | |
309 | |
310 /* Strip the header from files in Sun/DEC audio format; this requires some | |
311 extra processing as the header can be an arbitrary size and it might | |
312 result in alignment errors for subsequent conversions --- thus we do | |
313 some buffering, where needed */ | |
314 static size_t parsesundecaudio(void **data,size_t *sz,void **outbuf) | |
315 { | |
316 /* There is data left over from the last invocation of this function; join | |
317 it with the new data and return a sound chunk that is as big as a | |
318 single entry */ | |
319 if (parsestate.audio.left) { | |
320 if (parsestate.audio.left + *sz > parsestate.audio.align) { | |
321 int count; | |
322 memmove(parsestate.audio.leftover + parsestate.audio.left, | |
323 *data, | |
324 count = parsestate.audio.align - parsestate.audio.left); | |
325 *outbuf = parsestate.audio.leftover; | |
326 *sz -= count; | |
327 *data = (*(char **)data) + count; | |
328 parsestate.audio.left = 0; | |
329 return(parsestate.audio.align); } | |
330 else { | |
331 /* We need even more data in order to get one complete single entry! */ | |
332 memmove(parsestate.audio.leftover + parsestate.audio.left, | |
333 *data, | |
334 *sz); | |
335 *data = (*(char **)data) + *sz; | |
336 parsestate.audio.left += *sz; | |
337 *sz = 0; | |
338 return(0); } } | |
339 | |
340 /* This is the main sound chunk, strip of any extra data that does not fit | |
341 the alignment requirements and move these bytes into the leftover buffer*/ | |
342 if (parsestate.audio.isdata) { | |
343 int rc = *sz; | |
344 *outbuf = *data; | |
345 if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) { | |
346 memmove(parsestate.audio.leftover, | |
347 (char *)*outbuf + rc - parsestate.audio.left, | |
348 parsestate.audio.left); | |
349 rc -= parsestate.audio.left; } | |
350 *sz = 0; | |
351 return(rc); } | |
352 | |
353 /* This is the first invocation of this function; we need to parse the | |
354 header information and determine how many bytes we need to skip until | |
355 the start of the sound chunk */ | |
356 if (!parsestate.audio.skipping) { | |
357 unsigned char *header = *data; | |
358 if (*sz < 8) { | |
359 warn("Irrecoverable error while parsing Sun/DEC audio file"); | |
360 return(0); } | |
361 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
362 if (header[3]) { /* Sun audio (big endian) */ | |
363 parsestate.audio.align = ((header[15] > 2)+1)*header[23]; | |
364 parsestate.audio.skipping = header[7]+256*(header[6]+256* | |
365 (header[5]+256*header[4])); } | |
366 else { /* DEC audio (little endian) */ | |
367 parsestate.audio.align = ((header[12] > 2)+1)*header[20]; | |
368 parsestate.audio.skipping = header[4]+256*(header[5]+256* | |
369 (header[6]+256*header[7])); }} | |
370 | |
371 /* We are skipping extra data that has been attached to header; most usually | |
372 this will be just a comment, such as the original filename and/or the | |
373 creation date. Make sure that we do not return less than one single sound | |
374 sample entry to the caller; if this happens, rather decide to move those | |
375 few bytes into the leftover buffer and deal with it later */ | |
376 if (*sz >= parsestate.audio.skipping) { | |
377 /* Skip just the header information and return the sound chunk */ | |
378 int rc = *sz - parsestate.audio.skipping; | |
379 *outbuf = (char *)*data + parsestate.audio.skipping; | |
380 if ((parsestate.audio.left = rc % parsestate.audio.align) != 0) { | |
381 memmove(parsestate.audio.leftover, | |
382 (char *)*outbuf + rc - parsestate.audio.left, | |
383 parsestate.audio.left); | |
384 rc -= parsestate.audio.left; } | |
385 *sz = 0; | |
386 parsestate.audio.skipping = 0; | |
387 parsestate.audio.isdata++; | |
388 return(rc); } | |
389 else { | |
390 /* Skip everything */ | |
391 parsestate.audio.skipping -= *sz; | |
392 return(0); } | |
393 } | |
394 | |
395 /* If the soundcard could not be set to natively support the data format, we | |
396 try to do some limited on-the-fly conversion to a different format; if | |
397 no conversion is needed, though, we can output directly */ | |
398 static size_t sndcnvnop(void **data,size_t *sz,void **outbuf) | |
399 { | |
400 int rc = *sz; | |
401 | |
402 *outbuf = *data; | |
403 *sz = 0; | |
404 return(rc); | |
405 } | |
406 | |
407 /* Convert 8 bit unsigned stereo data to 8 bit unsigned mono data */ | |
408 static size_t sndcnv8U_2mono(void **data,size_t *sz,void **outbuf) | |
409 { | |
410 register unsigned char *src; | |
411 register unsigned char *dest; | |
412 int rc,count; | |
413 | |
414 count = *sz / 2; | |
415 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
416 else *sz = 0; | |
417 rc = count; | |
418 src = *data; | |
419 *outbuf = | |
420 dest = sndbuf; | |
421 while (count--) | |
422 *dest++ = (unsigned char)(((int)*((unsigned char *)src)++ + | |
423 (int)*((unsigned char *)src)++) / 2); | |
424 *data = src; | |
425 return(rc); | |
426 } | |
427 | |
428 /* Convert 8 bit signed stereo data to 8 bit signed mono data */ | |
429 static size_t sndcnv8S_2mono(void **data,size_t *sz,void **outbuf) | |
430 { | |
431 register unsigned char *src; | |
432 register unsigned char *dest; | |
433 int rc,count; | |
434 | |
435 count = *sz / 2; | |
436 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
437 else *sz = 0; | |
438 rc = count; | |
439 src = *data; | |
440 *outbuf = | |
441 dest = sndbuf; | |
442 while (count--) | |
443 *dest++ = (unsigned char)(((int)*((signed char *)src)++ + | |
444 (int)*((signed char *)src)++) / 2); | |
445 *data = src; | |
446 return(rc); | |
447 } | |
448 | |
449 /* Convert 8 bit signed stereo data to 8 bit unsigned mono data */ | |
450 static size_t sndcnv2monounsigned(void **data,size_t *sz,void **outbuf) | |
451 { | |
452 register unsigned char *src; | |
453 register unsigned char *dest; | |
454 int rc,count; | |
455 | |
456 count = *sz / 2; | |
457 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
458 else *sz = 0; | |
459 rc = count; | |
460 src = *data; | |
461 *outbuf = | |
462 dest = sndbuf; | |
463 while (count--) | |
464 *dest++ = (unsigned char)(((int)*((signed char *)src)++ + | |
465 (int)*((signed char *)src)++) / 2) ^ 0x80; | |
466 *data = src; | |
467 return(rc); | |
468 } | |
469 | |
470 /* Convert 8 bit signed mono data to 8 bit unsigned mono data */ | |
471 static size_t sndcnv2unsigned(void **data,size_t *sz,void **outbuf) | |
472 { | |
473 register unsigned char *src; | |
474 register unsigned char *dest; | |
475 int rc,count; | |
476 | |
477 count = *sz; | |
478 if (count > SNDBUFSZ) { *sz -= SNDBUFSZ; count = SNDBUFSZ; } | |
479 else *sz = 0; | |
480 rc = count; | |
481 src = *data; | |
482 *outbuf = | |
483 dest = sndbuf; | |
484 while (count--) | |
485 *dest++ = *((unsigned char *)src)++ ^ 0x80; | |
486 *data = src; | |
487 return(rc); | |
488 } | |
489 | |
490 /* Convert a number in the range -32768..32767 to an 8 bit ulaw encoded | |
491 number --- I hope, I got this conversion right :-) */ | |
492 static __inline__ signed char int2ulaw(int i) | |
493 { | |
494 /* Lookup table for fast calculation of number of bits that need shifting*/ | |
495 static short int t_bits[128] = { | |
496 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, | |
497 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, | |
498 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, | |
499 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}; | |
500 register int bits,logi; | |
501 | |
502 /* unrolling this condition (hopefully) improves execution speed */ | |
503 if (i < 0) { | |
504 if ((i = (132-i)) > 0x7FFF) i = 0x7FFF; | |
505 logi = (i >> ((bits = t_bits[i/256])+4)); | |
506 return((bits << 4 | logi) ^ 0x7F); } | |
507 else { | |
508 if ((i = 132+i) > 0x7FFF) i = 0x7FFF; | |
509 logi = (i >> ((bits = t_bits[i/256])+4)); | |
510 return(~(bits << 4 | logi)); } | |
511 } | |
512 | |
513 /* Convert 8 bit ulaw stereo data to 8 bit ulaw mono data */ | |
514 static size_t sndcnvULaw_2mono(void **data,size_t *sz,void **outbuf) | |
515 { | |
516 | |
517 static short int ulaw2int[256] = { | |
518 /* Precomputed lookup table for conversion from ulaw to 15 bit signed */ | |
519 -16062,-15550,-15038,-14526,-14014,-13502,-12990,-12478, | |
520 -11966,-11454,-10942,-10430, -9918, -9406, -8894, -8382, | |
521 -7998, -7742, -7486, -7230, -6974, -6718, -6462, -6206, | |
522 -5950, -5694, -5438, -5182, -4926, -4670, -4414, -4158, | |
523 -3966, -3838, -3710, -3582, -3454, -3326, -3198, -3070, | |
524 -2942, -2814, -2686, -2558, -2430, -2302, -2174, -2046, | |
525 -1950, -1886, -1822, -1758, -1694, -1630, -1566, -1502, | |
526 -1438, -1374, -1310, -1246, -1182, -1118, -1054, -990, | |
527 -942, -910, -878, -846, -814, -782, -750, -718, | |
528 -686, -654, -622, -590, -558, -526, -494, -462, | |
529 -438, -422, -406, -390, -374, -358, -342, -326, | |
530 -310, -294, -278, -262, -246, -230, -214, -198, | |
531 -186, -178, -170, -162, -154, -146, -138, -130, | |
532 -122, -114, -106, -98, -90, -82, -74, -66, | |
533 -60, -56, -52, -48, -44, -40, -36, -32, | |
534 -28, -24, -20, -16, -12, -8, -4, +0, | |
535 +16062,+15550,+15038,+14526,+14014,+13502,+12990,+12478, | |
536 +11966,+11454,+10942,+10430, +9918, +9406, +8894, +8382, | |
537 +7998, +7742, +7486, +7230, +6974, +6718, +6462, +6206, | |
538 +5950, +5694, +5438, +5182, +4926, +4670, +4414, +4158, | |
539 +3966, +3838, +3710, +3582, +3454, +3326, +3198, +3070, | |
540 +2942, +2814, +2686, +2558, +2430, +2302, +2174, +2046, | |
541 +1950, +1886, +1822, +1758, +1694, +1630, +1566, +1502, | |
542 +1438, +1374, +1310, +1246, +1182, +1118, +1054, +990, | |
543 +942, +910, +878, +846, +814, +782, +750, +718, | |
544 +686, +654, +622, +590, +558, +526, +494, +462, | |
545 +438, +422, +406, +390, +374, +358, +342, +326, | |
546 +310, +294, +278, +262, +246, +230, +214, +198, | |
547 +186, +178, +170, +162, +154, +146, +138, +130, | |
548 +122, +114, +106, +98, +90, +82, +74, +66, | |
549 +60, +56, +52, +48, +44, +40, +36, +32, | |
550 +28, +24, +20, +16, +12, +8, +4, +0}; | |
551 | |
552 register unsigned char *src; | |
553 register unsigned char *dest; | |
554 int rc,count; | |
555 | |
556 count = *sz / 2; | |
557 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
558 else *sz = 0; | |
559 rc = count; | |
560 src = *data; | |
561 *outbuf = | |
562 dest = sndbuf; | |
563 while (count--) | |
564 /* it is not possible to directly interpolate between two ulaw encoded | |
565 data bytes, thus we need to convert to linear format first and later | |
566 we convert back to ulaw format */ | |
567 *dest++ = int2ulaw(ulaw2int[*((unsigned char *)src)++] + | |
568 ulaw2int[*((unsigned char *)src)++]); | |
569 *data = src; | |
570 return(rc); | |
571 } | |
572 | |
573 /* Convert 16 bit little endian signed stereo data to 16 bit little endian | |
574 signed mono data */ | |
575 static size_t sndcnv16_2monoLE(void **data,size_t *sz,void **outbuf) | |
576 { | |
577 register unsigned char *src; | |
578 register unsigned char *dest; | |
579 int rc,count; | |
580 signed short i; | |
581 | |
582 count = *sz / 2; | |
583 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
584 else *sz = 0; | |
585 rc = count; | |
586 src = *data; | |
587 *outbuf = | |
588 dest = sndbuf; | |
589 for (count /= 2; count--; ) { | |
590 i = ((int)(((unsigned char *)src)[0]) + | |
591 256*(int)(((unsigned char *)src)[1]) + | |
592 (int)(((unsigned char *)src)[2]) + | |
593 256*(int)(((unsigned char *)src)[3])) / 2; | |
594 src += 4; | |
595 *dest++ = (unsigned char)(i & 0xFF); | |
596 *dest++ = (unsigned char)((i / 256) & 0xFF); } | |
597 *data = src; | |
598 return(rc); | |
599 } | |
600 | |
601 /* Convert 16 bit big endian signed stereo data to 16 bit big endian | |
602 signed mono data */ | |
603 static size_t sndcnv16_2monoBE(void **data,size_t *sz,void **outbuf) | |
604 { | |
605 register unsigned char *src; | |
606 register unsigned char *dest; | |
607 int rc,count; | |
608 signed short i; | |
609 | |
610 count = *sz / 2; | |
611 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
612 else *sz = 0; | |
613 rc = count; | |
614 src = *data; | |
615 *outbuf = | |
616 dest = sndbuf; | |
617 for (count /= 2; count--; ) { | |
618 i = ((int)(((unsigned char *)src)[1]) + | |
619 256*(int)(((unsigned char *)src)[0]) + | |
620 (int)(((unsigned char *)src)[3]) + | |
621 256*(int)(((unsigned char *)src)[2])) / 2; | |
622 src += 4; | |
623 *dest++ = (unsigned char)((i / 256) & 0xFF); | |
624 *dest++ = (unsigned char)(i & 0xFF); } | |
625 *data = src; | |
626 return(rc); | |
627 } | |
628 | |
629 /* Convert 16 bit little endian signed data to 8 bit unsigned data */ | |
630 static size_t sndcnv2byteLE(void **data,size_t *sz,void **outbuf) | |
631 { | |
632 register unsigned char *src; | |
633 register unsigned char *dest; | |
634 int rc,count; | |
635 | |
636 count = *sz / 2; | |
637 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
638 else *sz = 0; | |
639 rc = count; | |
640 src = *data; | |
641 *outbuf = | |
642 dest = sndbuf; | |
643 while (count--) { | |
644 *dest++ = (unsigned char)(((signed char *)src)[1] ^ (signed char)0x80); | |
645 ((char *)src) += 2; } | |
646 *data = src; | |
647 return(rc); | |
648 } | |
649 | |
650 /* Convert 16 bit big endian signed data to 8 bit unsigned data */ | |
651 static size_t sndcnv2byteBE(void **data,size_t *sz,void **outbuf) | |
652 { | |
653 register unsigned char *src; | |
654 register unsigned char *dest; | |
655 int rc,count; | |
656 | |
657 count = *sz / 2; | |
658 if (count > SNDBUFSZ) { *sz -= 2*SNDBUFSZ; count = SNDBUFSZ; } | |
659 else *sz = 0; | |
660 rc = count; | |
661 src = *data; | |
662 *outbuf = | |
663 dest = sndbuf; | |
664 while (count--) { | |
665 *dest++ = (unsigned char)(((signed char *)src)[0] ^ (signed char)0x80); | |
666 ((char *)src) += 2; } | |
667 *data = src; | |
668 return(rc); | |
669 } | |
670 | |
671 /* Convert 16 bit little endian signed stereo data to 8 bit unsigned | |
672 mono data */ | |
673 static size_t sndcnv2monobyteLE(void **data,size_t *sz,void **outbuf) | |
674 { | |
675 register unsigned char *src; | |
676 register unsigned char *dest; | |
677 int rc,count; | |
678 | |
679 count = *sz / 4; | |
680 if (count > SNDBUFSZ) { *sz -= 4*SNDBUFSZ; count = SNDBUFSZ; } | |
681 else *sz = 0; | |
682 rc = count; | |
683 src = *data; | |
684 *outbuf = | |
685 dest = sndbuf; | |
686 while (count--) { | |
687 *dest++ = (unsigned char)(((int)((signed char *)src)[1] + | |
688 (int)((signed char *)src)[3]) / 2 ^ 0x80); | |
689 ((char *)src) += 4; } | |
690 *data = src; | |
691 return(rc); | |
692 } | |
693 | |
694 /* Convert 16 bit big endian signed stereo data to 8 bit unsigned | |
695 mono data */ | |
696 static size_t sndcnv2monobyteBE(void **data,size_t *sz,void **outbuf) | |
697 { | |
698 register unsigned char *src; | |
699 register unsigned char *dest; | |
700 int rc,count; | |
701 | |
702 count = *sz / 4; | |
703 if (count > SNDBUFSZ) { *sz -= 4*SNDBUFSZ; count = SNDBUFSZ; } | |
704 else *sz = 0; | |
705 rc = count; | |
706 src = *data; | |
707 *outbuf = | |
708 dest = sndbuf; | |
709 while (count--) { | |
710 *dest++ = (unsigned char)(((int)((signed char *)src)[0] + | |
711 (int)((signed char *)src)[2]) / 2 ^ 0x80); | |
712 ((char *)src) += 4; } | |
713 *data = src; | |
714 return(rc); | |
715 } | |
716 | |
717 /* Look at the header of the sound file and try to determine the format; | |
718 we can recognize files in VOC, WAVE, and, Sun/DEC-audio format--- everything | |
719 else is assumed to be raw 8 bit unsigned data sampled at 8kHz */ | |
720 static fmtType analyze_format(unsigned char *format,int *fmt,int *speed, | |
721 int *tracks, | |
722 size_t (**parsesndfile)(void **,size_t *sz, | |
723 void **)) | |
724 { | |
725 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
726 if (!memcmp(format,"Creative Voice File\x1A\x1A\x00",22) && | |
727 (format[22]+256*format[23]) == | |
728 ((0x1233-format[24]-256*format[25])&0xFFFF)) { /* VOC */ | |
729 *fmt = AFMT_U8; | |
730 *speed = 8000; | |
731 *tracks = 2; | |
732 *parsesndfile = parsevoc; | |
733 return(fmtVoc); } | |
734 else if (!memcmp(format,"RIFF",4) && | |
735 !memcmp(format+8,"WAVEfmt ",8)) { /* WAVE */ | |
736 if (memcmp(format+20,"\001\000\001"/* PCM mono */,4) && | |
737 memcmp(format+20,"\001\000\002"/* PCM stereo */,4)) | |
738 return(fmtIllegal); | |
739 *fmt = (format[32]/(*tracks = format[22])) == 1 ? | |
740 AFMT_U8 : AFMT_S16_LE; | |
741 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
742 *speed = format[24]+256*(format[25]+256* | |
743 (format[26]+256*format[27])); | |
744 *parsesndfile = parsewave; | |
745 return(fmtWave); } | |
746 else if (!memcmp(format,".snd",4)) { /* Sun Audio (big endian) */ | |
747 if (format[7]+256*(format[6]+256*(format[5]+256*format[4])) < 24) { | |
748 *fmt = AFMT_MU_LAW; | |
749 *speed = 8000; | |
750 *tracks = 1; | |
751 *parsesndfile = parsesundecaudio; | |
752 return(fmtSunAudio); } | |
753 if (!memcmp(format+12,"\000\000\000\001",4)) *fmt = AFMT_MU_LAW; | |
754 else if (!memcmp(format+12,"\000\000\000\002",4)) *fmt = AFMT_S8; | |
755 else if (!memcmp(format+12,"\000\000\000\003",4)) *fmt = AFMT_S16_BE; | |
756 else return(fmtIllegal); | |
757 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
758 *speed = format[19]+256*(format[18]+256* | |
759 (format[17]+256*format[16])); | |
760 *tracks = format[23]; | |
761 *parsesndfile = parsesundecaudio; | |
762 return(fmtSunAudio); } | |
763 else if (!memcmp(format,".sd",4)) { /* DEC Audio (little endian) */ | |
764 if (format[4]+256*(format[5]+256*(format[6]+256*format[7])) < 24) { | |
765 *fmt = AFMT_MU_LAW; | |
766 *speed = 8000; | |
767 *tracks = 1; | |
768 *parsesndfile = parsesundecaudio; | |
769 return(fmtSunAudio); } | |
770 if (!memcmp(format+12,"\001\000\000",4)) *fmt = AFMT_MU_LAW; | |
771 else if (!memcmp(format+12,"\002\000\000",4)) *fmt = AFMT_S8; | |
772 else if (!memcmp(format+12,"\003\000\000",4)) *fmt = AFMT_S16_LE; | |
773 else return(fmtIllegal); | |
774 /* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */ | |
775 *speed = format[16]+256*(format[17]+256* | |
776 (format[18]+256*format[19])); | |
777 *tracks = format[20]; | |
778 *parsesndfile = parsesundecaudio; | |
779 return(fmtSunAudio); } | |
780 else { | |
781 *fmt = AFMT_U8; | |
782 *speed = 8000; | |
783 *tracks = 1; | |
784 *parsesndfile = parseraw; | |
785 return(fmtRaw); } | |
786 } | |
787 | |
788 /* Initialize the soundcard and mixer device with the parameters that we | |
789 found in the header of the sound file. If the soundcard is not capable of | |
790 natively supporting the required parameters, then try to set up conversion | |
791 routines. | |
792 The difficulty with setting up the sound card is that the parameters are | |
793 not fully orthogonal; changing one of them might affect some of the | |
794 others, too. Thus we do quite a lot of double checking; actually most of | |
795 this is not needed right now, but it will come in handy, if the kernel's | |
796 sounddriver ever changes or if third-party sounddrivers are used. */ | |
797 static int audio_init(int mixx_fd, int auddio_fd, int fmt, int speed, | |
798 int tracks, int *volume, | |
799 size_t (**sndcnv) (void **, size_t *sz, void **)) | |
800 { | |
801 int i,the_speed,the_stereo,the_fmt; | |
802 | |
803 *sndcnv = sndcnvnop; | |
804 | |
805 if (ioctl(auddio_fd,SNDCTL_DSP_SYNC,NULL) < 0) { | |
806 perror("SNDCTL_DSP_SYNC"); | |
807 return(0); } | |
808 | |
809 /* Initialize sound hardware with prefered parameters */ | |
810 | |
811 /* The PCSP driver does not support reading of the sampling rate via the | |
812 SOUND_PCM_READ_RATE ioctl; determine "the_speed" here */ | |
813 the_speed = speed; ioctl(audio_fd,SNDCTL_DSP_SPEED,&the_speed); | |
814 /* The PCSP driver does not support reading of the mono/stereo flag, thus | |
815 we assume, that failure to change this mode means we are in mono mode */ | |
816 if (((i = (the_stereo = tracks)-1),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0) | |
817 the_stereo = 1; | |
818 the_fmt = fmt; ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt); | |
819 | |
820 /* If the sound hardware cannot support 16 bit format or requires a | |
821 different byte sex then try to drop to 8 bit format */ | |
822 the_fmt = AFMT_QUERY; ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt); | |
823 if (fmt != the_fmt) { | |
824 if (fmt == AFMT_S16_LE || fmt == AFMT_S16_BE) { | |
825 *sndcnv = fmt == AFMT_S16_BE ? sndcnv2byteBE : sndcnv2byteLE; | |
826 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
827 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 || | |
828 fmt != the_fmt) { | |
829 perror("SNDCTL_DSP_SETFMT"); | |
830 return(0); } } | |
831 else { | |
832 perror("SNDCTL_DSP_SETFMT"); | |
833 return(0); } } | |
834 else if (fmt == AFMT_S8) { | |
835 *sndcnv = sndcnv2unsigned; | |
836 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
837 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 || | |
838 fmt != the_fmt) { | |
839 perror("SNDCTRL_DSP_SETFMT"); | |
840 return(0); } } | |
841 | |
842 /* Try to request stereo playback (if needed); if this cannot be supported | |
843 by the hardware, then install conversion routines for mono playback */ | |
844 | |
845 /* This ioctl will fail if we use the PCSP driver; thus the value of | |
846 "the_stereo" is still unchanged */ | |
847 ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo); | |
848 if (tracks != the_stereo) { | |
849 if (tracks == 2) { | |
850 tracks = 1; | |
851 *sndcnv = *sndcnv == sndcnv2byteLE ? sndcnv2monobyteLE : | |
852 *sndcnv == sndcnv2byteBE ? sndcnv2monobyteBE : | |
853 *sndcnv == sndcnv2unsigned ? sndcnv2monounsigned : | |
854 the_fmt == AFMT_S16_LE ? sndcnv16_2monoLE : | |
855 the_fmt == AFMT_S16_BE ? sndcnv16_2monoBE : | |
856 the_fmt == AFMT_S8 ? sndcnv8S_2mono : | |
857 the_fmt == AFMT_U8 ? sndcnv8U_2mono : | |
858 the_fmt == AFMT_MU_LAW ? sndcnvULaw_2mono : NULL; | |
859 if (*sndcnv == NULL) { /* this should not happen */ | |
860 perror("SNDCTL_DSP_STEREO"); | |
861 return(0); } | |
862 /* Switch to mono mode */ | |
863 if (((i = 0),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0 || i) { | |
864 perror("SNDCTL_DSP_STEREO"); | |
865 return(0); } | |
866 /* Now double check that everything is set as expected */ | |
867 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
868 (i != the_fmt && | |
869 (((i=the_fmt),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
870 i != the_fmt || | |
871 ((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
872 i != the_fmt)) || | |
873 (ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&i) >= 0 && | |
874 i != 1)) { | |
875 /* There was no way that we could set the soundcard to a meaningful | |
876 mode */ | |
877 perror("SNDCTL_DSP_SETFMT and SNDCTL_DSP_STEREO"); | |
878 return(0); } } | |
879 else { | |
880 /* Somebody set the soundcard to stereo even though we requested | |
881 mono; this should not happen... */ | |
882 if (((i = the_stereo = tracks),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i))<0 || | |
883 i != the_stereo-1) { | |
884 perror("SNDCTL_DSP_STEREO"); | |
885 return(0); } | |
886 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 || | |
887 i != the_fmt) { | |
888 perror("SNDCTL_DSP_SETFMT"); | |
889 return(0); } } } | |
890 | |
891 /* Fail if deviations from desired sampling frequency are too big */ | |
892 | |
893 /* This ioctl will fail if we use the PCSP driver; thus the value of | |
894 "the_speed" is still unchanged */ | |
895 ioctl(audio_fd,SOUND_PCM_READ_RATE,&the_speed); | |
896 if (speed*14 < the_speed*10 || speed*6 > the_speed*10) { | |
897 char buffer[256]; | |
898 sprintf(buffer,"SNDCTL_DSP_SPEED (req: %d, rtn: %d)",speed,the_speed); | |
899 perror(buffer); | |
900 return(0); } | |
901 | |
902 /* Use the mixer device for setting the playback volume */ | |
903 if (mixx_fd > 0) { | |
904 int vol = *volume & 0xFF; | |
905 if (ioctl(mixx_fd,SOUND_MIXER_READ_PCM,volume) < 0) | |
906 *volume = -1; | |
907 if (vol < 0) vol = 0; else if (vol > 100) vol = 100; | |
908 #ifdef NOVOLUMECTRLFORMULAW | |
909 if (fmt == AFMT_MU_LAW) | |
910 vol = 100; | |
911 #endif | |
912 vol |= 256*vol; | |
913 /* Do not signal an error, if volume control is unavailable! */ | |
914 ioctl(mixx_fd,SOUND_MIXER_WRITE_PCM,&vol); } | |
915 | |
916 #if defined(LINUXPLAYSTANDALONE) && 1 | |
917 /* Debugging output is displayed only when compiled as stand-alone version */ | |
918 {int the_volume; | |
919 the_fmt = AFMT_QUERY; | |
920 ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt); | |
921 ioctl(auddio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo); | |
922 ioctl(auddio_fd,SOUND_PCM_READ_RATE,&the_speed); | |
923 ioctl(mixx_fd,SOUND_MIXER_READ_PCM,&the_volume); | |
924 fprintf(stderr,"%s, %s, %dHz, L:%d/R:%d\n", | |
925 the_fmt == AFMT_MU_LAW ? "AFMT_MU_LAW" : | |
926 the_fmt == AFMT_A_LAW ? "AFMT_A_LAW" : | |
927 the_fmt == AFMT_IMA_ADPCM ? "AFMT_IMA_ADPCM" : | |
928 the_fmt == AFMT_U8 ? "AFMT_U8" : | |
929 the_fmt == AFMT_S16_LE ? "AFMT_S16_LE" : | |
930 the_fmt == AFMT_S16_BE ? "AFMT_S16_BE" : | |
931 the_fmt == AFMT_S8 ? "AFMT_S8" : | |
932 the_fmt == AFMT_U16_LE ? "AFMT_U16_LE" : | |
933 the_fmt == AFMT_U16_BE ? "AFMT_U16_BE" : | |
934 the_fmt == AFMT_MPEG ? "AFMT_MPEG" : | |
935 "AFMT_???", | |
936 the_stereo == 2 ? "stereo" : "mono", | |
937 the_speed, | |
938 the_volume / 256, the_volume % 256); } | |
939 #endif | |
940 | |
941 return(1); | |
942 } | |
943 | |
944 /* XEmacs requires code both for playback of pre-loaded data and for playback | |
945 from a soundfile; we use one function for both cases */ | |
946 static void linux_play_data_or_file(int fd,unsigned char *data, | |
947 int length,int volume) | |
948 { | |
949 size_t (*parsesndfile)(void **dayta,size_t *sz,void **outbuf); | |
950 size_t (*sndcnv)(void **dayta,size_t *sz,void **); | |
951 fmtType ffmt; | |
952 int fmt,speed,tracks; | |
953 unsigned char *pptr,*optr,*cptr,*sptr; | |
954 int wrtn,rrtn,crtn,prtn; | |
955 | |
956 /* We need to read at least the header information before we can start | |
957 doing anything */ | |
958 if (!data || length < HEADERSZ) | |
959 if (fd < 0) return; | |
960 else { | |
961 length = read(fd,sndbuf,SNDBUFSZ); | |
962 if (length < HEADERSZ) | |
963 return; | |
964 data = sndbuf; | |
965 length = SNDBUFSZ; } | |
966 | |
967 ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile); | |
968 | |
969 if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) { | |
970 warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)"); | |
971 return; } | |
972 | |
973 /* The VoxWare-SDK discourages opening /dev/audio; opening /dev/dsp and | |
974 properly intializing it via ioctl() is prefered */ | |
975 if ((audio_fd=open((audio_dev="/dev/dsp"), | |
976 (O_WRONLY|O_NDELAY),0)) < 0) { | |
977 perror(audio_dev); | |
978 if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; } | |
979 return; } | |
980 | |
981 /* The VoxWare-SDK discourages direct manipulation of the mixer device as | |
982 this could lead to problems, when multiple sound cards are installed */ | |
983 mix_fd = audio_fd; | |
984 | |
985 sighup_handler = signal(SIGHUP,(__sighandler_t)sighandler); | |
986 sigint_handler = signal(SIGINT,(__sighandler_t)sighandler); | |
987 | |
988 if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv)) | |
989 goto END_OF_PLAY; | |
990 audio_vol = volume; | |
991 | |
992 /* Initialize global parser state information to zero */ | |
993 memset(&parsestate,0,sizeof(parsestate)); | |
994 | |
995 /* Mainloop: read a block of data, parse its contents, perform all | |
996 the neccessary conversions and output it to the sound | |
997 device; repeat until all data has been processed */ | |
998 rrtn = length; | |
999 do { | |
1000 for (pptr = data; (prtn = parsesndfile((void **)&pptr,&rrtn, | |
1001 (void **)&optr)) > 0; ) | |
1002 for (cptr = optr; (crtn = sndcnv((void **)&cptr,&prtn, | |
1003 (void **)&sptr)) > 0; ) { | |
1004 for (;;) { | |
1005 if ((wrtn = write(audio_fd,sptr,crtn)) < 0) { | |
1006 perror("write"); goto END_OF_PLAY; } | |
1007 else if (wrtn) break; | |
1008 else if (ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL) < 0) { | |
1009 perror("SNDCTL_DSP_SYNC"); goto END_OF_PLAY; } } | |
1010 if (wrtn != crtn) { | |
1011 char buf[255]; | |
1012 sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn); | |
1013 warn(buf); | |
1014 goto END_OF_PLAY; } } | |
1015 if (fd >= 0) { | |
1016 if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) { | |
1017 perror("read"); goto END_OF_PLAY; } } | |
1018 else | |
1019 break; | |
1020 } while (rrtn > 0); | |
1021 | |
1022 /* Verify that we could fully parse the entire soundfile; this is needed | |
1023 only for files in WAVE format */ | |
1024 if (ffmt == fmtWave && parsestate.wave.state != wvOutOfBlock && | |
1025 parsestate.wave.state != wvFatal) | |
1026 warn("Unexpected end of WAVE file"); | |
1027 | |
1028 END_OF_PLAY: | |
1029 /* Now cleanup all used resources */ | |
1030 | |
1031 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL); | |
1032 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL); | |
1033 | |
1034 signal(SIGHUP,sighup_handler); | |
1035 signal(SIGINT,sigint_handler); | |
1036 | |
1037 if (mix_fd > 0) { | |
1038 if (audio_vol >= 0) { | |
1039 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol); | |
1040 audio_vol = -1; } | |
1041 if (mix_fd != audio_fd) | |
1042 close(mix_fd); | |
1043 mix_fd = -1; } | |
1044 | |
1045 close(audio_fd); | |
1046 audio_fd = -1; | |
1047 | |
1048 return; | |
1049 } | |
1050 | |
1051 /* Call "linux_play_data_or_file" with the appropriate parameters for | |
1052 playing a soundfile */ | |
1053 void play_sound_file (char *sound_file, int volume); | |
1054 void play_sound_file (char *sound_file, int volume) | |
1055 { | |
1056 int fd; | |
1057 | |
1058 if ((fd=open(sound_file,O_RDONLY,0)) < 0) { | |
1059 perror(sound_file); | |
1060 return; } | |
1061 linux_play_data_or_file(fd,NULL,0,volume); | |
1062 close(fd); | |
1063 return; | |
1064 } | |
1065 | |
1066 /* Call "linux_play_data_or_file" with the appropriate parameters for | |
1067 playing pre-loaded data */ | |
1068 void play_sound_data (unsigned char *data, int length, int volume); | |
1069 void play_sound_data (unsigned char *data, int length, int volume) | |
1070 { | |
1071 linux_play_data_or_file(-1,data,length,volume); | |
1072 return; | |
1073 } |