import -ko -b 1.1.3 XEmacs XEmacs-21_2 r21-2-35
[chise/xemacs-chise.git.1] / src / sound.c
1 /* Sound functions.
2    Copyright (C) 1992, 1993, 1994 Lucid Inc.
3    Copyright (C) 1994, 1995 Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* Originally written by Jamie Zawinski.
25    Hacked on quite a bit by various others. */
26
27 #include <config.h>
28 #include <time.h>
29 #include "lisp.h"
30
31 #include "buffer.h"
32 #ifdef HAVE_X_WINDOWS
33 #include "console-x.h"
34 #endif
35
36 #include "device.h"
37 #include "redisplay.h"
38 #include "sysdep.h"
39
40 #include "sysfile.h"
41
42 #ifdef HAVE_NATIVE_SOUND
43 # include "sysproc.h"
44 # include "nativesound.h"
45 #endif
46
47 #ifdef HAVE_ESD_SOUND
48 extern int esd_play_sound_file (char *file, int vol);
49 extern int esd_play_sound_data (unsigned char *data, size_t length, int vol);
50 # define DEVICE_CONNECTED_TO_ESD_P(x) 1 /* FIXME: better check */
51 #endif
52
53 int bell_volume;
54 int bell_inhibit_time;
55 Lisp_Object Vsound_alist;
56 Lisp_Object Vsynchronous_sounds;
57 Lisp_Object Vnative_sound_only_on_console;
58 Lisp_Object Q_volume, Q_pitch, Q_duration, Q_sound;
59
60
61 #ifdef HAVE_NAS_SOUND
62 extern int nas_play_sound_file (char *name, int volume);
63 extern int nas_play_sound_data (unsigned char *data, int length, int volume);
64 extern int nas_wait_for_sounds (void);
65 extern char *nas_init_play (Display *);
66
67 Lisp_Object Qnas;
68 #endif
69
70 DEFUN ("play-sound-file", Fplay_sound_file, 1, 3, "fSound file name: ", /*
71 Play the named sound file on DEVICE's speaker at the specified volume
72 \(0-100, default specified by the `bell-volume' variable).
73 On Unix machines the sound file must be in the Sun/NeXT U-LAW format
74 except under Linux where WAV files are also supported.  On Microsoft
75 Windows the sound file must be in WAV format.
76   DEVICE defaults to the selected device.
77 */
78      (file, volume, device))
79 {
80   /* This function can call lisp */
81   int vol;
82 #if defined (HAVE_NATIVE_SOUND) || defined (HAVE_NAS_SOUND) \
83        || defined (HAVE_ESD_SOUND)
84   struct device *d = decode_device (device);
85 #endif
86   struct gcpro gcpro1;
87
88   CHECK_STRING (file);
89   if (NILP (volume))
90     vol = bell_volume;
91   else
92     {
93       CHECK_INT (volume);
94       vol = XINT (volume);
95     }
96
97   GCPRO1 (file);
98   while (1)
99     {
100       file = Fexpand_file_name (file, Qnil);
101       if (!NILP(Ffile_readable_p (file)))
102         break;
103       else
104         {
105           /* #### This is crockish.  It might be a better idea to try
106              to open the file, and use report_file_error() if it
107              fails.  --hniksic */
108           if (NILP (Ffile_exists_p (file)))
109             file =
110               signal_simple_continuable_error ("File does not exist", file);
111           else
112             file =
113               signal_simple_continuable_error ("File is unreadable", file);
114         }
115     }
116   UNGCPRO;
117
118 #ifdef HAVE_NAS_SOUND
119   if (DEVICE_CONNECTED_TO_NAS_P (d))
120     {
121       char *fileext;
122
123       TO_EXTERNAL_FORMAT (LISP_STRING, file,
124                           C_STRING_ALLOCA, fileext,
125                           Qfile_name);
126       /* #### NAS code should allow specification of a device. */
127       if (nas_play_sound_file (fileext, vol))
128         return Qnil;
129     }
130 #endif /* HAVE_NAS_SOUND */
131
132 #ifdef HAVE_ESD_SOUND
133   if (DEVICE_CONNECTED_TO_ESD_P (d))
134     {
135       char *fileext;
136       int result;
137
138       TO_EXTERNAL_FORMAT (LISP_STRING, file,
139                           C_STRING_ALLOCA, fileext,
140                           Qfile_name);
141
142       /* #### ESD uses alarm(). But why should we also stop SIGIO? */
143       stop_interrupts ();
144       result = esd_play_sound_file (fileext, vol);
145       start_interrupts ();
146       if (result)
147        return Qnil;
148     }
149 #endif /* HAVE_ESD_SOUND */
150
151 #ifdef HAVE_NATIVE_SOUND
152   if (NILP (Vnative_sound_only_on_console) || DEVICE_ON_CONSOLE_P (d))
153     {
154       const char *fileext;
155
156       TO_EXTERNAL_FORMAT (LISP_STRING, file,
157                           C_STRING_ALLOCA, fileext,
158                           Qfile_name);
159       /* The sound code doesn't like getting SIGIO interrupts.
160          Unix sucks! */
161       stop_interrupts ();
162       play_sound_file ((char *) fileext, vol);
163       start_interrupts ();
164       QUIT;
165     }
166 #endif /* HAVE_NATIVE_SOUND */
167
168   return Qnil;
169 }
170
171 static void
172 parse_sound_alist_elt (Lisp_Object elt,
173                        Lisp_Object *volume,
174                        Lisp_Object *pitch,
175                        Lisp_Object *duration,
176                        Lisp_Object *sound)
177 {
178   *volume = Qnil;
179   *pitch = Qnil;
180   *duration = Qnil;
181   *sound = Qnil;
182   if (! CONSP (elt))
183     return;
184
185   /* The things we do for backward compatibility...
186      I wish I had just forced this to be a plist to begin with.
187    */
188
189   if (SYMBOLP (elt) || STRINGP (elt))           /* ( name . <sound> ) */
190     {
191       *sound = elt;
192     }
193   else if (!CONSP (elt))
194     {
195       return;
196     }
197   else if (NILP (XCDR (elt)) &&         /* ( name <sound> ) */
198            (SYMBOLP (XCAR (elt)) ||
199             STRINGP (XCAR (elt))))
200     {
201       *sound = XCAR (elt);
202     }
203   else if (INT_OR_FLOATP (XCAR (elt)) &&        /* ( name <vol> . <sound> ) */
204            (SYMBOLP (XCDR (elt)) ||
205             STRINGP (XCDR (elt))))
206     {
207       *volume = XCAR (elt);
208       *sound = XCDR (elt);
209     }
210   else if (INT_OR_FLOATP (XCAR (elt)) &&        /* ( name <vol> <sound> ) */
211            CONSP (XCDR (elt)) &&
212            NILP (XCDR (XCDR (elt))) &&
213            (SYMBOLP (XCAR (XCDR (elt))) ||
214             STRINGP (XCAR (XCDR (elt)))))
215     {
216       *volume = XCAR (elt);
217       *sound = XCAR (XCDR (elt));
218     }
219   else if ((SYMBOLP (XCAR (elt)) ||     /* ( name <sound> . <vol> ) */
220             STRINGP (XCAR (elt))) &&
221            INT_OR_FLOATP (XCDR (elt)))
222     {
223       *sound = XCAR (elt);
224       *volume = XCDR (elt);
225     }
226 #if 0 /* this one is ambiguous with the plist form */
227   else if ((SYMBOLP (XCAR (elt)) ||     /* ( name <sound> <vol> ) */
228             STRINGP (XCAR (elt))) &&
229            CONSP (XCDR (elt)) &&
230            NILP (XCDR (XCDR (elt))) &&
231            INT_OR_FLOATP (XCAR (XCDR (elt))))
232     {
233       *sound = XCAR (elt);
234       *volume = XCAR (XCDR (elt));
235     }
236 #endif /* 0 */
237   else                                  /* ( name [ keyword <value> ]* ) */
238     {
239       while (CONSP (elt))
240         {
241           Lisp_Object key, val;
242           key = XCAR (elt);
243           val = XCDR (elt);
244           if (!CONSP (val))
245             return;
246           elt = XCDR (val);
247           val = XCAR (val);
248           if (EQ (key, Q_volume))
249             {
250               if (INT_OR_FLOATP (val)) *volume = val;
251             }
252           else if (EQ (key, Q_pitch))
253             {
254               if (INT_OR_FLOATP (val)) *pitch = val;
255               if (NILP (*sound)) *sound = Qt;
256             }
257           else if (EQ (key, Q_duration))
258             {
259               if (INT_OR_FLOATP (val)) *duration = val;
260               if (NILP (*sound)) *sound = Qt;
261             }
262           else if (EQ (key, Q_sound))
263             {
264               if (SYMBOLP (val) || STRINGP (val)) *sound = val;
265             }
266         }
267     }
268 }
269
270 DEFUN ("play-sound", Fplay_sound, 1, 3, 0, /*
271 Play a sound of the provided type.
272 See the variable `sound-alist'.
273 */
274        (sound, volume, device))
275 {
276   int looking_for_default = 0;
277   /* variable `sound' is anything that can be a cdr in sound-alist */
278   Lisp_Object new_volume, pitch, duration, data;
279   int loop_count = 0;
280   int vol, pit, dur;
281   struct device *d = decode_device (device);
282
283   /* NOTE!  You'd better not signal an error in here. */
284
285
286  try_it_again:
287   while (1)
288     {
289       if (SYMBOLP (sound))
290         sound = Fcdr (Fassq (sound, Vsound_alist));
291       parse_sound_alist_elt (sound, &new_volume, &pitch, &duration, &data);
292       sound = data;
293       if (NILP (volume)) volume = new_volume;
294       if (EQ (sound, Qt) || EQ (sound, Qnil) || STRINGP (sound))
295         break;
296       if (loop_count++ > 500)   /* much bogosity has occurred */
297         break;
298     }
299
300   if (NILP (sound) && !looking_for_default)
301     {
302       looking_for_default = 1;
303       loop_count = 0;
304       sound = Qdefault;
305       goto try_it_again;
306     }
307
308
309   vol = (INT_OR_FLOATP (volume)   ? (int) XFLOATINT (volume)   : bell_volume);
310   pit = (INT_OR_FLOATP (pitch)    ? (int) XFLOATINT (pitch)    : -1);
311   dur = (INT_OR_FLOATP (duration) ? (int) XFLOATINT (duration) : -1);
312
313   /* If the sound is a string, and we're connected to Nas, do that.
314      Else if the sound is a string, and we're on console, play it natively.
315      Else just beep.
316    */
317 #ifdef HAVE_NAS_SOUND
318   if (DEVICE_CONNECTED_TO_NAS_P (d) && STRINGP (sound))
319     {
320       const Extbyte *soundext;
321       Extcount soundextlen;
322
323       TO_EXTERNAL_FORMAT (LISP_STRING, sound,
324                           ALLOCA, (soundext, soundextlen),
325                           Qbinary);
326       if (nas_play_sound_data ((unsigned char*)soundext, soundextlen, vol))
327         return Qnil;
328     }
329 #endif /* HAVE_NAS_SOUND */
330
331 #ifdef HAVE_ESD_SOUND
332   if (DEVICE_CONNECTED_TO_ESD_P (d) && STRINGP (sound))
333     {
334       Extbyte *soundext;
335       Extcount soundextlen;
336       int succes;
337
338       TO_EXTERNAL_FORMAT (LISP_STRING, sound, ALLOCA, (soundext, soundextlen),
339                           Qbinary);
340       
341       /* #### ESD uses alarm(). But why should we also stop SIGIO? */
342       stop_interrupts ();
343       succes = esd_play_sound_data (soundext, soundextlen, vol);
344       start_interrupts ();
345       QUIT;
346       if(succes)
347         return Qnil;
348     }
349 #endif /* HAVE_ESD_SOUND */
350
351 #ifdef HAVE_NATIVE_SOUND
352   if ((NILP (Vnative_sound_only_on_console) || DEVICE_ON_CONSOLE_P (d))
353       && STRINGP (sound))
354     {
355       const Extbyte *soundext;
356       Extcount soundextlen;
357       int succes;
358
359       TO_EXTERNAL_FORMAT (LISP_STRING, sound,
360                           ALLOCA, (soundext, soundextlen),
361                           Qbinary);
362       /* The sound code doesn't like getting SIGIO interrupts. Unix sucks! */
363       stop_interrupts ();
364       succes = play_sound_data ((unsigned char*)soundext, soundextlen, vol);
365       start_interrupts ();
366       QUIT;
367       if (succes)
368         return Qnil;
369     }
370 #endif  /* HAVE_NATIVE_SOUND */
371
372   DEVMETH (d, ring_bell, (d, vol, pit, dur));
373   return Qnil;
374 }
375
376 DEFUN ("device-sound-enabled-p", Fdevice_sound_enabled_p, 0, 1, 0, /*
377 Return t if DEVICE is able to play sound.  Defaults to selected device.
378 */
379        (device))
380 {
381 #ifdef HAVE_NAS_SOUND
382   if (DEVICE_CONNECTED_TO_NAS_P (decode_device (device)))
383     return Qt;
384 #endif
385 #ifdef HAVE_NATIVE_SOUND
386   if (DEVICE_ON_CONSOLE_P (decode_device (device)))
387     return Qt;
388 #endif
389   return Qnil;
390 }
391
392 DEFUN ("ding", Fding, 0, 3, 0, /*
393 Beep, or flash the frame.
394 Also, unless an argument is given,
395 terminate any keyboard macro currently executing.
396 When called from lisp, the second argument is what sound to make, and
397 the third argument is the device to make it in (defaults to the selected
398 device).
399 */
400        (arg, sound, device))
401 {
402   static time_t last_bell_time;
403   static struct device *last_bell_device;
404   time_t now;
405   struct device *d = decode_device (device);     
406
407   XSETDEVICE (device, d);
408   now = time (0);
409
410   if (NILP (arg) && !NILP (Vexecuting_macro))
411     /* Stop executing a keyboard macro. */
412     error ("Keyboard macro terminated by a command ringing the bell");
413   
414   if (d == last_bell_device && now-last_bell_time < bell_inhibit_time)
415     return Qnil;
416   else if (!NILP (Vvisible_bell) && DEVMETH (d, flash, (d)))
417     ;
418   else
419     Fplay_sound (sound, Qnil, device);
420   
421   last_bell_time = now;
422   last_bell_device = d;
423   return Qnil;    
424 }
425
426 DEFUN ("wait-for-sounds", Fwait_for_sounds, 0, 1, 0, /*
427 Wait for all sounds to finish playing on DEVICE.
428 */
429        (device))
430
431 {
432 #ifdef HAVE_NAS_SOUND
433   struct device *d = decode_device (device);
434   if (DEVICE_CONNECTED_TO_NAS_P (d))
435     {
436       /* #### somebody fix this to be device-dependent. */
437       nas_wait_for_sounds ();
438     }
439 #endif
440   return Qnil;
441 }
442
443 DEFUN ("connected-to-nas-p", Fconnected_to_nas_p, 0, 1, 0, /*
444 Return t if connected to NAS server for sounds on DEVICE.
445 */
446        (device))
447 {
448 #ifdef HAVE_NAS_SOUND
449   return DEVICE_CONNECTED_TO_NAS_P (decode_device (device)) ? Qt : Qnil;
450 #else
451   return Qnil;
452 #endif
453 }
454 #ifdef HAVE_NAS_SOUND
455
456 static void
457 init_nas_sound (struct device *d)
458 {
459 #ifdef HAVE_X_WINDOWS
460   if (DEVICE_X_P (d))
461     {
462       char *err_message = nas_init_play (DEVICE_X_DISPLAY (d));
463       DEVICE_CONNECTED_TO_NAS_P (d) = !err_message;
464       /* Print out the message? */
465     }
466 #endif /* HAVE_X_WINDOWS */
467 }
468
469 #endif /* HAVE_NAS_SOUND */
470
471 #ifdef HAVE_NATIVE_SOUND
472
473 static void
474 init_native_sound (struct device *d)
475 {
476   if (DEVICE_TTY_P (d) || DEVICE_STREAM_P (d) || DEVICE_MSWINDOWS_P(d))
477     DEVICE_ON_CONSOLE_P (d) = 1;
478 #ifdef HAVE_X_WINDOWS
479   else
480     {
481       /* When running on a machine with native sound support, we cannot use
482          digitized sounds as beeps unless emacs is running on the same machine
483          that $DISPLAY points to, and $DISPLAY points to frame 0 of that
484          machine.
485          */
486
487       Display *display = DEVICE_X_DISPLAY (d);
488       char *dpy = DisplayString (display);
489       char *tail = (char *) strchr (dpy, ':');
490       if (! tail ||
491           strncmp (tail, ":0", 2))
492         DEVICE_ON_CONSOLE_P (d) = 0;
493       else
494         {
495           char dpyname[255], localname[255];
496
497           /* some systems can't handle SIGIO or SIGALARM in gethostbyname. */
498           stop_interrupts ();
499           strncpy (dpyname, dpy, tail-dpy);
500           dpyname [tail-dpy] = 0;
501           if (!*dpyname ||
502               !strcmp (dpyname, "unix") ||
503               !strcmp (dpyname, "localhost"))
504             DEVICE_ON_CONSOLE_P (d) = 1;
505           else if (gethostname (localname, sizeof (localname)))
506             DEVICE_ON_CONSOLE_P (d) = 0;        /* can't find hostname? */
507           else
508             {
509               /* We have to call gethostbyname() on the result of gethostname()
510                  because the two aren't guaranteed to be the same name for the
511                  same host: on some losing systems, one is a FQDN and the other
512                  is not.  Here in the wide wonderful world of Unix it's rocket
513                  science to obtain the local hostname in a portable fashion.
514
515                  And don't forget, gethostbyname() reuses the structure it
516                  returns, so we have to copy the fucker before calling it
517                  again.
518
519                  Thank you master, may I have another.
520                  */
521               struct hostent *h = gethostbyname (dpyname);
522               if (!h)
523                 DEVICE_ON_CONSOLE_P (d) = 0;
524               else
525                 {
526                   char hn [255];
527                   struct hostent *l;
528                   strcpy (hn, h->h_name);
529                   l = gethostbyname (localname);
530                   DEVICE_ON_CONSOLE_P (d) = (l && !(strcmp (l->h_name, hn)));
531                 }
532             }
533           start_interrupts ();
534         }
535     }
536 #endif /* HAVE_X_WINDOWS */
537 }
538
539 #endif /* HAVE_NATIVE_SOUND */
540
541 void
542 init_device_sound (struct device *d)
543 {
544 #ifdef HAVE_NAS_SOUND
545   init_nas_sound (d);
546 #endif
547
548 #ifdef HAVE_NATIVE_SOUND
549   init_native_sound (d);
550 #endif
551 }
552
553 void
554 syms_of_sound (void)
555 {
556   defkeyword (&Q_volume,   ":volume");
557   defkeyword (&Q_pitch,    ":pitch");
558   defkeyword (&Q_duration, ":duration");
559   defkeyword (&Q_sound,    ":sound");
560
561 #ifdef HAVE_NAS_SOUND
562   defsymbol (&Qnas, "nas");
563 #endif
564
565   DEFSUBR (Fplay_sound_file);
566   DEFSUBR (Fplay_sound);
567   DEFSUBR (Fding);
568   DEFSUBR (Fwait_for_sounds);
569   DEFSUBR (Fconnected_to_nas_p);
570   DEFSUBR (Fdevice_sound_enabled_p);
571 }
572
573
574 void
575 vars_of_sound (void)
576 {
577 #ifdef HAVE_NATIVE_SOUND
578   Fprovide (intern ("native-sound"));
579 #endif
580 #ifdef HAVE_NAS_SOUND
581   Fprovide (intern ("nas-sound"));
582 #endif
583 #ifdef HAVE_ESD_SOUND
584   Fprovide (intern ("esd-sound"));
585 #endif
586
587   DEFVAR_INT ("bell-volume", &bell_volume /*
588 *How loud to be, from 0 to 100.
589 */ );
590   bell_volume = 50;
591   
592   DEFVAR_INT ("bell-inhibit-time", &bell_inhibit_time /*
593 *Don't ring the bell on the same device more than once within this many seconds.
594 */ );
595   bell_inhibit_time = 0;
596
597   DEFVAR_LISP ("sound-alist", &Vsound_alist /*
598 An alist associating names with sounds.
599 When `beep' or `ding' is called with one of the name symbols, the associated
600 sound will be generated instead of the standard beep.
601
602 Each element of `sound-alist' is a list describing a sound.
603 The first element of the list is the name of the sound being defined.
604 Subsequent elements of the list are alternating keyword/value pairs:
605
606    Keyword:     Value:
607    -------      -----
608    sound        A string of raw sound data, or the name of another sound to
609                 play.   The symbol `t' here means use the default X beep.
610    volume       An integer from 0-100, defaulting to `bell-volume'
611    pitch        If using the default X beep, the pitch (Hz) to generate.
612    duration     If using the default X beep, the duration (milliseconds).
613
614 For compatibility, elements of `sound-alist' may also be:
615
616    ( sound-name . <sound> )
617    ( sound-name <volume> <sound> )
618
619 You should probably add things to this list by calling the function
620 load-sound-file.
621
622 Caveats:
623  - XEmacs must be built with sound support for your system.  Not all
624    systems support sound. 
625
626  - The pitch, duration, and volume options are available everywhere, but
627    many X servers ignore the `pitch' option.
628
629 The following beep-types are used by emacs itself:
630
631     auto-save-error     when an auto-save does not succeed
632     command-error       when the emacs command loop catches an error
633     undefined-key       when you type a key that is undefined
634     undefined-click     when you use an undefined mouse-click combination
635     no-completion       during completing-read
636     y-or-n-p            when you type something other than 'y' or 'n'
637     yes-or-no-p         when you type something other than 'yes' or 'no'
638     default             used when nothing else is appropriate.
639
640 Other lisp packages may use other beep types, but these are the ones that
641 the C kernel of Emacs uses.
642 */ );
643   Vsound_alist = Qnil;
644
645   DEFVAR_LISP ("synchronous-sounds", &Vsynchronous_sounds /*
646 Play sounds synchronously, if non-nil.
647 Only applies if NAS is used and supports asynchronous playing
648 of sounds.  Otherwise, sounds are always played synchronously.
649 */ );
650   Vsynchronous_sounds = Qnil;
651
652   DEFVAR_LISP ("native-sound-only-on-console", &Vnative_sound_only_on_console /*
653 Non-nil value means play sounds only if XEmacs is running
654 on the system console.
655 Nil means always always play sounds, even if running on a non-console tty
656 or a secondary X display.
657
658 This variable only applies to native sound support.
659 */ );
660   Vnative_sound_only_on_console = Qt;
661
662 #if defined (HAVE_NATIVE_SOUND) && defined (hp9000s800)
663   {
664     void vars_of_hpplay (void);
665     vars_of_hpplay ();
666   }
667 #endif
668 }