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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With