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
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.
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
19 #include "../audioOutput.h"
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.
32 #define MPD_ALSA_PERIOD_TIME_US 50000
33 #define MPD_ALSA_RETRY_NR 5
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 {
48 alsa_writei_t *writei;
49 unsigned int buffer_time;
50 unsigned int period_time;
57 static AlsaData *newAlsaData(void)
59 AlsaData *ret = xmalloc(sizeof(AlsaData));
62 ret->pcmHandle = NULL;
63 ret->writei = snd_pcm_writei;
65 ret->buffer_time = MPD_ALSA_BUFFER_TIME_US;
66 ret->period_time = MPD_ALSA_PERIOD_TIME_US;
71 static void freeAlsaData(AlsaData * ad)
79 static int alsa_initDriver(AudioOutput * audioOutput, ConfigParam * param)
81 AlsaData *ad = newAlsaData();
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"))
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);
95 ad->device = xstrdup("default");
96 audioOutput->data = ad;
101 static void alsa_finishDriver(AudioOutput * audioOutput)
103 AlsaData *ad = audioOutput->data;
108 static int alsa_testDefault(void)
112 int ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,
114 snd_config_update_free_global();
117 WARNING("Error opening default alsa device: %s\n",
121 snd_pcm_close(handle);
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;
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) {
145 bitformat = SND_PCM_FORMAT_S8;
148 bitformat = SND_PCM_FORMAT_S16;
151 bitformat = SND_PCM_FORMAT_S24;
154 bitformat = SND_PCM_FORMAT_S32;
157 ERROR("ALSA device \"%s\" doesn't support %i bit audio\n",
158 ad->device, audioFormat->bits);
162 err = snd_pcm_open(&ad->pcmHandle, ad->device,
163 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
164 snd_config_update_free_global();
166 ad->pcmHandle = NULL;
170 cmd = "snd_pcm_nonblock";
171 err = snd_pcm_nonblock(ad->pcmHandle, 0);
175 period_time_ro = period_time = ad->period_time;
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);
186 err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
187 SND_PCM_ACCESS_MMAP_INTERLEAVED);
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");
194 ad->writei = snd_pcm_mmap_writei;
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);
203 ad->writei = snd_pcm_writei;
206 err = snd_pcm_hw_params_set_format(ad->pcmHandle, hwparams, bitformat);
208 ERROR("ALSA device \"%s\" does not support %i bit audio: "
209 "%s\n", ad->device, audioFormat->bits, snd_strerror(-err));
213 err = snd_pcm_hw_params_set_channels_near(ad->pcmHandle, hwparams,
216 ERROR("ALSA device \"%s\" does not support %i channels: "
217 "%s\n", ad->device, (int)audioFormat->channels,
221 audioFormat->channels = channels;
223 err = snd_pcm_hw_params_set_rate_near(ad->pcmHandle, hwparams,
225 if (err < 0 || sampleRate == 0) {
226 ERROR("ALSA device \"%s\" does not support %i Hz audio\n",
227 ad->device, (int)audioFormat->sampleRate);
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,
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,
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;
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);
261 cmd = "snd_pcm_hw_params_get_period_size";
262 err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
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);
278 cmd = "snd_pcm_sw_params_set_start_threshold";
279 err = snd_pcm_sw_params_set_start_threshold(ad->pcmHandle, swparams,
285 cmd = "snd_pcm_sw_params_set_avail_min";
286 err = snd_pcm_sw_params_set_avail_min(ad->pcmHandle, swparams,
291 cmd = "snd_pcm_sw_params_set_xfer_align";
292 err = snd_pcm_sw_params_set_xfer_align(ad->pcmHandle, swparams, 1);
296 cmd = "snd_pcm_sw_params";
297 err = snd_pcm_sw_params(ad->pcmHandle, swparams);
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);
313 ERROR("Error opening alsa device \"%s\" (%s): %s\n",
314 ad->device, cmd, snd_strerror(-err));
316 ERROR("Error opening alsa device \"%s\": %s\n", ad->device,
321 snd_pcm_close(ad->pcmHandle);
322 ad->pcmHandle = NULL;
323 audioOutput->open = 0;
327 static int alsa_errorRecovery(AlsaData * ad, int err)
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);
335 switch (snd_pcm_state(ad->pcmHandle)) {
336 case SND_PCM_STATE_PAUSED:
337 err = snd_pcm_pause(ad->pcmHandle, /* disable */ 0);
339 case SND_PCM_STATE_SUSPENDED:
340 err = ad->canResume ?
341 snd_pcm_resume(ad->pcmHandle) :
342 snd_pcm_prepare(ad->pcmHandle);
344 case SND_PCM_STATE_SETUP:
345 case SND_PCM_STATE_XRUN:
346 err = snd_pcm_prepare(ad->pcmHandle);
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;
354 /* unknown state, do nothing */
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;
373 if (snd_pcm_state(ad->pcmHandle) == SND_PCM_STATE_RUNNING) {
374 snd_pcm_drain(ad->pcmHandle);
376 snd_pcm_close(ad->pcmHandle);
377 ad->pcmHandle = NULL;
380 audioOutput->open = 0;
383 static int alsa_playAudio(AudioOutput * audioOutput, char *playChunk, int size)
385 AlsaData *ad = audioOutput->data;
388 size /= ad->sampleSize;
391 ret = ad->writei(ad->pcmHandle, playChunk, size);
393 if (ret == -EAGAIN || ret == -EINTR)
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);
407 playChunk += ret * ad->sampleSize;
414 AudioOutputPlugin alsaPlugin = {
421 alsa_dropBufferedAudio,
423 NULL, /* sendMetadataFunc */
426 #else /* HAVE ALSA */
428 DISABLED_AUDIO_OUTPUT_PLUGIN(alsaPlugin)
429 #endif /* HAVE_ALSA */