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