ALSA-非阻塞(交错)读取

时间:2019-07-04 11:05:37

标签: c alsa

我继承了一些在Linux嵌入式平台上运行的ALSA代码。 现有的实现使用snd_pcm_readi()snd_pcm_writei()进行阻止读取和写入。

我受命在ARM处理器上运行该程序,但是我发现阻塞的交错读取将CPU占用率提高到了99%,因此我正在探索非阻塞的读写。

我可以打开设备,

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

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

随后发生了我可以应要求提供的其他ALSA物品。
在这一点上值得一提的是:

  • 我们将采样率设置为48,000 [Hz]
  • 样本类型为带符号的32位整数
  • 设备始终将我们请求的周期大小覆盖为1024帧

像这样读取流:

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

在阻塞模式下,此调用大约需要15 [ms]才能完成。显然,变量actual返回时将读取1024。

问题是;在非阻塞模式下,此功能还需要15毫秒才能完成,并且actual总是在返回时读取1024。

我希望该函数将立即返回,其中actual为<= 1024,并且很有可能读取“ EAGAIN”(-11)。

在两次读取尝试之间,我计划使线程进入特定时间的睡眠状态,从而将CPU时间分配给其他进程。

我误解了ALSA API吗?还是我的代码缺少重要的步骤?

2 个答案:

答案 0 :(得分:0)

如果函数返回的值为1024,则在调用时至少有1024个帧可用。 (驱动程序实际启动设备可能需要15毫秒的时间。)

无论如何,阻塞或非阻塞模式对CPU使用率没有任何影响。要减少CPU使用率,请用defaultplughw替换hw设备,但随后会丢失设备共享或采样率/格式转换等功能。

答案 1 :(得分:0)

我通过如下包装snd_pcm_readi()解决了我的问题:

/*
** 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);
}

这对我来说很好。我的音频过程现在“仅”占用19%的CPU时间。 而且是否使用SND_PCM_NONBLOCK0打开PCM接口也没关系。

将执行callgrind分析,以查看是否可以在代码的其他位置保存更多的CPU周期。