XEmacs 21.2.22 "Mercedes".
[chise/xemacs-chise.git.1] / 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
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 }