简体   繁体   中英

ALSA - Non blocking (interleaved) read

I inherited some ALSA code that runs on a Linux embedded platform. The existing implementation does blocking reads and writes using snd_pcm_readi() and snd_pcm_writei() .

I am tasked to make this run on an ARM processor, but I find that the blocked interleaved reads push the CPU to 99%, so I am exploring non-blocking reads and writes.

I open the device as can be expected:

snd_pcm_handle *handle;
const char* hwname = "plughw:0"; // example name

snd_pcm_open(&handle, hwname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);

Other ALSA stuff then happens which I can supply on request.
Noteworthy to mention at this point that:

  • we set a sampling rate of 48,000 [Hz]
  • the sample type is signed 32 bit integer
  • the device always overrides our requested period size to 1024 frames

Reading the stream like so:

int32* buffer; // buffer set up to hold #period_size samples
int actual = snd_pcm_readi(handle, buffer, period_size);

This call takes approx 15 [ms] to complete in blocking mode. Obviously, variable actual will read 1024 on return.

The problem is; in non-blocking mode, this function also takes 15 msec to complete and actual also always reads 1024 on return.

I would expect that the function would return immediately, with actual being <=1024 and quite possibly reading "EAGAIN" (-11).

In between read attempts I plan to put the thread to sleep for a specific amount of time, yielding CPU time to other processes.

Am I misunderstanding the ALSA API? Or could it be that my code is missing a vital step?

If the function returns a value of 1024, then at least 1024 frames were available at the time of the call. (It's possible that the 15 ms is time needed by the driver to actually start the device.)

Anyway, blocking or non-blocking mode does not make any difference regarding CPU usage. To reduce CPU usage, replace the default device with plughw or hw , but then you lose features like device sharing or sample rate/format conversion.

I solved my problem by wrapping snd_pcm_readi() as follows:

/*
** Read interleaved stream in non-blocking mode
*/
template <typename SampleType>
snd_pcm_sframes_t snd_pcm_readi_nb(snd_pcm_t* pcm, SampleType* buffer, snd_pcm_uframes_t size, unsigned samplerate)
{
    const snd_pcm_sframes_t avail = ::snd_pcm_avail(pcm);
    if (avail < 0) {
        return avail;
    }

    if (avail < size) {

        snd_pcm_uframes_t remain = size - avail;
        unsigned long msec = (remain * 1000) / samplerate;

        static const unsigned long SLEEP_THRESHOLD_MS = 1;

        if (msec > SLEEP_THRESHOLD_MS) {
            msec -= SLEEP_THRESHOLD_MS;
            // exercise for the reader: sleep for msec
        }
    }

    return ::snd_pcm_readi(pcm, buffer, size);
}

This works quite well for me. My audio process now 'only' takes 19% CPU time. And it matters not if the PCM interface was opened using SND_PCM_NONBLOCK or 0 .

Going to perform callgrind analysis to see if more CPU cycles can be saved elsewhere in the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM