comparison src/linuxplay.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children abe6d1db359e
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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, with platform-independent functions
5 ** moved to a different file by 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 ** Changelog:
19 ** 1.0 -- first release; supports SunAudio, Wave and RAW file formats
20 ** detects (and rejects) VOC file format
21 ** tested with PC-Speaker driver only
22 ** 1.1 -- fixed bug with playback of stereo Wave files
23 ** fixed VOC file detection
24 ** fixed mono/8bit conversion
25 ** cleaned up mixer programming (c.f. VoxWare-SDK)
26 ** tested with PC-Speaker driver and with PAS16 soundcard
27 ** 1.2 -- first (incompatible) attempt at fixing reliable signal handling
28 ** 1.3 -- changed signal handling to use reliable signals; this is done
29 ** by including "syssignal.h"; it fixes nasty program crashes
30 ** when using native sound in TTY mode.
31 ** added support for DEC audio file format (this is basically the
32 ** same as Sun audio, but uses little endian format, instead).
33 ** strip the header from Sun audio and DEC audio files in order to
34 ** prevent noise at beginning of samples (thanks to Thomas Pundt
35 ** <pundtt@math.uni-muenster.de> for pointing out this bug and
36 ** providing information on the file format).
37 ** added a few more conversion routines.
38 ** made the code even more tolerant to the limits imposed by some
39 ** soundcards and try to accept soundfiles even if they are not
40 ** fully conformant to the standard.
41 ** 1.4 -- increased header size to 256; I hope there is no sample software
42 ** that requires this much.
43 ** added code for converting from signed to unsigned format as
44 ** some soundcards cannot handle signed 8bit data.
45 */
46
47 /* Synched up with: Not in FSF. */
48
49 /* XEmacs beta testers say: undef this by default. */
50 #undef NOVOLUMECTRLFORMULAW /* Changing the volume for uLaw-encoded
51 samples sounds very poor; possibly,
52 this is true only for the PC-Snd
53 driver, so undefine this symbol at your
54 discretion */
55
56 #ifdef HAVE_CONFIG_H
57 #include <config.h>
58 #endif
59
60 #include "miscplay.h"
61
62 #include <errno.h>
63 #include <fcntl.h>
64 #include SOUNDCARD_H_PATH /* Path computed by configure */
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <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 #include "sysfile.h"
81 #define perror(str) message("audio: %s, %s ",str,strerror(errno))
82 #define warn(str) message("audio: %s ",GETTEXT(str))
83 #endif
84
85 static void (*sighup_handler)(int);
86 static void (*sigint_handler)(int);
87
88 static int mix_fd;
89 static int audio_vol;
90 static int audio_fd;
91 static char *audio_dev = "/dev/dsp";
92
93 /* Intercept SIGINT and SIGHUP in order to close the audio and mixer
94 devices before terminating sound output; this requires reliable
95 signals as provided by "syssignal.h" */
96 static void sighandler(int sig)
97 {
98 if (mix_fd > 0) {
99 if (audio_vol >= 0) {
100 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
101 audio_vol = -1; }
102 if (mix_fd != audio_fd)
103 close(mix_fd);
104 mix_fd = -1; }
105 if (audio_fd > 0) {
106 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
107 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
108 close(audio_fd);
109 audio_fd = -1; }
110 if (sig == SIGHUP && sighup_handler) sighup_handler(sig);
111 else if (sig == SIGINT && sigint_handler) sigint_handler(sig);
112 else exit(1);
113 }
114
115 /* Initialize the soundcard and mixer device with the parameters that we
116 found in the header of the sound file. If the soundcard is not capable of
117 natively supporting the required parameters, then try to set up conversion
118 routines.
119 The difficulty with setting up the sound card is that the parameters are
120 not fully orthogonal; changing one of them might affect some of the
121 others, too. Thus we do quite a lot of double checking; actually most of
122 this is not needed right now, but it will come in handy, if the kernel's
123 sounddriver ever changes or if third-party sounddrivers are used. */
124 static int audio_init(int mixx_fd, int auddio_fd, int fmt, int speed,
125 int tracks, int *volume,
126 size_t (**sndcnv) (void **, size_t *sz, void **))
127 {
128 int i,the_speed,the_stereo,the_fmt;
129
130 *sndcnv = sndcnvnop;
131
132 if (ioctl(auddio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
133 perror("SNDCTL_DSP_SYNC");
134 return(0); }
135
136 /* Initialize sound hardware with preferred parameters */
137
138 /* If the sound hardware cannot support 16 bit format or requires a
139 different byte sex then try to drop to 8 bit format */
140
141 the_fmt = fmt;
142 if(ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0) {
143 perror("SNDCTL_DSP_SETFMT");
144 return(0);
145 }
146
147 if (fmt != the_fmt) {
148 if (fmt == AFMT_S16_LE || fmt == AFMT_S16_BE) {
149 *sndcnv = fmt == AFMT_S16_BE ? sndcnv2byteBE : sndcnv2byteLE;
150 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
151 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 ||
152 fmt != the_fmt) {
153 perror("SNDCTL_DSP_SETFMT");
154 return(0); } }
155 else if (fmt == AFMT_MU_LAW && the_fmt == AFMT_U8 ) {
156 /* the kernel will convert for us */ }
157 else {
158 perror("SNDCTL_DSP_SETFMT");
159 return(0); } }
160 else if (fmt == AFMT_S8) {
161 *sndcnv = sndcnv2unsigned;
162 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
163 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 ||
164 fmt != the_fmt) {
165 perror("SNDCTRL_DSP_SETFMT");
166 return(0); } }
167
168 /* The PCSP driver does not support reading of the sampling rate via the
169 SOUND_PCM_READ_RATE ioctl; determine "the_speed" here */
170 the_speed = speed; ioctl(audio_fd,SNDCTL_DSP_SPEED,&the_speed);
171 /* The PCSP driver does not support reading of the mono/stereo flag, thus
172 we assume, that failure to change this mode means we are in mono mode */
173 if (((i = (the_stereo = tracks)-1),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0)
174 the_stereo = 1;
175
176 /* Try to request stereo playback (if needed); if this cannot be supported
177 by the hardware, then install conversion routines for mono playback */
178
179 /* This ioctl will fail if we use the PCSP driver; thus the value of
180 "the_stereo" is still unchanged */
181 ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo);
182 if (tracks != the_stereo) {
183 if (tracks == 2) {
184 tracks = 1;
185 *sndcnv = *sndcnv == sndcnv2byteLE ? sndcnv2monobyteLE :
186 *sndcnv == sndcnv2byteBE ? sndcnv2monobyteBE :
187 *sndcnv == sndcnv2unsigned ? sndcnv2monounsigned :
188 the_fmt == AFMT_S16_LE ? sndcnv16_2monoLE :
189 the_fmt == AFMT_S16_BE ? sndcnv16_2monoBE :
190 the_fmt == AFMT_S8 ? sndcnv8S_2mono :
191 the_fmt == AFMT_U8 ? sndcnv8U_2mono :
192 the_fmt == AFMT_MU_LAW ? sndcnvULaw_2mono : NULL;
193 if (*sndcnv == NULL) { /* this should not happen */
194 perror("SNDCTL_DSP_STEREO");
195 return(0); }
196 /* Switch to mono mode */
197 if (((i = 0),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0 || i) {
198 perror("SNDCTL_DSP_STEREO");
199 return(0); }
200 /* Now double check that everything is set as expected */
201 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
202 (i != the_fmt &&
203 (((i=the_fmt),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
204 i != the_fmt ||
205 ((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
206 i != the_fmt)) ||
207 (ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&i) >= 0 &&
208 i != 1)) {
209 /* There was no way that we could set the soundcard to a meaningful
210 mode */
211 perror("SNDCTL_DSP_SETFMT and SNDCTL_DSP_STEREO");
212 return(0); } }
213 else {
214 /* Somebody set the soundcard to stereo even though we requested
215 mono; this should not happen... */
216 if (((i = the_stereo = tracks),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i))<0 ||
217 i != the_stereo-1) {
218 perror("SNDCTL_DSP_STEREO");
219 return(0); }
220 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
221 i != the_fmt) {
222 perror("SNDCTL_DSP_SETFMT");
223 return(0); } } }
224
225 /* Fail if deviations from desired sampling frequency are too big */
226
227 /* This ioctl will fail if we use the PCSP driver; thus the value of
228 "the_speed" is still unchanged */
229 ioctl(audio_fd,SOUND_PCM_READ_RATE,&the_speed);
230 if (speed*14 < the_speed*10 || speed*6 > the_speed*10) {
231 char buffer[256];
232 sprintf(buffer,"SNDCTL_DSP_SPEED (req: %d, rtn: %d)",speed,the_speed);
233 perror(buffer);
234 return(0); }
235
236 /* Use the mixer device for setting the playback volume */
237 if (mixx_fd > 0) {
238 int vol = *volume & 0xFF;
239 if (ioctl(mixx_fd,SOUND_MIXER_READ_PCM,volume) < 0)
240 *volume = -1;
241 if (vol < 0) vol = 0; else if (vol > 100) vol = 100;
242 #ifdef NOVOLUMECTRLFORMULAW
243 if (fmt == AFMT_MU_LAW)
244 vol = 100;
245 #endif
246 vol |= 256*vol;
247 /* Do not signal an error, if volume control is unavailable! */
248 ioctl(mixx_fd,SOUND_MIXER_WRITE_PCM,&vol); }
249
250 #if defined(LINUXPLAYSTANDALONE) && 1
251 /* Debugging output is displayed only when compiled as stand-alone version */
252 {int the_volume;
253 the_fmt = AFMT_QUERY;
254 ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt);
255 ioctl(auddio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo);
256 ioctl(auddio_fd,SOUND_PCM_READ_RATE,&the_speed);
257 ioctl(mixx_fd,SOUND_MIXER_READ_PCM,&the_volume);
258 fprintf(stderr,"%s, %s, %dHz, L:%d/R:%d\n",
259 the_fmt == AFMT_MU_LAW ? "AFMT_MU_LAW" :
260 the_fmt == AFMT_A_LAW ? "AFMT_A_LAW" :
261 the_fmt == AFMT_IMA_ADPCM ? "AFMT_IMA_ADPCM" :
262 the_fmt == AFMT_U8 ? "AFMT_U8" :
263 the_fmt == AFMT_S16_LE ? "AFMT_S16_LE" :
264 the_fmt == AFMT_S16_BE ? "AFMT_S16_BE" :
265 the_fmt == AFMT_S8 ? "AFMT_S8" :
266 the_fmt == AFMT_U16_LE ? "AFMT_U16_LE" :
267 the_fmt == AFMT_U16_BE ? "AFMT_U16_BE" :
268 the_fmt == AFMT_MPEG ? "AFMT_MPEG" :
269 "AFMT_???",
270 the_stereo == 2 ? "stereo" : "mono",
271 the_speed,
272 the_volume / 256, the_volume % 256); }
273 #endif
274
275 return(1);
276 }
277
278 /* XEmacs requires code both for playback of pre-loaded data and for playback
279 from a soundfile; we use one function for both cases */
280 static void linux_play_data_or_file(int fd,unsigned char *data,
281 int length,int volume)
282 {
283 size_t (*parsesndfile)(void **dayta,size_t *sz,void **outbuf);
284 size_t (*sndcnv)(void **dayta,size_t *sz,void **);
285 fmtType ffmt;
286 int fmt,speed,tracks;
287 unsigned char *pptr,*optr,*cptr,*sptr;
288 int wrtn,rrtn,crtn,prtn;
289 unsigned char sndbuf[SNDBUFSZ];
290
291 /* We need to read at least the header information before we can start
292 doing anything */
293 if (!data || length < HEADERSZ) {
294 if (fd < 0) return;
295 else {
296 length = read(fd,sndbuf,SNDBUFSZ);
297 if (length < HEADERSZ)
298 return;
299 data = sndbuf;
300 length = SNDBUFSZ; }
301 }
302
303 ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile);
304
305 if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) {
306 warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)");
307 return; }
308
309 /* The VoxWare-SDK discourages opening /dev/audio; opening /dev/dsp and
310 properly initializing it via ioctl() is preferred */
311 if ((audio_fd=open(audio_dev, O_WRONLY | O_NONBLOCK, 0)) < 0) {
312 perror(audio_dev);
313 if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; }
314 return; }
315
316 /* The VoxWare-SDK discourages direct manipulation of the mixer device as
317 this could lead to problems, when multiple sound cards are installed */
318 mix_fd = audio_fd;
319
320 sighup_handler = signal(SIGHUP, sighandler);
321 sigint_handler = signal(SIGINT, sighandler);
322
323 if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv))
324 goto END_OF_PLAY;
325 audio_vol = volume;
326
327 reset_parsestate();
328
329 /* Mainloop: read a block of data, parse its contents, perform all
330 the necessary conversions and output it to the sound
331 device; repeat until all data has been processed */
332 rrtn = length;
333 do {
334 for (pptr = data; (prtn = parsesndfile((void **)&pptr,(size_t *)&rrtn,
335 (void **)&optr)) > 0; )
336 for (cptr = optr; (crtn = sndcnv((void **)&cptr,(size_t *) &prtn,
337 (void **)&sptr)) > 0; ) {
338 for (;;) {
339 if ((wrtn = write(audio_fd,sptr,crtn)) < 0) {
340 perror("write"); goto END_OF_PLAY; }
341 else if (wrtn) break;
342 else if (ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
343 perror("SNDCTL_DSP_SYNC"); goto END_OF_PLAY; } }
344 if (wrtn != crtn) {
345 char buf[255];
346 sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn);
347 warn(buf);
348 goto END_OF_PLAY; } }
349 if (fd >= 0) {
350 if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) {
351 perror("read"); goto END_OF_PLAY; } }
352 else
353 break;
354 } while (rrtn > 0);
355
356 if (ffmt == fmtWave)
357 parse_wave_complete();
358
359
360 END_OF_PLAY:
361 /* Now cleanup all used resources */
362
363 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
364 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
365
366 signal(SIGHUP,sighup_handler);
367 signal(SIGINT,sigint_handler);
368
369 if (mix_fd > 0) {
370 if (audio_vol >= 0) {
371 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
372 audio_vol = -1; }
373 if (mix_fd != audio_fd)
374 close(mix_fd);
375 mix_fd = -1; }
376
377 close(audio_fd);
378 audio_fd = -1;
379
380 return;
381 }
382
383 /* Call "linux_play_data_or_file" with the appropriate parameters for
384 playing a soundfile */
385 void play_sound_file (char *sound_file, int volume);
386 void play_sound_file (char *sound_file, int volume)
387 {
388 int fd;
389
390 if ((fd=open(sound_file,O_RDONLY,0)) < 0) {
391 perror(sound_file);
392 return; }
393 linux_play_data_or_file(fd,NULL,0,volume);
394 close(fd);
395 return;
396 }
397
398 /* Call "linux_play_data_or_file" with the appropriate parameters for
399 playing pre-loaded data */
400 void play_sound_data (unsigned char *data, int length, int volume);
401 void play_sound_data (unsigned char *data, int length, int volume)
402 {
403 linux_play_data_or_file(-1,data,length,volume);
404 return;
405 }