如何使用ALSA的snd_pcm_writei()?
有人可以解释 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 * Latency = frames
?
所以如果我有: 采样率 = 44100 延迟 = 0.5 [秒] all_frames = 100000
我应该提供给 snd_pcm_writei()
的帧数为
sample_rate * Latency = Frames 44100*0.5 = 22050
以及 for 循环的迭代次数应该是?:
(int) 100000/22050 = 4;帧= 22050
和一个额外的帧,但只有
100000 mod 22050 = 11800
帧?
是这样的吗?
路易丝
http://www.alsa-project.org/ alsa-doc/alsa-lib/group___p_c_m.html#gf13067c0ebde29118ca05af76e5b17a9
Can someone explain how snd_pcm_writei
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer,
snd_pcm_uframes_t size)
works?
I have used it like so:
for (int i = 0; i < 1; i++) {
f = snd_pcm_writei(handle, buffer, frames);
...
}
Full source code at http://pastebin.com/m2f28b578
Does this mean, that I shouldn't give snd_pcm_writei()
the number of
all the frames in buffer
, but only
sample_rate * latency = frames
?
So if I e.g. have:
sample_rate = 44100
latency = 0.5 [s]
all_frames = 100000
The number of frames that I should give to snd_pcm_writei()
would be
sample_rate * latency = frames
44100*0.5 = 22050
and the number of iterations the for-loop should be?:
(int) 100000/22050 = 4; with frames=22050
and one extra, but only with
100000 mod 22050 = 11800
frames?
Is that how it works?
Louise
http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#gf13067c0ebde29118ca05af76e5b17a9
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
frames 应该是您想要从缓冲区写入的帧(样本)数。您系统的声音驱动程序将立即开始将这些样本传输到声卡,并且它们将以恒定的速率播放。
延迟是在多个地方引入的。在等待传输到卡时,驱动程序缓冲的数据存在延迟。在任何给定时刻,至少有一个充满数据的缓冲区正在传输到卡,并且应用程序端有缓冲,这似乎是您所关心的。
为了减少应用程序端的延迟,您需要编写适合您的最小缓冲区。如果您的应用程序执行 DSP 任务,那么这通常是一个窗口的数据量。
在循环中写入小缓冲区没有任何优势 - 只需继续并一次性写入所有内容 - 但有一点需要理解:为了最大限度地减少延迟,您的应用程序写入驱动程序的速度不应超过驱动程序将数据写入驱动程序的速度声卡,否则您最终会堆积更多数据并积累越来越多的延迟。
对于使与声音驱动程序同步生成数据相对容易的设计,请查看 jack (http://jackaudio.org/) 它基于向声音播放引擎注册回调函数。事实上,如果您确实担心延迟,那么使用插孔可能比尝试自己操作更好。
frames should be the number of frames (samples) you want to write from the buffer. Your system's sound driver will start transferring those samples to the sound card right away, and they will be played at a constant rate.
The latency is introduced in several places. There's latency from the data buffered by the driver while waiting to be transferred to the card. There's at least one buffer full of data that's being transferred to the card at any given moment, and there's buffering on the application side, which is what you seem to be concerned about.
To reduce latency on the application side you need to write the smallest buffer that will work for you. If your application performs a DSP task, that's typically one window's worth of data.
There's no advantage in writing small buffers in a loop - just go ahead and write everything in one go - but there's an important point to understand: to minimize latency, your application should write to the driver no faster than the driver is writing data to the sound card, or you'll end up piling up more data and accumulating more and more latency.
For a design that makes producing data in lockstep with the sound driver relatively easy, look at jack (http://jackaudio.org/) which is based on registering a callback function with the sound playback engine. In fact, you're probably just better off using jack instead of trying to do it yourself if you're really concerned about latency.
我认为“过早”设备关闭的原因是您需要在
snd_pcm_close(handle);
之前调用snd_pcm_drain(handle);
以确保播放所有数据在设备关闭之前。I think the reason for the "premature" device closure is that you need to call
snd_pcm_drain(handle);
prior tosnd_pcm_close(handle);
to ensure that all data is played before the device is closed.我使用在 ALSA 教程中找到的几个示例进行了一些测试,以确定为什么 snd_pcm_writei() 似乎对我不起作用,我得出的结论是,这些简单的示例正在执行 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 的延迟要少() 函数。
I did some testing to determine why
snd_pcm_writei()
didn't seem to work for me using several examples I found in the ALSA tutorials and what I concluded was that the simple examples were doing asnd_pcm_close ()
before the sound device could play the complete stream sent it to it.I set the rate to
11025
, used a 128 byte random buffer, and for loopedsnd_pcm_writei()
for 11025/128 for each second of sound. Two seconds required 86*2 callssnd_pcm_write()
to get two seconds of sound.In order to give the device sufficient time to convert the data to audio, I put used a for loop after the
snd_pcm_writei()
loop to delay execution of thesnd_pcm_close()
function.After testing, I had to conclude that the sample code didn't supply enough samples to overcome the device latency before the
snd_pcm_close
function was called which implies that the close function has less latency than thesnd_pcm_write()
function.如果 ALSA 驱动程序的启动阈值设置不正确(如果您的情况约为 2 秒),那么您需要在 snd_pcm_writei() 之后立即调用 snd_pcm_start() 来启动数据渲染。
或者您可以在 ALSA 设备的软件参数中设置适当的阈值。
参考:
If the ALSA driver's start threshold is not set properly (if in your case it is about 2s), then you will need to call snd_pcm_start() to start the data rendering immediately after snd_pcm_writei().
Or you may set appropriate threshold in the SW params of ALSA device.
ref: