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