(JSP-5368): Separate JX1-7A24.
[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 #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, crtn;
294   size_t         prtn, rrtn;
295   unsigned char  sndbuf[SNDBUFSZ];
296
297   /* We need to read at least the header information before we can start
298      doing anything */
299   if (!data || length < HEADERSZ) {
300     if (fd < 0) return 0;
301     else {
302       length = read(fd,sndbuf,SNDBUFSZ);
303       if (length < HEADERSZ)
304         return 0;
305       data   = sndbuf;
306       length = SNDBUFSZ; }
307   }
308
309   ffmt = analyze_format(data,&fmt,&speed,&tracks,&parsesndfile);
310
311   if (ffmt != fmtRaw && ffmt != fmtSunAudio && ffmt != fmtWave) {
312     warn("Unsupported file format (neither RAW, nor Sun/DECAudio, nor WAVE)");
313       return 0; }
314
315   /* The VoxWare-SDK discourages opening /dev/audio; opening /dev/dsp and
316      properly initializing it via ioctl() is preferred */
317   if ((audio_fd=open(audio_dev, O_WRONLY | O_NONBLOCK, 0)) < 0) {
318     /* JV. Much too verbose. In addition this can crash. See NOTE: in
319        Fplay_sound 
320        perror(audio_dev); */
321     if (mix_fd > 0 && mix_fd != audio_fd) { close(mix_fd); mix_fd = -1; }
322     return 0; }
323
324   /* The VoxWare-SDK discourages direct manipulation of the mixer device as
325      this could lead to problems, when multiple sound cards are installed */
326   mix_fd = audio_fd;
327
328   sighup_handler = signal(SIGHUP, sighandler);
329   sigint_handler = signal(SIGINT, sighandler);
330
331   if (!audio_init(mix_fd,audio_fd,fmt,speed,tracks,&volume,&sndcnv))
332     goto END_OF_PLAY;
333   audio_vol = volume;
334
335   reset_parsestate();
336
337   /* Mainloop: read a block of data, parse its contents, perform all
338                the necessary conversions and output it to the sound
339                device; repeat until all data has been processed */
340   rrtn = length;
341   do {
342     for (pptr = data; (prtn = parsesndfile((void **)&pptr, &rrtn,
343                                            (void **)&optr)) > 0; )
344       for (cptr = optr; (crtn = sndcnv((void **)&cptr, &prtn,
345                                        (void **)&sptr)) > 0; ) {
346         for (;;) {
347           if ((wrtn = write(audio_fd,sptr,crtn)) < 0) {
348             perror("write"); goto END_OF_PLAY; }
349           else if (wrtn) break;
350           else if (ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL) < 0) {
351             perror("SNDCTL_DSP_SYNC"); goto END_OF_PLAY; } }
352         if (wrtn != crtn) {
353           char buf[255];
354           sprintf(buf,"play: crtn = %d, wrtn = %d",crtn,wrtn);
355           warn(buf);
356           goto END_OF_PLAY; } }
357     if (fd >= 0) {
358       if ((rrtn = read(fd,sndbuf,SNDBUFSZ)) < 0) {
359         perror("read"); goto END_OF_PLAY; } }
360     else
361       break;
362   } while (rrtn > 0);
363
364   if (ffmt == fmtWave)
365     parse_wave_complete();
366
367
368 END_OF_PLAY:
369   /* Now cleanup all used resources */
370
371   ioctl(audio_fd,SNDCTL_DSP_SYNC,NULL);
372   ioctl(audio_fd,SNDCTL_DSP_RESET,NULL);
373
374   signal(SIGHUP,sighup_handler);
375   signal(SIGINT,sigint_handler);
376
377   if (mix_fd > 0) {
378     if (audio_vol >= 0) {
379       ioctl(mix_fd,SOUND_MIXER_WRITE_PCM,&audio_vol);
380       audio_vol = -1; }
381     if (mix_fd != audio_fd)
382       close(mix_fd);
383     mix_fd = -1; }
384
385   close(audio_fd);
386   audio_fd = -1;
387
388   return 1;
389 }
390
391 /* Call "linux_play_data_or_file" with the appropriate parameters for
392    playing a soundfile */
393 void play_sound_file (char *sound_file, int volume)
394 {
395   int fd;
396
397   if ((fd=open(sound_file,O_RDONLY,0)) < 0) {
398     perror(sound_file);
399     return; }
400   linux_play_data_or_file(fd,NULL,0,volume);
401   close(fd);
402   return;
403 }
404
405 /* Call "linux_play_data_or_file" with the appropriate parameters for
406    playing pre-loaded data */
407 int play_sound_data (unsigned char *data, int length, int volume)
408 {
409   return linux_play_data_or_file(-1,data,length,volume);
410 }