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 char *temp = alloca(48);
75 sad = get_audio_device_data(device);
76 /* cannot use incomplete or corrupt audio devices */
77 if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sad == NULL) {
78 write_c_string(" VOID", pcfun);
79 /* now that we are here, mark AO device as dead */
80 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
84 /* info about the connected output plugin */
85 write_c_string(" :device ", pcfun);
86 if (NILP(sad->device))
87 write_c_string("#default", pcfun);
89 print_internal(sad->device, pcfun, ef);
92 write_c_string(" :keep-open t", pcfun);
93 snprintf(temp, 47, " :handle 0x%lx",
94 (long unsigned int)sad->handle);
95 write_c_string(temp, pcfun);
97 write_c_string(" :keep-open nil", pcfun);
99 snprintf(temp, 47, " :params 0x%lx", (long unsigned int)sad->hwparams);
100 write_c_string(temp, pcfun);
107 sound_alsa_open_device(sound_alsa_data_t *sad)
109 if (sad->handle == NULL) {
111 if (NILP(sad->device))
114 dev = (char*)XSTRING_DATA(sad->device);
116 return snd_pcm_open(&sad->handle, dev,
117 SND_PCM_STREAM_PLAYBACK, 0);
124 sound_alsa_init_hardware(sound_alsa_data_t *sad)
128 if (sad->hwparams == NULL || sad->handle == NULL)
131 /* check if we can configure this device */
132 err = snd_pcm_hw_params_any(sad->handle, sad->hwparams);
136 err = snd_pcm_hw_params_set_access(
137 sad->handle, sad->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
143 sound_alsa_finish(ad_device_data *data)
145 sound_alsa_data_t *sad = data;
147 SXE_MUTEX_LOCK(&sad->mtx);
149 snd_pcm_hw_params_free(sad->hwparams);
150 sad->hwparams = NULL;
153 snd_pcm_hw_free(sad->handle);
154 snd_pcm_close(sad->handle);
157 SXE_MUTEX_UNLOCK(&sad->mtx);
158 SXE_MUTEX_FINI(&sad->mtx);
160 ALSA_DEBUG("audio-device finished.\n");
165 static ad_device_data *
166 sound_alsa_create(Lisp_Object alsa_options)
169 sound_alsa_data_t *sad = NULL;
172 /* option keywords */
173 Lisp_Object opt_device;
174 Lisp_Object opt_keepopen;
177 opt_device = Fplist_get(alsa_options, Q_device, Qnil);
178 if (!NILP(opt_device) && !STRINGP(opt_device)) {
179 wrong_type_argument(Qstringp, opt_device);
183 opt_keepopen = Fplist_get(alsa_options, Q_keep_open, Qnil);
184 if (!NILP(opt_keepopen))
187 /* initialise and fill */
188 sad = xnew_and_zero(sound_alsa_data_t);
189 sad->device = opt_device;
190 sad->keep_open = keep_open;
191 SXE_MUTEX_INIT(&sad->mtx);
193 /* Open the device */
194 if ((err = sound_alsa_open_device(sad)) < 0) {
196 error(GETTEXT("audio-alsa: "
197 "Opening ALSA device failed: %s."),
202 snd_pcm_hw_params_malloc(&sad->hwparams);
204 if ((err = sound_alsa_init_hardware(sad)) < 0) {
205 sound_alsa_finish(sad);
207 error(GETTEXT("Error: audio-alsa: "
208 "Opening ALSA device failed: %s."),
214 snd_pcm_hw_free(sad->handle);
215 snd_pcm_close(sad->handle);
219 return (ad_device_data*)sad;
225 sound_alsa_change_volume(audio_job_t aj, audio_job_event_args_t args)
227 SXE_MUTEX_LOCK(&aj->mtx);
228 aj->volume = args->volume_args;
229 SXE_MUTEX_UNLOCK(&aj->mtx);
233 sound_alsa_change_rate(audio_job_t aj, audio_job_event_args_t args)
235 SXE_MUTEX_LOCK(&aj->mtx);
236 aj->ratetrafo = args->rate_args;
237 SXE_MUTEX_UNLOCK(&aj->mtx);
241 sound_alsa_change_state(audio_job_t aj, audio_job_event_args_t args)
243 SXE_MUTEX_LOCK(&aj->mtx);
244 switch (args->state_args) {
246 ALSA_DEBUG_AJ("->pause state\n");
247 aj->play_state = MTPSTATE_PAUSE;
250 ALSA_DEBUG_AJ("->resume state\n");
251 aj->play_state = MTPSTATE_RUN;
254 ALSA_DEBUG_AJ("->start state\n");
257 ALSA_DEBUG_AJ("->stop state\n");
258 aj->play_state = MTPSTATE_STOP;
261 case no_audio_job_change_states:
263 ALSA_DEBUG_AJ("->unknown state\n");
266 SXE_MUTEX_UNLOCK(&aj->mtx);
270 sound_alsa_handle_aj_events(audio_job_t aj)
271 __attribute__((always_inline));
273 sound_alsa_handle_aj_events(audio_job_t aj)
275 sound_alsa_aj_data_t *sasd;
276 audio_job_event_t ev = NULL;
279 assert(audio_job_queue(aj));
282 SXE_MUTEX_LOCK(&aj->mtx);
283 sasd = audio_job_device_data(aj);
284 if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
285 SXE_MUTEX_UNLOCK(&aj->mtx);
288 SXE_MUTEX_UNLOCK(&aj->mtx);
290 ALSA_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
291 switch (audio_job_event_kind(ev)) {
292 case aj_change_state:
293 ALSA_DEBUG_AJ("change state event\n");
294 sound_alsa_change_state(aj, &audio_job_event_args(ev));
296 case aj_change_volume:
297 ALSA_DEBUG_AJ("change volume event\n");
298 sound_alsa_change_volume(aj, &audio_job_event_args(ev));
301 ALSA_DEBUG_AJ("change rate event\n");
302 sound_alsa_change_rate(aj, &audio_job_event_args(ev));
305 case no_audio_job_event_kinds:
307 ALSA_CRITICAL("unknown event\n");
310 free_audio_job_event(ev);
312 #endif /* EF_USE_ASYNEQ */
315 sound_alsa_prepare_device(sound_alsa_data_t *sad, sound_alsa_aj_data_t *sasd)
318 snd_pcm_state_t state;
319 snd_pcm_format_t tmpfmt[] = {
320 SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S24,
322 SND_PCM_FORMAT_FLOAT,
324 SND_PCM_FORMAT_UNKNOWN };
325 int err = 0, i, num_tmpfmt = sizeof(tmpfmt)/sizeof(snd_pcm_format_t);
327 sasd->format = SND_PCM_FORMAT_UNKNOWN;
330 switch (sasd->mtap->channels) {
332 if (snd_pcm_hw_params_test_channels(
333 sad->handle, sad->hwparams, 1) == 0) {
335 ALSA_DEBUG_HW("Using MONO.\n");
336 } else if (!snd_pcm_hw_params_test_channels(
337 sad->handle, sad->hwparams, 2)) {
339 ADD_MEDIA_SAMPLE_EFFECT(
340 sasd->coe_chain, sasd->coe_ch_cnt,
341 MEDIA_SAMPLE_EFFECT(sxe_mse_1ch_to_2ch), NULL);
342 ALSA_DEBUG_HW("MONO->STEREO coerce.\n");
346 if (snd_pcm_hw_params_test_channels(
347 sad->handle, sad->hwparams, 2) == 0) {
349 ALSA_DEBUG_HW("Using STEREO.\n");
350 } else if (!snd_pcm_hw_params_test_channels(
351 sad->handle, sad->hwparams, 1)) {
353 ADD_MEDIA_SAMPLE_EFFECT(
354 sasd->coe_chain, sasd->coe_ch_cnt,
355 MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
356 ALSA_DEBUG_HW("STEREO->MONO coerce.\n");
360 /* more channels here */
365 if (sasd->channels == 0 ||
366 (err = snd_pcm_hw_params_set_channels(
367 sad->handle, sad->hwparams, sasd->channels)) < 0) {
368 message(GETTEXT("audio-alsa: "
369 "Setting channels failed: %s."),
374 /* now we try to set a useful format */
375 ALSA_DEBUG("trying %d formats\n", num_tmpfmt);
377 while (i < num_tmpfmt &&
378 (snd_pcm_hw_params_test_format(
379 sad->handle, sad->hwparams, tmpfmt[i])))
381 if (i == num_tmpfmt) {
382 ALSA_DEBUG_HW("Setting sample format failed :(.\n");
384 message(GETTEXT("audio-alsa: "
385 "Setting sample format failed."));
389 switch ((unsigned int)(sasd->format = tmpfmt[i])) {
390 case SND_PCM_FORMAT_U8:
391 sasd->msf = sxe_msf_U8;
392 sasd->framesize = sasd->channels * sizeof(uint8_t);
393 ALSA_DEBUG_HW("Using U8.\n");
395 case SND_PCM_FORMAT_S16:
396 sasd->msf = sxe_msf_S16;
397 sasd->framesize = sasd->channels * sizeof(int16_t);
398 ALSA_DEBUG_HW("Using S16.\n");
400 case SND_PCM_FORMAT_S24:
401 sasd->msf = sxe_msf_S24;
402 sasd->framesize = sasd->channels * sizeof(int32_t);
403 ALSA_DEBUG_HW("Using S24.\n");
405 case SND_PCM_FORMAT_S32:
406 sasd->msf = sxe_msf_S32;
407 sasd->framesize = sasd->channels * sizeof(int32_t);
408 ALSA_DEBUG_HW("Using S32.\n");
410 case SND_PCM_FORMAT_FLOAT:
411 sasd->msf = sxe_msf_FLT;
412 sasd->framesize = sasd->channels * sizeof(float);
413 ALSA_DEBUG_HW("Using FLT.\n");
419 /* now set the format */
420 if ((err = snd_pcm_hw_params_set_format(
421 sad->handle, sad->hwparams, sasd->format)) < 0) {
422 ALSA_DEBUG_HW("Setting sample format failed: %s.\n",
425 message(GETTEXT("audio-alsa: "
426 "Setting sample format failed: %s."),
431 sasd->samplerate = sasd->mtap->samplerate;
432 if ((err = snd_pcm_hw_params_set_rate_near(
433 sad->handle, sad->hwparams, &sasd->samplerate, 0)) < 0) {
434 ALSA_DEBUG_HW("Setting sample rate failed: %s.\n",
437 message(GETTEXT("audio-alsa: "
438 "Setting sample rate failed: %s."),
442 /* we could feed the coerce chain with the rerate module */
443 if (sasd->samplerate != sasd->mtap->samplerate)
444 ALSA_DEBUG_HW("had to adapt samplerate, old: %d, new: %d.\n",
445 sasd->mtap->samplerate, sasd->samplerate);
447 ALSA_DEBUG_HW("Using samplerate: %d.\n", sasd->samplerate);
449 /* now set all the params */
450 if ((err = snd_pcm_hw_params(sad->handle, sad->hwparams)) < 0) {
451 message(GETTEXT("audio-alsa: "
452 "Setting parameters failed: %s."),
457 if ((err = snd_pcm_prepare(sad->handle)) < 0) {
458 message(GETTEXT("audio-alsa: "
459 "Cannot prepare ALSA device: %s."),
464 if ((state = snd_pcm_state(sad->handle)) != SND_PCM_STATE_PREPARED) {
465 message(GETTEXT("audio-alsa: "
466 "Cannot prepare ALSA device."));
474 sound_alsa_close_device(sound_alsa_data_t *sad)
476 SXE_MUTEX_LOCK(&sad->mtx);
478 if (sad->handle == NULL) {
479 SXE_MUTEX_UNLOCK(&sad->mtx);
483 if (sad->keep_open) {
484 SXE_MUTEX_UNLOCK(&sad->mtx);
489 snd_pcm_hw_free(sad->handle);
490 snd_pcm_close(sad->handle);
492 ALSA_DEBUG_HW("device handle closed\n");
494 SXE_MUTEX_UNLOCK(&sad->mtx);
500 sound_alsa_play(audio_job_t aj)
503 Lisp_Media_Stream *ms;
504 media_substream *mss;
506 media_thread_play_state mtp;
509 Lisp_Audio_Device *lad = NULL;
510 sound_alsa_data_t *sad = NULL;
512 size_t len = 0, tmplen = 0;
513 sxe_media_sample_t *tmpbuf;
514 int err = 0, i, resolution;
515 /* subthread stuff */
516 sound_alsa_aj_data_t _sasd, *sasd = &_sasd;
517 sxe_mse_volume_args _volargs, *volargs = &_volargs;
518 sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
520 int alloced_myself = 0;
522 SOUND_UNPACK_MT(aj, device, ms, mss, lad, sad, sasd->mtap);
524 SXE_MUTEX_LOCK(&sad->mtx);
526 message(GETTEXT("audio-alsa: "
528 /* this lock is unnecessary, we _could_ write concurrently
529 provided that the concurrent media streams have set the ALSA
530 device to the exact same hardware parameters.
531 In cleartext this means, we have to convert all sample data
532 to a common format, e.g. 48000Hz/STEREO/FLT or the like.
533 However, I'm tired at the moment so I leave this lock here.
535 SXE_MUTEX_UNLOCK(&sad->mtx);
541 /* trigger alsa, the device name should be an option */
542 if ((err = sound_alsa_open_device(sad)) < 0) {
543 ALSA_DEBUG_HW("Opening ALSA device failed: %s.",
547 message(GETTEXT("audio-alsa: "
548 "Opening ALSA device failed: %s."),
550 SXE_MUTEX_UNLOCK(&sad->mtx);
551 sound_alsa_close_device(sad);
555 if ((err = sound_alsa_init_hardware(sad)) < 0) {
556 ALSA_DEBUG_HW("Device not configurable: %s.\n",
559 message(GETTEXT("audio-alsa: "
560 "Cannot access ALSA device: %s."),
562 SXE_MUTEX_UNLOCK(&sad->mtx);
563 sound_alsa_close_device(sad);
568 sasd->samplerate = sasd->channels = 0;
570 sasd->coe_ch_cnt = 0;
572 if ((err = sound_alsa_prepare_device(sad, sasd)) < 0) {
573 SXE_MUTEX_UNLOCK(&sad->mtx);
574 sound_alsa_close_device(sad);
578 /* the volume effect */
579 ADD_MEDIA_SAMPLE_EFFECT(
580 sasd->coe_chain, sasd->coe_ch_cnt,
581 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
582 volargs->num_channels = sasd->channels;
584 /* the rerate effect */
585 ADD_MEDIA_SAMPLE_EFFECT(
586 sasd->coe_chain, sasd->coe_ch_cnt,
587 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
588 rrargs->num_channels = sasd->channels;
589 rrargs->srcrate = rrargs->tgtrate = 1;
591 ALSA_DEBUG_COE("have %d coerce functions in my chain.\n",
595 media_stream_meth(ms, rewind)(mss);
597 XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
599 /* ... and play it */
600 SXE_MUTEX_LOCK(&aj->mtx);
601 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
603 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
604 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
606 resolution = (sasd->samplerate * MTPSTATE_REACT_TIME) / 1000000;
607 tmpbuf = (sxe_media_sample_t*)aj->buffer;
608 SXE_MUTEX_UNLOCK(&aj->mtx);
609 SXE_MUTEX_UNLOCK(&sad->mtx);
611 while (aj->play_state != MTPSTATE_STOP) {
614 if (audio_job_queue(aj)) {
615 sound_alsa_handle_aj_events(aj);
619 SXE_MUTEX_LOCK(&aj->mtx);
620 mtp = aj->play_state;
621 SXE_MUTEX_UNLOCK(&aj->mtx);
624 len = media_stream_meth(ms, read)(
625 mss, aj->buffer, resolution);
627 ALSA_DEBUG_S("finished\n");
628 aj->play_state = MTPSTATE_STOP;
632 /* set up the volume args */
633 volargs->volume[0] = volargs->volume[1] =
635 /* set up the rerate args */
636 rrargs->tweak = aj->ratetrafo;
638 /* coerce the stuff, tmplen is in samples */
639 tmplen = sasd->channels*len;
640 for (i = 0; i < sasd->coe_ch_cnt; i++) {
641 ALSA_DEBUG_COE("calling coerce "
642 "%d on b:0x%x l:%d\n",
643 i, (unsigned int)tmpbuf, tmplen);
644 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
646 tmpbuf, tmpbuf, tmplen);
649 /* bring back to S16 or U8 */
650 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
651 aj->buffer, aj->buffer, tmplen);
653 snd_pcm_writei(sad->handle, aj->buffer, len);
656 ALSA_DEBUG("sleeping for %d\n", resolution);
657 memset(aj->buffer, 0, resolution*sasd->framesize);
658 snd_pcm_writei(sad->handle, aj->buffer,
662 case MTPSTATE_UNKNOWN:
664 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
666 ALSA_DEBUG("ACK, quit\n");
667 SXE_MUTEX_LOCK(&aj->mtx);
668 aj->play_state = MTPSTATE_STOP;
669 SXE_MUTEX_UNLOCK(&aj->mtx);
677 SXE_MUTEX_LOCK(&aj->mtx);
678 aj->state = MTSTATE_FINISHED;
680 /* -- Close and shutdown -- */
681 if (alloced_myself && aj->buffer) {
685 aj->buffer_alloc_size = 0;
686 SXE_MUTEX_UNLOCK(&aj->mtx);
688 sound_alsa_close_device(sad);
695 /* sound-alsa.c ends here */