1 /* sound-alsa.c - play a sound over the alsa
3 Copyright (C) 2006 Sebastian Freundt
5 This file is part of SXEmacs
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 /* Synched up with: Not in FSF. */
36 #include "sound-alsa.h"
40 #define MYSELF ADRIVER_ALSA
42 #define __ALSA_DEBUG__(args...) fprintf(stderr, "ALSA " args)
43 #ifndef ALSA_DEBUG_FLAG
44 #define ALSA_DEBUG(args...)
46 #define ALSA_DEBUG(args...) __ALSA_DEBUG__(args)
48 #define ALSA_DEBUG_HW(args...) ALSA_DEBUG("[hardware]: " args)
49 #define ALSA_DEBUG_S(args...) ALSA_DEBUG("[stream]: " args)
50 #define ALSA_DEBUG_COE(args...) ALSA_DEBUG("[coerce]: " args)
51 #define ALSA_DEBUG_AJ(args...) ALSA_DEBUG("[audio-job]: " args)
52 #define ALSA_CRITICAL(args...) __ALSA_DEBUG__("CRITICAL: " args)
55 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_alsa);
56 DEFINE_AUDIO_DEVICE_SIMPLE(sound_alsa);
60 sound_alsa_mark(ad_device_data *devdata)
62 sound_alsa_data_t *sad = devdata;
64 mark_object(sad->device);
70 sound_alsa_print(Lisp_Object device, Lisp_Object pcfun, int ef)
72 sound_alsa_data_t *sad = NULL;
73 sad = get_audio_device_data(device);
74 /* cannot use incomplete or corrupt audio devices */
75 if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sad == NULL) {
76 write_c_string(" VOID", pcfun);
77 /* now that we are here, mark AO device as dead */
78 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
82 /* info about the connected output plugin */
83 write_c_string(" :device ", pcfun);
84 if (NILP(sad->device))
85 write_c_string("#default", pcfun);
87 print_internal(sad->device, pcfun, ef);
90 write_fmt_string(pcfun, " :keep-open t :handle 0x%lx",
91 (long unsigned int)sad->handle);
93 write_c_string(" :keep-open nil", pcfun);
95 write_fmt_str(pcfun, " :params 0x%lx", (long unsigned int)sad->hwparams);
101 sound_alsa_open_device(sound_alsa_data_t *sad)
103 if (sad->handle == NULL) {
105 if (NILP(sad->device))
108 dev = (char*)XSTRING_DATA(sad->device);
110 return snd_pcm_open(&sad->handle, dev,
111 SND_PCM_STREAM_PLAYBACK, 0);
118 sound_alsa_init_hardware(sound_alsa_data_t *sad)
122 if (sad->hwparams == NULL || sad->handle == NULL)
125 /* check if we can configure this device */
126 err = snd_pcm_hw_params_any(sad->handle, sad->hwparams);
130 err = snd_pcm_hw_params_set_access(
131 sad->handle, sad->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
137 sound_alsa_finish(ad_device_data *data)
139 sound_alsa_data_t *sad = data;
141 SXE_MUTEX_LOCK(&sad->mtx);
143 snd_pcm_hw_params_free(sad->hwparams);
144 sad->hwparams = NULL;
147 snd_pcm_hw_free(sad->handle);
148 snd_pcm_close(sad->handle);
151 SXE_MUTEX_UNLOCK(&sad->mtx);
152 SXE_MUTEX_FINI(&sad->mtx);
154 ALSA_DEBUG("audio-device finished.\n");
159 static ad_device_data *
160 sound_alsa_create(Lisp_Object alsa_options)
163 sound_alsa_data_t *sad = NULL;
166 /* option keywords */
167 Lisp_Object opt_device;
168 Lisp_Object opt_keepopen;
171 opt_device = Fplist_get(alsa_options, Q_device, Qnil);
172 if (!NILP(opt_device) && !STRINGP(opt_device)) {
173 wrong_type_argument(Qstringp, opt_device);
177 opt_keepopen = Fplist_get(alsa_options, Q_keep_open, Qnil);
178 if (!NILP(opt_keepopen))
181 /* initialise and fill */
182 sad = xnew_and_zero(sound_alsa_data_t);
183 sad->device = opt_device;
184 sad->keep_open = keep_open;
185 SXE_MUTEX_INIT(&sad->mtx);
187 /* Open the device */
188 if ((err = sound_alsa_open_device(sad)) < 0) {
190 error(GETTEXT("audio-alsa: "
191 "Opening ALSA device failed: %s."),
196 snd_pcm_hw_params_malloc(&sad->hwparams);
198 if ((err = sound_alsa_init_hardware(sad)) < 0) {
199 sound_alsa_finish(sad);
201 error(GETTEXT("Error: audio-alsa: "
202 "Opening ALSA device failed: %s."),
208 snd_pcm_hw_free(sad->handle);
209 snd_pcm_close(sad->handle);
213 return (ad_device_data*)sad;
219 sound_alsa_change_volume(audio_job_t aj, audio_job_event_args_t args)
221 SXE_MUTEX_LOCK(&aj->mtx);
222 aj->volume = args->volume_args;
223 SXE_MUTEX_UNLOCK(&aj->mtx);
227 sound_alsa_change_rate(audio_job_t aj, audio_job_event_args_t args)
229 SXE_MUTEX_LOCK(&aj->mtx);
230 aj->ratetrafo = args->rate_args;
231 SXE_MUTEX_UNLOCK(&aj->mtx);
235 sound_alsa_change_state(audio_job_t aj, audio_job_event_args_t args)
237 SXE_MUTEX_LOCK(&aj->mtx);
238 switch (args->state_args) {
240 ALSA_DEBUG_AJ("->pause state\n");
241 aj->play_state = MTPSTATE_PAUSE;
244 ALSA_DEBUG_AJ("->resume state\n");
245 aj->play_state = MTPSTATE_RUN;
248 ALSA_DEBUG_AJ("->start state\n");
251 ALSA_DEBUG_AJ("->stop state\n");
252 aj->play_state = MTPSTATE_STOP;
255 case no_audio_job_change_states:
257 ALSA_DEBUG_AJ("->unknown state\n");
260 SXE_MUTEX_UNLOCK(&aj->mtx);
264 sound_alsa_handle_aj_events(audio_job_t aj)
265 __attribute__((always_inline));
267 sound_alsa_handle_aj_events(audio_job_t aj)
269 sound_alsa_aj_data_t *sasd;
270 audio_job_event_t ev = NULL;
273 assert(audio_job_queue(aj));
276 SXE_MUTEX_LOCK(&aj->mtx);
277 sasd = audio_job_device_data(aj);
278 SXE_SET_UNUSED(sasd);
280 if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
281 SXE_MUTEX_UNLOCK(&aj->mtx);
284 SXE_MUTEX_UNLOCK(&aj->mtx);
286 ALSA_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
287 switch (audio_job_event_kind(ev)) {
288 case aj_change_state:
289 ALSA_DEBUG_AJ("change state event\n");
290 sound_alsa_change_state(aj, &audio_job_event_args(ev));
292 case aj_change_volume:
293 ALSA_DEBUG_AJ("change volume event\n");
294 sound_alsa_change_volume(aj, &audio_job_event_args(ev));
297 ALSA_DEBUG_AJ("change rate event\n");
298 sound_alsa_change_rate(aj, &audio_job_event_args(ev));
301 case no_audio_job_event_kinds:
303 ALSA_CRITICAL("unknown event\n");
306 free_audio_job_event(ev);
308 #endif /* EF_USE_ASYNEQ */
311 sound_alsa_prepare_device(sound_alsa_data_t *sad, sound_alsa_aj_data_t *sasd)
314 snd_pcm_state_t state;
315 snd_pcm_format_t tmpfmt[] = {
316 SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S24,
318 SND_PCM_FORMAT_FLOAT,
320 SND_PCM_FORMAT_UNKNOWN };
321 int err = 0, i, num_tmpfmt = sizeof(tmpfmt)/sizeof(snd_pcm_format_t);
323 sasd->format = SND_PCM_FORMAT_UNKNOWN;
326 switch (sasd->mtap->channels) {
328 if (snd_pcm_hw_params_test_channels(
329 sad->handle, sad->hwparams, 1) == 0) {
331 ALSA_DEBUG_HW("Using MONO.\n");
332 } else if (!snd_pcm_hw_params_test_channels(
333 sad->handle, sad->hwparams, 2)) {
335 ADD_MEDIA_SAMPLE_EFFECT(
336 sasd->coe_chain, sasd->coe_ch_cnt,
337 MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch), NULL);
338 ALSA_DEBUG_HW("MONO->STEREO coerce.\n");
342 if (snd_pcm_hw_params_test_channels(
343 sad->handle, sad->hwparams, 2) == 0) {
345 ALSA_DEBUG_HW("Using STEREO.\n");
346 } else if (!snd_pcm_hw_params_test_channels(
347 sad->handle, sad->hwparams, 1)) {
349 ADD_MEDIA_SAMPLE_EFFECT(
350 sasd->coe_chain, sasd->coe_ch_cnt,
351 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
352 ALSA_DEBUG_HW("STEREO->MONO coerce.\n");
356 /* more channels here */
361 if (sasd->channels == 0 ||
362 (err = snd_pcm_hw_params_set_channels(
363 sad->handle, sad->hwparams, sasd->channels)) < 0) {
364 message(GETTEXT("audio-alsa: "
365 "Setting channels failed: %s."),
370 /* now we try to set a useful format */
371 ALSA_DEBUG("trying %d formats\n", num_tmpfmt);
373 while (i < num_tmpfmt &&
374 (snd_pcm_hw_params_test_format(
375 sad->handle, sad->hwparams, tmpfmt[i])))
377 if (i == num_tmpfmt) {
378 ALSA_DEBUG_HW("Setting sample format failed :(.\n");
380 message(GETTEXT("audio-alsa: "
381 "Setting sample format failed."));
385 switch ((unsigned int)(sasd->format = tmpfmt[i])) {
386 case SND_PCM_FORMAT_U8:
387 sasd->msf = sxe_msf_U8;
388 sasd->framesize = sasd->channels * sizeof(uint8_t);
389 ALSA_DEBUG_HW("Using U8.\n");
391 case SND_PCM_FORMAT_S16:
392 sasd->msf = sxe_msf_S16;
393 sasd->framesize = sasd->channels * sizeof(int16_t);
394 ALSA_DEBUG_HW("Using S16.\n");
396 case SND_PCM_FORMAT_S24:
397 sasd->msf = sxe_msf_S24;
398 sasd->framesize = sasd->channels * sizeof(int32_t);
399 ALSA_DEBUG_HW("Using S24.\n");
401 case SND_PCM_FORMAT_S32:
402 sasd->msf = sxe_msf_S32;
403 sasd->framesize = sasd->channels * sizeof(int32_t);
404 ALSA_DEBUG_HW("Using S32.\n");
406 case SND_PCM_FORMAT_FLOAT:
407 sasd->msf = sxe_msf_FLT;
408 sasd->framesize = sasd->channels * sizeof(float);
409 ALSA_DEBUG_HW("Using FLT.\n");
415 /* now set the format */
416 if ((err = snd_pcm_hw_params_set_format(
417 sad->handle, sad->hwparams, sasd->format)) < 0) {
418 ALSA_DEBUG_HW("Setting sample format failed: %s.\n",
421 message(GETTEXT("audio-alsa: "
422 "Setting sample format failed: %s."),
427 sasd->samplerate = sasd->mtap->samplerate;
428 if ((err = snd_pcm_hw_params_set_rate_near(
429 sad->handle, sad->hwparams, &sasd->samplerate, 0)) < 0) {
430 ALSA_DEBUG_HW("Setting sample rate failed: %s.\n",
433 message(GETTEXT("audio-alsa: "
434 "Setting sample rate failed: %s."),
438 /* we could feed the coerce chain with the rerate module */
439 if (sasd->samplerate != sasd->mtap->samplerate)
440 ALSA_DEBUG_HW("had to adapt samplerate, old: %d, new: %d.\n",
441 sasd->mtap->samplerate, sasd->samplerate);
443 ALSA_DEBUG_HW("Using samplerate: %d.\n", sasd->samplerate);
445 /* now set all the params */
446 if ((err = snd_pcm_hw_params(sad->handle, sad->hwparams)) < 0) {
447 message(GETTEXT("audio-alsa: "
448 "Setting parameters failed: %s."),
453 if ((err = snd_pcm_prepare(sad->handle)) < 0) {
454 message(GETTEXT("audio-alsa: "
455 "Cannot prepare ALSA device: %s."),
460 if ((state = snd_pcm_state(sad->handle)) != SND_PCM_STATE_PREPARED) {
461 message(GETTEXT("audio-alsa: "
462 "Cannot prepare ALSA device."));
470 sound_alsa_close_device(sound_alsa_data_t *sad)
472 SXE_MUTEX_LOCK(&sad->mtx);
474 if (sad->handle == NULL) {
475 SXE_MUTEX_UNLOCK(&sad->mtx);
479 if (sad->keep_open) {
480 SXE_MUTEX_UNLOCK(&sad->mtx);
485 snd_pcm_hw_free(sad->handle);
486 snd_pcm_close(sad->handle);
488 ALSA_DEBUG_HW("device handle closed\n");
490 SXE_MUTEX_UNLOCK(&sad->mtx);
496 sound_alsa_play(audio_job_t aj)
499 Lisp_Media_Stream *ms;
500 media_substream *mss;
502 media_thread_play_state mtp;
505 Lisp_Audio_Device *lad = NULL;
506 sound_alsa_data_t *sad = NULL;
508 size_t len = 0, tmplen = 0;
509 sxe_media_sample_t *tmpbuf;
510 int err = 0, i, resolution;
511 /* subthread stuff */
512 sound_alsa_aj_data_t _sasd, *sasd = &_sasd;
513 sxe_mse_volume_args _volargs, *volargs = &_volargs;
514 sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
516 int alloced_myself = 0;
518 SOUND_UNPACK_MT(aj, device, ms, mss, lad, sad, sasd->mtap);
520 SXE_MUTEX_LOCK(&sad->mtx);
522 message(GETTEXT("audio-alsa: "
524 /* this lock is unnecessary, we _could_ write concurrently
525 provided that the concurrent media streams have set the ALSA
526 device to the exact same hardware parameters.
527 In cleartext this means, we have to convert all sample data
528 to a common format, e.g. 48000Hz/STEREO/FLT or the like.
529 However, I'm tired at the moment so I leave this lock here.
531 SXE_MUTEX_UNLOCK(&sad->mtx);
537 /* trigger alsa, the device name should be an option */
538 if ((err = sound_alsa_open_device(sad)) < 0) {
539 ALSA_DEBUG_HW("Opening ALSA device failed: %s.",
543 message(GETTEXT("audio-alsa: "
544 "Opening ALSA device failed: %s."),
546 SXE_MUTEX_UNLOCK(&sad->mtx);
547 sound_alsa_close_device(sad);
551 if ((err = sound_alsa_init_hardware(sad)) < 0) {
552 ALSA_DEBUG_HW("Device not configurable: %s.\n",
555 message(GETTEXT("audio-alsa: "
556 "Cannot access ALSA device: %s."),
558 SXE_MUTEX_UNLOCK(&sad->mtx);
559 sound_alsa_close_device(sad);
564 sasd->samplerate = sasd->channels = 0;
566 sasd->coe_ch_cnt = 0;
568 if ((err = sound_alsa_prepare_device(sad, sasd)) < 0) {
569 SXE_MUTEX_UNLOCK(&sad->mtx);
570 sound_alsa_close_device(sad);
574 /* the volume effect */
575 ADD_MEDIA_SAMPLE_EFFECT(
576 sasd->coe_chain, sasd->coe_ch_cnt,
577 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
578 volargs->num_channels = sasd->channels;
580 /* the rerate effect */
581 ADD_MEDIA_SAMPLE_EFFECT(
582 sasd->coe_chain, sasd->coe_ch_cnt,
583 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
584 rrargs->num_channels = sasd->channels;
585 rrargs->srcrate = rrargs->tgtrate = 1;
587 ALSA_DEBUG_COE("have %d coerce functions in my chain.\n",
591 media_stream_meth(ms, rewind)(mss);
593 XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
595 /* ... and play it */
596 SXE_MUTEX_LOCK(&aj->mtx);
597 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
599 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
600 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
602 resolution = (sasd->samplerate * MTPSTATE_REACT_TIME) / 1000000;
603 tmpbuf = (sxe_media_sample_t*)aj->buffer;
604 SXE_MUTEX_UNLOCK(&aj->mtx);
605 SXE_MUTEX_UNLOCK(&sad->mtx);
607 while (aj->play_state != MTPSTATE_STOP) {
610 if (audio_job_queue(aj)) {
611 sound_alsa_handle_aj_events(aj);
615 SXE_MUTEX_LOCK(&aj->mtx);
616 mtp = aj->play_state;
617 SXE_MUTEX_UNLOCK(&aj->mtx);
620 len = media_stream_meth(ms, read)(
621 mss, aj->buffer, resolution);
623 ALSA_DEBUG_S("finished\n");
624 SXE_MUTEX_LOCK(&aj->mtx);
625 aj->play_state = MTPSTATE_STOP;
626 SXE_MUTEX_UNLOCK(&aj->mtx);
630 /* set up the volume args */
631 volargs->volume[0] = volargs->volume[1] =
633 /* set up the rerate args */
634 rrargs->tweak = aj->ratetrafo;
636 /* coerce the stuff, tmplen is in samples */
637 tmplen = sasd->channels*len;
638 for (i = 0; i < sasd->coe_ch_cnt; i++) {
639 ALSA_DEBUG_COE("calling coerce "
640 "%d on b:0x%x l:%d\n",
641 i, (unsigned int)tmpbuf, tmplen);
642 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
644 tmpbuf, tmpbuf, tmplen);
647 /* bring back to S16 or U8 */
648 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
649 aj->buffer, aj->buffer, tmplen);
651 snd_pcm_writei(sad->handle, aj->buffer, len);
654 ALSA_DEBUG("sleeping for %d\n", resolution);
655 memset(aj->buffer, 0, resolution*sasd->framesize);
656 snd_pcm_writei(sad->handle, aj->buffer,
660 case MTPSTATE_UNKNOWN:
662 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
664 ALSA_DEBUG("ACK, quit\n");
665 SXE_MUTEX_LOCK(&aj->mtx);
666 aj->play_state = MTPSTATE_STOP;
667 SXE_MUTEX_UNLOCK(&aj->mtx);
675 SXE_MUTEX_LOCK(&aj->mtx);
676 aj->state = MTSTATE_FINISHED;
678 /* -- Close and shutdown -- */
679 if (alloced_myself && aj->buffer) {
683 aj->buffer_alloc_size = 0;
684 SXE_MUTEX_UNLOCK(&aj->mtx);
686 sound_alsa_close_device(sad);
693 /* sound-alsa.c ends here */