我尝试使用ALSA从USB音频设备获取输入,并将其作为一系列signed short
值写入磁盘。我最终得到的是看似有效数据的块,其中散布着大块的零。我猜我的缓冲区设置不正确,并没有正确使用内存映射。
我正在尝试:
- 采样率:8K(由设备强制)
- 缓冲区大小:2048
- 期间大小:512
- 一个频道
设备似乎正确打开并接受各种参数。在一些设置之后,循环运行如下:
snd_pcm_avail_update
snd_pcm_mmap_begin
memcpy data from mmap buffer to array of short
snd_pcm_mmap_commit
memcpy是指向short数组的指针,并以每次传递返回的帧数递增。
在此记录几秒钟后,我将其关闭并将后续缓冲区写入磁盘,作为每行的单个短值。我期待的是在1200和2300 Hz之间变化的PCM数据中的第二个或两个。我得到的是一些有很多零的数据。
我想知道的是:我的缓冲区和期间值是否理性?有没有人成功使用ALSA的内存映射输出?
编辑:一些代码
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t offset, frames, size;
short* pCID = (short*)malloc( 50000 * sizeof( short ));
short* ppCID = pCID;
while( size > 0 )
{
frames = size;
snd_pcm_mmap_begin (device, &areas, &offset, &frames);
short* pd = (short*)areas[0].addr;
memcpy( ppCID, (pd + (offset*sizeof(short))), frames * sizeof( short ));
ppCID += frames;
snd_pcm_mmap_commit(device, offset, frames);
size -= frames;
}
(为清楚起见,删除了错误检查)
完成所有操作后,我遍历pCID并写入磁盘。每行一个值。
答案 0 :(得分:6)
ARM上有一个带有USB音频驱动程序的known bug,其中内核和同一缓冲区的应用程序映射可能不是缓存一致的。
仅当代码可以直接处理样本而不将其复制到另一个缓冲区时,使用ALSA内存映射函数才有意义。
如果您复制它们,那么{em>完全相同 snd_pcm_readi
已经完成了。
换句话说,就是不要使用内存映射。
捕获时,缓冲区大小对延迟没有影响,因此您应该尽可能大,以避免可能的超出。
较小的周期大小可以降低延迟,但是您的程序不会做任何与实时相关的事情,因此您可以使用更大的周期来节省一点功率。
答案 1 :(得分:1)
来自documentation:“必须在此调用之前直接调用snd_pcm_avail_update()函数。否则,此函数可能返回错误的可用帧数。”我认为问题是snd_pcm_mmap_begin误报了可用帧的数量,所以你正在读取尚未写入的区域。
另外,我不是肯定的,但我不认为alsa mmap函数会阻塞直到有数据,尽管这可能会被我在这里看不到的其他代码所覆盖。它与文件mmap并不完全相同,所以不要开始认为它是。如果你的粉丝开始变得疯狂并且一切都变得缓慢,那么你的代码可能正在从应用程序切换到内核上下文,并且当有零个字节需要读取时,它会连续返回。
正如之前的海报所说,无论如何,这是snd_pcm_readi的完美用例,所以请使用它。