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