ALSA:snd_pcm_writei 调用时缓冲区欠载 [英] ALSA: buffer underrun on snd_pcm_writei call

查看:104
本文介绍了ALSA:snd_pcm_writei 调用时缓冲区欠载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在运行我最近从废墟中恢复过来的旧程序时,我的缓冲区不足.

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

程序将原始声音文件完全加载到内存中(2100 字节长,525 帧)并准备 ALSA 以进行输出(44.1khz,2 声道,有符号 16 位):

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
", snd_strerror(err));
  return -1;
}

PCM 状态在第一次播放声音之前是 PREPARED.声音第一次正确播放,但是,第二次播放时,状态为 RUNNING 并且 -EPIPE(Broken pipe")被 抛出snd_pcm_writei.播放逻辑:

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
", snd_strerror(frames));
  frames = snd_pcm_recover(audio_handle, frames, 0);

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

奇怪的是,它在第​​三次正确播放,下一次再次失败.换句话说,它每隔一次就会失败并返回 -EPIPE 错误.

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.

为简单起见,我以 1 秒的间隔播放声音.

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

上面的逻辑有什么问题?

What's wrong with the logic above?

更新代码以使用新的 ALSA API;可以在以下位置找到:http://paste.ubuntu.com/7257181/

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

编辑

刚刚发现,如果在重新发出 snd_pcm_writei 调用之前实际测试 -EPIPE 条件并重新准备 PCM 句柄,一切都很好.我的(新)问题是……为什么?

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

推荐答案

如果你用 525 帧(大约 12 毫秒)填充缓冲区然后等待一秒钟,你肯定会得到欠载,因为没有数据剩下的 988 毫秒.

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.

您可以使用 snd_pcm_drain 等待实际数据停止播放(注意:通常情况下,设备直到下一个周期边界才会真正停止).或者,继续将零样本送入缓冲区.

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.

这篇关于ALSA:snd_pcm_writei 调用时缓冲区欠载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆