From 52be1f66c294caeb628d4d46d0045473403841d5 Mon Sep 17 00:00:00 2001 From: waker Date: Thu, 3 May 2012 19:22:39 +0200 Subject: [by Martin Panter ] Retry with the same data after recovering from an underrun or other error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The palsa_callback() function seems to limit the rate it returns data, and if a buffer of data is dropped because snd_pcm_writei() failed, the data rate is not fast enough to keep up with ALSA and another buffer underrun occurs. This could cause an indefinite cycle, and the audio would sound slighly choppy and sped up. If the original data is retried, the ALSA buffer eventually tends to become full; perhaps the rate limit is a little faster than real time. When playback continues on to an MP3 file cued in the playlist, the MP3 seems to be scanned before it starts playing. If the scanning takes too long, in my case because the MP3 file is mounted with SSHFS over wifi, it causes a buffer underrun. The code below could also be inserted, just before the snd_pcm_writei() call, to artificially cause an underrun a few seconds into playback: static int n = 0; ++n; if (n >= 200 && n < 300) { trace ("dropping %i\n", n); err = 0; } else err = snd_pcm_writei (...); --- plugins/alsa/alsa.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'plugins') diff --git a/plugins/alsa/alsa.c b/plugins/alsa/alsa.c index 618465b7..08fbc9b8 100644 --- a/plugins/alsa/alsa.c +++ b/plugins/alsa/alsa.c @@ -613,6 +613,9 @@ palsa_thread (void *context) { continue; } LOCK; + char buf[period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels]; + int bytes_to_write = 0; + /* find out how much space is available for playback data */ snd_pcm_sframes_t frames_to_deliver = snd_pcm_avail_update (audio); @@ -624,12 +627,13 @@ palsa_thread (void *context) { break; } err = 0; - char buf[period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels]; - UNLOCK; // holding a lock here may cause deadlock in the streamer - int bytes_to_write = palsa_callback (buf, period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels); - LOCK; - if (alsa_terminate) { - break; + if (!bytes_to_write) { + UNLOCK; // holding a lock here may cause deadlock in the streamer + bytes_to_write = palsa_callback (buf, period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels); + LOCK; + if (OUTPUT_STATE_PLAYING != state || alsa_terminate) { + break; + } } if (bytes_to_write >= (plugin.fmt.bps>>3) * plugin.fmt.channels) { @@ -643,6 +647,7 @@ palsa_thread (void *context) { else { UNLOCK; usleep (10000); + bytes_to_write = 0; LOCK; continue; } @@ -669,9 +674,10 @@ palsa_thread (void *context) { //} snd_pcm_prepare (audio); snd_pcm_start (audio); - continue; } + continue; } + bytes_to_write = 0; frames_to_deliver = snd_pcm_avail_update (audio); } UNLOCK; -- cgit v1.2.3