chiark / gitweb /
server/speaker.c: Only copy whole frames into collection buffers.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 19 May 2013 02:48:25 +0000 (03:48 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 22 May 2013 23:46:49 +0000 (00:46 +0100)
At least the ALSA playback function gets into a real mess if a buffer
contains a partial frame: it leaves the partial frame behind, but is
called again until the buffer is empty, resulting in an infinite loop.

Thanks for Joe Birr-Pixton for helping diagnose this bug and coming up
with the right fix.

server/speaker.c

index 212ffb9..1d26c6c 100644 (file)
@@ -418,9 +418,13 @@ static int addfd(int fd, int events) {
 static size_t speaker_callback(void *buffer,
                                size_t max_samples,
                                void attribute((unused)) *userdata) {
-  const size_t max_bytes = max_samples * uaudio_sample_size;
+  size_t max_bytes = max_samples * uaudio_sample_size;
   size_t provided_samples = 0;
 
+  /* Be sure to keep the amount of data in a buffer a whole number of frames:
+   * otherwise the playing threads can become stuck. */
+  max_bytes -= max_bytes % (uaudio_sample_size * uaudio_channels);
+
   pthread_mutex_lock(&lock);
   /* TODO perhaps we should immediately go silent if we've been asked to pause
    * or cancel the playing track (maybe block in the cancel case and see what
@@ -437,6 +441,8 @@ static size_t speaker_callback(void *buffer,
       /* Limit to what we were asked for */
       if(bytes > max_bytes)
         bytes = max_bytes;
+      /* And truncate to a whole number of frames. */
+      bytes -= bytes % (uaudio_sample_size * uaudio_channels);
       /* Provide it */
       memcpy(buffer, playing->buffer + playing->start, bytes);
       playing->start += bytes;