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
61 #include "nativesound.h"
65 #include SOUNDCARD_H_FILE /* Path computed by configure */
71 #include <sys/ioctl.h>
72 #include <sys/signal.h>
75 #ifdef LINUXPLAYSTANDALONE
76 #define perror(str) fprintf(stderr,"audio: %s %s\n",str,strerror(errno));
77 #define warn(str) fprintf(stderr,"audio: %s\n",str);
80 #include "syssignal.h"
82 #define perror(str) message("audio: %s, %s ",str,strerror(errno))
83 #define warn(str) message("audio: %s ",GETTEXT(str))
86 static SIGTYPE (*sighup_handler) (int);
87 static SIGTYPE (*sigint_handler) (int);
92 static char *audio_dev = "/dev/dsp";
94 /* Intercept SIGINT and SIGHUP in order to close the audio and mixer
95 devices before terminating sound output; this requires reliable
96 signals as provided by "syssignal.h" */
101 if (audio_vol >= 0) {
102 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
104 if (mix_fd != audio_fd)
108 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
109 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
112 if (sig == SIGHUP && sighup_handler) sighup_handler(sig);
113 else if (sig == SIGINT && sigint_handler) sigint_handler(sig);
117 /* Initialize the soundcard and mixer device with the parameters that we
118 found in the header of the sound file. If the soundcard is not capable of
119 natively supporting the required parameters, then try to set up conversion
121 The difficulty with setting up the sound card is that the parameters are
122 not fully orthogonal; changing one of them might affect some of the
123 others, too. Thus we do quite a lot of double checking; actually most of
124 this is not needed right now, but it will come in handy, if the kernel's
125 sounddriver ever changes or if third-party sounddrivers are used. */
126 static int audio_init(int mixx_fd, int auddio_fd, int fmt, int speed,
127 int tracks, int *volume,
128 size_t (**sndcnv) (void **, size_t *sz, void **))
130 int i,the_speed,the_stereo,the_fmt;
134 if (ioctl(auddio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
135 perror("SNDCTL_DSP_SYNC");
138 /* Initialize sound hardware with preferred parameters */
140 /* If the sound hardware cannot support 16 bit format or requires a
141 different byte sex then try to drop to 8 bit format */
144 if(ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0) {
145 perror("SNDCTL_DSP_SETFMT");
149 if (fmt != the_fmt) {
150 if (fmt == AFMT_S16_LE || fmt == AFMT_S16_BE) {
151 *sndcnv = fmt == AFMT_S16_BE ? sndcnv2byteBE : sndcnv2byteLE;
152 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
153 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 ||
155 perror("SNDCTL_DSP_SETFMT");
157 else if (fmt == AFMT_MU_LAW && the_fmt == AFMT_U8 ) {
158 /* the kernel will convert for us */ }
160 perror("SNDCTL_DSP_SETFMT");
162 else if (fmt == AFMT_S8) {
163 *sndcnv = sndcnv2unsigned;
164 if (((i=fmt=AFMT_U8),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
165 fmt != i || ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0 ||
167 perror("SNDCTRL_DSP_SETFMT");
170 /* The PCSP driver does not support reading of the sampling rate via the
171 SOUND_PCM_READ_RATE ioctl; determine "the_speed" here */
172 the_speed = speed; ioctl(audio_fd,SNDCTL_DSP_SPEED,&the_speed);
173 /* The PCSP driver does not support reading of the mono/stereo flag, thus
174 we assume, that failure to change this mode means we are in mono mode */
175 if (((i = (the_stereo = tracks)-1),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0)
178 /* Try to request stereo playback (if needed); if this cannot be supported
179 by the hardware, then install conversion routines for mono playback */
181 /* This ioctl will fail if we use the PCSP driver; thus the value of
182 "the_stereo" is still unchanged */
183 ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo);
184 if (tracks != the_stereo) {
187 *sndcnv = *sndcnv == sndcnv2byteLE ? sndcnv2monobyteLE :
188 *sndcnv == sndcnv2byteBE ? sndcnv2monobyteBE :
189 *sndcnv == sndcnv2unsigned ? sndcnv2monounsigned :
190 the_fmt == AFMT_S16_LE ? sndcnv16_2monoLE :
191 the_fmt == AFMT_S16_BE ? sndcnv16_2monoBE :
192 the_fmt == AFMT_S8 ? sndcnv8S_2mono :
193 the_fmt == AFMT_U8 ? sndcnv8U_2mono :
194 the_fmt == AFMT_MU_LAW ? sndcnvULaw_2mono : NULL;
195 if (*sndcnv == NULL) { /* this should not happen */
196 perror("SNDCTL_DSP_STEREO");
198 /* Switch to mono mode */
199 if (((i = 0),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0 || i) {
200 perror("SNDCTL_DSP_STEREO");
202 /* Now double check that everything is set as expected */
203 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
205 (((i=the_fmt),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
207 ((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
209 (ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&i) >= 0 &&
211 /* There was no way that we could set the soundcard to a meaningful
213 perror("SNDCTL_DSP_SETFMT and SNDCTL_DSP_STEREO");
216 /* Somebody set the soundcard to stereo even though we requested
217 mono; this should not happen... */
218 if (((i = the_stereo = tracks),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i))<0 ||
220 perror("SNDCTL_DSP_STEREO");
222 if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
224 perror("SNDCTL_DSP_SETFMT");
227 /* Fail if deviations from desired sampling frequency are too big */
229 /* This ioctl will fail if we use the PCSP driver; thus the value of
230 "the_speed" is still unchanged */
231 ioctl(audio_fd,SOUND_PCM_READ_RATE,&the_speed);
232 if (speed*14 < the_speed*10 || speed*6 > the_speed*10) {
234 sprintf(buffer,"SNDCTL_DSP_SPEED (req: %d, rtn: %d)",speed,the_speed);
238 /* Use the mixer device for setting the playback volume */
240 int vol = *volume & 0xFF;
241 if (ioctl(mixx_fd,SOUND_MIXER_READ_PCM,volume) < 0)
243 if (vol < 0) vol = 0; else if (vol > 100) vol = 100;
244 #ifdef NOVOLUMECTRLFORMULAW
245 if (fmt == AFMT_MU_LAW)
249 /* Do not signal an error, if volume control is unavailable! */
250 ioctl(mixx_fd,SOUND_MIXER_WRITE_PCM,&vol); }
252 #if defined(LINUXPLAYSTANDALONE) && 1
253 /* Debugging output is displayed only when compiled as stand-alone version */
255 the_fmt = AFMT_QUERY;
256 ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt);
257 ioctl(auddio_fd,SOUND_PCM_READ_CHANNELS,&the_stereo);
258 ioctl(auddio_fd,SOUND_PCM_READ_RATE,&the_speed);
259 ioctl(mixx_fd,SOUND_MIXER_READ_PCM,&the_volume);
260 fprintf(stderr,"%s, %s, %dHz, L:%d/R:%d\n",
261 the_fmt == AFMT_MU_LAW ? "AFMT_MU_LAW" :
262 the_fmt == AFMT_A_LAW ? "AFMT_A_LAW" :
263 the_fmt == AFMT_IMA_ADPCM ? "AFMT_IMA_ADPCM" :
264 the_fmt == AFMT_U8 ? "AFMT_U8" :
265 the_fmt == AFMT_S16_LE ? "AFMT_S16_LE" :
266 the_fmt == AFMT_S16_BE ? "AFMT_S16_BE" :
267 the_fmt == AFMT_S8 ? "AFMT_S8" :
268 the_fmt == AFMT_U16_LE ? "AFMT_U16_LE" :
269 the_fmt == AFMT_U16_BE ? "AFMT_U16_BE" :
270 the_fmt == AFMT_MPEG ? "AFMT_MPEG" :
272 the_stereo == 2 ? "stereo" : "mono",
274 the_volume / 256, the_volume % 256); }
280 /* XEmacs requires code both for playback of pre-loaded data and for playback
281 from a soundfile; we use one function for both cases.
283 Returns 1 on succes. 0 otherwise.
285 static int linux_play_data_or_file(int fd,unsigned char *data,
286 int length,int volume)
288 size_t (*parsesndfile)(void **dayta,size_t *sz,void **outbuf);
289 size_t (*sndcnv)(void **dayta,size_t *sz,void **);
291 int fmt,speed,tracks;
292 unsigned char *pptr,*optr,*cptr,*sptr;
293 int wrtn,rrtn,crtn,prtn;
294 unsigned char sndbuf[SNDBUFSZ];
296 /* We need to read at least the header information before we can start
298 if (!data || length < HEADERSZ) {
299 if (fd < 0) return 0;
301 length = read(fd,sndbuf,SNDBUFSZ);
302 if (length < HEADERSZ)
308 ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile);
310 if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) {
311 warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)");
314 /* The VoxWare-SDK discourages opening /dev/audio; opening /dev/dsp and
315 properly initializing it via ioctl() is preferred */
316 if ((audio_fd=open(audio_dev, O_WRONLY | O_NONBLOCK, 0)) < 0) {
317 /* JV. Much too verbose. In addition this can crash. See NOTE: in
319 perror(audio_dev); */
320 if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; }
323 /* The VoxWare-SDK discourages direct manipulation of the mixer device as
324 this could lead to problems, when multiple sound cards are installed */
327 sighup_handler = signal(SIGHUP, sighandler);
328 sigint_handler = signal(SIGINT, sighandler);
330 if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv))
336 /* Mainloop: read a block of data, parse its contents, perform all
337 the necessary conversions and output it to the sound
338 device; repeat until all data has been processed */
341 for (pptr = data; (prtn = parsesndfile((void **)&pptr,(size_t *)&rrtn,
342 (void **)&optr)) > 0; )
343 for (cptr = optr; (crtn = sndcnv((void **)&cptr,(size_t *) &prtn,
344 (void **)&sptr)) > 0; ) {
346 if ((wrtn = write(audio_fd,sptr,crtn)) < 0) {
347 perror("write"); goto END_OF_PLAY; }
348 else if (wrtn) break;
349 else if (ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
350 perror("SNDCTL_DSP_SYNC"); goto END_OF_PLAY; } }
353 sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn);
355 goto END_OF_PLAY; } }
357 if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) {
358 perror("read"); goto END_OF_PLAY; } }
364 parse_wave_complete();
368 /* Now cleanup all used resources */
370 ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
371 ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
373 signal(SIGHUP,sighup_handler);
374 signal(SIGINT,sigint_handler);
377 if (audio_vol >= 0) {
378 ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
380 if (mix_fd != audio_fd)
390 /* Call "linux_play_data_or_file" with the appropriate parameters for
391 playing a soundfile */
392 void play_sound_file (char *sound_file, int volume)
396 if ((fd=open(sound_file,O_RDONLY,0)) < 0) {
399 linux_play_data_or_file(fd,NULL,0,volume);
404 /* Call "linux_play_data_or_file" with the appropriate parameters for
405 playing pre-loaded data */
406 int play_sound_data (unsigned char *data, int length, int volume)
408 return linux_play_data_or_file(-1,data,length,volume);