Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recording from ALSA - understanding memory mapping

Im attempting to use ALSA to take the input from a USB audio device and write it out to disk as a series of signed short values. What I'm ending up with are blocks of what appear to be valid data interspersed with large blocks of zeros. I'm guessing that I have my buffers setup incorrectly and am not using the memory mapping properly.

What I'm trying:

  • sample rate: 8K (this is forced by the device)
  • buffer size: 2048
  • period size: 512
  • one channel

The device appears to be opened properly and accepts the various params. After some setup the loop runs as:

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

The memcpy is a pointer to the array of short and is incremented by the number of frames returned each pass.

After this records for a few seconds I close it and write the subsequent buffer to disk as a single short value on each line. What I'm expecting is a second or two of PCM data varying between 1200 and 2300 Hz. What I'm getting is some data with lots of zeros.

What I'm wondering is: are my values for buffer and period rational? Has anyone succeeded in using the memory mapped output from ALSA?

EDIT: Some 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;
}

(error checking removed for clarity)
When all is said and done I loop through pCID and write to disk. One value per line.

like image 653
ethrbunny Avatar asked Feb 07 '13 22:02

ethrbunny


2 Answers

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

Using the ALSA memory mapping functions makes sense only if the code can handlle the samples directly without copying them to another buffer. If you do copy them, you are doing exactly the same that snd_pcm_readi already does. In other words, just don't use memory mapping.

When capturing, the buffer size has no effect on latency, so you should make it as large as possible to avoid possible overruns.

Smaller period sizes give you lower latency, but your program does not do anything real-time related, so you could use a larger period size to save a little bit of power.

like image 140
CL. Avatar answered Sep 22 '22 10:09

CL.


From the documentation: "It is necessary to call the snd_pcm_avail_update() function directly before this call. Otherwise, this function can return a wrong count of available frames." I would assume that the issue is that snd_pcm_mmap_begin is misreporting the number of available frames and so you are reading from an area that has not yet been written to.

Also, I'm not positive, but I don't think that the alsa mmap functions will block until there is data, although that may be covered by other code I'm not seeing here. It's not quite the same thing as a file mmap, so don't start thinking that it is. If your fan starts going crazy and everything gets slow, chances are your code is spinning switching from application to kernel context and back again continuously when there are zero bytes to be read.

As the previous poster has noted, this is the perfect use case for snd_pcm_readi anyway, so use that.

like image 35
user2040142 Avatar answered Sep 22 '22 10:09

user2040142