Question

I'm getting buffer underruns when running an old program I recently brought back from the ashes.

The program loads up a raw sound file fully into memory (2100 bytes long, 525 frames) and prepares ALSA for output (44.1khz, 2 channels, signed 16 bit):

if ((err = snd_pcm_set_params(audio_handle,
                              SND_PCM_FORMAT_S16_LE,
                              SND_PCM_ACCESS_RW_INTERLEAVED,
                              2,
                              44100,
                              1,
                              sound.playback_us)) < 0) {
  printf("E: Failed to prepare PCM: %s\n", snd_strerror(err));
  return -1;
}

PCM state is PREPARED prior to first playing the sound. Sound plays correctly the first time around, however, the second time it is played, state is RUNNING and a -EPIPE ("Broken pipe") is thrown by snd_pcm_writei. Playback logic:

frames = snd_pcm_writei(audio_handle,
                        sound.data,
                        write_size);

if(frames < 0) {
  printf("E: %s: attempting to recover\n", snd_strerror(frames));
  frames = snd_pcm_recover(audio_handle, frames, 0);

  if(frames < 0) {
    printf("E: snd_pcm_writei failed\n");
    break;
  }
} else if(frames > 0 && frames < write_size)
  printf("E: Short write (expected %li, wrote %li)\n", write_size, frames);
else
  printf("wrote %li frames\n", frames);

Strangely, it plays correctly at the third time, failing once again the following time. In other words, it fails with a -EPIPE error every other time.

For the sake of simplicity, I'm playing the sounds at 1 second intervals.

What's wrong with the logic above?


Updated code to use new ALSA API; can be found at: http://paste.ubuntu.com/7257181/


EDIT

Just found out that if one actually tests for an -EPIPE condition and re-prepares the PCM handle before re-issuing the snd_pcm_writei call, all is good. My (new) question then is... why?

if(frames == -EPIPE) {
  snd_pcm_prepare(pcm.handle);
  frames = snd_pcm_writei(pcm.handle,
                          sound.data, //sound.data + (offset << 1),
                          write_size);
}
Was it helpful?

Solution

If you fill the buffer with 525 frames (about 12 ms) and then wait for one second, you are guaranteed to get an underrun because there is no data for the remaining 988 ms.

You could use snd_pcm_drain to wait until the actual data has stopped playing (note: typcially, the device will not actually stop until the next period boundary). Alternatively, continue feeding zero samples into the buffer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top