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);
}