[alsa-devel] [PATCH 1/5] sync asound.h with kernel version
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/sound/asound.h | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-)
diff --git a/include/sound/asound.h b/include/sound/asound.h index 1f23cd6..3d46e9a 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -140,7 +140,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13)
typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -267,9 +267,17 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ -#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ +#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ + +#define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
+ + typedef int __bitwise snd_pcm_state_t; #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ @@ -407,6 +415,22 @@ struct snd_pcm_channel_info { unsigned int step; /* samples distance in bits */ };
+enum { + /* + * first definition for backwards compatibility only, + * maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else + */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0, + + /* timestamp definitions */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED +}; + struct snd_pcm_status { snd_pcm_state_t state; /* stream state */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ @@ -418,9 +442,11 @@ struct snd_pcm_status { snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ snd_pcm_state_t suspended_state; /* suspended stream state */ - __u32 reserved_alignment; /* must be filled with zero */ - struct timespec audio_tstamp; /* from sample counter or wall clock */ - unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */ };
struct snd_pcm_mmap_status { @@ -533,6 +559,7 @@ enum { #define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) #define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) #define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) +#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status) #define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) #define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) #define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)
Enable kernel-side functionality by letting user select what sort of timestamp it desires
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/pcm.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/pcm/pcm.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+)
diff --git a/include/pcm.h b/include/pcm.h index 0655e7f..b5bbd2d 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -330,6 +330,26 @@ typedef enum _snd_pcm_tstamp_type { SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, } snd_pcm_tstamp_type_t;
+typedef struct _snd_pcm_audio_tstamp_config { + /* 5 of max 16 bits used */ + unsigned int type_requested:4; + unsigned int report_delay:1; /* add total delay to A/D or D/A */ +} snd_pcm_audio_tstamp_config_t; + +typedef struct _snd_pcm_audio_tstamp_report { + /* 6 of max 16 bits used for bit-fields */ + + /* for backwards compatibility */ + unsigned int valid:1; + + /* actual type if hardware could not support requested timestamp */ + unsigned int actual_type:4; + + /* accuracy represented in ns units */ + unsigned int accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ + unsigned int accuracy; /* up to 4.29s, will be packed in separate field */ +} snd_pcm_audio_tstamp_report_t; + /** Unsigned frames quantity */ typedef unsigned long snd_pcm_uframes_t; /** Signed frames quantity */ @@ -980,6 +1000,30 @@ void snd_pcm_status_get_trigger_htstamp(const snd_pcm_status_t *obj, snd_htimest void snd_pcm_status_get_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr); void snd_pcm_status_get_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); +void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); +void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, + snd_pcm_audio_tstamp_report_t *audio_tstamp_report); +void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj, + snd_pcm_audio_tstamp_config_t *audio_tstamp_config); + +static inline void snd_pcm_pack_audio_tstamp_config(unsigned int *data, + snd_pcm_audio_tstamp_config_t *config) +{ + *data = config->report_delay; + *data <<= 4; + *data |= config->type_requested; +} + +static inline void snd_pcm_unpack_audio_tstamp_report(unsigned int data, unsigned int accuracy, + snd_pcm_audio_tstamp_report_t *report) +{ + data >>= 16; + report->valid = data & 1; + report->actual_type = (data >> 1) & 0xF; + report->accuracy_report = (data >> 5) & 1; + report->accuracy = accuracy; +} + snd_pcm_sframes_t snd_pcm_status_get_delay(const snd_pcm_status_t *obj); snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj); snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index e74e02f..441b3db 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -6334,6 +6334,44 @@ void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestam }
/** + * \brief Get "now" hi-res driver timestamp from a PCM status container. Defines when the status + * was generated by driver, may differ from normal timestamp. + * \param obj pointer to #snd_pcm_status_t + * \param ptr Pointer to returned timestamp + */ +void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr) +{ + assert(obj && ptr); + *ptr = obj->driver_tstamp; +} + +/** + * \brief Get audio_tstamp_report from a PCM status container + * \param obj pointer to #snd_pcm_status_t + * \param ptr Pointer to returned report (valid fields are accuracy and type) + */ +void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, + snd_pcm_audio_tstamp_report_t *audio_tstamp_report) +{ + assert(obj && audio_tstamp_report); + snd_pcm_unpack_audio_tstamp_report(obj->audio_tstamp_data, + obj->audio_tstamp_accuracy, + audio_tstamp_report); +} + +/** + * \brief set audio_tstamp_config from a PCM status container + * \param obj pointer to #snd_pcm_status_t + * \param ptr Pointer to config (valid fields are type and report_analog_delay) + */ +void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj, + snd_pcm_audio_tstamp_config_t *audio_tstamp_config) +{ + assert(obj && audio_tstamp_config); + snd_pcm_pack_audio_tstamp_config(&obj->audio_tstamp_data, audio_tstamp_config); +} + +/** * \brief Get delay from a PCM status container (see #snd_pcm_delay) * \return Delay in frames *
extend support to link, link_estimated and link_synchronized timestamp. wall-clock is deprecated
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- include/pcm.h | 3 ++- src/pcm/pcm.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/include/pcm.h b/include/pcm.h index b5bbd2d..a1d14a9 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -688,7 +688,8 @@ int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params); -int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); /* deprecated, use audio_ts_type */ +int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type); int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, unsigned int *rate_num, unsigned int *rate_den); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 441b3db..294da52 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3190,12 +3190,45 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param */ int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params) { + /* deprecated */ + return snd_pcm_hw_params_supports_audio_ts_type(params, + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT); +} + +/** + * \brief Check if hardware supports type of audio timestamps + * \param params Configuration space + * \param type Audio timestamp type + * \retval 0 Hardware doesn't support type of audio timestamps + * \retval 1 Hardware supports type of audio timestamps + * + * This function should only be called when the configuration space + * contains a single configuration. Call #snd_pcm_hw_params to choose + * a single configuration from the configuration space. + */ +int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type) +{ assert(params); if (CHECK_SANITY(params->info == ~0U)) { SNDMSG("invalid PCM info field"); return 0; /* FIXME: should be a negative error? */ } - return !!(params->info & SNDRV_PCM_INFO_HAS_WALL_CLOCK); + switch (type) { + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT: + return !!(params->info & SNDRV_PCM_INFO_HAS_WALL_CLOCK); /* deprecated */ + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT: + return 1; /* always supported, based on hw_ptr */ + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK: + return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ATIME); + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE: + return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME); + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED: + return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME); + case SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED: + return !!(params->info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME); + default: + return 0; + } }
/**
use STATUS_EXT ioctl if PCM protocol is > 2.0.12 All audio timestamp configuration will be ignored with an older protocol.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- src/pcm/pcm_hw.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index c34b766..232b197 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -510,10 +510,18 @@ static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_hw_t *hw = pcm->private_data; int fd = hw->fd, err; - if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) { - err = -errno; - SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err); - return err; + if (SNDRV_PROTOCOL_VERSION(2, 0, 13) > hw->version) { + if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err); + return err; + } + } else { + if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS_EXT, status) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_STATUS_EXT failed (%i)", err); + return err; + } } if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version) { status->tstamp.tv_nsec *= 1000L;
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- test/audio_time.c | 491 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 313 insertions(+), 178 deletions(-)
diff --git a/test/audio_time.c b/test/audio_time.c index 7435db6..e369e59 100644 --- a/test/audio_time.c +++ b/test/audio_time.c @@ -4,13 +4,39 @@ * helpful to verify the information reported by drivers. */
-#include "../include/asoundlib.h" +#include <stdio.h> +#include <malloc.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <time.h> +#include <locale.h> #include <math.h> +#include "../include/asoundlib.h"
-static char *device = "hw:0,0"; - +static char *command; +static char *pcm_name = "hw:0"; snd_output_t *output = NULL;
+static void usage(char *command) +{ + printf("Usage: %s [OPTION]... \n" + "\n" + "-h, --help help\n" + "-c, --capture capture tstamps \n" + "-d, --delay add delay \n" + "-D, --device=NAME select PCM by name \n" + "-p, --playback playback tstamps \n" + "-t, --ts_type=TYPE Default(0),link(1),link_estimated(2),synchronized(3) \n" + , command); +} + + long long timestamp2ns(snd_htimestamp_t t) { long long nsec; @@ -31,15 +57,20 @@ long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2) return nsec1 - nsec2; }
-void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, - snd_htimestamp_t *trigger_timestamp, - snd_htimestamp_t *audio_timestamp, - snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay) +void _gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, + snd_htimestamp_t *trigger_timestamp, + snd_htimestamp_t *audio_timestamp, + snd_pcm_audio_tstamp_config_t *audio_tstamp_config, + snd_pcm_audio_tstamp_report_t *audio_tstamp_report, + snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay) { int err; snd_pcm_status_t *status;
snd_pcm_status_alloca(&status); + + snd_pcm_status_set_audio_htstamp_config(status, audio_tstamp_config); + if ((err = snd_pcm_status(handle, status)) < 0) { printf("Stream status error: %s\n", snd_strerror(err)); exit(0); @@ -47,26 +78,30 @@ void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp); snd_pcm_status_get_htstamp(status, timestamp); snd_pcm_status_get_audio_htstamp(status, audio_timestamp); + snd_pcm_status_get_audio_htstamp_report(status, audio_tstamp_report); *avail = snd_pcm_status_get_avail(status); *delay = snd_pcm_status_get_delay(status); }
-#define PERIOD 6000 +#define TIMESTAMP_FREQ 8 /* Hz */ +#define SAMPLE_FREQ 48000 +#define PERIOD (SAMPLE_FREQ/TIMESTAMP_FREQ) #define PCM_LINK /* sync start for playback and capture */ #define TRACK_CAPTURE /* dump capture timing info */ #define TRACK_PLAYBACK /* dump playback timing info */ -#define TRACK_SAMPLE_COUNTS /* show difference between sample counters and audiotimestamps returned by driver */ +/*#define TRACK_SAMPLE_COUNTS */ /* show difference between sample counters and audiotimestamps returned by driver */ #define PLAYBACK_BUFFERS 4 -#define TSTAMP_TYPE SND_PCM_TSTAMP_TYPE_MONOTONIC +#define TSTAMP_TYPE SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW
-int main(void) +int main(int argc, char *argv[]) { - int err; - unsigned int i; - snd_pcm_t *handle_p = NULL; - snd_pcm_t *handle_c = NULL; - snd_pcm_sframes_t frames; + int c; + int err; + unsigned int i; + snd_pcm_t *handle_p = NULL; + snd_pcm_t *handle_c = NULL; + snd_pcm_sframes_t frames; snd_htimestamp_t tstamp_c, tstamp_p; snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p; snd_htimestamp_t audio_tstamp_c, audio_tstamp_p; @@ -87,206 +122,306 @@ int main(void) snd_pcm_sframes_t delay_p, delay_c; snd_pcm_uframes_t avail_p, avail_c;
- if ((err = snd_pcm_open(&handle_p, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { - printf("Playback open error: %s\n", snd_strerror(err)); - goto _exit; - } - if ((err = snd_pcm_set_params(handle_p, - SND_PCM_FORMAT_S16, - SND_PCM_ACCESS_RW_INTERLEAVED, - 2, - 48000, - 0, - 500000)) < 0) { /* 0.5sec */ - printf("Playback open error: %s\n", snd_strerror(err)); - goto _exit; + snd_pcm_audio_tstamp_config_t audio_tstamp_config_p; + snd_pcm_audio_tstamp_config_t audio_tstamp_config_c; + snd_pcm_audio_tstamp_report_t audio_tstamp_report_p; + snd_pcm_audio_tstamp_report_t audio_tstamp_report_c; + + int option_index; + static const char short_options[] = "hcpdD:t:"; + + static const struct option long_options[] = { + {"capture", 0, 0, 'c'}, + {"delay", 0, 0, 'd'}, + {"device", required_argument, 0, 'D'}, + {"help", no_argument, 0, 'h'}, + {"playback", 0, 0, 'p'}, + {"ts_type", required_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + int do_delay = 0; + int do_playback = 0; + int do_capture = 0; + int type = 0; + + while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { + switch (c) { + case 'h': + usage(command); + return 0; + case 'p': + do_playback = 1; + break; + case 'c': + do_capture = 1; + break; + case 'd': + do_delay = 1; + break; + case 'D': + pcm_name = optarg; + break; + case 't': + type = atoi(optarg); + break; + } }
- snd_pcm_hw_params_alloca(&hwparams_p); - /* get the current hwparams */ - err = snd_pcm_hw_params_current(handle_p, hwparams_p); - if (err < 0) { - printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err)); - goto _exit; - } - if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_p)) - printf("Playback relies on audio wallclock timestamps\n"); - else - printf("Playback relies on audio sample counter timestamps\n"); - - snd_pcm_sw_params_alloca(&swparams_p); - /* get the current swparams */ - err = snd_pcm_sw_params_current(handle_p, swparams_p); - if (err < 0) { - printf("Unable to determine current swparams_p: %s\n", snd_strerror(err)); - goto _exit; - } + memset(&audio_tstamp_config_p, 0, sizeof(snd_pcm_audio_tstamp_config_t)); + memset(&audio_tstamp_config_c, 0, sizeof(snd_pcm_audio_tstamp_config_t)); + memset(&audio_tstamp_report_p, 0, sizeof(snd_pcm_audio_tstamp_report_t)); + memset(&audio_tstamp_report_c, 0, sizeof(snd_pcm_audio_tstamp_report_t));
- /* enable tstamp */ - err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE); - if (err < 0) { - printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); - goto _exit; - } + if (do_playback) { + if ((err = snd_pcm_open(&handle_p, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + printf("Playback open error: %s\n", snd_strerror(err)); + goto _exit; + }
- err = snd_pcm_sw_params_set_tstamp_type(handle_p, swparams_p, TSTAMP_TYPE); - if (err < 0) { - printf("Unable to set tstamp type : %s\n", snd_strerror(err)); - goto _exit; - } + if ((err = snd_pcm_set_params(handle_p, + SND_PCM_FORMAT_S16, + SND_PCM_ACCESS_RW_INTERLEAVED, + 2, + SAMPLE_FREQ, + 0, + 4*1000000/TIMESTAMP_FREQ)) < 0) { + printf("Playback open error: %s\n", snd_strerror(err)); + goto _exit; + }
- /* write the sw parameters */ - err = snd_pcm_sw_params(handle_p, swparams_p); - if (err < 0) { - printf("Unable to set swparams_p : %s\n", snd_strerror(err)); - goto _exit; - } + snd_pcm_hw_params_alloca(&hwparams_p); +/* get the current hwparams */ + err = snd_pcm_hw_params_current(handle_p, hwparams_p); + if (err < 0) { + printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err)); + goto _exit; + }
- if ((err = snd_pcm_open(&handle_c, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { - printf("Capture open error: %s\n", snd_strerror(err)); - goto _exit; - } - if ((err = snd_pcm_set_params(handle_c, - SND_PCM_FORMAT_S16, - SND_PCM_ACCESS_RW_INTERLEAVED, - 2, - 48000, - 0, - 500000)) < 0) { /* 0.5sec */ - printf("Capture open error: %s\n", snd_strerror(err)); - goto _exit; - } + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 0)) + printf("Playback supports audio compat timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 1)) + printf("Playback supports audio default timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 2)) + printf("Playback supports audio link timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 3)) + printf("Playback supports audio link absolute timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 4)) + printf("Playback supports audio link estimated timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 5)) + printf("Playback supports audio link synchronized timestamps\n"); + + snd_pcm_sw_params_alloca(&swparams_p); + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle_p, swparams_p); + if (err < 0) { + printf("Unable to determine current swparams_p: %s\n", snd_strerror(err)); + goto _exit; + }
- snd_pcm_hw_params_alloca(&hwparams_c); - /* get the current hwparams */ - err = snd_pcm_hw_params_current(handle_c, hwparams_c); - if (err < 0) { - printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err)); - goto _exit; - } - if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_c)) - printf("Capture relies on audio wallclock timestamps\n"); - else - printf("Capture relies on audio sample counter timestamps\n"); - - snd_pcm_sw_params_alloca(&swparams_c); - /* get the current swparams */ - err = snd_pcm_sw_params_current(handle_c, swparams_c); - if (err < 0) { - printf("Unable to determine current swparams_c: %s\n", snd_strerror(err)); - goto _exit; - } + /* enable tstamp */ + err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE); + if (err < 0) { + printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); + goto _exit; + }
- /* enable tstamp */ - err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE); - if (err < 0) { - printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); - goto _exit; - } + err = snd_pcm_sw_params_set_tstamp_type(handle_p, swparams_p, TSTAMP_TYPE); + if (err < 0) { + printf("Unable to set tstamp type : %s\n", snd_strerror(err)); + goto _exit; + } + + /* write the sw parameters */ + err = snd_pcm_sw_params(handle_p, swparams_p); + if (err < 0) { + printf("Unable to set swparams_p : %s\n", snd_strerror(err)); + goto _exit; + }
- err = snd_pcm_sw_params_set_tstamp_type(handle_c, swparams_c, TSTAMP_TYPE); - if (err < 0) { - printf("Unable to set tstamp type : %s\n", snd_strerror(err)); - goto _exit; }
- /* write the sw parameters */ - err = snd_pcm_sw_params(handle_c, swparams_c); - if (err < 0) { - printf("Unable to set swparams_c : %s\n", snd_strerror(err)); - goto _exit; + if (do_capture) { + + if ((err = snd_pcm_open(&handle_c, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { + printf("Capture open error: %s\n", snd_strerror(err)); + goto _exit; + } + if ((err = snd_pcm_set_params(handle_c, + SND_PCM_FORMAT_S16, + SND_PCM_ACCESS_RW_INTERLEAVED, + 2, + SAMPLE_FREQ, + 0, + 4*1000000/TIMESTAMP_FREQ)) < 0) { + printf("Capture open error: %s\n", snd_strerror(err)); + goto _exit; + } + + snd_pcm_hw_params_alloca(&hwparams_c); + /* get the current hwparams */ + err = snd_pcm_hw_params_current(handle_c, hwparams_c); + if (err < 0) { + printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err)); + goto _exit; + } + + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 0)) + printf("Capture supports audio compat timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 1)) + printf("Capture supports audio default timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 2)) + printf("Capture supports audio link timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 3)) + printf("Capture supports audio link absolute timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 4)) + printf("Capture supports audio link estimated timestamps\n"); + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 5)) + printf("Capture supports audio link synchronized timestamps\n"); + + snd_pcm_sw_params_alloca(&swparams_c); + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle_c, swparams_c); + if (err < 0) { + printf("Unable to determine current swparams_c: %s\n", snd_strerror(err)); + goto _exit; + } + + /* enable tstamp */ + err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE); + if (err < 0) { + printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); + goto _exit; + } + + err = snd_pcm_sw_params_set_tstamp_type(handle_c, swparams_c, TSTAMP_TYPE); + if (err < 0) { + printf("Unable to set tstamp type : %s\n", snd_strerror(err)); + goto _exit; + } + + /* write the sw parameters */ + err = snd_pcm_sw_params(handle_c, swparams_c); + if (err < 0) { + printf("Unable to set swparams_c : %s\n", snd_strerror(err)); + goto _exit; + } }
+ if (do_playback && do_capture) { #ifdef PCM_LINK - if ((err = snd_pcm_link(handle_c, handle_p)) < 0) { - printf("Streams link error: %s\n", snd_strerror(err)); - exit(0); - } + if ((err = snd_pcm_link(handle_c, handle_p)) < 0) { + printf("Streams link error: %s\n", snd_strerror(err)); + exit(0); + } #endif - - i = PLAYBACK_BUFFERS; - while (i--) { - frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); - if (frames < 0) { - printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); - goto _exit; - } - frame_count_p += frames; }
- if (PLAYBACK_BUFFERS != 4) - snd_pcm_start(handle_p); + if (do_playback) { + i = PLAYBACK_BUFFERS; + while (i--) { + frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); + if (frames < 0) { + printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + goto _exit; + } + frame_count_p += frames; + } + + if (PLAYBACK_BUFFERS != 4) + snd_pcm_start(handle_p); + }
+ if (do_capture) { #ifndef PCM_LINK - /* need to start capture explicitly */ - snd_pcm_start(handle_c); + /* need to start capture explicitly */ + snd_pcm_start(handle_c); +#else + if (!do_playback) + /* need to start capture explicitly */ + snd_pcm_start(handle_c); #endif + }
- while (1) { - - frames = snd_pcm_wait(handle_c, -1); - if (frames < 0) { - printf("snd_pcm_wait failed: %s\n", snd_strerror(frames)); - goto _exit; - } - - frames = snd_pcm_readi(handle_c, buffer_c, PERIOD); - if (frames < 0) { - printf("snd_pcm_readi failed: %s\n", snd_strerror(frames)); - goto _exit; - } - frame_count_c += frames; + while (1) {
- frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); - if (frames < 0) { - printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); - goto _exit; - } + if (do_capture) {
- frame_count_p += frames; + frames = snd_pcm_wait(handle_c, -1); + if (frames < 0) { + printf("snd_pcm_wait failed: %s\n", snd_strerror(frames)); + goto _exit; + }
-#if defined(TRACK_PLAYBACK) - gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &audio_tstamp_p, &avail_p, &delay_p); + frames = snd_pcm_readi(handle_c, buffer_c, PERIOD); + if (frames < 0) { + printf("snd_pcm_readi failed: %s\n", snd_strerror(frames)); + goto _exit; + } + frame_count_c += frames;
+#if defined(TRACK_CAPTURE) + audio_tstamp_config_c.type_requested = type; + audio_tstamp_config_c.report_delay = do_delay; + _gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, + &audio_tstamp_c, &audio_tstamp_config_c, &audio_tstamp_report_c, + &avail_c, &delay_c); #if defined(TRACK_SAMPLE_COUNTS) - curr_count_p = frame_count_p - delay_p; /* written minus queued */ + curr_count_c = frame_count_c + delay_c; /* read plus queued */
- printf("playback: curr_count %lli driver count %lli, delta %lli\n", - (long long)curr_count_p * 1000000000LL / 48000 , - timestamp2ns(audio_tstamp_p), - (long long)curr_count_p * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_p) - ); + + printf("capture: curr_count %lli driver count %lli, delta %lli\n", + (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ , + timestamp2ns(audio_tstamp_c), + (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_c) + ); #endif
- printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", - timediff(tstamp_p, trigger_tstamp_p), - timestamp2ns(audio_tstamp_p), - timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p) - ); + printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", + timediff(tstamp_c, trigger_tstamp_c), + timestamp2ns(audio_tstamp_c), + timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c) + ); #endif + }
-#if defined(TRACK_CAPTURE) - gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &audio_tstamp_c, &avail_c, &delay_c); + if (do_playback) { + frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); + if (frames < 0) { + printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + goto _exit; + }
-#if defined(TRACK_SAMPLE_COUNTS) - curr_count_c = frame_count_c + delay_c; /* read plus queued */ + frame_count_p += frames; + +#if defined(TRACK_PLAYBACK)
+ audio_tstamp_config_p.type_requested = type; + audio_tstamp_config_p.report_delay = do_delay; + _gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, + &audio_tstamp_p, &audio_tstamp_config_p, &audio_tstamp_report_p, + &avail_p, &delay_p);
- printf("capture: curr_count %lli driver count %lli, delta %lli\n", - (long long)curr_count_c * 1000000000LL / 48000 , - timestamp2ns(audio_tstamp_c), - (long long)curr_count_c * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_c) - ); +#if defined(TRACK_SAMPLE_COUNTS) + curr_count_p = frame_count_p - delay_p; /* written minus queued */ + + printf("playback: curr_count %lli driver count %lli, delta %lli\n", + (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ , + timestamp2ns(audio_tstamp_p), + (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_p) + ); #endif
- printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", - timediff(tstamp_c, trigger_tstamp_c), - timestamp2ns(audio_tstamp_c), - timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c) - ); + printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", + timediff(tstamp_p, trigger_tstamp_p), + timestamp2ns(audio_tstamp_p), + timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p) + ); #endif + } +
- } + } /* while(1) */
_exit: if (handle_p)
participants (1)
-
Pierre-Louis Bossart