Initial git import
[sxemacs] / src / media / sound-alsa.c
1 /* sound-alsa.c - play a sound over the alsa
2
3    Copyright (C) 2006 Sebastian Freundt
4
5 This file is part of SXEmacs
6
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.
11
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.
16
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/>. */
19
20
21 /* Synched up with: Not in FSF. */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "lisp.h"
28
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34
35 #include "media.h"
36 #include "sound-alsa.h"
37
38 Lisp_Object Qalsa;
39
40 #define MYSELF ADRIVER_ALSA
41
42 #define __ALSA_DEBUG__(args...)         fprintf(stderr, "ALSA " args)
43 #ifndef ALSA_DEBUG_FLAG
44 #define ALSA_DEBUG(args...)
45 #else
46 #define ALSA_DEBUG(args...)             __ALSA_DEBUG__(args)
47 #endif
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)
53
54 \f
55 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_alsa);
56 DEFINE_AUDIO_DEVICE_SIMPLE(sound_alsa);
57
58 \f
59 static Lisp_Object
60 sound_alsa_mark(ad_device_data *devdata)
61 {
62         sound_alsa_data_t *sad = devdata;
63
64         mark_object(sad->device);
65
66         return Qnil;
67 }
68
69 static void
70 sound_alsa_print(Lisp_Object device, Lisp_Object pcfun, int ef)
71 {
72         sound_alsa_data_t *sad = NULL;
73         char *temp = alloca(48);
74
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;
81                 return;
82         }
83
84         /* info about the connected output plugin */
85         write_c_string(" :device ", pcfun);
86         if (NILP(sad->device))
87                 write_c_string("#default", pcfun);
88         else
89                 print_internal(sad->device, pcfun, ef);
90
91         if (sad->keep_open) {
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);
96         } else
97                 write_c_string(" :keep-open nil", pcfun);
98
99         snprintf(temp, 47, " :params 0x%lx", (long unsigned int)sad->hwparams);
100         write_c_string(temp, pcfun);
101
102         return;
103 }
104
105 \f
106 static int
107 sound_alsa_open_device(sound_alsa_data_t *sad)
108 {
109         if (sad->handle == NULL) {
110                 const char *dev;
111                 if (NILP(sad->device))
112                         dev = "default";
113                 else
114                         dev = (char*)XSTRING_DATA(sad->device);
115
116                 return snd_pcm_open(&sad->handle, dev,
117                                     SND_PCM_STREAM_PLAYBACK, 0);
118         }
119
120         return 0;
121 }
122
123 static int
124 sound_alsa_init_hardware(sound_alsa_data_t *sad)
125 {
126         int err = 0;
127
128         if (sad->hwparams == NULL || sad->handle == NULL)
129                 return -1;
130
131         /* check if we can configure this device */
132         err = snd_pcm_hw_params_any(sad->handle, sad->hwparams);
133         if (err < 0)
134                 return err;
135
136         err = snd_pcm_hw_params_set_access(
137                 sad->handle, sad->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
138
139         return err;
140 }
141
142 static void
143 sound_alsa_finish(ad_device_data *data)
144 {
145         sound_alsa_data_t *sad = data;
146
147         SXE_MUTEX_LOCK(&sad->mtx);
148         if (sad->hwparams)
149                 snd_pcm_hw_params_free(sad->hwparams);
150         sad->hwparams = NULL;
151
152         if (sad->handle) {
153                 snd_pcm_hw_free(sad->handle);
154                 snd_pcm_close(sad->handle);
155         }
156         sad->handle = NULL;
157         SXE_MUTEX_UNLOCK(&sad->mtx);
158         SXE_MUTEX_FINI(&sad->mtx);
159
160         ALSA_DEBUG("audio-device finished.\n");
161
162         return;
163 }
164
165 static ad_device_data *
166 sound_alsa_create(Lisp_Object alsa_options)
167 {
168         /* result */
169         sound_alsa_data_t *sad = NULL;
170         int keep_open = 0;
171         int err = 0;
172         /* option keywords */
173         Lisp_Object opt_device;
174         Lisp_Object opt_keepopen;
175
176         /* parse options */
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);
180                 return NULL;
181         }
182
183         opt_keepopen = Fplist_get(alsa_options, Q_keep_open, Qnil);
184         if (!NILP(opt_keepopen))
185                 keep_open = 1;
186
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);
192
193         /* Open the device */
194         if ((err = sound_alsa_open_device(sad)) < 0) {
195                 xfree(sad);
196                 error(GETTEXT("audio-alsa: "
197                               "Opening ALSA device failed: %s."),
198                         snd_strerror(err));
199                 return NULL;
200         }
201
202         snd_pcm_hw_params_malloc(&sad->hwparams);
203
204         if ((err = sound_alsa_init_hardware(sad)) < 0) {
205                 sound_alsa_finish(sad);
206                 xfree(sad);
207                 error(GETTEXT("Error: audio-alsa: "
208                               "Opening ALSA device failed: %s."),
209                       snd_strerror(err));
210                 return NULL;
211         }
212
213         if (!keep_open) {
214                 snd_pcm_hw_free(sad->handle);
215                 snd_pcm_close(sad->handle);
216                 sad->handle = NULL;
217         }
218
219         return (ad_device_data*)sad;
220 }
221
222 \f
223 #ifdef EF_USE_ASYNEQ
224 static inline void
225 sound_alsa_change_volume(audio_job_t aj, audio_job_event_args_t args)
226 {
227         SXE_MUTEX_LOCK(&aj->mtx);
228         aj->volume = args->volume_args;
229         SXE_MUTEX_UNLOCK(&aj->mtx);
230 }
231
232 static inline void
233 sound_alsa_change_rate(audio_job_t aj, audio_job_event_args_t args)
234 {
235         SXE_MUTEX_LOCK(&aj->mtx);
236         aj->ratetrafo = args->rate_args;
237         SXE_MUTEX_UNLOCK(&aj->mtx);
238 }
239
240 static inline void
241 sound_alsa_change_state(audio_job_t aj, audio_job_event_args_t args)
242 {
243         SXE_MUTEX_LOCK(&aj->mtx);
244         switch (args->state_args) {
245         case aj_pause:
246                 ALSA_DEBUG_AJ("->pause state\n");
247                 aj->play_state = MTPSTATE_PAUSE;
248                 break;
249         case aj_resume:
250                 ALSA_DEBUG_AJ("->resume state\n");
251                 aj->play_state = MTPSTATE_RUN;
252                 break;
253         case aj_start:
254                 ALSA_DEBUG_AJ("->start state\n");
255                 break;
256         case aj_stop:
257                 ALSA_DEBUG_AJ("->stop state\n");
258                 aj->play_state = MTPSTATE_STOP;
259                 break;
260
261         case no_audio_job_change_states:
262         default:
263                 ALSA_DEBUG_AJ("->unknown state\n");
264                 break;
265         }
266         SXE_MUTEX_UNLOCK(&aj->mtx);
267 }
268
269 static inline void
270 sound_alsa_handle_aj_events(audio_job_t aj)
271         __attribute__((always_inline));
272 static inline void
273 sound_alsa_handle_aj_events(audio_job_t aj)
274 {
275         sound_alsa_aj_data_t *sasd;
276         audio_job_event_t ev = NULL;
277
278 #if 0
279         assert(audio_job_queue(aj));
280 #endif
281
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);
286                 return;
287         }
288         SXE_MUTEX_UNLOCK(&aj->mtx);
289
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));
295                 break;
296         case aj_change_volume:
297                 ALSA_DEBUG_AJ("change volume event\n");
298                 sound_alsa_change_volume(aj, &audio_job_event_args(ev));
299                 break;
300         case aj_change_rate:
301                 ALSA_DEBUG_AJ("change rate event\n");
302                 sound_alsa_change_rate(aj, &audio_job_event_args(ev));
303                 break;
304
305         case no_audio_job_event_kinds:
306         default:
307                 ALSA_CRITICAL("unknown event\n");
308                 break;
309         }
310         free_audio_job_event(ev);
311 }
312 #endif  /* EF_USE_ASYNEQ */
313 \f
314 static int
315 sound_alsa_prepare_device(sound_alsa_data_t *sad, sound_alsa_aj_data_t *sasd)
316 {
317         /* alsa stuff */
318         snd_pcm_state_t state;
319         snd_pcm_format_t tmpfmt[] = {
320                 SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S24,
321                 SND_PCM_FORMAT_S16,
322                 SND_PCM_FORMAT_FLOAT,
323                 SND_PCM_FORMAT_U8,
324                 SND_PCM_FORMAT_UNKNOWN };
325         int err = 0, i, num_tmpfmt = sizeof(tmpfmt)/sizeof(snd_pcm_format_t);
326
327         sasd->format = SND_PCM_FORMAT_UNKNOWN;
328         sasd->channels = 0;
329
330         switch (sasd->mtap->channels) {
331         case 1:
332                 if (snd_pcm_hw_params_test_channels(
333                             sad->handle, sad->hwparams, 1) == 0) {
334                         sasd->channels = 1;
335                         ALSA_DEBUG_HW("Using MONO.\n");
336                 } else if (!snd_pcm_hw_params_test_channels(
337                                    sad->handle, sad->hwparams, 2)) {
338                         sasd->channels = 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");
343                 }
344                 break;
345         case 2:
346                 if (snd_pcm_hw_params_test_channels(
347                             sad->handle, sad->hwparams, 2) == 0) {
348                         sasd->channels = 2;
349                         ALSA_DEBUG_HW("Using STEREO.\n");
350                 } else if (!snd_pcm_hw_params_test_channels(
351                                    sad->handle, sad->hwparams, 1)) {
352                         sasd->channels = 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");
357                 }
358                 break;
359
360                 /* more channels here */
361         default:
362                 break;
363         }
364
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."),
370                         snd_strerror(err));
371                 return -1;
372         }
373
374         /* now we try to set a useful format */
375         ALSA_DEBUG("trying %d formats\n", num_tmpfmt);
376         i = 0;
377         while (i < num_tmpfmt &&
378                (snd_pcm_hw_params_test_format(
379                          sad->handle, sad->hwparams, tmpfmt[i])))
380                 i++;
381         if (i == num_tmpfmt) {
382                 ALSA_DEBUG_HW("Setting sample format failed :(.\n");
383                 /* warning? */
384                 message(GETTEXT("audio-alsa: "
385                                 "Setting sample format failed."));
386                 return -1;
387         }
388
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");
394                 break;
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");
399                 break;
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");
404                 break;
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");
409                 break;
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");
414                 break;
415         default:
416                 break;
417         }
418
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",
423                               snd_strerror(err));
424                 /* warning? */
425                 message(GETTEXT("audio-alsa: "
426                                 "Setting sample format failed: %s."),
427                         snd_strerror(err));
428                 return -1;
429         }
430
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",
435                               snd_strerror(err));
436                 /* warning? */
437                 message(GETTEXT("audio-alsa: "
438                                 "Setting sample rate failed: %s."),
439                         snd_strerror(err));
440                 return -1;
441         }
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);
446
447         ALSA_DEBUG_HW("Using samplerate: %d.\n", sasd->samplerate);
448
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."),
453                         snd_strerror(err));
454                 return -1;
455         }
456
457         if ((err = snd_pcm_prepare(sad->handle)) < 0) {
458                 message(GETTEXT("audio-alsa: "
459                                 "Cannot prepare ALSA device: %s."),
460                         snd_strerror(err));
461                 return -1;
462         }
463
464         if ((state = snd_pcm_state(sad->handle)) != SND_PCM_STATE_PREPARED) {
465                 message(GETTEXT("audio-alsa: "
466                                 "Cannot prepare ALSA device."));
467                 return -1;
468         }
469
470         return 1;
471 }
472
473 static int
474 sound_alsa_close_device(sound_alsa_data_t *sad)
475 {
476         SXE_MUTEX_LOCK(&sad->mtx);
477         sad->lock = 0;
478         if (sad->handle == NULL) {
479                 SXE_MUTEX_UNLOCK(&sad->mtx);
480                 return 0;
481         }
482
483         if (sad->keep_open) {
484                 SXE_MUTEX_UNLOCK(&sad->mtx);
485                 return 0;
486         }
487
488         /* close /dev/dsp */
489         snd_pcm_hw_free(sad->handle);
490         snd_pcm_close(sad->handle);
491
492         ALSA_DEBUG_HW("device handle closed\n");
493         sad->handle = NULL;
494         SXE_MUTEX_UNLOCK(&sad->mtx);
495
496         return 0;
497 }
498
499 static int
500 sound_alsa_play(audio_job_t aj)
501 {
502         /* stream stuff */
503         Lisp_Media_Stream *ms;
504         media_substream *mss;
505         /* thread stuff */
506         media_thread_play_state mtp;
507         /* device stuff */
508         Lisp_Object device;
509         Lisp_Audio_Device *lad = NULL;
510         sound_alsa_data_t *sad = NULL;
511         /* buffering */
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;
519         /* cache stuff */
520         int alloced_myself = 0;
521
522         SOUND_UNPACK_MT(aj, device, ms, mss, lad, sad, sasd->mtap);
523
524         SXE_MUTEX_LOCK(&sad->mtx);
525         if (sad->lock) {
526                 message(GETTEXT("audio-alsa: "
527                                 "Device locked."));
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.
534                 */
535                 SXE_MUTEX_UNLOCK(&sad->mtx);
536                 return 0;
537         }
538
539         sad->lock = 1;
540
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.",
544                               snd_strerror(err));
545                 sad->handle = NULL;
546                 /* warning? */
547                 message(GETTEXT("audio-alsa: "
548                                 "Opening ALSA device failed: %s."),
549                         snd_strerror(err));
550                 SXE_MUTEX_UNLOCK(&sad->mtx);
551                 sound_alsa_close_device(sad);
552                 return 0;
553         }
554
555         if ((err = sound_alsa_init_hardware(sad)) < 0) {
556                 ALSA_DEBUG_HW("Device not configurable: %s.\n",
557                               snd_strerror(err));
558                 /* warning? */
559                 message(GETTEXT("audio-alsa: "
560                                 "Cannot access ALSA device: %s."),
561                         snd_strerror(err));
562                 SXE_MUTEX_UNLOCK(&sad->mtx);
563                 sound_alsa_close_device(sad);
564                 return 0;
565         }
566
567         /* init the sasd */
568         sasd->samplerate = sasd->channels = 0;
569         sasd->framesize = 0;
570         sasd->coe_ch_cnt = 0;
571
572         if ((err = sound_alsa_prepare_device(sad, sasd)) < 0) {
573                 SXE_MUTEX_UNLOCK(&sad->mtx);
574                 sound_alsa_close_device(sad);
575                 return 0;
576         }
577
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;
583
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;
590
591         ALSA_DEBUG_COE("have %d coerce functions in my chain.\n",
592                        sasd->coe_ch_cnt);
593
594         /* rewind it ... */
595         media_stream_meth(ms, rewind)(mss);
596
597         XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
598
599         /* ... and play it */
600         SXE_MUTEX_LOCK(&aj->mtx);
601         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
602                 alloced_myself = 1;
603                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
604                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
605         }
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);
610
611         while (aj->play_state != MTPSTATE_STOP) {
612
613 #ifdef EF_USE_ASYNEQ
614                 if (audio_job_queue(aj)) {
615                         sound_alsa_handle_aj_events(aj);
616                 }
617 #endif
618
619                 SXE_MUTEX_LOCK(&aj->mtx);
620                 mtp = aj->play_state;
621                 SXE_MUTEX_UNLOCK(&aj->mtx);
622                 switch (mtp) {
623                 case MTPSTATE_RUN:
624                         len = media_stream_meth(ms, read)(
625                                 mss, aj->buffer, resolution);
626                         if (!len) {
627                                 ALSA_DEBUG_S("finished\n");
628                                 aj->play_state = MTPSTATE_STOP;
629                                 break;
630                         }
631
632                         /* set up the volume args */
633                         volargs->volume[0] = volargs->volume[1] =
634                                 aj->volume;
635                         /* set up the rerate args */
636                         rrargs->tweak = aj->ratetrafo;
637
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(
645                                         sasd->coe_chain, i,
646                                         tmpbuf, tmpbuf, tmplen);
647                         }
648
649                         /* bring back to S16 or U8 */
650                         MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
651                                 aj->buffer, aj->buffer, tmplen);
652
653                         snd_pcm_writei(sad->handle, aj->buffer, len);
654                         break;
655                 case MTPSTATE_PAUSE:
656                         ALSA_DEBUG("sleeping for %d\n", resolution);
657                         memset(aj->buffer, 0, resolution*sasd->framesize);
658                         snd_pcm_writei(sad->handle, aj->buffer,
659                                        resolution);
660                         break;
661
662                 case MTPSTATE_UNKNOWN:
663                 case MTPSTATE_STOP:
664                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
665                 default:
666                         ALSA_DEBUG("ACK, quit\n");
667                         SXE_MUTEX_LOCK(&aj->mtx);
668                         aj->play_state = MTPSTATE_STOP;
669                         SXE_MUTEX_UNLOCK(&aj->mtx);
670                         break;
671                 }
672         }
673
674 #if 0
675         snd_pcm_drain(ad);
676 #endif
677         SXE_MUTEX_LOCK(&aj->mtx);
678         aj->state = MTSTATE_FINISHED;
679
680         /* -- Close and shutdown -- */
681         if (alloced_myself && aj->buffer) {
682                 xfree(aj->buffer);
683         }
684         aj->buffer = NULL;
685         aj->buffer_alloc_size = 0;
686         SXE_MUTEX_UNLOCK(&aj->mtx);
687
688         sound_alsa_close_device(sad);
689
690         return 1;
691 }
692
693 #undef MYSELF
694
695 /* sound-alsa.c ends here */