Add prompt stack for recursive minibuffer
[sxemacs] / src / media / sound.c
1 /* New Generation Sound Functions.
2    Copyright (C) 2006 Sebastian Freundt
3
4 This file is part of SXEmacs
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19
20 /* Inspired by XEmacs' sound.c written by Jamie Zawinski */
21
22 /* Synched up with: Not in FSF. */
23
24 #include <config.h>
25 #include <time.h>
26 #include "lisp.h"
27
28 #include "syssignal.h"
29
30 #include "buffer.h"
31
32 #include "ui/device.h"
33 #include "ui/redisplay.h"
34 #include "sysdep.h"
35
36 #include "sysfile.h"
37 #include "opaque.h"
38 #include "semaphore.h"
39
40 #include "media.h"
41 #include "sound.h"
42
43 Fixnum bell_volume;
44 Fixnum bell_inhibit_time;
45 Lisp_Object Vsound_alist;
46 Lisp_Object Vsynchronous_sounds;
47 Lisp_Object Vnative_sound_only_on_console;
48 Lisp_Object Q_volume, Q_pitch, Q_duration, Q_sound;
49 Lisp_Object Q_device, Q_server, Q_client, Q_keep_open;
50 Lisp_Object Qplay_sound;
51
52 #ifdef HAVE_AO_SOUND
53 #include "sound-ao.h"
54 #endif
55 #ifdef HAVE_ARTS_SOUND
56 #include "sound-arts.h"
57 #endif
58 #ifdef HAVE_POLYP_SOUND
59 #include "sound-polyp.h"
60 #endif
61 #ifdef HAVE_PULSE_SOUND
62 #include "sound-pulse.h"
63 #endif
64 #ifdef HAVE_ESD_SOUND
65 #include "sound-esd.h"
66 #endif
67 #ifdef HAVE_NAS_SOUND
68 #include "sound-nas.h"
69 #endif
70 #ifdef HAVE_JACK_SOUND
71 #include "sound-jack.h"
72 #endif
73 #ifdef HAVE_ALSA_SOUND
74 #include "sound-alsa.h"
75 #endif
76 #ifdef HAVE_OSS_SOUND
77 #include "sysproc.h"
78 #include "sound-oss.h"
79 #endif
80
81 Lisp_Object Qaudio_devicep;
82 Lisp_Object Qaudio_jobp;
83 Lisp_Object Vdefault_audio_device;
84
85 static audio_job_t make_audio_job(Lisp_Object, Lisp_Object, Lisp_Object);
86 static Lisp_Object make_audio_asyneq_job(audio_job_t);
87 static inline void finish_audio_job_data(audio_job_t);
88 static inline void
89 exec_sentinel(void *job, Lisp_Object, Lisp_Object, Lisp_Object);
90
91 #ifdef EF_USE_ASYNEQ
92 #include "events/worker-asyneq.h"
93
94 /*****************************************************************/
95 /*                      Audio Jobs                               */
96 /*****************************************************************/
97 /* sound-mst handler */
98 \f
99 static void
100 mark_audio_job(worker_job_t job)
101 {
102         audio_job_t aj;
103
104         aj = audio_job(job);
105         if (aj == NULL) {
106                 return;
107         }
108
109         SOUND_DEBUG_AJ("Marking audio job 0x%lx (job 0x%lx)\n",
110                        (long unsigned int)aj,
111                        (long unsigned int)job);
112         SXE_MUTEX_LOCK(&aj->mtx);
113         mark_object(aj->stream);
114         mark_object(aj->device);
115         mark_object(aj->result);
116         mark_object(aj->sentinel);
117         SXE_MUTEX_UNLOCK(&aj->mtx);
118         return;
119 }
120
121 static void
122 print_audio_job(worker_job_t job, Lisp_Object pcf)
123 {
124         audio_job_t aj = audio_job(job);
125         SXE_MUTEX_LOCK(&aj->mtx);
126         write_fmt_string(pcf, " carrying  #<audio-job 0x%lx>", (long unsigned int)aj);
127         SXE_MUTEX_UNLOCK(&aj->mtx);
128         return;
129 }
130
131 static void
132 finish_audio_job(worker_job_t job)
133 {
134         audio_job_t aj;
135
136         lock_worker_job(job);
137         aj = audio_job(job);
138
139         SOUND_DEBUG_AJ("Finishing audio job 0x%lx (job 0x%lx)\n",
140                        (long unsigned int)aj,
141                        (long unsigned int)job);
142         if (aj) {
143                 finish_audio_job_data(aj);
144         }
145         worker_job_data(job) = NULL;
146         unlock_worker_job(job);
147         return;
148 }
149
150 static void
151 audio_job_handle(worker_job_t job)
152 {
153         /* thread-safe */
154         /* usually called from aux threads */
155         audio_job_t aj;
156         Lisp_Object device;
157 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
158         Lisp_Object ljob = (Lisp_Object)job;
159 #endif  /* !BDWGC */
160         int(*playfun)(audio_job_t);
161         struct gcpro gcpro1;
162
163         GCPRO1(ljob);
164         lock_worker_job(job);
165         aj = audio_job(job);
166         SXE_MUTEX_LOCK(&aj->mtx);
167         SOUND_DEBUG_AJ("inherit scratch buffer 0x%lx (sz=%ld)\n",
168                        (long unsigned int)worker_job_buffer(job),
169                        (long int)worker_job_buffer_alloc_size(job));
170         aj->buffer = worker_job_buffer(job);
171         aj->buffer_alloc_size = worker_job_buffer_alloc_size(job);
172         device = audio_job_device(job);
173         SOUND_DEBUG_MT("starting thread 0x%lx@0x%lx\n",
174                        (long unsigned int)aj,
175                        (long unsigned int)job);
176         aj->play_state = MTPSTATE_RUN;
177
178         SOUND_DEBUG_AJ("fetching play function\n");
179         playfun = XAUDIO_DEVICE(device)->meths->play;
180         SOUND_DEBUG_AJ("fetched 0x%lx\n", (long unsigned int)playfun);
181
182         SXE_MUTEX_UNLOCK(&aj->mtx);
183         SOUND_DEBUG_AJ("calling play function\n");
184         (void)playfun(aj);
185         SOUND_DEBUG_AJ("play function finished\n");
186         unlock_worker_job(job);
187         UNGCPRO;
188         return;
189 }
190
191 static void
192 audio_job_started(worker_job_t job)
193 {
194         if (NILP(audio_job_sentinel(job) /* sentinel */)) {
195                 return;
196         }
197         /* called from main thread */
198         exec_sentinel(job, audio_job_stream(job), intern("started"),
199                       audio_job_sentinel(job));
200         return;
201 }
202
203 static void
204 audio_job_finished(worker_job_t job)
205 {
206         if (NILP(audio_job_sentinel(job) /* sentinel */)) {
207                 return;
208         }
209         /* called from main thread */
210         exec_sentinel(job, audio_job_stream(job), intern("finished"),
211                       audio_job_sentinel(job));
212         return;
213 }
214
215 static struct work_handler_s audio_job_handler = {
216         mark_audio_job, print_audio_job, finish_audio_job,
217         audio_job_handle, audio_job_started, audio_job_finished
218 };
219
220 static Lisp_Object
221 make_audio_asyneq_job(audio_job_t aj)
222 {
223         /* create a job digestible by the asyneq */
224         Lisp_Object job = Qnil;
225         struct gcpro gcpro1;
226         worker_job_t j;
227
228         GCPRO1(job);
229         j = make_worker_job(&audio_job_handler);
230         job = wrap_object(j);
231         XWORKER_JOB_DATA(job) = aj;
232         /* the scratch buffer thingie */
233         SXE_MUTEX_LOCK(&aj->mtx);
234         aj->buffer = XWORKER_JOB_BUFFER(job);
235         aj->buffer_alloc_size = XWORKER_JOB_BUFFER_ALLOC_SIZE(job);
236         /* generate an event queue for job control */
237         audio_job_queue(aj) = make_noseeum_event_queue();
238         SXE_MUTEX_UNLOCK(&aj->mtx);
239         UNGCPRO;
240         return job;
241 }
242
243 \f
244 DEFUN("set-audio-job-sentinel", Fset_audio_job_sentinel, 2, 2, 0, /*
245 Give JOB the sentinel SENTINEL; `nil' for none.
246 The sentinel is called as a function whenever the stream state changes.
247
248 The function should take three (optional four) arguments 
249   (JOB STREAM STATE &optional OLD-STATE)
250 where
251 - JOB is the worker job object currently coping with the stream,
252 - STREAM is bound to the stream object, and
253 - STATE is one of 'unknown, 'started, 'paused, 'stopped, 'finished
254   and indicates the current state of the job
255 - OLD-STATE is again one of the above state symbols but indicates
256   the previous state of the job.
257 */
258       (job, sentinel))
259 {
260         CHECK_AUDIO_JOB(job);
261         XAUDIO_JOB_SENTINEL(job) = sentinel;
262         return sentinel;
263 }
264
265 DEFUN("play-media-stream&", Fplay_media_streamX,
266       1, 4, 0, /*
267 Play the media stream STREAM on an audio device DEVICE.
268
269 Optional second argument DEVICE must be an audio device
270 created by `make-audio-device'.
271 If omitted DEVICE defaults to the value of `default-audio-device'.
272
273 Optional third argument SENTINEL specifies a lisp function to be
274 called whenever the stream state changes.  The function should
275 take three (optional four) arguments 
276   (JOB STREAM STATE &optional OLD-STATE)
277 where
278 - JOB is the worker job object currently coping with the stream,
279 - STREAM is bound to the stream object, and
280 - STATE is one of 'unknown, 'started, 'paused, 'stopped and indicates
281   the current state of the job
282 - OLD-STATE is again one of the above state symbols but indicates
283   the previous state of the job.
284
285 See also `set-media-thread-sentinel'.
286
287 Optional fourth argument VOLUME specifies an intial value for
288 the playback volume.
289 */
290       (stream, device, sentinel, volume))
291 {
292         audio_job_t aj;
293         Lisp_Object job = Qnil;
294         int vol;
295         struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
296
297         CHECK_MEDIA_STREAM(stream);
298
299         if (NILP(device))
300                 device = Vdefault_audio_device;
301         else
302                 CHECK_AUDIO_DEVICE(device);
303
304         /* hm, it's useful to stop here if default-audio-device is nil,
305          * i merely spit out a warning and return nil, that should suffice
306          */
307         if (NILP(device)) {
308                 warn_when_safe(
309                         Qdevice, Qnotice,
310                         GETTEXT("play-media-stream: "
311                                 "no device specified, "
312                                 "consider setting `default-audio-device'."));
313                 return Qnil;
314         }
315
316         if (!NILP(volume)) {
317                 CHECK_NUMBER(volume);
318                 volume = Fcoerce_number(volume, Qint, Qnil);
319                 vol = XINT(volume);
320                 if (vol < MEDIA_SAMPLE_VOLUME_MIN)
321                         vol = 0;
322                 else if (vol > MEDIA_SAMPLE_VOLUME_MAX)
323                         vol = MEDIA_SAMPLE_VOLUME_MAX;
324         } else
325                 vol = MEDIA_SAMPLE_VOLUME_NORM;
326
327         GCPRO4(job, device, stream, sentinel);
328         /* create the job data object */
329         aj = make_audio_job(stream, device, sentinel);
330         if (aj == Qnull_pointer) {
331                 UNGCPRO;
332                 return Qnil;
333         }
334         aj->volume = vol;
335         aj->ratetrafo = 1.0;
336         /* now prepare the job to dispatch */
337         job = make_audio_asyneq_job(aj);
338         /* add some props */
339         /* ... and dispatch it */
340         eq_enqueue(delegate_eq, job);
341         /* brag about new jobs in the queue */
342         eq_queue_trigger_all(delegate_eq);
343         UNGCPRO;
344         return job;
345 }
346 #endif  /* EF_USE_ASYNEQ */
347
348 \f
349 /* media thread sentinels */
350 static Lisp_Object
351 exec_sentinel_unwind(Lisp_Object SXE_UNUSED(datum))
352 {
353         return Qnil;
354 }
355
356 static inline void
357 exec_sentinel(void *job, Lisp_Object stream,
358               Lisp_Object state, Lisp_Object sentinel)
359 {
360         /* This function can GC */
361         /* called from main thread */
362         Lisp_Object funcell[4] = {sentinel, (Lisp_Object)job, stream, state};
363         int speccount = specpdl_depth();
364         struct gcpro gcpro1;
365
366         GCPROn(funcell, countof(funcell));
367
368         record_unwind_protect(exec_sentinel_unwind, Qnil);
369         /* call the funcell */
370         Ffuncall(countof(funcell), funcell);
371         /* reset to previous state */
372         restore_match_data();
373         UNGCPRO;
374         unbind_to(speccount, Qnil);
375         return;
376 }
377
378 static inline audio_job_t
379 allocate_audio_job(void)
380 {
381         audio_job_t aj = xnew(struct audio_job_s);
382         SOUND_DEBUG_AJ("allocated: 0x%lx\n", (long unsigned int)aj);
383         return aj;
384 }
385
386 static audio_job_t
387 make_audio_job(Lisp_Object stream, Lisp_Object device, Lisp_Object sentinel)
388 {
389         audio_job_t aj = NULL;
390         media_substream *mss;
391
392         /* traverse the substreams, try to find the first audio stream */
393         for (mss = XMEDIA_STREAM_FIRST(stream);
394              mss && media_substream_type(mss) != MTYPE_AUDIO;
395              mss = media_substream_next(mss));
396
397         if (mss == NULL) {
398                 /* throw error */
399                 return Qnull_pointer;
400         }
401
402         aj = allocate_audio_job();
403         aj->stream = stream;
404         aj->device = device;
405         aj->substream = mss;
406         aj->result = Qnil;
407         aj->sentinel = sentinel;
408
409         aj->state = MTSTATE_UNKNOWN;
410         aj->play_state = MTPSTATE_UNKNOWN;
411 #ifdef EF_USE_ASYNEQ
412         SXE_MUTEX_INIT(&aj->mtx);
413         audio_job_queue(aj) = NULL;
414 #endif
415
416         aj->job_device_data = NULL;
417         aj->job_stream_data = NULL;
418
419         aj->buffer = NULL;
420         aj->buffer_alloc_size = 0;
421
422         SOUND_DEBUG_AJ("created: 0x%lx\n", (long unsigned int)aj);
423         return aj;
424 }
425
426 static inline void
427 finish_audio_job_data(audio_job_t aj)
428 {
429         SOUND_DEBUG_AJ("finishing: 0x%lx\n", (long unsigned int)aj);
430         SXE_MUTEX_LOCK(&aj->mtx);
431         if (audio_job_device_data(aj)) {
432                 SOUND_DEBUG_AJ("audio-job device data still alive. Bug?\n");
433                 audio_job_device_data(aj) = NULL;
434         }
435         if (audio_job_stream_data(aj)) {
436                 SOUND_DEBUG_AJ("audio-job stream data still alive. Bug?\n");
437                 audio_job_stream_data(aj) = NULL;
438         }
439
440         if (aj->buffer) {
441                 SOUND_CRITICAL("strange, buffer is non-NULL: 0x%lx\n",
442                                (long unsigned int)aj->buffer);
443                 /* xfree(aj->buffer); */
444         }
445         aj->buffer_alloc_size = 0;
446
447 #ifdef EF_USE_ASYNEQ
448         if (audio_job_queue(aj)) {
449                 SOUND_DEBUG_AJ("finishing audio job queue\n");
450                 free_event_queue(audio_job_queue(aj));
451         }
452         audio_job_queue(aj) = (void*)0xB16B00B5;
453 #endif  /* EF_USE_ASYNEQ */
454
455         aj->resolution = 0;
456         aj->framesize = 0;
457         aj->channels = 0;
458         aj->volume = 0;
459         SXE_MUTEX_UNLOCK(&aj->mtx);
460         SXE_MUTEX_FINI(&aj->mtx);
461
462         SOUND_DEBUG_AJ("finished: 0x%lx\n", (long unsigned int)aj);
463         xfree(aj);
464 }
465
466 #if defined __GNUC__
467 static int
468 sound_DO_NOT_play_stream(audio_job_t mst)
469         __attribute__((unused));
470 #endif
471 static int
472 sound_DO_NOT_play_stream(audio_job_t mst)
473 {
474         /* just a dummy function */
475         return 0;
476 }
477
478 DEFUN("play-media-stream-synchronously", Fplay_media_stream_synchronously,
479       1, 4, 0, /*
480 Play the media stream STREAM on an audio device synchronously.
481 This function disregards the value of `synchronous-sounds',
482 instead streams will always be played in synchronous mode.
483
484 Optional second argument DEVICE must be an audio device
485 created by `make-audio-device'.
486 If omitted DEVICE defaults to the value of `default-audio-device'.
487
488 Optional third argument SENTINEL specifies a lisp function to be
489 called after the stream playback finishes.  The function should
490 take one argument (STREAM) where STREAM is bound to the
491 media stream which finished.  See `set-media-thread-sentinel'.
492
493 Optional fourth argument VOLUME specifies an intial value for
494 the playback volume.
495 */
496       (stream, device, sentinel, volume))
497 {
498         audio_job_t aj;
499         int vol;
500
501         CHECK_MEDIA_STREAM(stream);
502
503         if (NILP(device))
504                 device = Vdefault_audio_device;
505         else
506                 CHECK_AUDIO_DEVICE(device);
507
508         /* hm, it's useful to stop here if default-audio-device is nil,
509          * i merely spit out a warning and return nil, that should suffice
510          */
511         if (NILP(device)) {
512                 warn_when_safe(
513                         Qdevice, Qnotice,
514                         GETTEXT("play-media-stream: "
515                                 "no device specified, "
516                                 "consider setting `default-audio-device'."));
517                 return Qnil;
518         }
519
520         if (!NILP(volume)) {
521                 CHECK_NUMBER(volume);
522                 volume = Fcoerce_number(volume, Qint, Qnil);
523                 vol = XINT(volume);
524                 if (vol < MEDIA_SAMPLE_VOLUME_MIN)
525                         vol = 0;
526                 else if (vol > MEDIA_SAMPLE_VOLUME_MAX)
527                         vol = MEDIA_SAMPLE_VOLUME_MAX;
528         } else {
529                 vol = MEDIA_SAMPLE_VOLUME_NORM;
530         }
531
532         aj = make_audio_job(stream, device, Qnil);
533         if (aj == Qnull_pointer) {
534                 SOUND_DEBUG_AJ("audio job is void ... cancelling play\n");
535                 return Qnil;
536         }
537         aj->volume = vol;
538         aj->ratetrafo = 1.0;
539         aj->play_state = MTPSTATE_RUN;
540
541 #if defined EF_USE_ASYNEQ
542         aj->queue = NULL;
543 #endif
544
545         SOUND_DEBUG_AJ("calling play meth\n");
546         XAUDIO_DEVICE(device)->meths->play(aj);
547
548         if (!NILP(sentinel)) {
549                 exec_sentinel(
550                         (void*)Qnil, stream, intern("finished"), sentinel);
551         }
552
553         finish_audio_job_data(aj);
554
555         return Qt;
556 }
557
558 \f
559 #ifdef EF_USE_ASYNEQ
560 struct audio_job_event_s pause_event = {aj_change_state, {aj_pause}, 0};
561 struct audio_job_event_s resume_event = {aj_change_state, {aj_resume}, 0};
562 struct audio_job_event_s start_event = {aj_change_state, {aj_start}, 0};
563 struct audio_job_event_s stop_event = {aj_change_state, {aj_stop}, 0};
564 struct audio_job_event_s volnorm_event = {
565         aj_change_volume, {MEDIA_SAMPLE_VOLUME_NORM}, 0};
566 struct audio_job_event_s volmute_event = {
567         aj_change_volume, {MEDIA_SAMPLE_VOLUME_MIN}, 0};
568
569 DEFUN("pause-audio-job", Fpause_audio_job, 1, 1, 0, /*
570 Pause the audio job JOB.
571 Optionally JOB can be 'all in which case all running
572 media threads are paused.
573 */
574       (job))
575 {
576         if (!EQ(job, Qall)) {
577                 CHECK_AUDIO_JOB(job);
578                 /* connect to job's queue and place a PAUSE event there */
579                 if (XAUDIO_JOB_QUEUE(job)) {
580                         eq_noseeum_enqueue(XAUDIO_JOB_QUEUE(job), &pause_event);
581                 }
582         }
583         return Qt;
584 }
585
586 DEFUN("resume-audio-job", Fresume_audio_job, 1, 1, 0, /*
587 Resume a paused audio job JOB.
588 Optionally JOB can be 'all in which case all paused
589 media threads are resumed.
590 */
591       (job))
592 {
593         if (!EQ(job, Qall)) {
594                 CHECK_AUDIO_JOB(job);
595                 /* connect to job's queue and place a RESUME event there */
596                 if (XAUDIO_JOB_QUEUE(job)) {
597                         eq_noseeum_enqueue(XAUDIO_JOB_QUEUE(job),
598                                            &resume_event);
599                 }
600         }
601         return Qt;
602 }
603
604 DEFUN("stop-audio-job", Fstop_audio_job, 1, 1, 0, /*
605 Stop a audio job JOB.
606 Optionally JOB can be 'all in which case all media threads
607 are stopped.
608 */
609       (job))
610 {
611         if (!EQ(job, Qall)) {
612                 CHECK_AUDIO_JOB(job);
613                 /* connect to job's queue and place a STOP event there */
614                 if (XAUDIO_JOB_QUEUE(job)) {
615                         eq_noseeum_enqueue(XAUDIO_JOB_QUEUE(job), &stop_event);
616                 }
617         }
618         return Qt;
619 }
620
621 DEFUN("set-audio-job-volume", Fset_audio_job_volume, 1, 2, 0, /*
622 Set the volume of the audio job JOB to VOLUME.
623
624 JOB is assumed to be a media thread object with an audio substream.
625 Optionally JOB can be 'all in which case the volume change
626 applies to all (currently handled) media threads.
627
628 VOLUME is either a comparable number (see `comparablep') or
629 a vector of comparable numbers.
630 In the former case VOLUME sets the master volume of all channels.
631 In the latter case VOLUME sets the volumes channelwise.
632
633 Any volume value is coerced to an integer.
634 A volume of 128 is the norm.
635 A volume of 0 is muting the respective channels.
636 Volumes greater than 128 cause an amplification of the stream,
637 255 is the maximal volume value.  Note that clipping may occur.
638 */
639       (job, volume))
640 {
641         int vol = 0;
642         Lisp_Object tmpv = Qnil;
643
644         CHECK_AUDIO_JOB(job);
645         if (volume == Qt) {
646                 if (XAUDIO_JOB_QUEUE(job)) {
647                         eq_noseeum_enqueue(
648                                 XAUDIO_JOB_QUEUE(job), &volnorm_event);
649                 }
650                 return make_int(MEDIA_SAMPLE_VOLUME_NORM);
651         } else if (volume == Qnil) {
652                 if (XAUDIO_JOB_QUEUE(job)) {
653                         eq_noseeum_enqueue(
654                                 XAUDIO_JOB_QUEUE(job), &volmute_event);
655                 }
656                 return make_int(MEDIA_SAMPLE_VOLUME_NORM);
657         } else if (COMPARABLEP(volume)) {
658                 volume = Fcoerce_number(volume, Qint, Qnil);
659                 vol = XINT(volume);
660
661                 if (vol < 0)
662                         vol = MEDIA_SAMPLE_VOLUME_MIN;
663                 else if (vol > MEDIA_SAMPLE_VOLUME_MAX)
664                         vol = MEDIA_SAMPLE_VOLUME_MAX;
665         } else if (VECTORP(volume)) {
666                 tmpv = XVECTOR_DATA(volume)[0];
667                 tmpv = Fcoerce_number(tmpv, Qint, Qnil);
668                 vol = XINT(tmpv);
669
670                 if (vol < 0)
671                         vol = MEDIA_SAMPLE_VOLUME_MIN;
672                 else if (vol > MEDIA_SAMPLE_VOLUME_MAX)
673                         vol = MEDIA_SAMPLE_VOLUME_MAX;
674         } else {
675                 return wrong_type_argument(Qnumberp, volume);
676         }
677
678         /* place an VOLCHANGE event in job's queue */
679         if (XAUDIO_JOB_QUEUE(job)) {
680                 audio_job_event_t aje = make_audio_job_event(aj_change_volume);
681                 audio_job_event_args(aje).volume_args = vol;
682                 eq_noseeum_enqueue(XAUDIO_JOB_QUEUE(job), aje);
683         }
684         return volume;
685 }
686
687 DEFUN("audio-job-volume", Faudio_job_volume, 1, 1, 0, /*
688 Return the current volume of audio job JOB.
689 */
690       (job))
691 {
692         CHECK_AUDIO_JOB(job);
693
694         return make_int(XAUDIO_JOB(job)->volume);
695 }
696
697 DEFUN("set-audio-job-rate", Fset_audio_job_rate, 1, 2, 0, /*
698 Set the rate of audio job JOB to RATE.
699
700 If RATE is `t' or `nil', reset the rate to 1.0.
701 */
702       (job, rate))
703 {
704         float ratetrafo;
705
706         CHECK_AUDIO_JOB(job);
707         if (rate == Qt || rate == Qnil) {
708                 ratetrafo = 1.0;
709         } else if (COMPARABLEP(rate)) {
710                 rate = Fcoerce_number(rate, Qfloat, Qnil);
711                 ratetrafo = XFLOAT_DATA(rate);
712
713                 if (ratetrafo <= 0.5)
714                         ratetrafo = 0.5;
715                 else if (ratetrafo > 2.0)
716                         ratetrafo = 2.0;
717         } else {
718                 return wrong_type_argument(Qnumberp, rate);
719         }
720
721         /* place a rate change event in job's queue */
722         if (XAUDIO_JOB_QUEUE(job)) {
723                 audio_job_event_t aje = make_audio_job_event(aj_change_rate);
724                 audio_job_event_args(aje).rate_args = ratetrafo;
725                 eq_noseeum_enqueue(XAUDIO_JOB_QUEUE(job), aje);
726         }
727         return rate;
728 }
729
730 DEFUN("audio-job-rate", Faudio_job_rate, 1, 1, 0, /*
731 Return the current rate of audio job JOB.
732 */
733       (job))
734 {
735         CHECK_AUDIO_JOB(job);
736
737         return make_float(XAUDIO_JOB(job)->ratetrafo);
738 }
739 #endif  /* EF_USE_ASYNEQ */
740
741 \f
742 DEFUN("ding", Fding, 0, 3, 0,   /*
743 Beep, or flash the frame.
744 Also, unless an argument is given,
745 terminate any keyboard macro currently executing.
746 When called from lisp, the second argument is what sound to make, and
747 the third argument is the device to make it in (defaults to the selected
748 device), but may also be an audio device created by `make-audio-device'.
749 */
750       (arg, sound, device))
751 {
752         static time_t last_bell_time;
753         static struct device *last_bell_device;
754         time_t now;
755         struct device *d = decode_device(device);
756         struct gcpro gcpro1, gcpro2, gcpro3;
757
758         GCPRO3(arg, sound, device);
759
760         /* XSETDEVICE(device, d); */
761         now = time(0);
762
763         if (NILP(arg) && !NILP(Vexecuting_macro))
764                 /* Stop executing a keyboard macro. */
765                 error
766                     ("Keyboard macro terminated by a command ringing the bell");
767
768         if (d == last_bell_device && now - last_bell_time < bell_inhibit_time) {
769                 return Qnil;
770         } else if (!NILP(Vvisible_bell) && DEVMETH(d, flash, (d))) {
771                 ;
772         } else if (NILP(sound)) {
773                 DEVMETH(d, ring_bell, (d, bell_volume, -1, -1));
774         } else if (!NILP(Ffboundp(Qplay_sound))) {
775                 call3(Qplay_sound, sound, Qnil, device);
776         }
777
778         last_bell_time = now;
779         last_bell_device = d;
780         RETURN_UNGCPRO(Qnil);
781 }
782
783 \f
784 /* LEGACY */
785 DEFUN("device-sound-enabled-p", Fdevice_sound_enabled_p, 0, 1, 0,       /*
786 Return t if DEVICE is able to play sound.  Defaults to selected device.
787 */
788       (device))
789 {
790         if (DEVICEP(device)) {
791 #ifdef HAVE_NAS_SOUND
792                 if (DEVICE_CONNECTED_TO_NAS_P(decode_device(device)))
793                         return Qt;
794 #endif
795 #ifdef HAVE_OSS_SOUND
796                 if (DEVICE_ON_CONSOLE_P(decode_device(device)))
797                         return Qt;
798 #endif
799         }
800
801         if (NILP(device))
802                 device = Vdefault_audio_device;
803
804         if (!NILP(device) &&
805             AUDIO_DEVICEP(device) &&
806             XAUDIO_DEVICE_STATE(device) != ASTATE_DEAD)
807                 return Qt;
808
809         return Qnil;
810 }
811
812 /* LEGACY */
813 DEFUN("wait-for-sounds", Fwait_for_sounds, 0, 1, 0,     /*
814 Wait for all sounds to finish playing on DEVICE.
815 */
816       (device))
817 {
818         return Qnil;
819 }
820
821 /* LEGACY */
822 DEFUN("connected-to-nas-p", Fconnected_to_nas_p, 0, 1, 0,       /*
823 Return t if connected to NAS server for sounds on DEVICE.
824 */
825       (device))
826 {
827 #ifdef HAVE_NAS_SOUND
828         return DEVICE_CONNECTED_TO_NAS_P(decode_device(device)) ? Qt : Qnil;
829 #else
830         return Qnil;
831 #endif
832 }
833
834 \f
835 /*****************************************************************/
836 /*                      audio device hack                        */
837 /*****************************************************************/
838 /* Indeed the console->device->frame->window structure is not what I'd call
839  * applicable to audio devices. That is why this seamless fake here exists :)
840  * -hroptatyr
841  */
842 static Lisp_Object
843 audio_device_mark(Lisp_Object obj)
844 {
845         if (XAUDIO_DEVICE_METH(obj, mark))
846                 return XAUDIO_DEVICE_METH(obj, mark)(
847                         XAUDIO_DEVICE_DATA(obj));
848
849         return Qnil;
850 }
851
852 static void
853 audio_device_finalise(void *header, int for_disksave)
854 {
855         Lisp_Audio_Device *ad = (Lisp_Audio_Device*)header;
856
857         SOUND_DEBUG_DEV("GCor asked me to finalise: 0x%lx\n",
858                         (long unsigned int)ad);
859
860         if (audio_device_data(ad) &&
861             audio_device_meth(ad, finish))
862                 audio_device_meth(ad, finish)(audio_device_data(ad));
863
864         if (audio_device_data(ad))
865                 xfree(audio_device_data(ad));
866         audio_device_data(ad) = NULL;
867
868         /* avoid some warning */
869         if (for_disksave || ad == NULL);
870 }
871
872 static void
873 audio_device_print(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
874 {
875         write_c_string("#<audio-device :type ", printcharfun);
876
877
878         switch (XAUDIO_DEVICE_DRIVER(obj)) {
879         case ADRIVER_OSS:
880                 write_c_string("oss", printcharfun);
881                 break;
882
883         case ADRIVER_ARTS:
884                 write_c_string("arts", printcharfun);
885                 break;
886
887         case ADRIVER_NAS:
888                 write_c_string("nas", printcharfun);
889                 break;
890
891         case ADRIVER_ESD:
892                 write_c_string("esd", printcharfun);
893                 break;
894
895         case ADRIVER_POLYP:
896                 write_c_string("polyp", printcharfun);
897                 break;
898
899         case ADRIVER_PULSE:
900                 write_c_string("pulse", printcharfun);
901                 break;
902
903         case ADRIVER_AO:
904                 write_c_string("ao", printcharfun);
905                 break;
906
907         case ADRIVER_JACK:
908                 write_c_string("jack", printcharfun);
909                 break;
910
911         case ADRIVER_ALSA:
912                 write_c_string("alsa", printcharfun);
913                 break;
914
915         case NUMBER_OF_AUDIO_DRIVERS:
916         case ADRIVER_UNKNOWN:
917         default:
918                 write_c_string("unknown", printcharfun);
919                 break;
920         }
921
922         if (XAUDIO_DEVICE_METH(obj, print)) {
923                 XAUDIO_DEVICE_METH(obj, print)(obj, printcharfun, escapeflag);
924         }
925
926         /* info about the general state */
927         write_c_string(" :device-state ", printcharfun);
928         switch (XAUDIO_DEVICE_STATE(obj)) {
929         case ASTATE_DEAD:
930         case ASTATE_SUSPENDED:
931                 write_c_string("#dead", printcharfun);
932                 break;
933         case ASTATE_ALIVE:
934                 write_c_string("#ready", printcharfun);
935                 break;
936
937         case NUMBER_OF_AUDIO_STATES:
938         case ASTATE_UNKNOWN:
939         default:
940                 write_c_string("#unknown", printcharfun);
941                 break;
942         }
943
944         write_c_string(">", printcharfun);
945 }
946
947 static int
948 audio_device_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
949 {
950         return Qnil;
951
952         /* less warnings */
953         if (depth || obj1 || obj2);
954 }
955
956 static unsigned long
957 audio_device_hash (Lisp_Object obj, int SXE_UNUSED(depth))
958 {
959         return (unsigned long)obj;
960         /* audio_device_hashcode(XAUDIO_DEVICE_DATA(obj)); */
961 }
962
963 static const struct lrecord_description audio_device_description[] = {
964         { XD_INT, offsetof(Lisp_Audio_Device, driver) },
965         { XD_INT, offsetof(Lisp_Audio_Device, state) },
966         { XD_OPAQUE_PTR, offsetof(Lisp_Audio_Device, device_data) },
967         { XD_END }
968 };
969
970 DEFINE_LRECORD_IMPLEMENTATION("audio_device", audio_device,
971                               audio_device_mark, audio_device_print,
972                               audio_device_finalise,
973                               audio_device_equal, audio_device_hash,
974                               audio_device_description,
975                               Lisp_Audio_Device);
976
977 static Lisp_Audio_Device *
978 audio_device_allocate(void)
979 {
980         Lisp_Audio_Device *ad;
981
982         ad = alloc_lcrecord_type(Lisp_Audio_Device, &lrecord_audio_device);
983         return ad;
984 }
985
986 audio_driver decode_audio_type(Lisp_Object type)
987 {
988         audio_driver ad = ADRIVER_UNKNOWN;
989
990         if (0);
991 #ifdef HAVE_OSS_SOUND
992         else if (EQ(type, Qoss))
993                 ad = ADRIVER_OSS;
994 #endif
995 #ifdef HAVE_NAS_SOUND
996         else if (EQ(type, Qnas))
997                 ad = ADRIVER_NAS;
998 #endif
999 #ifdef HAVE_ESD_SOUND
1000         else if (EQ(type, Qesd))
1001                 ad = ADRIVER_ESD;
1002 #endif
1003 #ifdef HAVE_POLYP_SOUND
1004         else if (EQ(type, Qpolyp))
1005                 ad = ADRIVER_POLYP;
1006 #endif
1007 #ifdef HAVE_PULSE_SOUND
1008         else if (EQ(type, Qpulse))
1009                 ad = ADRIVER_PULSE;
1010 #endif
1011 #ifdef HAVE_AO_SOUND
1012         else if (EQ(type, Qao))
1013                 ad = ADRIVER_AO;
1014 #endif
1015 #ifdef HAVE_ARTS_SOUND
1016         else if (EQ(type, Qarts))
1017                 ad = ADRIVER_ARTS;
1018 #endif
1019 #ifdef HAVE_JACK_SOUND
1020         else if (EQ(type, Qjack))
1021                 ad = ADRIVER_JACK;
1022 #endif
1023 #ifdef HAVE_ALSA_SOUND
1024         else if (EQ(type, Qalsa))
1025                 ad = ADRIVER_ALSA;
1026 #endif
1027
1028         return ad;
1029 }
1030
1031 audio_driver decode_audio_device(Lisp_Object device)
1032 {
1033         struct device *d = NULL;
1034         audio_driver ad = ADRIVER_UNKNOWN;
1035
1036         if (NILP(device) && !NILP(Vdefault_audio_device))
1037                 device = Vdefault_audio_device;
1038
1039         if (AUDIO_DEVICEP(device)) {
1040                 ad = XAUDIO_DEVICE_DRIVER(device);
1041
1042         } else if (DEVICEP(device) || NILP(device)) {
1043                 d = decode_device(device);
1044
1045                 if (0);
1046 #ifdef HAVE_NAS_SOUND
1047                 else if (DEVICE_CONNECTED_TO_NAS_P(d))
1048                         ad = ADRIVER_NAS;
1049 #endif
1050 #ifdef HAVE_AO_SOUND
1051                 else if (DEVICE_CONNECTED_TO_AO_P(d))
1052                         ad = ADRIVER_AO;
1053 #endif
1054 #ifdef HAVE_PULSE_SOUND
1055                 else if (DEVICE_CONNECTED_TO_PULSE_P(d))
1056                         ad = ADRIVER_PULSE;
1057 #endif
1058 #ifdef HAVE_POLYP_SOUND
1059                 else if (DEVICE_CONNECTED_TO_POLYP_P(d))
1060                         ad = ADRIVER_POLYP;
1061 #endif
1062 #ifdef HAVE_ESD_SOUND
1063                 else if (DEVICE_CONNECTED_TO_ESD_P(d))
1064                         ad = ADRIVER_ESD;
1065 #endif
1066 #ifdef HAVE_ARTS_SOUND
1067                 else if (DEVICE_CONNECTED_TO_ARTS_P(d))
1068                         ad = ADRIVER_ARTS;
1069 #endif
1070 #ifdef HAVE_ALSA_SOUND
1071                 else if (DEVICE_CONNECTED_TO_ALSA_P(d))
1072                         ad = ADRIVER_ALSA;
1073 #endif
1074 #ifdef HAVE_OSS_SOUND
1075                 else if (NILP(Vnative_sound_only_on_console) ||
1076                          DEVICE_ON_CONSOLE_P(d))
1077                         ad = ADRIVER_OSS;
1078 #endif
1079         }
1080
1081         return ad;
1082 }
1083
1084 void *get_audio_device_data(Lisp_Object device)
1085 {
1086         if (AUDIO_DEVICEP(device))
1087                 return XAUDIO_DEVICE_DATA(device);
1088         else
1089                 return NULL;
1090 }
1091
1092 Lisp_Audio_Device *get_audio_device(Lisp_Object device)
1093 {
1094         if (AUDIO_DEVICEP(device))
1095                 return XAUDIO_DEVICE(device);
1096         else
1097                 return NULL;
1098 }
1099
1100 \f
1101 static Lisp_Object
1102 make_audio_device(Lisp_Object type)
1103 {
1104         Lisp_Audio_Device *ad;
1105         Lisp_Object lad;
1106
1107         CHECK_SYMBOL(type);
1108
1109         ad = audio_device_allocate();
1110         audio_device_driver(ad) = decode_audio_type(type);
1111         audio_device_data(ad) = NULL;
1112         XSETAUDIO_DEVICE(lad, ad);
1113         XAUDIO_DEVICE_STATE(lad) = ASTATE_UNKNOWN;
1114
1115         return lad;
1116 }
1117
1118 DEFUN("make-audio-device", Fmake_audio_device, 1, MANY, 0,      /*
1119 DRIVER &rest DEVICE-OPTIONS
1120
1121 Create a new device to output audio via DRIVER.
1122 DRIVER should be a symbol out of 'oss, 'nas, 'esd, 'pulse,
1123 'jack, 'alsa, 'arts or 'ao.
1124
1125 The rest arguments may be used to pass options to the selected
1126 output driver. These should be `:keyword value' pairs.
1127
1128 Valid keywords for ALSA are:
1129 :device - the name of the hardware interface (default: "default"),
1130   you may want to try "plughw:0,0" first
1131 :keep-open - whether to exclusively reserve the device.
1132   Note this may prevent other applications from using the device.
1133
1134 Valid keywords for (deprecated) OSS are:
1135 :device - the name of the hardware interface (default: "/dev/dsp")
1136 :keep-open - whether to exclusively reserve the device.
1137   Note this may prevent other applications from using the device.
1138
1139 Valid keywords for ESD are:
1140 :server - to use a distant ESD daemon (e.g. "my.machine.box:16001")
1141 The default for ESD output is to use a locally running daemon and
1142 to connect to it via unix domain sockets.
1143
1144 Valid keywords for Pulse are:
1145 :server - the host name to connect to (default: "localhost")
1146 :sink - the name of the sink to connect to (e.g. "output")
1147 :source - the name of the source to record from (e.g. "mic_in")
1148 :client - how to call the client on the server (default "SXEmacs")
1149 :stream - how to call the stream on the server (e.g. "fancy-sound")
1150 :immediate - connect to sink immediately and keep the connection
1151   alive as long as the audio device exists (default `t')
1152 :threaded - initiate a threaded mainloop (default `t')
1153 :force - if non-nil the device object is created even though the
1154   pulse mainloop could not be started; if `nil' any mainloop failure
1155   results in an error.  This can be useful if you want to have an
1156   audio device object although the server is not (yet) up or not
1157   (yet) accepting connections from you. (default `nil')
1158
1159 Valid keywords for Jack are:
1160 :server - the jack server to connect to (default "default")
1161 :client - how to call the client on the server (default "SXEmacs")
1162
1163 Valid keywords for AO are:
1164 :driver - the name of the output driver (e.g. "alsa", "esd", etc.)
1165 :options - a list of AO suboptions (see AO documentation)
1166 The default for AO output is to pass nothing and entirely use the
1167 system and user configuration files.
1168
1169 Valid keywords for NAS are:
1170 :server - the NAS server to connect to.  This can be either:
1171   - an X display string like "localhost:0.0", the X display string
1172     the current frame is on can be obtained by the function
1173     `device-connection'
1174   - or a SXEmacs device name like "localhost-11-0" which can be
1175     obtained by `device-name'
1176   - or a SXEmacs device object, obtainable by `frame-device', like
1177     #<x-device on "localhost:11.0" 0xee4>
1178 If the :server keyword is omitted SXEmacs tries to determine a
1179 sensible default in this order:
1180   - use the frame device of the current frame
1181   - use the frame device of the initial frame
1182   - use the display specified in $AUDIOSERVER
1183   - use the display specified in $DISPLAY
1184   - try "localhost:0.0"
1185
1186 Valid keywords for aRts are:
1187 none at the moment
1188
1189 */
1190       (int nargs, Lisp_Object *args))
1191 {
1192         Lisp_Object ad, driver, device_options = Qnil;
1193         audio_driver dev_driver;
1194         ad_device_data *device_data = NULL;
1195
1196         driver = args[0];
1197
1198         CHECK_SYMBOL(driver);
1199         ad = make_audio_device(driver);
1200
1201         dev_driver = XAUDIO_DEVICE_DRIVER(ad);
1202
1203         switch (dev_driver) {
1204         case ADRIVER_NAS:
1205 #ifdef HAVE_NAS_SOUND
1206                 XAUDIO_DEVICE_METHS(ad) = sound_nas;
1207                 break;
1208 #endif
1209         case ADRIVER_ARTS:
1210 #ifdef HAVE_ARTS_SOUND
1211                 XAUDIO_DEVICE_METHS(ad) = sound_arts;
1212                 break;
1213 #endif
1214         case ADRIVER_ALSA:
1215 #ifdef HAVE_ALSA_SOUND
1216                 XAUDIO_DEVICE_METHS(ad) = sound_alsa;
1217                 break;
1218 #endif
1219         case ADRIVER_OSS:
1220 #ifdef HAVE_OSS_SOUND
1221                 XAUDIO_DEVICE_METHS(ad) = sound_oss;
1222                 break;
1223 #endif
1224         case ADRIVER_AO:
1225 #ifdef HAVE_AO_SOUND
1226                 XAUDIO_DEVICE_METHS(ad) = sound_ao;
1227                 break;
1228 #endif
1229         case ADRIVER_POLYP:
1230         case ADRIVER_PULSE:
1231 #ifdef HAVE_PULSE_SOUND
1232                 XAUDIO_DEVICE_METHS(ad) = sound_pulse;
1233                 break;
1234 #endif
1235         case ADRIVER_ESD:
1236 #ifdef HAVE_ESD_SOUND
1237                 XAUDIO_DEVICE_METHS(ad) = sound_esd;
1238                 break;
1239 #endif
1240         case ADRIVER_JACK:
1241 #ifdef HAVE_JACK_SOUND
1242                 XAUDIO_DEVICE_METHS(ad) = sound_jack;
1243                 break;
1244 #endif
1245         case ADRIVER_UNKNOWN:
1246         case NUMBER_OF_AUDIO_DRIVERS:
1247         default:
1248                 return Qnil;
1249                 break;
1250         }
1251
1252         {
1253                 Lisp_Object tmp = Flist(nargs, args);
1254                 device_options = XCDR(tmp);
1255         }
1256
1257         if (XAUDIO_DEVICE_METH(ad, create) &&
1258             !(device_data = XAUDIO_DEVICE_METH(ad, create)(device_options))) {
1259                 XAUDIO_DEVICE_STATE(ad) = ASTATE_DEAD;
1260         }
1261         XAUDIO_DEVICE_DATA(ad) = device_data;
1262         return ad;
1263 }
1264
1265 DEFUN("audio-device-p", Faudio_device_p, 1, 1, 0, /*
1266 Return non-nil if OBJECT is an audio-device.
1267 */
1268       (object))
1269 {
1270         if (AUDIO_DEVICEP(object))
1271                 return Qt;
1272         else
1273                 return Qnil;
1274 }
1275
1276 #if 0
1277 DEFUN("delete-audio-device", Fdelete_audio_device, 1, 1, 0, /*
1278 Deinitialise the audio device DEVICE and free its resources.
1279 */
1280       (device))
1281 {
1282         CHECK_AUDIO_DEVICE(device);
1283
1284         audio_device_finalise(XAUDIO_DEVICE(device), 0);
1285         return device;
1286 }
1287 #endif
1288
1289 \f
1290 void syms_of_sound(void)
1291 {
1292         INIT_LRECORD_IMPLEMENTATION(audio_device);
1293
1294         defkeyword(&Q_volume, ":volume");
1295         defkeyword(&Q_pitch, ":pitch");
1296         defkeyword(&Q_duration, ":duration");
1297         defkeyword(&Q_sound, ":sound");
1298
1299         /* located in sound.el */
1300         defsymbol(&Qplay_sound, "play-sound");
1301
1302 #ifdef HAVE_NAS_SOUND
1303         defsymbol(&Qnas, "nas");
1304 #endif
1305 #ifdef HAVE_ESD_SOUND
1306         defsymbol(&Qesd, "esd");
1307 #endif
1308 #ifdef HAVE_POLYP_SOUND
1309         defsymbol(&Qpolyp, "polyp");
1310 #endif
1311 #ifdef HAVE_PULSE_SOUND
1312         defsymbol(&Qpulse, "pulse");
1313 #endif
1314 #ifdef HAVE_AO_SOUND
1315         defsymbol(&Qao, "ao");
1316 #endif
1317 #ifdef HAVE_ARTS_SOUND
1318         defsymbol(&Qarts, "arts");
1319 #endif
1320 #ifdef HAVE_ALSA_SOUND
1321         defsymbol(&Qalsa, "alsa");
1322 #endif
1323 #ifdef HAVE_JACK_SOUND
1324         defsymbol(&Qjack, "jack");
1325 #endif
1326 #ifdef HAVE_OSS_SOUND
1327         defsymbol(&Qoss, "oss");
1328 #endif
1329         defsymbol(&Qaudio_devicep, "audio-device-p");
1330         defsymbol(&Qaudio_jobp, "audio-job-p");
1331
1332         /* some more symbols */
1333         defsymbol(&Q_device, ":device");
1334         defsymbol(&Q_keep_open, ":keep-open");
1335         defsymbol(&Q_server, ":server");
1336         defsymbol(&Q_client, ":client");
1337
1338         DEFSUBR(Fplay_media_stream_synchronously);
1339 #ifdef EF_USE_ASYNEQ
1340         DEFSUBR(Fplay_media_streamX);
1341         DEFSUBR(Fset_audio_job_sentinel);
1342         DEFSUBR(Fpause_audio_job);
1343         DEFSUBR(Fresume_audio_job);
1344         DEFSUBR(Fstop_audio_job);
1345         DEFSUBR(Fset_audio_job_volume);
1346         DEFSUBR(Faudio_job_volume);
1347         DEFSUBR(Fset_audio_job_rate);
1348         DEFSUBR(Faudio_job_rate);
1349 #endif
1350
1351         DEFSUBR(Fding);
1352         DEFSUBR(Fwait_for_sounds);
1353         DEFSUBR(Fconnected_to_nas_p);
1354         DEFSUBR(Fdevice_sound_enabled_p);
1355
1356         /* audio device fake */
1357         DEFSUBR(Fmake_audio_device);
1358         DEFSUBR(Faudio_device_p);
1359 #if 0
1360         DEFSUBR(Fdelete_audio_device); /* too dangerous atm */
1361 #endif
1362 }
1363
1364 void vars_of_sound(void)
1365 {
1366 #ifdef HAVE_OSS_SOUND
1367 #  if 0
1368         Fprovide(intern("native-sound")); /* for compatibility */
1369         /* transition time is over! */
1370 #  endif
1371         Fprovide(intern("oss-sound"));
1372 #endif
1373 #ifdef HAVE_NAS_SOUND
1374         Fprovide(intern("nas-sound"));
1375 #endif
1376 #ifdef HAVE_ESD_SOUND
1377         Fprovide(intern("esd-sound"));
1378 #endif
1379 #ifdef HAVE_POLYP_SOUND
1380         Fprovide(intern("polyp-sound"));
1381 #endif
1382 #ifdef HAVE_PULSE_SOUND
1383         Fprovide(intern("pulse-sound"));
1384 #endif
1385 #ifdef HAVE_AO_SOUND
1386         Fprovide(intern("ao-sound"));
1387 #endif
1388 #ifdef HAVE_ARTS_SOUND
1389         Fprovide(intern("arts-sound"));
1390 #endif
1391 #ifdef HAVE_ALSA_SOUND
1392         Fprovide(intern("alsa-sound"));
1393 #endif
1394         Fprovide(intern("audio"));
1395
1396         DEFVAR_INT("bell-volume", &bell_volume  /*
1397 *How loud to be, from 0 to 255, where 127 is the norm (100%).
1398 Values above raise the volume and values below lower it.
1399                                                  */ );
1400         bell_volume = 63;
1401
1402         DEFVAR_INT("bell-inhibit-time", &bell_inhibit_time      /*
1403 *Don't ring the bell on the same device more than once within this many seconds.
1404                                                                  */ );
1405         bell_inhibit_time = 0;
1406
1407         DEFVAR_LISP("sound-alist", &Vsound_alist        /*
1408 An alist associating names with sounds.
1409 When `beep' or `ding' is called with one of the name symbols, the associated
1410 sound will be generated instead of the standard beep.
1411
1412 Each element of `sound-alist' is a list describing a sound.
1413 The first element of the list is the name of the sound being defined.
1414 Subsequent elements of the list are alternating keyword/value pairs:
1415
1416 Keyword: Value:
1417 -------  -----
1418 sound    A string of raw sound data (deprecated), or the name of another
1419          sound to play.   The symbol `t' here means use the default X beep.
1420 volume   An integer from 0-100, defaulting to `bell-volume'
1421 pitch    If using the default X beep, the pitch (Hz) to generate.
1422 duration If using the default X beep, the duration (milliseconds).
1423 stream   A media stream object containing the sound.
1424
1425 You should probably add things to this list by calling the function
1426 load-sound-file.
1427
1428 Note: SXEmacs must be built with sound support for your system.  Not all
1429 systems support sound. 
1430 Note: The pitch, duration, and volume options are available everywhere,
1431 but many X servers ignore the `pitch' option.
1432
1433 The following beep-types are used by SXEmacs itself:
1434
1435 auto-save-error  when an auto-save does not succeed
1436 command-error    when the emacs command loop catches an error
1437 undefined-key    when you type a key that is undefined
1438 undefined-click  when you use an undefined mouse-click combination
1439 no-completion    during completing-read
1440 y-or-n-p         when you type something other than 'y' or 'n'
1441 yes-or-no-p      when you type something other than 'yes' or 'no'
1442 default          used when nothing else is appropriate.
1443
1444 Other lisp packages may use other beep types, but these are the ones that
1445 the C kernel of Emacs uses.
1446                                                          */ );
1447         Vsound_alist = Qnil;
1448
1449         DEFVAR_LISP("synchronous-sounds", &Vsynchronous_sounds  /*
1450 Play sounds synchronously, if non-nil.
1451 Only applies if SXEmacs has been compiled with a threading library.
1452 Otherwise, sounds are always played synchronously.
1453                                                                  */ );
1454         Vsynchronous_sounds = Qt;
1455
1456         DEFVAR_LISP("native-sound-only-on-console", &Vnative_sound_only_on_console      /*
1457 Non-nil value means play sounds only if SXEmacs is running
1458 on the system console.
1459 Nil means always play sounds, even if running on a non-console tty
1460 or a secondary X display.
1461
1462 This variable only applies to native sound support.
1463                                                                                          */ );
1464         Vnative_sound_only_on_console = Qt;
1465
1466         DEFVAR_LISP("default-audio-device", &Vdefault_audio_device      /*
1467 Default audio device to use.
1468                                                                          */ );
1469         Vdefault_audio_device = Qnil;
1470 }
1471
1472 /* sound.c ends here */