1 /* linuxplay.c - play a sound file on the speaker
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>.
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
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
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.
47 /* Synched up with: Not in FSF. */
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
64 #include SOUNDCARD_H_PATH /* Path computed by configure */
70 #include <sys/ioctl.h>
71 #include <sys/signal.h>
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);
79 #include "syssignal.h"
81 #define perror(str) message("audio: %s, %s ",str,strerror(errno))
82 #define warn(str) message("audio: %s ",GETTEXT(str))
85 static void (*sighup_handler)(int);
86 static void (*sigint_handler)(int);
91 static char *audio_dev = "/dev/dsp";
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)
100 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
102 if (mix_fd != audio_fd)
106 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
107 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
110 if (sig == SIGHUP && sighup_handler) sighup_handler(sig);
111 else if (sig == SIGINT && sigint_handler) sigint_handler(sig);
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
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 **))
128 int i,the_speed,the_stereo,the_fmt;
132 if (ioctl(auddio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
133 perror("SNDCTL_DSP_SYNC");
136 /* Initialize sound hardware with preferred parameters */
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 */
142 if(ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0) {
143 perror("SNDCTL_DSP_SETFMT");
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 ||
153 perror("SNDCTL_DSP_SETFMT");
155 else if (fmt == AFMT_MU_LAW && the_fmt == AFMT_U8 ) {
156 /* the kernel will convert for us */ }
158 perror("SNDCTL_DSP_SETFMT");
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 ||
165 perror("SNDCTRL_DSP_SETFMT");
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)
176 /* Try to request stereo playback (if needed); if this cannot be supported
177 by the hardware, then install conversion routines for mono playback */
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) {
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");
196 /* Switch to mono mode */
197 if (((i = 0),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0 || i) {
198 perror("SNDCTL_DSP_STEREO");
200 /* Now double check that everything is set as expected */
201 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
203 (((i=the_fmt),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
205 ((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
207 (ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&i) >= 0 &&
209 /* There was no way that we could set the soundcard to a meaningful
211 perror("SNDCTL_DSP_SETFMT and SNDCTL_DSP_STEREO");
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 ||
218 perror("SNDCTL_DSP_STEREO");
220 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
222 perror("SNDCTL_DSP_SETFMT");
225 /* Fail if deviations from desired sampling frequency are too big */
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) {
232 sprintf(buffer,"SNDCTL_DSP_SPEED (req: %d, rtn: %d)",speed,the_speed);
236 /* Use the mixer device for setting the playback volume */
238 int vol = *volume & 0xFF;
239 if (ioctl(mixx_fd,SOUND_MIXER_READ_PCM,volume) < 0)
241 if (vol < 0) vol = 0; else if (vol > 100) vol = 100;
242 #ifdef NOVOLUMECTRLFORMULAW
243 if (fmt == AFMT_MU_LAW)
247 /* Do not signal an error, if volume control is unavailable! */
248 ioctl(mixx_fd,SOUND_MIXER_WRITE_PCM,&vol); }
250 #if defined(LINUXPLAYSTANDALONE) && 1
251 /* Debugging output is displayed only when compiled as stand-alone version */
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" :
270 the_stereo == 2 ? "stereo" : "mono",
272 the_volume / 256, the_volume % 256); }
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)
283 size_t (*parsesndfile)(void **dayta,size_t *sz,void **outbuf);
284 size_t (*sndcnv)(void **dayta,size_t *sz,void **);
286 int fmt,speed,tracks;
287 unsigned char *pptr,*optr,*cptr,*sptr;
288 int wrtn,rrtn,crtn,prtn;
289 unsigned char sndbuf[SNDBUFSZ];
291 /* We need to read at least the header information before we can start
293 if (!data || length < HEADERSZ) {
296 length = read(fd,sndbuf,SNDBUFSZ);
297 if (length < HEADERSZ)
303 ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile);
305 if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) {
306 warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)");
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) {
313 if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; }
316 /* The VoxWare-SDK discourages direct manipulation of the mixer device as
317 this could lead to problems, when multiple sound cards are installed */
320 sighup_handler = signal(SIGHUP, sighandler);
321 sigint_handler = signal(SIGINT, sighandler);
323 if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv))
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 */
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; ) {
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; } }
346 sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn);
348 goto END_OF_PLAY; } }
350 if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) {
351 perror("read"); goto END_OF_PLAY; } }
357 parse_wave_complete();
361 /* Now cleanup all used resources */
363 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
364 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
366 signal(SIGHUP,sighup_handler);
367 signal(SIGINT,sigint_handler);
370 if (audio_vol >= 0) {
371 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
373 if (mix_fd != audio_fd)
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)
390 if ((fd=open(sound_file,O_RDONLY,0)) < 0) {
393 linux_play_data_or_file(fd,NULL,0,volume);
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)
403 linux_play_data_or_file(-1,data,length,volume);