Recording from ALSA - Understanding Memory Mapping

I am trying to use ALSA to input input from a USB audio device and write it to disk as a series of signed short values. What ends up with me are the blocks of what appears to be real data, alternating with large blocks of zeros. I assume that my buffer settings are not configured correctly and I am using memory mapping incorrectly.

What am I trying:

  • Sampling frequency: 8K (this is forcibly used by the device)
  • buffer size: 2048
  • period: 512
  • one channel

The device opens correctly and accepts various parameters. After some tweaking, the loop works like:

 snd_pcm_avail_update snd_pcm_mmap_begin memcpy data from mmap buffer to array of short snd_pcm_mmap_commit 

memcpy is a pointer to an array of short ones and increases by the number of frames returned by each pass.

After that, in a few seconds I will close it and write the subsequent buffer to disk as one short value in each line. What I expect is a second or two PCM data ranging from 1200 to 2300 Hz. What I get is some data with lots of zeros.

I wonder: my values ​​for the buffer and rational period? Has anyone succeeded in using memory mapped output from ALSA?

EDIT: Code

 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; } 

(validation error removed for clarity)
When everything is said and done, I look through the pCID and write to disk. One value for each row.

+7
source share
2 answers

There is a known bug with the USB audio driver on ARM, where the kernel and application cards of the same buffer may not be cache-coherent.

Using ALSA memory matching functions makes sense only if the code can process the samples directly without copying them to another buffer. If you copy them, you do the same as snd_pcm_readi . In other words, just do not use memory mapping.

When capturing, the buffer size does not affect latency, so you should do it as large as possible to avoid possible overruns.

Smaller period sizes give you lower latency, but your program doesn't do anything in real time, so you can use a larger period size to save some energy.

+5
source

From the documentation : "Before calling, you must call the snd_pcm_avail_update () function. Otherwise, this function may return the wrong number of available frames." I would suggest that the problem is that snd_pcm_mmap_begin incorrectly reports the number of available frames and therefore you are reading from an area that has not yet been recorded.

Also, I'm not sure, but I don’t think that alsa mmap functions will be blocked until there is data, although this may be covered by other code that I do not see here. This is not exactly the same as the mmap file, so do not start to think that it is. If your fan starts to go crazy and everything becomes slow, most likely your code rotates, switching from the application to the kernel context and returning again when there are zero bytes to read.

As the previous poster noted, this is an ideal use case for snd_pcm_readi, so use it.

+1
source

All Articles