从ALSA录音 - 理解内存映射

7
我正在尝试使用ALSA从USB音频设备获取输入,并将其作为一系列有符号短整型值写入磁盘。但结果是有效数据块与大量零块交错出现。我猜测我的缓冲设置不正确,没有正确使用内存映射。

我所尝试的:

  • 采样率: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是一个指向短数组的指针,每次传递返回的帧数都会增加。

在记录几秒钟后,我关闭它并将随后的缓冲区作为单个短值写入每行的磁盘。我期望的是1200到2300赫兹之间变化的几秒钟的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并写入磁盘。每行一个值。
2个回答

6

在ARM上,USB音频驱动程序存在已知问题,内核和应用程序对同一缓冲区的映射可能不是高速缓存一致。

仅当代码可以直接处理样本而不将其复制到另一个缓冲区时,使用ALSA内存映射函数才有意义。如果您要复制它们,则会做snd_pcm_readi已经做过的事情。换句话说,不要使用内存映射。

在捕获时,缓冲区大小对延迟没有影响,因此您应该使其尽可能大,以避免可能的溢出。

较小的周期大小可为您提供较低的延迟,但您的程序与实时相关性不大,因此您可以使用较大的周期大小来节省一些电源。


1
文档中可以看到:"在调用此函数之前,必须直接调用snd_pcm_avail_update()函数,否则该函数可能会返回可用帧的错误计数。" 我认为问题在于snd_pcm_mmap_begin错误地报告了可用帧的数量,因此您正在读取尚未写入的区域。
此外,我不确定,但我认为alsa mmap函数不会阻塞直到有数据,尽管可能被其他代码覆盖,这与文件mmap不完全相同,因此不要开始认为它是。如果您的风扇开始疯狂转动并且一切变得缓慢,那么当没有字节可读时,您的代码将不断地在应用程序和内核上下文之间切换。
正如先前的帖子所指出的那样,这是使用snd_pcm_readi的完美案例,因此请使用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接