如何使用 ALSA 的 snd_pcm_writei()?

Posted

技术标签:

【中文标题】如何使用 ALSA 的 snd_pcm_writei()?【英文标题】:How to use ALSA's snd_pcm_writei()? 【发布时间】:2011-01-11 23:04:04 【问题描述】:

谁能解释一下snd_pcm_writei

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer,
                                 snd_pcm_uframes_t size)

有效吗?

我是这样使用的:

for (int i = 0; i < 1; i++) 
   f = snd_pcm_writei(handle, buffer, frames);

   ...

完整源代码http://pastebin.com/m2f28b578

这是否意味着,我不应该给snd_pcm_writei() 的数量 buffer 中的所有帧,但只有

sample_rate * 延迟 = frames

?

所以如果我例如有: 采样率 = 44100 延迟 = 0.5 [s] all_frames = 100000

我应该给snd_pcm_writei() 的帧数是

sample_rate * 延迟 = 帧数 44100*0.5 = 22050

for循环的迭代次数应该是多少?:

(int) 100000/22050 = 4;帧数=22050

还有一个额外的,但仅限于

100000 模 22050 = 11800

帧?

它是这样工作的吗?

路易丝

http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#gf13067c0ebde29118ca05af76e5b17a9

【问题讨论】:

【参考方案1】:

frames 应该是您要从缓冲区写入的帧(样本)数。您系统的声音驱动程序将立即开始将这些样本传输到声卡,并且它们将以恒定速率播放。

在几个地方引入了延迟。在等待传输到卡时,驱动程序缓冲的数据存在延迟。在任何给定时刻,至少有一个充满数据的缓冲区正在传输到卡上,并且在应用程序端有缓冲,这似乎是您所关心的。

为了减少应用程序端的延迟,您需要编写适合您的最小缓冲区。如果您的应用程序执行一个 DSP 任务,那通常是一个窗口的数据。

在循环中写入小缓冲区没有任何优势 - 只需继续一次将所有内容都写入 - 但有一点需要理解:为了最大限度地减少延迟,您的应用程序写入驱动程序的速度不应快于驱动程序的写入速度数据到声卡,否则您最终会堆积更多数据并累积越来越多的延迟。

对于使与声音驱动程序同步生成数据相对容易的设计,请查看基于向声音播放引擎注册回调函数的 jack (http://jackaudio.org/)。事实上,如果您真的担心延迟,最好还是使用 jack 而不是自己尝试。

【讨论】:

我只需要播放约 17000 帧,所以应用程序端的延迟是可以的。最奇怪的是,如果我给 snd_pcm_writei() 所有帧,并删除 for 循环,则什么都不会播放。如果我保留for循环,例如将其设置为 5 次迭代,它会循环两次 ~17000 帧。如果我在第 11 行查看 libsndfile 的示例,pastebin.com/m559397b3,他会逐步通过缓冲区。当我这样做时,什么都没有播放。另外我觉得奇怪的是, snd_pcm_writei() 总是返回我给它的相同数量的帧。它永远不会返回一个较低的数字,这是我所期望的。 ? 17000 个样本的播放时间不到一秒。通过将缓冲区反复写入声音驱动程序,您基本上是在一次又一次地播放它,但根据您的描述,听起来好像系统上的某些东西阻止了声音的开始播放。首先,步入缓冲区是正确的做法;您不需要多次编写示例。其次,要查看您的声音驱动程序或声卡是否无法播放声音的开头,请尝试在您的缓冲区中填充大约一秒钟的 0 个样本,看看这是否会有所不同。 非常感谢您清除这一切。现在当你提到它时,我在播放 ALSA 时确实遇到了一些音频问题,例如播放器。前 2 秒左右没有播放任何内容,然后播放。我会尝试升级到 Fedora 12,然后再试一次。再次感谢 =) 我想补充一下,frames 等于 number_of_samples / channels。【参考方案2】:

我认为“过早”关闭设备的原因是您需要在snd_pcm_close(handle);之前调用snd_pcm_drain(handle);以确保在设备关闭之前播放所有数据。

【讨论】:

【参考方案3】:

我做了一些测试,以确定为什么snd_pcm_writei() 似乎对我不起作用,并使用了我在 ALSA 教程中找到的几个示例,我得出的结论是,这些简单示例在声音设备可以执行之前执行了snd_pcm_close ()播放发送给它的完整流。

我将速率设置为 11025,使用 128 字节的随机缓冲区,并为每秒 11025/128 的声音循环 snd_pcm_writei()。两秒钟需要 86*2 次调用 snd_pcm_write() 才能获得两秒钟的声音。

为了给设备足够的时间将数据转换为音频,我在snd_pcm_writei()循环之后使用了一个for循环来延迟snd_pcm_close()函数的执行。

经过测试,我不得不得出结论,在调用 snd_pcm_close 函数之前,示例代码没有提供足够的样本来克服设备延迟,这意味着 close 函数的延迟低于 snd_pcm_write() 函数。

【讨论】:

【参考方案4】:

如果 ALSA 驱动程序的启动阈值设置不正确(如果在您的情况下约为 2s),那么您需要在 snd_pcm_writei() 之后立即调用 snd_pcm_start() 来开始数据渲染。 或者您可以在 ALSA 设备的 SW 参数中设置适当的阈值。

参考:

http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html

【讨论】:

以上是关于如何使用 ALSA 的 snd_pcm_writei()?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ALSA 混合多个 PCM 流

ALSA:如何判断声音何时播放完毕

如何使用 Alsa 库更改扬声器的音量?

使用 ALSA/CAF 进行 Android 通话录音

jack_connect MIDI (Alsa) 端口

如何使用 ALSA 的 snd_pcm_writei()?