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 if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
279 SXE_MUTEX_UNLOCK(&aj->mtx);
282 SXE_MUTEX_UNLOCK(&aj->mtx);
284 ALSA_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
285 switch (audio_job_event_kind(ev)) {
286 case aj_change_state:
287 ALSA_DEBUG_AJ("change state event\n");
288 sound_alsa_change_state(aj, &audio_job_event_args(ev));
290 case aj_change_volume:
291 ALSA_DEBUG_AJ("change volume event\n");
292 sound_alsa_change_volume(aj, &audio_job_event_args(ev));
295 ALSA_DEBUG_AJ("change rate event\n");
296 sound_alsa_change_rate(aj, &audio_job_event_args(ev));
299 case no_audio_job_event_kinds:
301 ALSA_CRITICAL("unknown event\n");
304 free_audio_job_event(ev);
306 #endif /* EF_USE_ASYNEQ */
309 sound_alsa_prepare_device(sound_alsa_data_t *sad, sound_alsa_aj_data_t *sasd)
312 snd_pcm_state_t state;
313 snd_pcm_format_t tmpfmt[] = {
314 SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S24,
316 SND_PCM_FORMAT_FLOAT,
318 SND_PCM_FORMAT_UNKNOWN };
319 int err = 0, i, num_tmpfmt = sizeof(tmpfmt)/sizeof(snd_pcm_format_t);
321 sasd->format = SND_PCM_FORMAT_UNKNOWN;
324 switch (sasd->mtap->channels) {
326 if (snd_pcm_hw_params_test_channels(
327 sad->handle, sad->hwparams, 1) == 0) {
329 ALSA_DEBUG_HW("Using MONO.\n");
330 } else if (!snd_pcm_hw_params_test_channels(
331 sad->handle, sad->hwparams, 2)) {
333 ADD_MEDIA_SAMPLE_EFFECT(
334 sasd->coe_chain, sasd->coe_ch_cnt,
335 MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch), NULL);
336 ALSA_DEBUG_HW("MONO->STEREO coerce.\n");
340 if (snd_pcm_hw_params_test_channels(
341 sad->handle, sad->hwparams, 2) == 0) {
343 ALSA_DEBUG_HW("Using STEREO.\n");
344 } else if (!snd_pcm_hw_params_test_channels(
345 sad->handle, sad->hwparams, 1)) {
347 ADD_MEDIA_SAMPLE_EFFECT(
348 sasd->coe_chain, sasd->coe_ch_cnt,
349 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
350 ALSA_DEBUG_HW("STEREO->MONO coerce.\n");
354 /* more channels here */
359 if (sasd->channels == 0 ||
360 (err = snd_pcm_hw_params_set_channels(
361 sad->handle, sad->hwparams, sasd->channels)) < 0) {
362 message(GETTEXT("audio-alsa: "
363 "Setting channels failed: %s."),
368 /* now we try to set a useful format */
369 ALSA_DEBUG("trying %d formats\n", num_tmpfmt);
371 while (i < num_tmpfmt &&
372 (snd_pcm_hw_params_test_format(
373 sad->handle, sad->hwparams, tmpfmt[i])))
375 if (i == num_tmpfmt) {
376 ALSA_DEBUG_HW("Setting sample format failed :(.\n");
378 message(GETTEXT("audio-alsa: "
379 "Setting sample format failed."));
383 switch ((unsigned int)(sasd->format = tmpfmt[i])) {
384 case SND_PCM_FORMAT_U8:
385 sasd->msf = sxe_msf_U8;
386 sasd->framesize = sasd->channels * sizeof(uint8_t);
387 ALSA_DEBUG_HW("Using U8.\n");
389 case SND_PCM_FORMAT_S16:
390 sasd->msf = sxe_msf_S16;
391 sasd->framesize = sasd->channels * sizeof(int16_t);
392 ALSA_DEBUG_HW("Using S16.\n");
394 case SND_PCM_FORMAT_S24:
395 sasd->msf = sxe_msf_S24;
396 sasd->framesize = sasd->channels * sizeof(int32_t);
397 ALSA_DEBUG_HW("Using S24.\n");
399 case SND_PCM_FORMAT_S32:
400 sasd->msf = sxe_msf_S32;
401 sasd->framesize = sasd->channels * sizeof(int32_t);
402 ALSA_DEBUG_HW("Using S32.\n");
404 case SND_PCM_FORMAT_FLOAT:
405 sasd->msf = sxe_msf_FLT;
406 sasd->framesize = sasd->channels * sizeof(float);
407 ALSA_DEBUG_HW("Using FLT.\n");
413 /* now set the format */
414 if ((err = snd_pcm_hw_params_set_format(
415 sad->handle, sad->hwparams, sasd->format)) < 0) {
416 ALSA_DEBUG_HW("Setting sample format failed: %s.\n",
419 message(GETTEXT("audio-alsa: "
420 "Setting sample format failed: %s."),
425 sasd->samplerate = sasd->mtap->samplerate;
426 if ((err = snd_pcm_hw_params_set_rate_near(
427 sad->handle, sad->hwparams, &sasd->samplerate, 0)) < 0) {
428 ALSA_DEBUG_HW("Setting sample rate failed: %s.\n",
431 message(GETTEXT("audio-alsa: "
432 "Setting sample rate failed: %s."),
436 /* we could feed the coerce chain with the rerate module */
437 if (sasd->samplerate != sasd->mtap->samplerate)
438 ALSA_DEBUG_HW("had to adapt samplerate, old: %d, new: %d.\n",
439 sasd->mtap->samplerate, sasd->samplerate);
441 ALSA_DEBUG_HW("Using samplerate: %d.\n", sasd->samplerate);
443 /* now set all the params */
444 if ((err = snd_pcm_hw_params(sad->handle, sad->hwparams)) < 0) {
445 message(GETTEXT("audio-alsa: "
446 "Setting parameters failed: %s."),
451 if ((err = snd_pcm_prepare(sad->handle)) < 0) {
452 message(GETTEXT("audio-alsa: "
453 "Cannot prepare ALSA device: %s."),
458 if ((state = snd_pcm_state(sad->handle)) != SND_PCM_STATE_PREPARED) {
459 message(GETTEXT("audio-alsa: "
460 "Cannot prepare ALSA device."));
468 sound_alsa_close_device(sound_alsa_data_t *sad)
470 SXE_MUTEX_LOCK(&sad->mtx);
472 if (sad->handle == NULL) {
473 SXE_MUTEX_UNLOCK(&sad->mtx);
477 if (sad->keep_open) {
478 SXE_MUTEX_UNLOCK(&sad->mtx);
483 snd_pcm_hw_free(sad->handle);
484 snd_pcm_close(sad->handle);
486 ALSA_DEBUG_HW("device handle closed\n");
488 SXE_MUTEX_UNLOCK(&sad->mtx);
494 sound_alsa_play(audio_job_t aj)
497 Lisp_Media_Stream *ms;
498 media_substream *mss;
500 media_thread_play_state mtp;
503 Lisp_Audio_Device *lad = NULL;
504 sound_alsa_data_t *sad = NULL;
506 size_t len = 0, tmplen = 0;
507 sxe_media_sample_t *tmpbuf;
508 int err = 0, i, resolution;
509 /* subthread stuff */
510 sound_alsa_aj_data_t _sasd, *sasd = &_sasd;
511 sxe_mse_volume_args _volargs, *volargs = &_volargs;
512 sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
514 int alloced_myself = 0;
516 SOUND_UNPACK_MT(aj, device, ms, mss, lad, sad, sasd->mtap);
518 SXE_MUTEX_LOCK(&sad->mtx);
520 message(GETTEXT("audio-alsa: "
522 /* this lock is unnecessary, we _could_ write concurrently
523 provided that the concurrent media streams have set the ALSA
524 device to the exact same hardware parameters.
525 In cleartext this means, we have to convert all sample data
526 to a common format, e.g. 48000Hz/STEREO/FLT or the like.
527 However, I'm tired at the moment so I leave this lock here.
529 SXE_MUTEX_UNLOCK(&sad->mtx);
535 /* trigger alsa, the device name should be an option */
536 if ((err = sound_alsa_open_device(sad)) < 0) {
537 ALSA_DEBUG_HW("Opening ALSA device failed: %s.",
541 message(GETTEXT("audio-alsa: "
542 "Opening ALSA device failed: %s."),
544 SXE_MUTEX_UNLOCK(&sad->mtx);
545 sound_alsa_close_device(sad);
549 if ((err = sound_alsa_init_hardware(sad)) < 0) {
550 ALSA_DEBUG_HW("Device not configurable: %s.\n",
553 message(GETTEXT("audio-alsa: "
554 "Cannot access ALSA device: %s."),
556 SXE_MUTEX_UNLOCK(&sad->mtx);
557 sound_alsa_close_device(sad);
562 sasd->samplerate = sasd->channels = 0;
564 sasd->coe_ch_cnt = 0;
566 if ((err = sound_alsa_prepare_device(sad, sasd)) < 0) {
567 SXE_MUTEX_UNLOCK(&sad->mtx);
568 sound_alsa_close_device(sad);
572 /* the volume effect */
573 ADD_MEDIA_SAMPLE_EFFECT(
574 sasd->coe_chain, sasd->coe_ch_cnt,
575 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
576 volargs->num_channels = sasd->channels;
578 /* the rerate effect */
579 ADD_MEDIA_SAMPLE_EFFECT(
580 sasd->coe_chain, sasd->coe_ch_cnt,
581 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
582 rrargs->num_channels = sasd->channels;
583 rrargs->srcrate = rrargs->tgtrate = 1;
585 ALSA_DEBUG_COE("have %d coerce functions in my chain.\n",
589 media_stream_meth(ms, rewind)(mss);
591 XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
593 /* ... and play it */
594 SXE_MUTEX_LOCK(&aj->mtx);
595 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
597 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
598 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
600 resolution = (sasd->samplerate * MTPSTATE_REACT_TIME) / 1000000;
601 tmpbuf = (sxe_media_sample_t*)aj->buffer;
602 SXE_MUTEX_UNLOCK(&aj->mtx);
603 SXE_MUTEX_UNLOCK(&sad->mtx);
605 while (aj->play_state != MTPSTATE_STOP) {
608 if (audio_job_queue(aj)) {
609 sound_alsa_handle_aj_events(aj);
613 SXE_MUTEX_LOCK(&aj->mtx);
614 mtp = aj->play_state;
615 SXE_MUTEX_UNLOCK(&aj->mtx);
618 len = media_stream_meth(ms, read)(
619 mss, aj->buffer, resolution);
621 ALSA_DEBUG_S("finished\n");
622 SXE_MUTEX_LOCK(&aj->mtx);
623 aj->play_state = MTPSTATE_STOP;
624 SXE_MUTEX_UNLOCK(&aj->mtx);
628 /* set up the volume args */
629 volargs->volume[0] = volargs->volume[1] =
631 /* set up the rerate args */
632 rrargs->tweak = aj->ratetrafo;
634 /* coerce the stuff, tmplen is in samples */
635 tmplen = sasd->channels*len;
636 for (i = 0; i < sasd->coe_ch_cnt; i++) {
637 ALSA_DEBUG_COE("calling coerce "
638 "%d on b:0x%x l:%d\n",
639 i, (unsigned int)tmpbuf, tmplen);
640 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
642 tmpbuf, tmpbuf, tmplen);
645 /* bring back to S16 or U8 */
646 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
647 aj->buffer, aj->buffer, tmplen);
649 snd_pcm_writei(sad->handle, aj->buffer, len);
652 ALSA_DEBUG("sleeping for %d\n", resolution);
653 memset(aj->buffer, 0, resolution*sasd->framesize);
654 snd_pcm_writei(sad->handle, aj->buffer,
658 case MTPSTATE_UNKNOWN:
660 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
662 ALSA_DEBUG("ACK, quit\n");
663 SXE_MUTEX_LOCK(&aj->mtx);
664 aj->play_state = MTPSTATE_STOP;
665 SXE_MUTEX_UNLOCK(&aj->mtx);
673 SXE_MUTEX_LOCK(&aj->mtx);
674 aj->state = MTSTATE_FINISHED;
676 /* -- Close and shutdown -- */
677 if (alloced_myself && aj->buffer) {
681 aj->buffer_alloc_size = 0;
682 SXE_MUTEX_UNLOCK(&aj->mtx);
684 sound_alsa_close_device(sad);
691 /* sound-alsa.c ends here */