8

我试图使用 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 赫兹之间变化的 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 并写入磁盘。每行一个值。

4

2 回答 2

6

ARM 上的 USB 音频驱动程序存在一个已知错误,其中内核和应用程序的同一缓冲区映射可能不是缓存一致的。

仅当代码可以直接处理样本而不将它们复制到另一个缓冲区时,使用 ALSA 内存映射函数才有意义。如果您确实复制它们,那么您所做的与已经做的完全相同snd_pcm_readi。换句话说,就是不要使用内存映射。

捕获时,缓冲区大小对延迟没有影响,因此应将其设置得尽可能大以避免可能的溢出。

较小的周期大小可以降低延迟,但您的程序不会执行任何与实时相关的操作,因此您可以使用较大的周期大小来节省一点功率。

于 2013-02-08T08:28:05.270 回答
1

来自文档:“有必要在此调用之前直接调用 snd_pcm_avail_update() 函数。否则,此函数可能会返回错误的可用帧数。” 我认为问题在于 snd_pcm_mmap_begin 误报了可用帧的数量,因此您正在读取尚未写入的区域。

另外,我并不肯定,但我认为 alsa mmap 函数在有数据之前不会阻塞,尽管这可能被我在这里没有看到的其他代码所覆盖。它与文件 mmap 不太一样,所以不要开始认为它是。如果您的粉丝开始发疯并且一切变得缓慢,那么您的代码很可能正在从应用程序切换到内核上下文并在要读取零字节时不断地再次切换回来。

正如之前的海报所指出的,无论如何,这都是 snd_pcm_readi 的完美用例,所以使用它。

于 2013-02-08T22:34:56.157 回答