diff --git a/audio_alsa.c b/audio_alsa.c index 6c0f8063c..c65728b1c 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -155,7 +155,7 @@ static void start(int sample_rate) { die("Unexpected sample rate!"); int ret, dir = 0; - snd_pcm_uframes_t frames = 64; + snd_pcm_uframes_t period_size, buffer_size; ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); if (ret < 0) die("Alsa initialization failed: unable to open pcm device: %s\n", snd_strerror(ret)); @@ -166,10 +166,52 @@ static void start(int sample_rate) { snd_pcm_hw_params_set_format(alsa_handle, alsa_params, SND_PCM_FORMAT_S16); snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 2); snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, (unsigned int *)&sample_rate, &dir); - snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, &frames, &dir); - ret = snd_pcm_hw_params(alsa_handle, alsa_params); - if (ret < 0) - die("unable to set hw parameters: %s\n", snd_strerror(ret)); + + // setting period and buffer is a simplified version of what XBMC does + snd_pcm_hw_params_get_period_size_max(alsa_params, &period_size, NULL); + snd_pcm_hw_params_get_buffer_size_max(alsa_params, &buffer_size); + debug(1, "Hardware supports period_size_max: %d, buffer_size_max: %d\n", period_size, buffer_size); + + // we want about 333 ms of buffer, and 50ms period + // buffer might still need some tweaking to get reliable operation on RPi + USB DAC... + // make sure we do not exceed what HW supports + period_size = (period_size < sample_rate / 20 ? period_size : sample_rate / 20); + buffer_size = (buffer_size < sample_rate / 3 ? buffer_size : sample_rate / 3); + + // make sure buffer size is at least 4 times period size + period_size = (period_size < buffer_size / 4 ? period_size : buffer_size / 4); + debug(1, "Trying to set period_size: %d, buffer_size: %d\n", period_size, buffer_size); + + // we keep the originals and try setting period and buffer using copies + snd_pcm_uframes_t period_temp, buffer_temp; + period_temp = period_size; + buffer_temp = buffer_size; + snd_pcm_hw_params_t *alsa_params_copy; + snd_pcm_hw_params_alloca(&alsa_params_copy); + snd_pcm_hw_params_copy(alsa_params_copy, alsa_params); + + // some HW seems to be picky about the order period and buffer are set, so try both ways + // first try with buffer_size, period_size + if (snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params_copy, &buffer_temp) != 0 + || snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params_copy, &period_temp, NULL) != 0 + || snd_pcm_hw_params(alsa_handle, alsa_params_copy) != 0) { + period_temp = period_size; + buffer_temp = buffer_size; + snd_pcm_hw_params_copy(alsa_params_copy, alsa_params); + // retry with period_size, buffer_size + if (snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params_copy, &period_temp, NULL) != 0 + || snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params_copy, &buffer_temp) != 0 + || snd_pcm_hw_params(alsa_handle, alsa_params_copy) != 0) { + // set what alsa would have + warn("Setting period and buffer failed, going with the defaults\n"); + ret = snd_pcm_hw_params(alsa_handle, alsa_params); + if (ret < 0) + die("unable to set hw parameters: %s\n", snd_strerror(ret)); + // using alsa defaults, so see what they are + snd_pcm_get_params(alsa_handle, &buffer_size, &period_size); + debug(1, "Defaults are period_size: %d, buffer_size: %d\n", period_size, buffer_size); + } + } } static void play(short buf[], int samples) {