more alsa work
git-svn-id: https://svn.musicpd.org/mpd/trunk@3019 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
parent
b94fa9c949
commit
8004ae341f
|
@ -43,19 +43,21 @@ typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t *pcm, const void *buffer,
|
|||
|
||||
typedef struct _AlsaData {
|
||||
char * device;
|
||||
snd_pcm_t * pcm_handle;
|
||||
int mmap;
|
||||
snd_pcm_t * pcmHandle;
|
||||
alsa_writei_t * writei;
|
||||
int sampleSize;
|
||||
int useMmap;
|
||||
int canPause;
|
||||
int canResume;
|
||||
} AlsaData;
|
||||
|
||||
static AlsaData * newAlsaData() {
|
||||
AlsaData * ret = malloc(sizeof(AlsaData));
|
||||
|
||||
ret->device = NULL;
|
||||
ret->pcm_handle = NULL;
|
||||
ret->pcmHandle = NULL;
|
||||
ret->writei = snd_pcm_writei;
|
||||
ret->mmap = 0;
|
||||
ret->useMmap = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -116,43 +118,43 @@ static int alsa_openDevice(AudioOutput * audioOutput)
|
|||
return -1;
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&ad->pcm_handle, ad->device,
|
||||
err = snd_pcm_open(&ad->pcmHandle, ad->device,
|
||||
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if(err < 0) {
|
||||
ad->pcm_handle = NULL;
|
||||
ad->pcmHandle = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = snd_pcm_nonblock(ad->pcm_handle, 0);
|
||||
err = snd_pcm_nonblock(ad->pcmHandle, 0);
|
||||
if(err < 0) goto error;
|
||||
|
||||
/* configure HW params */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
err = snd_pcm_hw_params_any(ad->pcm_handle, hwparams);
|
||||
err = snd_pcm_hw_params_any(ad->pcmHandle, hwparams);
|
||||
if(err < 0) goto error;
|
||||
|
||||
if(ad->mmap) {
|
||||
err = snd_pcm_hw_params_set_access(ad->pcm_handle, hwparams,
|
||||
if(ad->useMmap) {
|
||||
err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
|
||||
SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
||||
if(err < 0) {
|
||||
ERROR("Cannot set mmap'ed mode on alsa device \"%s\": "
|
||||
" %s\n", ad->device,
|
||||
snd_strerror(-err));
|
||||
ERROR("Falling back to direct write mode\n");
|
||||
ad->mmap = 0;
|
||||
ad->useMmap = 0;
|
||||
}
|
||||
else ad->writei = snd_pcm_mmap_writei;
|
||||
}
|
||||
|
||||
if(!ad->mmap) {
|
||||
err = snd_pcm_hw_params_set_access(ad->pcm_handle, hwparams,
|
||||
if(!ad->useMmap) {
|
||||
err = snd_pcm_hw_params_set_access(ad->pcmHandle, hwparams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if(err < 0) goto error;
|
||||
ad->writei = snd_pcm_writei;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(ad->pcm_handle, hwparams, bitformat);
|
||||
err = snd_pcm_hw_params_set_format(ad->pcmHandle, hwparams, bitformat);
|
||||
if(err < 0) {
|
||||
ERROR("Alsa device \"%s\" does not support %i bit audio: "
|
||||
"%s\n", ad->device, (int)bitformat,
|
||||
|
@ -160,7 +162,7 @@ static int alsa_openDevice(AudioOutput * audioOutput)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_channels(ad->pcm_handle, hwparams,
|
||||
err = snd_pcm_hw_params_set_channels(ad->pcmHandle, hwparams,
|
||||
audioFormat->channels);
|
||||
if(err < 0) {
|
||||
ERROR("Alsa device \"%s\" does not support %i channels: "
|
||||
|
@ -169,7 +171,7 @@ static int alsa_openDevice(AudioOutput * audioOutput)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_rate_near(ad->pcm_handle, hwparams,
|
||||
err = snd_pcm_hw_params_set_rate_near(ad->pcmHandle, hwparams,
|
||||
&sampleRate, 0);
|
||||
if(err < 0 || sampleRate == 0) {
|
||||
ERROR("Alsa device \"%s\" does not support %i Hz audio\n",
|
||||
|
@ -177,15 +179,15 @@ static int alsa_openDevice(AudioOutput * audioOutput)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm_handle, hwparams,
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(ad->pcmHandle, hwparams,
|
||||
&alsa_buffer_time, 0);
|
||||
if(err < 0) goto error;
|
||||
|
||||
err = snd_pcm_hw_params_set_period_time_near(ad->pcm_handle, hwparams,
|
||||
err = snd_pcm_hw_params_set_period_time_near(ad->pcmHandle, hwparams,
|
||||
&alsa_period_time, 0);
|
||||
if(err < 0) goto error;
|
||||
|
||||
err = snd_pcm_hw_params(ad->pcm_handle, hwparams);
|
||||
err = snd_pcm_hw_params(ad->pcmHandle, hwparams);
|
||||
if(err < 0) goto error;
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
|
||||
|
@ -194,15 +196,18 @@ static int alsa_openDevice(AudioOutput * audioOutput)
|
|||
err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size, 0);
|
||||
if(err < 0) goto error;
|
||||
|
||||
ad->canPause = snd_pcm_hw_params_can_pause(hwparams);
|
||||
ad->canResume = snd_pcm_hw_params_can_resume(hwparams);
|
||||
|
||||
/* configure SW params */
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_current(ad->pcm_handle, swparams);
|
||||
snd_pcm_sw_params_current(ad->pcmHandle, swparams);
|
||||
|
||||
err = snd_pcm_sw_params_set_start_threshold(ad->pcm_handle, swparams,
|
||||
err = snd_pcm_sw_params_set_start_threshold(ad->pcmHandle, swparams,
|
||||
alsa_buffer_size - alsa_period_size);
|
||||
if(err < 0) goto error;
|
||||
|
||||
err = snd_pcm_sw_params(ad->pcm_handle, swparams);
|
||||
err = snd_pcm_sw_params(ad->pcmHandle, swparams);
|
||||
if(err < 0) goto error;
|
||||
|
||||
ad->sampleSize = (audioFormat->bits/8)*audioFormat->channels;
|
||||
|
@ -215,8 +220,8 @@ error:
|
|||
ERROR("Error opening alsa device \"%s\": %s\n", ad->device,
|
||||
snd_strerror(-err));
|
||||
fail:
|
||||
if(ad->pcm_handle) snd_pcm_close(ad->pcm_handle);
|
||||
ad->pcm_handle = NULL;
|
||||
if(ad->pcmHandle) snd_pcm_close(ad->pcmHandle);
|
||||
ad->pcmHandle = NULL;
|
||||
audioOutput->open = 0;
|
||||
return -1;
|
||||
}
|
||||
|
@ -224,8 +229,8 @@ fail:
|
|||
static void alsa_dropBufferedAudio(AudioOutput * audioOutput) {
|
||||
AlsaData * ad = audioOutput->data;
|
||||
|
||||
snd_pcm_drop(ad->pcm_handle);
|
||||
snd_pcm_prepare(ad->pcm_handle);
|
||||
snd_pcm_drop(ad->pcmHandle);
|
||||
snd_pcm_prepare(ad->pcmHandle);
|
||||
}
|
||||
|
||||
inline static int alsa_errorRecovery(AlsaData * ad, int err) {
|
||||
|
@ -236,12 +241,19 @@ inline static int alsa_errorRecovery(AlsaData * ad, int err) {
|
|||
DEBUG("alsa device \"%s\" was suspended\n", ad->device);
|
||||
}
|
||||
|
||||
switch(snd_pcm_state(ad->pcm_handle)) {
|
||||
switch(snd_pcm_state(ad->pcmHandle)) {
|
||||
case SND_PCM_STATE_PAUSED:
|
||||
err = snd_pcm_pause(ad->pcmHandle, /* disable */ 0);
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
err = ad->canResume ?
|
||||
snd_pcm_resume(ad->pcmHandle) :
|
||||
snd_pcm_prepare(ad->pcmHandle);
|
||||
break;
|
||||
case SND_PCM_STATE_SETUP:
|
||||
case SND_PCM_STATE_XRUN:
|
||||
err = snd_pcm_prepare(ad->pcm_handle);
|
||||
if(err < 0) return -1;
|
||||
return 0;
|
||||
err = snd_pcm_prepare(ad->pcmHandle);
|
||||
break;
|
||||
default:
|
||||
/* unknown state, do nothing */
|
||||
break;
|
||||
|
@ -253,10 +265,10 @@ inline static int alsa_errorRecovery(AlsaData * ad, int err) {
|
|||
static void alsa_closeDevice(AudioOutput * audioOutput) {
|
||||
AlsaData * ad = audioOutput->data;
|
||||
|
||||
if(ad->pcm_handle) {
|
||||
snd_pcm_drain(ad->pcm_handle);
|
||||
snd_pcm_close(ad->pcm_handle);
|
||||
ad->pcm_handle = NULL;
|
||||
if(ad->pcmHandle) {
|
||||
snd_pcm_drain(ad->pcmHandle);
|
||||
snd_pcm_close(ad->pcmHandle);
|
||||
ad->pcmHandle = NULL;
|
||||
}
|
||||
|
||||
audioOutput->open = 0;
|
||||
|
@ -271,17 +283,21 @@ static int alsa_playAudio(AudioOutput * audioOutput, char * playChunk,
|
|||
size /= ad->sampleSize;
|
||||
|
||||
while (size > 0) {
|
||||
ret = ad->writei(ad->pcm_handle, playChunk, size);
|
||||
ret = ad->writei(ad->pcmHandle, playChunk, size);
|
||||
|
||||
if(ret == -EAGAIN) continue;
|
||||
|
||||
if(ret < 0 && alsa_errorRecovery(ad, ret) < 0) {
|
||||
ERROR("closing alsa device \"%s\" due to write error:"
|
||||
" %s\n", ad->device,
|
||||
snd_strerror(-errno));
|
||||
alsa_closeDevice(audioOutput);
|
||||
return -1;
|
||||
if(ret < 0) {
|
||||
if( alsa_errorRecovery(ad, ret) < 0) {
|
||||
ERROR("closing alsa device \"%s\" due to write "
|
||||
"error: %s\n", ad->device,
|
||||
snd_strerror(-errno));
|
||||
alsa_closeDevice(audioOutput);
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
playChunk += ret * ad->sampleSize;
|
||||
size -= ret;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue