1 /** sound-oss.c - play a sound file on using the (deprecated) OSS
3 ** Copyright (C) 2006 Sebastian Freundt
5 ** Copyright (C) 1995,96 by Markus Gutschke (gutschk@math.uni-muenster.de)
6 ** This is version 1.3 of linuxplay.c, with platform-independent functions
7 ** moved to a different file by Robert Bihlmeyer <robbe@orcus.priv.at>.
9 ** Parts of this code were inspired by sunplay.c, which is copyright 1989 by
10 ** Jef Poskanzer and 1991,92 by Jamie Zawinski; c.f. sunplay.c for further
13 ** Permission to use, copy, modify, and distribute this software and its
14 ** documentation for any purpose and without fee is hereby granted, provided
15 ** that the above copyright notice appear in all copies and that both that
16 ** copyright notice and this permission notice appear in supporting
17 ** documentation. This software is provided "as is" without express or
22 /* Synched up with: Not in FSF. */
24 /* XEmacs beta testers say: undef this by default. */
25 #undef NOVOLUMECTRLFORMULAW /* Changing the volume for uLaw-encoded
26 samples sounds very poor; possibly,
27 this is true only for the PC-Snd
28 driver, so undefine this symbol at your
42 #include <sys/ioctl.h>
49 #include "sound-oss.h"
51 Lisp_Object Qoss; /* cannot be Qnative */
53 #define MYSELF ADRIVER_OSS
55 #define __OSS_DEBUG__(args...) fprintf(stderr, "OSS " args)
56 #ifndef OSS_DEBUG_FLAG
57 #define OSS_DEBUG(args...)
59 #define OSS_DEBUG(args...) __OSS_DEBUG__(args)
61 #define OSS_DEBUG_HW(args...) OSS_DEBUG("[hardware]: " args)
62 #define OSS_DEBUG_S(args...) OSS_DEBUG("[stream]: " args)
63 #define OSS_DEBUG_COE(args...) OSS_DEBUG("[coerce]: " args)
64 #define OSS_DEBUG_AJ(args...) OSS_DEBUG("[audio-job]: " args)
65 #define OSS_CRITICAL(args...) __OSS_DEBUG__("CRITICAL: " args)
67 #define audio_dev "/dev/dsp"
70 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_oss);
71 DEFINE_AUDIO_DEVICE_SIMPLE(sound_oss);
75 sound_oss_mark(ad_device_data *devdata)
77 sound_oss_data_t *sod = devdata;
79 mark_object(sod->device);
85 sound_oss_print(Lisp_Object device, Lisp_Object pcfun, int ef)
87 sound_oss_data_t *sod = NULL;
89 sod = get_audio_device_data(device);
90 /* cannot use incomplete or corrupt audio devices */
91 if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sod == NULL) {
92 write_c_string(" VOID", pcfun);
93 /* now that we are here, mark AO device as dead */
94 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
98 /* info about the connected output plugin */
99 write_c_string(" :device ", pcfun);
100 if (NILP(sod->device))
101 write_c_string("\"/dev/dsp\"", pcfun);
103 print_internal(sod->device, pcfun, ef);
106 write_c_string(" :busy t", pcfun);
108 write_c_string(" :busy nil", pcfun);
110 if (sod->keep_open) {
111 write_c_string(" :keep-open t", pcfun);
113 write_c_string(" :keep-open nil", pcfun);
120 sound_oss_open_device(sound_oss_data_t *sod)
122 if (sod->device_fd < 0) {
124 if ((sod->device_fd = open(audio_dev, O_WRONLY, 0)) < 0)
125 return sod->device_fd;
132 sound_oss_close_device(sound_oss_data_t *sod)
135 if (sod->device_fd < 0)
142 ioctl(sod->device_fd, SNDCTL_DSP_SYNC, NULL);
143 ioctl(sod->device_fd, SNDCTL_DSP_RESET, NULL);
145 OSS_DEBUG_HW("device file closed\n");
146 close(sod->device_fd);
153 sound_oss_init_device(sound_oss_data_t *sod, sound_oss_aj_data_t *sosd)
156 AFMT_S16_LE, AFMT_S16_BE, AFMT_U8,
158 int i, num_tmp = sizeof(tmp)/sizeof(int), fd = sod->device_fd;
160 if (ioctl(sod->device_fd, SNDCTL_DSP_SYNC, NULL) < 0) {
161 OSS_DEBUG_HW("SNDCTL_DSP_SYNC failed.\n");
165 /* Initialize sound hardware with preferred parameters */
167 /* try to set channels */
168 sosd->channels = (sosd->mtap->channels) -1;
169 if (ioctl(sod->device_fd, SNDCTL_DSP_STEREO, &sosd->channels) < 0) {
170 OSS_DEBUG_HW("cannot set channels\n");
174 if (++sosd->channels != sosd->mtap->channels) {
175 if (sosd->channels == 1) {
176 /* mono, source is stereo */
177 ADD_MEDIA_SAMPLE_EFFECT(
178 sosd->coe_chain, sosd->coe_ch_cnt,
179 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
180 OSS_DEBUG_COE("STEREO->MONO coerce.\n");
181 } else if (sosd->channels == 2) {
182 /* stereo, source is mono */
183 ADD_MEDIA_SAMPLE_EFFECT(
184 sosd->coe_chain, sosd->coe_ch_cnt,
185 MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch), NULL);
186 OSS_DEBUG_COE("MONO->STEREO coerce.\n");
189 OSS_DEBUG_HW("Hardware supports %d channels, "
190 "source has %d channels.\n",
191 sosd->channels, sosd->mtap->channels);
197 /* we try some sample formats */
199 OSS_DEBUG("trying %d formats\n", num_tmp);
200 while (i < num_tmp && ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &tmp[i]) < 0) {
205 OSS_DEBUG_HW("Your soundcard is bullshit.\n");
211 OSS_DEBUG_HW("Using U8.\n");
212 sosd->msf = sxe_msf_U8;
213 sosd->framesize = sosd->channels * sizeof(uint8_t);
217 OSS_DEBUG_HW("Using S16.\n");
218 sosd->msf = sxe_msf_S16;
219 sosd->framesize = sosd->channels * sizeof(int16_t);
222 OSS_DEBUG_HW(".oO{ I must not be here }\n");
228 /* The PCSP driver does not support reading of the sampling rate via the
229 SOUND_PCM_READ_RATE ioctl; determine "the_speed" here */
230 sosd->samplerate = sosd->mtap->samplerate;
231 if (ioctl(sod->device_fd, SNDCTL_DSP_SPEED, &sosd->samplerate) < 0 ||
232 sosd->samplerate > 1.02 * sosd->mtap->samplerate ||
233 sosd->samplerate < 0.98 * sosd->mtap->samplerate) {
234 OSS_DEBUG_HW("OSS cannot set rate: %d\n",
236 /* actually we should use the rerate effect */
240 #if 0 /* stupid mixer device */
241 /* Use the mixer device for setting the playback volume */
242 if (sod->device_fd > 0) {
245 /* Do not signal an error, if volume control is unavailable! */
246 ioctl(sod->device_fd, SOUND_MIXER_WRITE_PCM, &vol);
253 static ad_device_data *
254 sound_oss_create(Lisp_Object oss_options)
257 sound_oss_data_t *sod = NULL;
259 /* option keywords */
260 Lisp_Object opt_device;
261 Lisp_Object opt_keepopen;
264 opt_device = Fplist_get(oss_options, Q_device, Qnil);
265 if (!NILP(opt_device) && !STRINGP(opt_device)) {
266 wrong_type_argument(Qstringp, opt_device);
270 opt_keepopen = Fplist_get(oss_options, Q_keep_open, Qnil);
271 if (!NILP(opt_keepopen))
274 /* initialise and fill */
275 sod = xnew_and_zero(sound_oss_data_t);
276 sod->device = opt_device;
277 sod->keep_open = keep_open;
279 SXE_MUTEX_INIT(&sod->mtx);
281 /* Open the device */
288 return (ad_device_data*)sod;
292 sound_oss_finish(ad_device_data *data)
294 sound_oss_data_t *sod = data;
298 sound_oss_close_device(sod);
299 SXE_MUTEX_FINI(&sod->mtx);
301 OSS_DEBUG("audio-device finished.\n");
308 sound_oss_change_volume(audio_job_t aj, audio_job_event_args_t args)
310 SXE_MUTEX_LOCK(&aj->mtx);
311 aj->volume = args->volume_args;
312 SXE_MUTEX_UNLOCK(&aj->mtx);
316 sound_oss_change_rate(audio_job_t aj, audio_job_event_args_t args)
318 SXE_MUTEX_LOCK(&aj->mtx);
319 aj->ratetrafo = args->rate_args;
320 SXE_MUTEX_UNLOCK(&aj->mtx);
324 sound_oss_change_state(audio_job_t aj, audio_job_event_args_t args)
326 SXE_MUTEX_LOCK(&aj->mtx);
327 switch (args->state_args) {
329 OSS_DEBUG_AJ("->pause state\n");
330 aj->play_state = MTPSTATE_PAUSE;
333 OSS_DEBUG_AJ("->resume state\n");
334 aj->play_state = MTPSTATE_RUN;
337 OSS_DEBUG_AJ("->start state\n");
340 OSS_DEBUG_AJ("->stop state\n");
341 aj->play_state = MTPSTATE_STOP;
343 case no_audio_job_change_states:
345 OSS_DEBUG_AJ("->unknown state\n");
348 SXE_MUTEX_UNLOCK(&aj->mtx);
352 sound_oss_handle_aj_events(audio_job_t aj)
353 __attribute__((always_inline));
355 sound_oss_handle_aj_events(audio_job_t aj)
357 sound_oss_aj_data_t *sasd;
358 audio_job_event_t ev = NULL;
361 assert(audio_job_queue(aj));
364 SXE_MUTEX_LOCK(&aj->mtx);
365 sasd = audio_job_device_data(aj);
366 SXE_SET_UNUSED(sasd);
368 if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
369 SXE_MUTEX_UNLOCK(&aj->mtx);
372 SXE_MUTEX_UNLOCK(&aj->mtx);
374 OSS_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
375 switch (audio_job_event_kind(ev)) {
376 case aj_change_state:
377 OSS_DEBUG_AJ("change state event\n");
378 sound_oss_change_state(aj, &audio_job_event_args(ev));
380 case aj_change_volume:
381 OSS_DEBUG_AJ("change volume event\n");
382 sound_oss_change_volume(aj, &audio_job_event_args(ev));
385 OSS_DEBUG_AJ("change rate event\n");
386 sound_oss_change_rate(aj, &audio_job_event_args(ev));
388 case no_audio_job_event_kinds:
390 OSS_CRITICAL("unknown event\n");
393 free_audio_job_event(ev);
395 #endif /* EF_USE_ASYNEQ */
398 sound_oss_play(audio_job_t aj)
401 Lisp_Media_Stream *ms;
402 media_substream *mss;
404 media_thread_play_state mtp;
407 Lisp_Audio_Device *lad = NULL;
408 sound_oss_data_t *sod = NULL;
412 sxe_media_sample_t *tmpbuf;
417 /* subthread stuff */
418 sound_oss_aj_data_t _sosd, *sosd = &_sosd;
419 sxe_mse_volume_args _volargs, *volargs = &_volargs;
420 sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
422 int alloced_myself = 0;
424 SOUND_UNPACK_MT(aj, device, ms, mss, lad, sod, sosd->mtap);
426 SXE_MUTEX_LOCK(&sod->mtx);
428 OSS_DEBUG_HW("Device locked.\n");
429 message(GETTEXT("audio-oss: "
431 /* this lock is probably unnecessary */
432 SXE_MUTEX_UNLOCK(&sod->mtx);
438 /* okay, njsf said /dev/dsp writing is not mt safe,
439 * also i hate OSS, so let's block everything here :) -hroptatyr
441 #if defined(HAVE_THREADS) && 0
442 pthread_mutex_lock(&mss->substream_mutex);
445 if (sound_oss_open_device(sod) < 0) {
446 OSS_DEBUG_HW("Opening device failed.\n");
449 message(GETTEXT("audio-oss: "
450 "Opening OSS device failed."));
451 sound_oss_close_device(sod);
452 SXE_MUTEX_UNLOCK(&sod->mtx);
457 sosd->paused = sosd->volume = 0;
458 sosd->samplerate = sosd->channels = 0;
459 sosd->coe_ch_cnt = 0;
461 if (sound_oss_init_device(sod, sosd) < 0) {
462 OSS_DEBUG_HW("Device not configurable.\n");
464 message(GETTEXT("audio-oss: "
465 "Cannot access OSS device."));
466 sound_oss_close_device(sod);
467 SXE_MUTEX_UNLOCK(&sod->mtx);
470 if(sosd->channels==0) {
471 message(GETTEXT("audio-oss: "
473 sound_oss_close_device(sod);
474 SXE_MUTEX_UNLOCK(&sod->mtx);
479 /* the volume effect */
480 ADD_MEDIA_SAMPLE_EFFECT(
481 sosd->coe_chain, sosd->coe_ch_cnt,
482 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
483 volargs->num_channels = sosd->channels;
485 /* the rerate effect */
486 ADD_MEDIA_SAMPLE_EFFECT(
487 sosd->coe_chain, sosd->coe_ch_cnt,
488 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
489 rrargs->num_channels = sosd->channels;
490 rrargs->srcrate = rrargs->tgtrate = 1;
492 OSS_DEBUG_COE("have %d coerce functions in my chain.\n",
496 XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
497 SXE_MUTEX_UNLOCK(&sod->mtx);
499 /* rewind the stream */
500 media_stream_meth(ms, rewind)(mss);
502 /* play chunks of the stream */
503 SXE_MUTEX_LOCK(&aj->mtx);
504 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
506 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
507 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
509 tmpbuf = (sxe_media_sample_t*)aj->buffer;
510 resolution = (sosd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
513 SXE_MUTEX_UNLOCK(&aj->mtx);
515 while (aj->play_state != MTPSTATE_STOP) {
518 if (audio_job_queue(aj)) {
519 sound_oss_handle_aj_events(aj);
523 SXE_MUTEX_LOCK(&aj->mtx);
524 mtp = aj->play_state;
525 SXE_MUTEX_UNLOCK(&aj->mtx);
531 /* otherwise we simply fetch a new bunch of samples */
532 len = media_stream_meth(ms, read)(
533 mss, aj->buffer, resolution);
535 OSS_DEBUG_S("finished\n");
536 SXE_MUTEX_LOCK(&aj->mtx);
537 aj->play_state = MTPSTATE_STOP;
538 SXE_MUTEX_UNLOCK(&aj->mtx);
541 /* set up the volume args */
542 volargs->volume[0] = volargs->volume[1] =
544 /* set up the rerate args */
545 rrargs->tweak = aj->ratetrafo;
547 /* coerce the stuff */
548 tmplen = sosd->channels*len;
549 for (i = 0; i < sosd->coe_ch_cnt; i++) {
550 OSS_DEBUG_COE("calling coerce "
551 "%d on b:0x%x l:%d\n",
552 i, (unsigned int)tmpbuf, tmplen);
553 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
555 tmpbuf, tmpbuf, tmplen);
558 /* bring back to S16 or U8 */
559 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sosd->msf)(
560 aj->buffer, aj->buffer, tmplen);
562 /* convert tmplen back to number of frames */
563 natlen = tmplen * sosd->framesize / sosd->channels;
567 OSS_DEBUG_S("remaining cruft: %d bytes\n", natlen);
568 if ((written = write(fd, bptr, natlen)) < 0) {
569 OSS_DEBUG_S("ERROR in write()\n");
571 } else if (written) {
576 ioctl(fd, SNDCTL_DSP_SYNC, NULL);
580 OSS_DEBUG("sleeping for %d\n", resolution);
583 case MTPSTATE_UNKNOWN:
585 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
587 OSS_DEBUG("ACK, quit\n");
588 SXE_MUTEX_LOCK(&aj->mtx);
589 aj->play_state = MTPSTATE_STOP;
590 SXE_MUTEX_UNLOCK(&aj->mtx);
595 /* Now cleanup all used resources */
597 SXE_MUTEX_LOCK(&aj->mtx);
598 if (alloced_myself && aj->buffer) {
602 aj->buffer_alloc_size = 0;
603 SXE_MUTEX_UNLOCK(&aj->mtx);
605 sound_oss_close_device(sod);
607 #if defined(HAVE_THREADS) && 0
608 pthread_mutex_unlock(&mss->substream_mutex);