(U-00024532): Use `->denotational' and `->subsumptive'.
[chise/xemacs-chise.git-] / src / linuxplay.c
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 #include "nativesound.h"
62
63 #include <errno.h>
64 #include <fcntl.h>
65 #include SOUNDCARD_H_FILE /* Path computed by configure */
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <fcntl.h>
70 #include <sys/file.h>
71 #include <sys/ioctl.h>
72 #include <sys/signal.h>
73 #include <unistd.h>
74
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);
78 #else
79 #include "lisp.h"
80 #include "syssignal.h"
81 #include "sysfile.h"
82 #define perror(str) message("audio: %s, %s ",str,strerror(errno))
83 #define warn(str)   message("audio: %s ",GETTEXT(str))
84 #endif
85
86 static  SIGTYPE (*sighup_handler) (int);
87 static  SIGTYPE (*sigint_handler) (int);
88
89 static int           mix_fd;
90 static int           audio_vol;
91 static int           audio_fd;
92 static char          *audio_dev = "/dev/dsp";
93
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" */
97 static SIGTYPE
98 sighandler (int sig)
99 {
100   if (mix_fd > 0) {
101     if (audio_vol >= 0) {
102       ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
103       audio_vol = -1; }
104     if (mix_fd != audio_fd)
105       close(mix_fd);
106     mix_fd = -1; }
107   if (audio_fd > 0) {
108     ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
109     ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
110     close(audio_fd);
111     audio_fd = -1; }
112   if (sig == SIGHUP && sighup_handler)      sighup_handler(sig);
113   else if (sig == SIGINT && sigint_handler) sigint_handler(sig);
114   else exit(1);
115 }
116
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
120    routines.
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 **))
129 {
130   int i,the_speed,the_stereo,the_fmt;
131
132   *sndcnv = sndcnvnop;
133
134   if (ioctl(auddio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
135     perror("SNDCTL_DSP_SYNC");
136     return(0); }
137
138   /* Initialize sound hardware with preferred parameters */
139
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 */
142
143   the_fmt = fmt;
144   if(ioctl(audio_fd,SNDCTL_DSP_SETFMT,&the_fmt) < 0) {
145         perror("SNDCTL_DSP_SETFMT");
146         return(0);
147   }
148
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 ||
154           fmt != the_fmt) {
155         perror("SNDCTL_DSP_SETFMT");
156         return(0); } }
157     else if (fmt == AFMT_MU_LAW && the_fmt == AFMT_U8 ) {
158       /* the kernel will convert for us */ }
159     else {
160       perror("SNDCTL_DSP_SETFMT");
161       return(0); } }
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 ||
166         fmt != the_fmt) {
167       perror("SNDCTRL_DSP_SETFMT");
168       return(0); } }
169
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)
176     the_stereo = 1;
177
178   /* Try to request stereo playback (if needed); if this cannot be supported
179      by the hardware, then install conversion routines for mono playback */
180
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) {
185     if (tracks == 2) {
186       tracks = 1;
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");
197         return(0); }
198       /* Switch to mono mode */
199       if (((i = 0),ioctl(audio_fd,SNDCTL_DSP_STEREO,&i)) < 0 || i) {
200         perror("SNDCTL_DSP_STEREO");
201         return(0); }
202       /* Now double check that everything is set as expected */
203       if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
204           (i != the_fmt &&
205            (((i=the_fmt),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
206             i != the_fmt ||
207             ((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
208             i != the_fmt)) ||
209           (ioctl(audio_fd,SOUND_PCM_READ_CHANNELS,&i) >= 0 &&
210            i != 1)) {
211         /* There was no way that we could set the soundcard to a meaningful
212            mode */
213         perror("SNDCTL_DSP_SETFMT and SNDCTL_DSP_STEREO");
214         return(0); } }
215     else {
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 ||
219           i != the_stereo-1) {
220         perror("SNDCTL_DSP_STEREO");
221         return(0); }
222       if (((i = AFMT_QUERY),ioctl(audio_fd,SNDCTL_DSP_SETFMT,&i)) < 0 ||
223           i != the_fmt) {
224         perror("SNDCTL_DSP_SETFMT");
225         return(0); } } }
226
227   /* Fail if deviations from desired sampling frequency are too big */
228
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) {
233     char buffer[256];
234     sprintf(buffer,"SNDCTL_DSP_SPEED (req: %d, rtn: %d)",speed,the_speed);
235     perror(buffer);
236     return(0); }
237
238   /* Use the mixer device for setting the playback volume */
239   if (mixx_fd > 0) {
240     int vol = *volume & 0xFF;
241     if (ioctl(mixx_fd,SOUND_MIXER_READ_PCM,volume) < 0)
242       *volume = -1;
243     if (vol < 0) vol = 0; else if (vol > 100) vol = 100;
244 #ifdef NOVOLUMECTRLFORMULAW
245     if (fmt == AFMT_MU_LAW)
246       vol = 100;
247 #endif
248     vol |= 256*vol;
249     /* Do not signal an error, if volume control is unavailable! */
250     ioctl(mixx_fd,SOUND_MIXER_WRITE_PCM,&vol); }
251
252 #if defined(LINUXPLAYSTANDALONE) && 1
253   /* Debugging output is displayed only when compiled as stand-alone version */
254   {int the_volume;
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" :
271           "AFMT_???",
272           the_stereo == 2 ? "stereo" : "mono",
273           the_speed,
274           the_volume / 256, the_volume % 256); }
275 #endif
276
277   return(1);
278 }
279
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.
282
283    Returns 1 on succes. 0 otherwise.
284 */
285 static int linux_play_data_or_file(int fd,unsigned char *data,
286                                     int length,int volume)
287 {
288   size_t         (*parsesndfile)(void **dayta,size_t *sz,void **outbuf);
289   size_t         (*sndcnv)(void **dayta,size_t *sz,void **);
290   fmtType        ffmt;
291   int            fmt,speed,tracks;
292   unsigned char *pptr,*optr,*cptr,*sptr;
293   int            wrtn,rrtn,crtn,prtn;
294   unsigned char         sndbuf[SNDBUFSZ];
295
296   /* We need to read at least the header information before we can start
297      doing anything */
298   if (!data || length < HEADERSZ) {
299     if (fd < 0) return 0;
300     else {
301       length = read(fd,sndbuf,SNDBUFSZ);
302       if (length < HEADERSZ)
303         return 0;
304       data   = sndbuf;
305       length = SNDBUFSZ; }
306   }
307
308   ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile);
309
310   if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) {
311     warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)");
312       return 0; }
313
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
318        Fplay_sound 
319        perror(audio_dev); */
320     if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; }
321     return 0; }
322
323   /* The VoxWare-SDK discourages direct manipulation of the mixer device as
324      this could lead to problems, when multiple sound cards are installed */
325   mix_fd = audio_fd;
326
327   sighup_handler = signal(SIGHUP, sighandler);
328   sigint_handler = signal(SIGINT, sighandler);
329
330   if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv))
331     goto END_OF_PLAY;
332   audio_vol = volume;
333
334   reset_parsestate();
335
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 */
339   rrtn = length;
340   do {
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; ) {
345         for (;;) {
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; } }
351         if (wrtn != crtn) {
352           char buf[255];
353           sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn);
354           warn(buf);
355           goto END_OF_PLAY; } }
356     if (fd >= 0) {
357       if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) {
358         perror("read"); goto END_OF_PLAY; } }
359     else
360       break;
361   } while (rrtn > 0);
362
363   if (ffmt == fmtWave)
364     parse_wave_complete();
365
366
367 END_OF_PLAY:
368   /* Now cleanup all used resources */
369
370   ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
371   ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
372
373   signal(SIGHUP,sighup_handler);
374   signal(SIGINT,sigint_handler);
375
376   if (mix_fd > 0) {
377     if (audio_vol >= 0) {
378       ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
379       audio_vol = -1; }
380     if (mix_fd != audio_fd)
381       close(mix_fd);
382     mix_fd = -1; }
383
384   close(audio_fd);
385   audio_fd = -1;
386
387   return 1;
388 }
389
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)
393 {
394   int fd;
395
396   if ((fd=open(sound_file,O_RDONLY,0)) < 0) {
397     perror(sound_file);
398     return; }
399   linux_play_data_or_file(fd,NULL,0,volume);
400   close(fd);
401   return;
402 }
403
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)
407 {
408   return linux_play_data_or_file(-1,data,length,volume);
409 }