pcm: provide a simple mechanism to start playback at a given time
RFC 1/1 pcm: provide a simple mechanism to start playback at a given time
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(-)
include/global.h +9 -1
@@ -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 ;/** \} */ #ifdef __cplusplus
include/pcm.h +2 -0
@@ -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)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);
include/sound/asound.h +7 -0
@@ -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; +}; +#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) +/***************************************************************************** * *
src/pcm/pcm.c +21 -1
@@ -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 +*/ +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;
src/pcm/pcm_hw.c +15 -0
@@ -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,
src/pcm/pcm_local.h +1 -0
@@ -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);
Signature · 1 line
1.9.3
Hi Nick,
Some more comments :)
Quoted 29 lines · Nick Stoughton
Initial implementation / Request For Comment.
Given an absolute time based on a given clock (
CLOCK_MONOTONIC,CLOCK_MONOTONIC_RAW,CLOCK_REALTIMEetc), 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(-)include/global.h +2
-0@@ -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 ;
include/global.h struct timespec { · line 162
Quoted 10 lines
include/global.h +7
-1/** 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.
Quoted 8 lines
include/global.h +0
-0/** \} */ #ifdef __cplusplusinclude/pcm.h +0
-0
include/pcm.h int snd_pcm_prepare(snd_pcm_t *pcm); · line 479
Quoted 6 lines
include/pcm.h +2
-0@@ -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...
Quoted 8 lines
include/pcm.h +0
-0int 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);include/sound/asound.h +0
-0
include/sound/asound.h struct snd_pcm_mmap_control { · line 441
Quoted 8 lines
include/sound/asound.h +5
-0@@ -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.
Quoted 17 lines
include/sound/asound.h +2
-0#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) +/***************************************************************************** * * src/pcm/pcm.c +0
-0
src/pcm/pcm.c int snd_pcm_start(snd_pcm_t *pcm) · line 1090
Quoted 7 lines
src/pcm/pcm.c +4
-0@@ -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...
Quoted 75 lines
src/pcm/pcm.c +17
-1+*/ +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;src/pcm/pcm_hw.c +15
-0@@ -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,src/pcm/pcm_local.h +1
-0@@ -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);