[alsa-devel] [RFC] pcm: provide a simple mechanism to start playback at a given time

Tim Cussins timcussins at eml.cc
Thu Oct 16 13:15:18 CEST 2014


Hi Nick,

Some more comments :)

On 14/10/14 09:34, Nick Stoughton wrote:
> Initial implementation / Request For Comment.
>
> Given an absolute time based on a given clock (CLOCK_MONOTONIC,
> CLOCK_MONOTONIC_RAW, CLOCK_REALTIME etc), setup a high resolution timer
> to cause playback to be triggered at that time.
> ---
>   include/global.h       | 10 +++++++++-
>   include/pcm.h          |  2 ++
>   include/sound/asound.h |  7 +++++++
>   src/pcm/pcm.c          | 22 +++++++++++++++++++++-
>   src/pcm/pcm_hw.c       | 15 +++++++++++++++
>   src/pcm/pcm_local.h    |  1 +
>   6 files changed, 55 insertions(+), 2 deletions(-)
>
> diff --git a/include/global.h b/include/global.h
> index 16a26dc..f0fb661 100644
> --- a/include/global.h
> +++ b/include/global.h
> @@ -144,6 +144,8 @@ struct timespec {
>   	time_t		tv_sec;		/* seconds */
>   	long		tv_nsec;	/* nanoseconds */
>   };
> +
> +typedef int clockid_t;
>   #endif
>   #endif
>
> @@ -151,7 +153,13 @@ struct timespec {
>   typedef struct timeval snd_timestamp_t;
>   /** Hi-res timestamp */
>   typedef struct timespec snd_htimestamp_t;
> -
> +/** clock type */
> +typedef clockid_t snd_clock_t;
> +/** a combined clock and time */
> +typedef struct {
> +        snd_clock_t             clock;
> +        snd_htimestamp_t        time;
> +} snd_clock_time_t;

See comments on snd_timestamp_type_t from previous email.

>   /** \} */
>
>   #ifdef __cplusplus
> diff --git a/include/pcm.h b/include/pcm.h
> index db88ad5..dfaf69f 100644
> --- a/include/pcm.h
> +++ b/include/pcm.h
> @@ -474,6 +474,8 @@ int snd_pcm_prepare(snd_pcm_t *pcm);
>   int snd_pcm_reset(snd_pcm_t *pcm);
>   int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status);
>   int snd_pcm_start(snd_pcm_t *pcm);
> +int snd_pcm_start_at(snd_pcm_t *pcm, snd_clock_time_t *start_time);

> +#define SND_PCM_START_AT(pcm, start_time) snd_pcm_start_at(pcm, start_time)

A debug macro, right? Just checking...

>   int snd_pcm_drop(snd_pcm_t *pcm);
>   int snd_pcm_drain(snd_pcm_t *pcm);
>   int snd_pcm_pause(snd_pcm_t *pcm, int enable);
> diff --git a/include/sound/asound.h b/include/sound/asound.h
> index 6ee5867..92647ce 100644
> --- a/include/sound/asound.h
> +++ b/include/sound/asound.h
> @@ -434,6 +434,11 @@ struct snd_pcm_mmap_control {
>   	snd_pcm_uframes_t avail_min;	/* RW: min available frames for wakeup */
>   };
>
> +struct snd_clock_time {
> +        clockid_t       clock;
> +        struct timespec time;
> +};
> +

As above.

>   #define SNDRV_PCM_SYNC_PTR_HWSYNC	(1<<0)	/* execute hwsync */
>   #define SNDRV_PCM_SYNC_PTR_APPL		(1<<1)	/* get appl_ptr from driver (r/w op) */
>   #define SNDRV_PCM_SYNC_PTR_AVAIL_MIN	(1<<2)	/* get avail_min from driver */
> @@ -547,6 +552,8 @@ enum {
>   #define SNDRV_PCM_IOCTL_READN_FRAMES	_IOR('A', 0x53, struct snd_xfern)
>   #define SNDRV_PCM_IOCTL_LINK		_IOW('A', 0x60, int)
>   #define SNDRV_PCM_IOCTL_UNLINK		_IO('A', 0x61)
> +#define SNDRV_PCM_IOCTL_START_AT        _IOR('A', 0x62, struct snd_clock_time)
> +
>
>   /*****************************************************************************
>    *                                                                           *
> diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
> index 4a7be6c..7051a22 100644
> --- a/src/pcm/pcm.c
> +++ b/src/pcm/pcm.c
> @@ -1084,6 +1084,25 @@ int snd_pcm_start(snd_pcm_t *pcm)
>   }
>
>   /**
> + * \brief Start a PCM at a specified point in the future
> + * \param pcm PCM handle
> + * \param start_time time to start - based on specified clock
> + * \return 0 on success otherwise a negative error code

Let's figure out what the error codes are going to be:

   EINVAL for invalid arguments (broken timespec, invalid clock type)
   ENOSYS if start_at isn't supported in this configuration
   ETIME  if timespec points into the past? Maybe not a great idea...

> + */
> +int snd_pcm_start_at(snd_pcm_t *pcm, snd_clock_time_t *start_at)
> +{
> +        assert(pcm);
> +        assert(start_at);
> +	if (CHECK_SANITY(! pcm->setup)) {
> +		SNDMSG("PCM not set up");
> +		return -EIO;
> +	}
> +        if (pcm->fast_ops->start_at)
> +                return pcm->fast_ops->start_at(pcm->fast_op_arg, start_at);
> +        return -EINVAL;
> +}
> +
> +/**
>    * \brief Stop a PCM dropping pending frames
>    * \param pcm PCM handle
>    * \return 0 on success otherwise a negative error code
> @@ -2444,8 +2463,9 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
>   		                continue;
>   			return -errno;
>                   }
> -		if (! err_poll)
> +		if (! err_poll) {
>   			break;
> +                }
>   		err = snd_pcm_poll_descriptors_revents(pcm, pfd, npfds, &revents);
>   		if (err < 0)
>   			return err;
> diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
> index c34b766..9888253 100644
> --- a/src/pcm/pcm_hw.c
> +++ b/src/pcm/pcm_hw.c
> @@ -620,6 +620,20 @@ static int snd_pcm_hw_start(snd_pcm_t *pcm)
>   	return 0;
>   }
>
> +static int snd_pcm_hw_start_at(snd_pcm_t *pcm, snd_clock_time_t *start_time)
> +{
> +	snd_pcm_hw_t *hw = pcm->private_data;
> +	int err;
> +
> +	sync_ptr(hw, 0);
> +	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_START_AT, start_time) < 0) {
> +		err = -errno;
> +		SYSMSG("SNDRV_PCM_IOCTL_START_AT failed (%i)", err);
> +		return err;
> +	}
> +	return 0;
> +}
> +
>   static int snd_pcm_hw_drop(snd_pcm_t *pcm)
>   {
>   	snd_pcm_hw_t *hw = pcm->private_data;
> @@ -1336,6 +1350,7 @@ static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
>   	.prepare = snd_pcm_hw_prepare,
>   	.reset = snd_pcm_hw_reset,
>   	.start = snd_pcm_hw_start,
> +	.start_at = snd_pcm_hw_start_at,
>   	.drop = snd_pcm_hw_drop,
>   	.drain = snd_pcm_hw_drain,
>   	.pause = snd_pcm_hw_pause,
> diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
> index 394505f..c3e58af 100644
> --- a/src/pcm/pcm_local.h
> +++ b/src/pcm/pcm_local.h
> @@ -154,6 +154,7 @@ typedef struct {
>   	int (*prepare)(snd_pcm_t *pcm);
>   	int (*reset)(snd_pcm_t *pcm);
>   	int (*start)(snd_pcm_t *pcm);
> +	int (*start_at)(snd_pcm_t *pcm, snd_clock_time_t *start_time);
>   	int (*drop)(snd_pcm_t *pcm);
>   	int (*drain)(snd_pcm_t *pcm);
>   	int (*pause)(snd_pcm_t *pcm, int enable);
>



More information about the Alsa-devel mailing list