Update to 6762
[qball-mpd.git] / src / audioOutputs / .svn / text-base / audioOutput_alsa.c.svn-base
blob8c8f73b427cf8b18dcc6d1adc8aaa62f27a71e43
1 /* the Music Player Daemon (MPD)
2  * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3  * This project's homepage is: http://www.musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
19 #include "../audioOutput.h"
21 #include <stdlib.h>
23 #ifdef HAVE_ALSA
25 #define ALSA_PCM_NEW_HW_PARAMS_API
26 #define ALSA_PCM_NEW_SW_PARAMS_API
28 #define MPD_ALSA_BUFFER_TIME_US 500000
29 /* the default period time of xmms is 50 ms, so let's use that as well.
30  * a user can tweak this parameter via the "period_time" config parameter.
31  */
32 #define MPD_ALSA_PERIOD_TIME_US 50000
33 #define MPD_ALSA_RETRY_NR 5
35 #include "../conf.h"
36 #include "../log.h"
38 #include <string.h>
40 #include <alsa/asoundlib.h>
42 typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
43                                         snd_pcm_uframes_t size);
45 typedef struct _AlsaData {
46         char *device;
47         snd_pcm_t *pcmHandle;
48         alsa_writei_t *writei;
49         unsigned int buffer_time;
50         unsigned int period_time;
51         int sampleSize;
52         int useMmap;
53         int canPause;
54         int canResume;
55 } AlsaData;
57 static AlsaData *newAlsaData(void)
59         AlsaData *ret = xmalloc(sizeof(AlsaData));
61         ret->device = NULL;
62         ret->pcmHandle = NULL;
63         ret->writei = snd_pcm_writei;
64         ret->useMmap = 0;
65         ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
66         ret->period_time = MPD_ALSA_PERIOD_TIME_US;
68         return ret;
71 static void freeAlsaData(AlsaData * ad)
73         if (ad->device)
74                 free(ad->device);
76         free(ad);
79 static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
81         AlsaData *ad = newAlsaData();
83         if (param) {
84                 BlockParam *bp = getBlockParam(param, "device");
85                 ad->device = bp ? xstrdup(bp->value) : xstrdup("default");
87                 if ((bp = getBlockParam(param, "use_mmap")) &&
88                     !strcasecmp(bp->value, "yes"))
89                         ad->useMmap = 1;
90                 if ((bp = getBlockParam(param, "buffer_time")))
91                         ad->buffer_time = atoi(bp->value);
92                 if ((bp = getBlockParam(param, "period_time")))
93                         ad->period_time = atoi(bp->value);
94         } else
95                 ad->device = xstrdup("default");
96         audioOutput->data = ad;
98         return 0;
101 static void alsa_finishDriver(AudioOutput * audioOutput)
103         AlsaData *ad = audioOutput->data;
105         freeAlsaData(ad);
108 static int alsa_testDefault(void)
110         snd_pcm_t *handle;
112         int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
113                                SND_PCM_NONBLOCK);
114         snd_config_update_free_global();
116         if (ret) {
117                 WARNING("Error opening default alsa device: %s\n",
118                         snd_strerror(-ret));
119                 return -1;
120         } else
121                 snd_pcm_close(handle);
123         return 0;
126 static int alsa_openDevice(AudioOutput * audioOutput)
128         AlsaData *ad = audioOutput->data;
129         AudioFormat *audioFormat = &audioOutput->outAudioFormat;
130         snd_pcm_format_t bitformat;
131         snd_pcm_hw_params_t *hwparams;
132         snd_pcm_sw_params_t *swparams;
133         unsigned int sampleRate = audioFormat->sampleRate;
134         unsigned int channels = audioFormat->channels;
135         snd_pcm_uframes_t alsa_buffer_size;
136         snd_pcm_uframes_t alsa_period_size;
137         int err;
138         const char *cmd = NULL;
139         int retry = MPD_ALSA_RETRY_NR;
140         unsigned int period_time, period_time_ro;
141         unsigned int buffer_time;
143         switch (audioFormat->bits) {
144         case 8:
145                 bitformat = SND_PCM_FORMAT_S8;
146                 break;
147         case 16:
148                 bitformat = SND_PCM_FORMAT_S16;
149                 break;
150         case 24:
151                 bitformat = SND_PCM_FORMAT_S24;
152                 break;
153         case 32:
154                 bitformat = SND_PCM_FORMAT_S32;
155                 break;
156         default:
157                 ERROR("ALSA device \"%s\" doesn't support %i bit audio\n",
158                       ad->device, audioFormat->bits);
159                 return -1;
160         }
162         err = snd_pcm_open(&ad->pcmHandle, ad->device,
163                            SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
164         snd_config_update_free_global();
165         if (err < 0) {
166                 ad->pcmHandle = NULL;
167                 goto error;
168         }
170         cmd = "snd_pcm_nonblock";
171         err = snd_pcm_nonblock(ad->pcmHandle, 0);
172         if (err < 0)
173                 goto error;
175         period_time_ro = period_time = ad->period_time;
176 configure_hw:
177         /* configure HW params */
178         snd_pcm_hw_params_alloca(&hwparams);
180         cmd = "snd_pcm_hw_params_any";
181         err = snd_pcm_hw_params_any(ad->pcmHandle, hwparams);
182         if (err < 0)
183                 goto error;
185         if (ad->useMmap) {
186                 err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
187                                                    SND_PCM_ACCESS_MMAP_INTERLEAVED);
188                 if (err < 0) {
189                         ERROR("Cannot set mmap'ed mode on alsa device \"%s\": "
190                               " %s\n", ad->device, snd_strerror(-err));
191                         ERROR("Falling back to direct write mode\n");
192                         ad->useMmap = 0;
193                 } else
194                         ad->writei = snd_pcm_mmap_writei;
195         }
197         if (!ad->useMmap) {
198                 cmd = "snd_pcm_hw_params_set_access";
199                 err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
200                                                    SND_PCM_ACCESS_RW_INTERLEAVED);
201                 if (err < 0)
202                         goto error;
203                 ad->writei = snd_pcm_writei;
204         }
206         err = snd_pcm_hw_params_set_format(ad->pcmHandle, hwparams, bitformat);
207         if (err < 0) {
208                 ERROR("ALSA device \"%s\" does not support %i bit audio: "
209                       "%s\n", ad->device, audioFormat->bits, snd_strerror(-err));
210                 goto fail;
211         }
213         err = snd_pcm_hw_params_set_channels_near(ad->pcmHandle, hwparams,
214                                                   &channels);
215         if (err < 0) {
216                 ERROR("ALSA device \"%s\" does not support %i channels: "
217                       "%s\n", ad->device, (int)audioFormat->channels,
218                       snd_strerror(-err));
219                 goto fail;
220         }
221         audioFormat->channels = channels;
223         err = snd_pcm_hw_params_set_rate_near(ad->pcmHandle, hwparams,
224                                               &sampleRate, NULL);
225         if (err < 0 || sampleRate == 0) {
226                 ERROR("ALSA device \"%s\" does not support %i Hz audio\n",
227                       ad->device, (int)audioFormat->sampleRate);
228                 goto fail;
229         }
230         audioFormat->sampleRate = sampleRate;
232         buffer_time = ad->buffer_time;
233         cmd = "snd_pcm_hw_params_set_buffer_time_near";
234         err = snd_pcm_hw_params_set_buffer_time_near(ad->pcmHandle, hwparams,
235                                                      &buffer_time, NULL);
236         if (err < 0)
237                 goto error;
239         period_time = period_time_ro;
240         cmd = "snd_pcm_hw_params_set_period_time_near";
241         err = snd_pcm_hw_params_set_period_time_near(ad->pcmHandle, hwparams,
242                                                      &period_time, NULL);
243         if (err < 0)
244                 goto error;
246         cmd = "snd_pcm_hw_params";
247         err = snd_pcm_hw_params(ad->pcmHandle, hwparams);
248         if (err == -EPIPE && --retry > 0) {
249                 period_time_ro = period_time_ro >> 1;
250                 goto configure_hw;
251         } else if (err < 0)
252                 goto error;
253         if (retry != MPD_ALSA_RETRY_NR)
254                 DEBUG("ALSA period_time set to %d\n", period_time);
256         cmd = "snd_pcm_hw_params_get_buffer_size";
257         err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
258         if (err < 0)
259                 goto error;
261         cmd = "snd_pcm_hw_params_get_period_size";
262         err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
263                                                 NULL);
264         if (err < 0)
265                 goto error;
267         ad->canPause = snd_pcm_hw_params_can_pause(hwparams);
268         ad->canResume = snd_pcm_hw_params_can_resume(hwparams);
270         /* configure SW params */
271         snd_pcm_sw_params_alloca(&swparams);
273         cmd = "snd_pcm_sw_params_current";
274         err = snd_pcm_sw_params_current(ad->pcmHandle, swparams);
275         if (err < 0)
276                 goto error;
278         cmd = "snd_pcm_sw_params_set_start_threshold";
279         err = snd_pcm_sw_params_set_start_threshold(ad->pcmHandle, swparams,
280                                                     alsa_buffer_size -
281                                                     alsa_period_size);
282         if (err < 0)
283                 goto error;
285         cmd = "snd_pcm_sw_params_set_avail_min";
286         err = snd_pcm_sw_params_set_avail_min(ad->pcmHandle, swparams,
287                                               alsa_period_size);
288         if (err < 0)
289                 goto error;
291         cmd = "snd_pcm_sw_params_set_xfer_align";
292         err = snd_pcm_sw_params_set_xfer_align(ad->pcmHandle, swparams, 1);
293         if (err < 0)
294                 goto error;
296         cmd = "snd_pcm_sw_params";
297         err = snd_pcm_sw_params(ad->pcmHandle, swparams);
298         if (err < 0)
299                 goto error;
301         ad->sampleSize = (audioFormat->bits / 8) * audioFormat->channels;
303         audioOutput->open = 1;
305         DEBUG("alsa device \"%s\" will be playing %i bit, %i channel audio at "
306               "%i Hz\n", ad->device, (int)audioFormat->bits,
307               channels, sampleRate);
309         return 0;
311 error:
312         if (cmd) {
313                 ERROR("Error opening alsa device \"%s\" (%s): %s\n",
314                       ad->device, cmd, snd_strerror(-err));
315         } else {
316                 ERROR("Error opening alsa device \"%s\": %s\n", ad->device,
317                       snd_strerror(-err));
318         }
319 fail:
320         if (ad->pcmHandle)
321                 snd_pcm_close(ad->pcmHandle);
322         ad->pcmHandle = NULL;
323         audioOutput->open = 0;
324         return -1;
327 static int alsa_errorRecovery(AlsaData * ad, int err)
329         if (err == -EPIPE) {
330                 DEBUG("Underrun on alsa device \"%s\"\n", ad->device);
331         } else if (err == -ESTRPIPE) {
332                 DEBUG("alsa device \"%s\" was suspended\n", ad->device);
333         }
335         switch (snd_pcm_state(ad->pcmHandle)) {
336         case SND_PCM_STATE_PAUSED:
337                 err = snd_pcm_pause(ad->pcmHandle, /* disable */ 0);
338                 break;
339         case SND_PCM_STATE_SUSPENDED:
340                 err = ad->canResume ?
341                     snd_pcm_resume(ad->pcmHandle) :
342                     snd_pcm_prepare(ad->pcmHandle);
343                 break;
344         case SND_PCM_STATE_SETUP:
345         case SND_PCM_STATE_XRUN:
346                 err = snd_pcm_prepare(ad->pcmHandle);
347                 break;
348         case SND_PCM_STATE_DISCONNECTED:
349                 /* so alsa_closeDevice won't try to drain: */
350                 snd_pcm_close(ad->pcmHandle);
351                 ad->pcmHandle = NULL;
352                 break;
353         default:
354                 /* unknown state, do nothing */
355                 break;
356         }
358         return err;
361 static void alsa_dropBufferedAudio(AudioOutput * audioOutput)
363         AlsaData *ad = audioOutput->data;
365         alsa_errorRecovery(ad, snd_pcm_drop(ad->pcmHandle));
368 static void alsa_closeDevice(AudioOutput * audioOutput)
370         AlsaData *ad = audioOutput->data;
372         if (ad->pcmHandle) {
373                 if (snd_pcm_state(ad->pcmHandle) == SND_PCM_STATE_RUNNING) {
374                         snd_pcm_drain(ad->pcmHandle);
375                 }
376                 snd_pcm_close(ad->pcmHandle);
377                 ad->pcmHandle = NULL;
378         }
380         audioOutput->open = 0;
383 static int alsa_playAudio(AudioOutput * audioOutput, char *playChunk, int size)
385         AlsaData *ad = audioOutput->data;
386         int ret;
388         size /= ad->sampleSize;
390         while (size > 0) {
391                 ret = ad->writei(ad->pcmHandle, playChunk, size);
393                 if (ret == -EAGAIN || ret == -EINTR)
394                         continue;
396                 if (ret < 0) {
397                         if (alsa_errorRecovery(ad, ret) < 0) {
398                                 ERROR("closing alsa device \"%s\" due to write "
399                                       "error: %s\n", ad->device,
400                                       snd_strerror(-errno));
401                                 alsa_closeDevice(audioOutput);
402                                 return -1;
403                         }
404                         continue;
405                 }
407                 playChunk += ret * ad->sampleSize;
408                 size -= ret;
409         }
411         return 0;
414 AudioOutputPlugin alsaPlugin = {
415         "alsa",
416         alsa_testDefault,
417         alsa_initDriver,
418         alsa_finishDriver,
419         alsa_openDevice,
420         alsa_playAudio,
421         alsa_dropBufferedAudio,
422         alsa_closeDevice,
423         NULL,   /* sendMetadataFunc */
426 #else /* HAVE ALSA */
428 DISABLED_AUDIO_OUTPUT_PLUGIN(alsaPlugin)
429 #endif /* HAVE_ALSA */