确定采样音频的时间戳

时间:2017-08-09 15:47:12

标签: c alsa

我继承了一个使用snd_pcm_readn()对音频进行采样的应用程序。 确定与检索到的每个样本相关联的时间戳的最可靠方法是什么?

据推测,ALSA或捕获卡本身会有某种缓冲,这意味着clock_gettime()可能会出现变量错误 (特别是因为这是一个多线程应用程序)。

在我看来它应该是这样的:

if((alsa_result = snd_pcm_readn(card->capture_handle,
         card->capture_buff,
         card->buffer_size)) == 0)
{

    snd_pcm_status_t pcm_status;
    snd_timestamp_t  snd_timestamp; //actually a struct timeval under the hood
    if (snd_pcm_status(card->capture_handle, &pcm_status) == 0)
    {
       snd_pcm_status_get_tstamp(&pcm_status, &snd_timestamp);
       // translate timeval into milliseconds
       const uint64_t millis = (snd_timestamp.tv_sec * (uint64_t)1000) + (snd_timestamp.tv_usec / 1000);
    }

}

但是,snd_timestamp在什么时间引用?它是检索的第一个样本还是最后一个样本,我们是否需要考虑自snd_pcm_readn()返回后可能已捕获的样本?

我发现的ALSA文档在细节上是粗略的。如果未启用时间戳,则使用snd_pcm_status_get_tstamp()是否有效(例如,请参阅https://www.kernel.org/doc/html/v4.10/sound/designs/timestamping.html)。

参考良好(最好是在线)ALSA相关阅读材料的奖励积分。

与此相关,我目前无法获得上面的代码进行编译,因为它说:

error: storage size of ‘pcm_status’ isn’t known
  snd_pcm_status_t pcm_status;

我有#include <alsa/pcm.h>和:

>grep -r _snd_pcm_status /usr/include/
/usr/include/alsa/pcm.h: typedef struct _snd_pcm_status snd_pcm_status_t;

但似乎没有_snd_pcm_status在明显位置的定义。

2017年8月11日更新

正如所指出的,snd_pcm_status_t是不透明的,必须由ALSA分配。 我仍然无法让snd_pcm_status_get_tstamp返回&#39;右边&#39;时间戳:

//setup code...
if ((alsa_error = snd_pcm_sw_params_malloc(&sw_params)) < 0)
{
   //handle error
}
if ((alsa_error = snd_pcm_sw_params_set_tstamp_mode(card->capture_handle, sw_params, SND_PCM_TSTAMP_ENABLE)) < 0)
{
   //handle error
}
if ((alsa_error = snd_pcm_sw_params_set_tstamp_mode(card->capture_handle, sw_params, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)) < 0)
{
   //handle error
}
snd_pcm_sw_params_free(sw_params);


// later on
if((alsa_result = snd_pcm_readn(card->capture_handle,
     card->capture_buff,
     card->buffer_size)) == 0)
{   
   time_t timeStamp = 0;
   snd_timestamp_t  snd_timestamp = {0,0}; //actually a struct timeval
   if (snd_pcm_status(card->capture_handle, card->status) == 0)
   {
       snd_pcm_status_get_tstamp(card->status, &snd_timestamp);
       timeStamp = snd_timestamp.tv_sec;
   }
}

timeStamp似乎是单调的时钟而不是像我想的那样得到时间(ALSA文档没有指定)。

我刚刚得到&#34; 1970-01-11T08:33:42:894822&#34; 符合:

cat / proc / timer_list | grep现在    现在是894831565155289 nsecs

我可以获得now - monotonic的基准时间,但对于声称为SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY的时钟而言,这似乎是错误的。所以我总结一下我的设置代码或理解是错误的。

那是我刚读过的样本的时间标记()吗?

1 个答案:

答案 0 :(得分:2)

必须使用snd_pcm_sw_params_set_tstamp_mode() / SND_PCM_TSTAMP_NONE致电ENABLE,在软件参数中启用时间戳。

时间戳可以使用不同的时钟;通过snd_pcm_sw_params_set_tstamp_type() / SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY / MONOTONIC拨打MONOTONIC_RAW来选择一个。

可以使用snd_pcm_status_get_trigger_tstamp()检索上次启动或停止流的时间戳。可以使用snd_pcm_status_get_tstamp()检索最后一次指针更新的时间戳。 (相应的指针位置,snd_pcm_status_get_avail()。)

snd_pcm_status_t是一种隐藏类型,因为它在不同的库版本中可以有不同的大小。 它必须动态分配;见this example