Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

determining the time-stamp of sampled audio

Tags:

c

alsa

I have inherited an application which samples audio using snd_pcm_readn(). What is the most reliable way to determine the time-stamp that should be associated with each sample retrieved?

Presumably there will be some kind of buffering going on with ALSA or the capture card itself meaning clock_gettime() could have a variable error (especially as this is a multi-threaded application).

It looks to me like it ought to be something like:

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

}

However, to what time does snd_timestamp refer? Is it the first sample retrieved or the last one and do we need to account for samples that might have been captured since snd_pcm_readn() returned?

The ALSA documentation I have found is sketchy on the details. Is it even valid to use snd_pcm_status_get_tstamp() if time-stamps are not enabled (e.g. see https://www.kernel.org/doc/html/v4.10/sound/designs/timestamping.html).

Bonus points for references to good (preferably online) ALSA related reading material.


Related to this I cannot currently get the code above to compile as it says:

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

I have #include <alsa/pcm.h> and:

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

but there seems to be no definition of _snd_pcm_status in an obvious location.


Update 11-Aug-2017

As pointed out snd_pcm_status_t is opaque and must be allocated by ALSA. I still cannot quite get snd_pcm_status_get_tstamp to return the 'right' time-stamp:

//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 appears to be the monotonic clock rather than get time of day like I thought I was asking for (the ALSA docs do not specify).

I just got "1970-01-11T08:33:42: 894822" which accords with:

cat /proc/timer_list | grep now now at 894831565155289 nsecs

I could just get a base time of now - monotonic but that seems wrong for a clock claiming to be SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY. So I conclude my setup code or understanding is at fault somehow.

And is that the time-stamp of the samples I just readn() anyway?

like image 822
Bruce Adams Avatar asked Aug 09 '17 15:08

Bruce Adams


1 Answers

Timestamping must be enabled in the software params by calling snd_pcm_sw_params_set_tstamp_mode() with SND_PCM_TSTAMP_NONE/ENABLE.

Timestamps can use different clocks; select one by calling snd_pcm_sw_params_set_tstamp_type() with SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY/MONOTONIC/MONOTONIC_RAW.

The timestamp of the last time the stream was started or stopped can be retrieved with snd_pcm_status_get_trigger_tstamp(). The timestamp of the last pointer update can be retrieved with snd_pcm_status_get_tstamp(). (The corresponding pointer position, with snd_pcm_status_get_avail().)

snd_pcm_status_t is a hidden type because it can have different sizes in different library versions. It must be allocated dynamically; see this example.

like image 148
CL. Avatar answered Nov 14 '22 02:11

CL.