From 9a435c2d934af9f08e9762e0cca377b35b249edf Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Fri, 20 Oct 2000 09:18:13 +0000 Subject: [PATCH] Added support for auto mmap. Much improved version of pcm_share (without async signals) --- doc/asoundrc.doc | 4 +- src/pcm/pcm.c | 90 +++++- src/pcm/pcm_adpcm.c | 2 +- src/pcm/pcm_alaw.c | 2 +- src/pcm/pcm_hw.c | 48 +-- src/pcm/pcm_linear.c | 2 +- src/pcm/pcm_local.h | 24 +- src/pcm/pcm_mulaw.c | 2 +- src/pcm/pcm_multi.c | 2 +- src/pcm/pcm_plug.c | 4 - src/pcm/pcm_plugin.c | 26 +- src/pcm/pcm_rate.c | 2 +- src/pcm/pcm_route.c | 2 +- src/pcm/pcm_share.c | 735 +++++++++++++++++++++++++++++-------------- 14 files changed, 619 insertions(+), 326 deletions(-) diff --git a/doc/asoundrc.doc b/doc/asoundrc.doc index e9e6d537..a63519ac 100644 --- a/doc/asoundrc.doc +++ b/doc/asoundrc.doc @@ -74,7 +74,9 @@ sname Slave PCM name on server - share Share PCM Fields: sname Slave name -[schannels] Slave channels +[schannels] Slave channels +[sformat] Slave format +[srate] Slave rate binding Bindings table .N Slave channel for client channel N diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 49772a83..7e6f1df9 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include "pcm_local.h" #include "list.h" @@ -138,27 +140,39 @@ int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) return pcm->ops->channel_setup(pcm->op_arg, setup); } -int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) +int _snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) { int err; snd_pcm_setup_t setup; - int mmap = 0; - assert(pcm && params); + if ((err = pcm->ops->params(pcm->op_arg, params)) < 0) + return err; + pcm->valid_setup = 0; + return snd_pcm_setup(pcm, &setup); +} + +int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params) +{ + int err; if (pcm->mmap_info) { - mmap = 1; err = snd_pcm_munmap(pcm); if (err < 0) return err; } - if ((err = pcm->ops->params(pcm->op_arg, params)) < 0) - return err; - pcm->valid_setup = 0; - err = snd_pcm_setup(pcm, &setup); - if (pcm->mmap_auto || mmap) - snd_pcm_mmap(pcm); + err = _snd_pcm_params(pcm, params); + if (pcm->valid_setup) + snd_pcm_mmap(pcm); return err; } +int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) +{ + assert(pcm && params); + if (pcm->mmap_auto) + return snd_pcm_params_mmap(pcm, params); + assert(!pcm->mmap_info); + return _snd_pcm_params(pcm, params); +} + int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status) { assert(pcm && status); @@ -227,7 +241,7 @@ int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t frames) int err; assert(pcm); assert(pcm->valid_setup); - assert(frames > 0 && frames < pcm->setup.buffer_size); + assert(frames > 0); err = pcm->fast_ops->set_avail_min(pcm->fast_op_arg, frames); if (err < 0) return err; @@ -1069,6 +1083,60 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, return err; } +int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) +{ + i->type = SND_PCM_MMAP_USER; + i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size); + i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); + if (i->u.user.shmid < 0) { + SYSERR("shmget failed"); + return -errno; + } + i->addr = shmat(i->u.user.shmid, 0, 0); + if (i->addr == (void*) -1) { + SYSERR("shmat failed"); + return -errno; + } + return 0; +} + +int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd) +{ + i->type = SND_PCM_MMAP_KERNEL; + i->size = pcm->setup.mmap_bytes; + i->addr = mmap(NULL, pcm->setup.mmap_bytes, + PROT_WRITE | PROT_READ, + MAP_FILE|MAP_SHARED, + fd, SND_PCM_MMAP_OFFSET_DATA); + if (i->addr == MAP_FAILED || + i->addr == NULL) { + SYSERR("data mmap failed"); + return -errno; + } + i->u.kernel.fd = fd; + return 0; +} + +int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) +{ + if (i->type == SND_PCM_MMAP_USER) { + if (shmdt(i->addr) < 0) { + SYSERR("shmdt failed"); + return -errno; + } + if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) { + SYSERR("shmctl IPC_RMID failed"); + return -errno; + } + } else { + if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) { + SYSERR("data munmap failed"); + return -errno; + } + } + return 0; +} + void snd_pcm_error(const char *file, int line, const char *function, int err, const char *fmt, ...) { va_list arg; diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index 4834fe59..4f0929a3 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -383,7 +383,7 @@ static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params) params->format.sfmt = adpcm->sformat; params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; - err = snd_pcm_params(slave, params); + err = snd_pcm_params_mmap(slave, params); params->format.sfmt = adpcm->cformat; params->xfer_mode = adpcm->cxfer_mode; params->mmap_shape = adpcm->cmmap_shape; diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index 33913b26..20e170d4 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -265,7 +265,7 @@ static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) params->format.sfmt = alaw->sformat; params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; - err = snd_pcm_params(slave, params); + err = snd_pcm_params_mmap(slave, params); params->format.sfmt = alaw->cformat; params->xfer_mode = alaw->cxfer_mode; params->mmap_shape = alaw->cmmap_shape; diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index b6570d4e..0c290cf3 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -393,37 +393,21 @@ static int snd_pcm_hw_mmap(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; snd_pcm_mmap_info_t *i = calloc(1, sizeof(*i)); + int err; if (!i) return -ENOMEM; if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { - i->type = SND_PCM_MMAP_USER; - i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size); - i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); - if (i->u.user.shmid < 0) { - SYSERR("shmget failed"); - free(i); - return -errno; - } - i->addr = shmat(i->u.user.shmid, 0, 0); - if (i->addr == (void*) -1) { - SYSERR("shmat failed"); + err = snd_pcm_alloc_user_mmap(pcm, i); + if (err < 0) { free(i); - return -errno; + return err; } } else { - i->type = SND_PCM_MMAP_KERNEL; - i->size = pcm->setup.mmap_bytes; - i->addr = mmap(NULL, pcm->setup.mmap_bytes, - PROT_WRITE | PROT_READ, - MAP_FILE|MAP_SHARED, - hw->fd, SND_PCM_MMAP_OFFSET_DATA); - if (i->addr == MAP_FAILED || - i->addr == NULL) { - SYSERR("data mmap failed"); + err = snd_pcm_alloc_kernel_mmap(pcm, i, hw->fd); + if (err < 0) { free(i); - return -errno; + return err; } - i->u.kernel.fd = hw->fd; } pcm->mmap_info = i; pcm->mmap_info_count = 1; @@ -452,21 +436,9 @@ static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm) static int snd_pcm_hw_munmap(snd_pcm_t *pcm) { - if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { - if (shmdt(pcm->mmap_info->addr) < 0) { - SYSERR("shmdt failed"); - return -errno; - } - if (shmctl(pcm->mmap_info->u.user.shmid, IPC_RMID, 0) < 0) { - SYSERR("shmctl IPC_RMID failed"); - return -errno; - } - } else { - if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) { - SYSERR("data munmap failed"); - return -errno; - } - } + int err = snd_pcm_free_mmap(pcm, pcm->mmap_info); + if (err < 0) + return err; pcm->mmap_info_count = 0; free(pcm->mmap_info); pcm->mmap_info = 0; diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index 31cddbef..b0cc2393 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -120,7 +120,7 @@ static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params) params->format.sfmt = linear->sformat; params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; - err = snd_pcm_params(slave, params); + err = snd_pcm_params_mmap(slave, params); params->format.sfmt = linear->cformat; params->xfer_mode = linear->cxfer_mode; params->mmap_shape = linear->cmmap_shape; diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 1f9fee28..a649251c 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -112,8 +112,7 @@ struct snd_pcm { void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf); void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs); -int snd_pcm_mmap(snd_pcm_t *pcm); -int snd_pcm_munmap(snd_pcm_t *pcm); +int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params); int snd_pcm_mmap_ready(snd_pcm_t *pcm); ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset); void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames); @@ -137,6 +136,9 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, snd_pcm_xfer_areas_func_t func); ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size); ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size); +int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i); +int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd); +int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i); static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm) { @@ -158,10 +160,13 @@ static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm) static inline size_t snd_pcm_mmap_avail(snd_pcm_t *pcm) { + ssize_t avail; + avail = *pcm->hw_ptr - *pcm->appl_ptr; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - return snd_pcm_mmap_playback_avail(pcm); - else - return snd_pcm_mmap_capture_avail(pcm); + avail += pcm->setup.buffer_size; + if (avail < 0) + avail += pcm->setup.boundary; + return avail; } static inline ssize_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm) @@ -184,10 +189,13 @@ static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm) static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm) { + ssize_t avail; + avail = *pcm->hw_ptr - *pcm->appl_ptr; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - return snd_pcm_mmap_playback_hw_avail(pcm); - else - return snd_pcm_mmap_capture_hw_avail(pcm); + avail += pcm->setup.buffer_size; + if (avail < 0) + avail += pcm->setup.boundary; + return pcm->setup.buffer_size - avail; } #define snd_pcm_mmap_playback_delay snd_pcm_mmap_playback_hw_avail diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index ae78f39a..1c4c046e 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -282,7 +282,7 @@ static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) params->format.sfmt = mulaw->sformat; params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; - err = snd_pcm_params(slave, params); + err = snd_pcm_params_mmap(slave, params); params->format.sfmt = mulaw->cformat; params->xfer_mode = mulaw->cxfer_mode; params->mmap_shape = mulaw->cmmap_shape; diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 94ccc44c..d22929bf 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -221,7 +221,7 @@ static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params) for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *slave = multi->slaves[i].pcm; p.format.channels = multi->slaves[i].channels_count; - err = snd_pcm_params(slave, &p); + err = snd_pcm_params_mmap(slave, &p); if (err < 0) { params->fail_mask = p.fail_mask; params->fail_reason = p.fail_reason; diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index 8c2a5f6d..d659ccb5 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -323,7 +323,6 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_for err = snd_pcm_rate_open(new, NULL, slv->sfmt, slv->rate, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - (*new)->mmap_auto = 1; slv->rate = clt->rate; if (snd_pcm_format_linear(clt->sfmt)) slv->sfmt = clt->sfmt; @@ -388,7 +387,6 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm err = snd_pcm_route_open(new, NULL, slv->sfmt, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - (*new)->mmap_auto = 1; slv->channels = clt->channels; if (snd_pcm_format_linear(clt->sfmt)) slv->sfmt = clt->sfmt; @@ -453,7 +451,6 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_f err = f(new, NULL, slv->sfmt, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - (*new)->mmap_auto = 1; slv->sfmt = cfmt; return 1; } @@ -492,7 +489,6 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, } k++; } - plug->slave->mmap_auto = 0; assert(0); return 0; } diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index e0880776..9d881264 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -322,19 +322,10 @@ int snd_pcm_plugin_mmap(snd_pcm_t *pcm) i = calloc(1, sizeof(*i)); if (!i) return -ENOMEM; - i->type = SND_PCM_MMAP_USER; - i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size); - i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); - if (i->u.user.shmid < 0) { - SYSERR("shmget failed"); + err = snd_pcm_alloc_user_mmap(pcm, i); + if (err < 0) { free(i); - return -errno; - } - i->addr = shmat(i->u.user.shmid, 0, 0); - if (i->addr == (void*) -1) { - SYSERR("shmat failed"); - free(i); - return -errno; + return err; } pcm->mmap_info = i; pcm->mmap_info_count = 1; @@ -348,14 +339,9 @@ int snd_pcm_plugin_munmap(snd_pcm_t *pcm) int err = snd_pcm_munmap(slave); if (err < 0) return err; - if (shmdt(pcm->mmap_info->addr) < 0) { - SYSERR("shmdt failed"); - return -errno; - } - if (shmctl(pcm->mmap_info->u.user.shmid, IPC_RMID, 0) < 0) { - SYSERR("shmctl IPC_RMID failed"); - return -errno; - } + err = snd_pcm_free_mmap(pcm, pcm->mmap_info); + if (err < 0) + return err; free(pcm->mmap_info); pcm->mmap_info_count = 0; pcm->mmap_info = 0; diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index c4c8b241..ec8176cd 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -342,7 +342,7 @@ static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params) /* FIXME: boundary? */ slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED; slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; - err = snd_pcm_params(slave, &slave_params); + err = snd_pcm_params_mmap(slave, &slave_params); params->fail_mask = slave_params.fail_mask; params->fail_reason = slave_params.fail_reason; return err; diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index 7b6883c4..25a160ad 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -494,7 +494,7 @@ static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params) params->format.channels = route->req_schannels; params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; - err = snd_pcm_params(slave, params); + err = snd_pcm_params_mmap(slave, params); params->format.sfmt = route->cformat; params->format.channels = route->cchannels; params->xfer_mode = route->cxfer_mode; diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index ace777d9..541bafc4 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -35,11 +35,39 @@ static LIST_HEAD(slaves); static pthread_mutex_t slaves_mutex = PTHREAD_MUTEX_INITIALIZER; +char *slaves_mutex_holder; + +#define _S(x) #x +#define S(x) _S(x) + +#if 1 +#define Pthread_mutex_lock(mutex) pthread_mutex_lock(mutex) +#define Pthread_mutex_unlock(mutex) pthread_mutex_unlock(mutex) +#else +#define Pthread_mutex_lock(mutex) \ +do { \ + int err = pthread_mutex_trylock(mutex); \ + if (err == EBUSY) { \ + fprintf(stderr, "lock " #mutex " is busy (%s): waiting in " __FUNCTION__ "\n", *(mutex##_holder)); \ + pthread_mutex_lock(mutex); \ + fprintf(stderr, "... got\n"); \ + } \ + *(mutex##_holder) = __FUNCTION__; \ +} while (0) + +#define Pthread_mutex_unlock(mutex) \ +do { \ + *(mutex##_holder) = 0; \ + pthread_mutex_unlock(mutex); \ +} while (0) +#endif typedef struct { struct list_head clients; struct list_head list; snd_pcm_t *pcm; + int sformat; + int srate; size_t channels_count; size_t open_count; size_t setup_count; @@ -47,8 +75,14 @@ typedef struct { size_t prepared_count; size_t running_count; size_t safety_threshold; + size_t silence_frames; + size_t hw_ptr; + int poll[2]; + int polling; pthread_t thread; pthread_mutex_t mutex; + char *mutex_holder; + pthread_cond_t poll_cond; } snd_pcm_share_slave_t; typedef struct { @@ -59,8 +93,10 @@ typedef struct { int *slave_channels; int xfer_mode; int xrun_mode; + size_t avail_min; int async_sig; pid_t async_pid; + int drain_silenced; struct timeval trigger_time; int state; size_t hw_ptr; @@ -71,43 +107,169 @@ typedef struct { void *stopped_data; } snd_pcm_share_t; - static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state); +static size_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave) +{ + ssize_t avail; + snd_pcm_t *pcm = slave->pcm; + avail = slave->hw_ptr - *pcm->appl_ptr; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + avail += pcm->setup.buffer_size; + if (avail < 0) + avail += pcm->setup.boundary; + return avail; +} -static void _snd_pcm_update_poll(snd_pcm_t *pcm) +/* Warning: take the mutex before to call this */ +/* Return number of frames to mmap_forward the slave */ +static size_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave) +{ + struct list_head *i; + size_t buffer_size, boundary; + size_t slave_appl_ptr; + ssize_t frames, safety_frames; + ssize_t min_frames, max_frames; + size_t avail, slave_avail; + size_t slave_hw_avail; + slave_avail = snd_pcm_share_slave_avail(slave); + boundary = slave->pcm->setup.boundary; + buffer_size = slave->pcm->setup.buffer_size; + min_frames = slave_avail; + max_frames = 0; + slave_appl_ptr = *slave->pcm->appl_ptr; + for (i = slave->clients.next; i != &slave->clients; i = i->next) { + snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list); + snd_pcm_t *pcm = share->pcm; + switch (share->state) { + case SND_PCM_STATE_RUNNING: + break; + case SND_PCM_STATE_DRAINING: + if (pcm->stream != SND_PCM_STREAM_PLAYBACK) + continue; + break; + default: + continue; + } + avail = snd_pcm_mmap_avail(pcm); + frames = slave_avail - avail; + if (frames > max_frames) + max_frames = frames; + if (share->state != SND_PCM_STATE_RUNNING) + continue; + if (frames < min_frames) + min_frames = frames; + } + if (max_frames == 0) + return 0; + frames = min_frames; + /* Slave xrun prevention */ + slave_hw_avail = buffer_size - slave_avail; + safety_frames = slave->safety_threshold - slave_hw_avail; + if (safety_frames > 0 && + frames < safety_frames) { + /* Avoid to pass over the last */ + if (max_frames < safety_frames) + frames = max_frames; + else + frames = safety_frames; + } + if (frames < 0) + return 0; + return frames; +} + + +/* + - stop PCM on xrun + - update poll status + - draining silencing + - return distance in frames to next event +*/ +static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - size_t avail; - int ready; + snd_pcm_t *spcm = slave->pcm; + size_t buffer_size = spcm->setup.buffer_size; + int ready = 1, running = 0; + size_t avail = 0, slave_avail; + ssize_t hw_avail; + size_t missing = INT_MAX; + ssize_t ready_missing; + // printf("state=%d hw_ptr=%d appl_ptr=%d slave appl_ptr=%d safety=%d silence=%d\n", share->state, slave->hw_ptr, share->appl_ptr, *slave->pcm->appl_ptr, slave->safety_threshold, slave->silence_frames); switch (share->state) { + case SND_PCM_STATE_RUNNING: + break; case SND_PCM_STATE_DRAINING: - if (pcm->stream == SND_PCM_STREAM_CAPTURE) - ready = 1; - else { - share->hw_ptr = *slave->pcm->hw_ptr; - avail = snd_pcm_mmap_avail(pcm); - if (avail >= pcm->setup.buffer_size) { + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + break; + /* Fall through */ + default: + return INT_MAX; + } + if (slave_xrun && pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) { + _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); + goto update_poll; + } + share->hw_ptr = slave->hw_ptr; + avail = snd_pcm_mmap_avail(pcm); + hw_avail = buffer_size - avail; + slave_avail = snd_pcm_share_slave_avail(slave); + if (avail < slave_avail) { + /* Some frames need still to be transferred */ + ssize_t slave_hw_avail = buffer_size - slave_avail; + ssize_t safety_missing = slave_hw_avail - slave->safety_threshold; + if (safety_missing < 0) { + ssize_t err; + ssize_t frames = slave_avail - avail; + if (-safety_missing <= frames) { + frames = -safety_missing; + missing = 1; + } + err = snd_pcm_mmap_forward(spcm, frames); + assert(err == frames); + slave_avail -= frames; + } else { + if (safety_missing == 0) + missing = 1; + else + missing = safety_missing; + } + } + switch (share->state) { + case SND_PCM_STATE_DRAINING: + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + if (hw_avail <= 0) { _snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP); - ready = 1; - } else - ready = 0; + break; + } + if ((size_t)hw_avail < missing) + missing = hw_avail; + running = 1; + ready = 0; } break; case SND_PCM_STATE_RUNNING: - share->hw_ptr = *slave->pcm->hw_ptr; - avail = snd_pcm_mmap_avail(pcm); - if (avail >= pcm->setup.buffer_size && - pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) { - _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); - ready = 1; - } else - ready = (avail >= pcm->setup.avail_min); + if (pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) { + if (hw_avail <= 0) { + _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); + break; + } + if ((size_t)hw_avail < missing) + missing = hw_avail; + } + ready_missing = share->avail_min - avail; + if (ready_missing > 0) { + ready = 0; + if ((size_t)ready_missing < missing) + missing = ready_missing; + } + running = 1; break; - default: - ready = 1; } + + update_poll: if (ready != share->ready) { char buf[1]; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { @@ -123,132 +285,135 @@ static void _snd_pcm_update_poll(snd_pcm_t *pcm) } share->ready = ready; } + if (!running) + return INT_MAX; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK && + share->state == SND_PCM_STATE_DRAINING && + !share->drain_silenced) { + /* drain silencing */ + if (avail >= slave->silence_frames) { + size_t offset = share->appl_ptr % buffer_size; + size_t xfer = 0; + size_t size = slave->silence_frames; + while (xfer < size) { + size_t frames = size - xfer; + size_t cont = buffer_size - offset; + if (cont < frames) + frames = cont; + snd_pcm_areas_silence(pcm->running_areas, offset, pcm->setup.format.channels, frames, pcm->setup.format.sfmt); + offset += frames; + if (offset >= buffer_size) + offset = 0; + xfer += frames; + } + share->drain_silenced = 1; + } else { + size_t silence_missing; + silence_missing = slave->silence_frames - avail; + if (silence_missing < missing) + missing = silence_missing; + } + } + // printf("missing=%d\n", missing); + return missing; } -static void snd_pcm_share_interrupt(snd_pcm_share_slave_t *slave) +static size_t _snd_pcm_share_slave_missing(snd_pcm_share_slave_t *slave) { + size_t missing = INT_MAX; struct list_head *i; - pthread_mutex_lock(&slave->mutex); - snd_pcm_avail_update(slave->pcm); - /* Update poll status */ + ssize_t avail = snd_pcm_avail_update(slave->pcm); + int slave_xrun = (avail == -EPIPE); + slave->hw_ptr = *slave->pcm->hw_ptr; for (i = slave->clients.next; i != &slave->clients; i = i->next) { snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list); snd_pcm_t *pcm = share->pcm; - switch (share->state) { - case SND_PCM_STATE_DRAINING: - if (pcm->stream == SND_PCM_STREAM_CAPTURE) - break; - /* Fall through */ - case SND_PCM_STATE_RUNNING: - if (pcm->mode & SND_PCM_ASYNC) - kill(share->async_pid, share->async_sig); - _snd_pcm_update_poll(pcm); - break; - default: - break; - - } + size_t m = _snd_pcm_share_missing(pcm, slave_xrun); + if (m < missing) + missing = m; } - pthread_mutex_unlock(&slave->mutex); -} - - -void sigio_handler(int sig ATTRIBUTE_UNUSED) -{ + return missing; } void *snd_pcm_share_slave_thread(void *data) { snd_pcm_share_slave_t *slave = data; + snd_pcm_t *spcm = slave->pcm; + struct pollfd pfd[2]; int err; - struct sigaction act; - err = snd_pcm_async(slave->pcm, SIGIO, 0); - assert(err == 0); - act.sa_handler = sigio_handler; - sigemptyset(&act.sa_mask); - sigaddset(&act.sa_mask, SIGIO); - act.sa_flags = 0; - err = sigaction(SIGIO, &act, NULL); - assert(err == 0); - while (1) { - int sig; - sigwait(&act.sa_mask, &sig); - snd_pcm_share_interrupt(slave); + pfd[0].fd = slave->poll[0]; + pfd[0].events = POLLIN; + pfd[1].fd = snd_pcm_poll_descriptor(spcm); + pfd[1].events = POLLIN | POLLOUT; + Pthread_mutex_lock(&slave->mutex); + err = pipe(slave->poll); + assert(err >= 0); + while (slave->open_count > 0) { + size_t missing; + // printf("begin min_missing\n"); + missing = _snd_pcm_share_slave_missing(slave); + // printf("min_missing=%d\n", missing); + if (missing < INT_MAX) { + size_t hw_ptr; + ssize_t avail_min; + hw_ptr = slave->hw_ptr + missing; + hw_ptr += spcm->setup.frag_size - 1; + if (hw_ptr >= spcm->setup.boundary) + hw_ptr -= spcm->setup.boundary; + hw_ptr -= hw_ptr % spcm->setup.frag_size; + avail_min = hw_ptr - *spcm->appl_ptr; + if (spcm->stream == SND_PCM_STREAM_PLAYBACK) + avail_min += spcm->setup.buffer_size; + if (avail_min < 0) + avail_min += spcm->setup.boundary; + // printf("avail_min=%d\n", avail_min); + if ((size_t)avail_min != spcm->setup.avail_min) + snd_pcm_set_avail_min(spcm, avail_min); + slave->polling = 1; + Pthread_mutex_unlock(&slave->mutex); + err = poll(pfd, 2, -1); + Pthread_mutex_lock(&slave->mutex); + if (pfd[0].revents & POLLIN) { + char buf[1]; + read(pfd[0].fd, buf, 1); + } + } else { + slave->polling = 0; + pthread_cond_wait(&slave->poll_cond, &slave->mutex); + } } + Pthread_mutex_unlock(&slave->mutex); return NULL; } -/* Warning: take the mutex before to call this */ -static void _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave) +static void _snd_pcm_share_update(snd_pcm_t *pcm) { - struct list_head *i; - size_t buffer_size, boundary; - size_t slave_appl_ptr; - ssize_t frames, safety_frames; - size_t min_frames, max_frames; - ssize_t avail; - int err; -#if 0 - avail = snd_pcm_avail_update(slave->pcm); - if (avail <= 0) - return; - assert(avail > 0); -#else - avail = snd_pcm_mmap_avail(slave->pcm); -#endif - boundary = slave->pcm->setup.boundary; - buffer_size = slave->pcm->setup.buffer_size; - min_frames = buffer_size; - max_frames = 0; - slave_appl_ptr = *slave->pcm->appl_ptr; - for (i = slave->clients.next; i != &slave->clients; i = i->next) { - snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list); - snd_pcm_t *pcm = share->pcm; - switch (share->state) { - case SND_PCM_STATE_RUNNING: - // share->hw_ptr = *slave->pcm->hw_ptr; - break; - case SND_PCM_STATE_DRAINING: - { - if (pcm->stream != SND_PCM_STREAM_PLAYBACK) - continue; - // share->hw_ptr = *slave->pcm->hw_ptr; - break; - } - default: - continue; - } - frames = share->appl_ptr - slave_appl_ptr; - if (frames > (ssize_t)buffer_size) - frames -= pcm->setup.boundary; - else if (frames < -(ssize_t)pcm->setup.buffer_size) - frames += pcm->setup.boundary; - if (frames < 0) { - continue; - } - if ((size_t)frames < min_frames) - min_frames = frames; - if ((size_t)frames > max_frames) - max_frames = frames; - } - if (max_frames == 0) + snd_pcm_share_t *share = pcm->private; + snd_pcm_share_slave_t *slave = share->slave; + snd_pcm_t *spcm = slave->pcm; + size_t missing; + ssize_t avail = snd_pcm_avail_update(spcm); + slave->hw_ptr = *slave->pcm->hw_ptr; + missing = _snd_pcm_share_missing(pcm, avail == -EPIPE); + if (!slave->polling) { + pthread_cond_signal(&slave->poll_cond); return; - frames = min_frames; - if (frames > avail) - frames = avail; - /* Slave xrun prevention */ - safety_frames = slave->safety_threshold - snd_pcm_mmap_hw_avail(slave->pcm); - if (safety_frames > 0 && - frames < (ssize_t)safety_frames) { - /* Avoid to pass over the last */ - if (max_frames < (size_t)safety_frames) - frames = max_frames; - else - frames = safety_frames; } - if (frames > 0) { - err = snd_pcm_mmap_forward(slave->pcm, frames); - assert(err == frames); + if (missing < INT_MAX) { + size_t hw_ptr; + ssize_t avail_min; + hw_ptr = slave->hw_ptr + missing; + hw_ptr += spcm->setup.frag_size - 1; + if (hw_ptr >= spcm->setup.boundary) + hw_ptr -= spcm->setup.boundary; + hw_ptr -= hw_ptr % spcm->setup.frag_size; + avail_min = hw_ptr - *spcm->appl_ptr; + if (spcm->stream == SND_PCM_STREAM_PLAYBACK) + avail_min += spcm->setup.buffer_size; + if (avail_min < 0) + avail_min += spcm->setup.boundary; + if ((size_t)avail_min < spcm->setup.avail_min) + snd_pcm_set_avail_min(spcm, avail_min); } } @@ -290,12 +455,27 @@ static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; return -EINVAL; } + if (slave->sformat >= 0) { + if ((req_mask & SND_PCM_PARAMS_SFMT) && + info->req.format.sfmt != slave->sformat) { + info->req.fail_mask |= SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + info->req.format.sfmt = slave->sformat; + info->req_mask |= SND_PCM_PARAMS_SFMT; + } + if (slave->srate >= 0) { + info->req.format.rate = slave->srate; + info->req_mask |= SND_PCM_PARAMS_RATE; + } + info->req_mask |= SND_PCM_PARAMS_CHANNELS; info->req.format.channels = slave->channels_count; err = snd_pcm_params_info(slave->pcm, info); info->req.format.channels = channels; info->req_mask = req_mask; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); if (slave->setup_count > 1 || (slave->setup_count == 1 && !pcm->valid_setup)) { snd_pcm_setup_t *s = &slave->pcm->setup; @@ -322,7 +502,7 @@ static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info info->flags |= SND_PCM_INFO_COMPLEX; } _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -333,7 +513,7 @@ static int snd_pcm_share_mmap(snd_pcm_t *pcm) snd_pcm_mmap_info_t *i; size_t count; int err = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); if (slave->mmap_count == 0) { err = snd_pcm_mmap(slave->pcm); if (err < 0) @@ -348,28 +528,17 @@ static int snd_pcm_share_mmap(snd_pcm_t *pcm) err = -ENOMEM; goto _end; } - i->type = SND_PCM_MMAP_USER; - i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size); - i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); - if (i->u.user.shmid < 0) { - SYSERR("shmget failed"); - free(i); - err = -errno; - goto _end; - } - i->addr = shmat(i->u.user.shmid, 0, 0); - if (i->addr == (void*) -1) { - SYSERR("shmat failed"); + err = snd_pcm_alloc_user_mmap(pcm, i); + if (err < 0) { free(i); - err = -errno; - goto _end; + return err; } share->stopped_data = i->addr; memcpy(i + 1, slave->pcm->mmap_info, count * sizeof(*pcm->mmap_info)); pcm->mmap_info_count = count + 1; pcm->mmap_info = i; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return 0; } @@ -379,28 +548,21 @@ static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) snd_pcm_share_slave_t *slave = share->slave; snd_pcm_mmap_info_t *i = pcm->mmap_info; int err = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); slave->mmap_count--; if (slave->mmap_count == 0) { err = snd_pcm_munmap(slave->pcm); if (err < 0) goto _end; } - if (shmdt(i->addr) < 0) { - SYSERR("shmdt failed"); - err = -errno; - goto _end; - } - if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) { - SYSERR("shmctl IPC_RMID failed"); - err =-errno; + err = snd_pcm_free_mmap(pcm, i); + if (err < 0) goto _end; - } free(i); pcm->mmap_info_count = 0; pcm->mmap_info = 0; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -418,12 +580,12 @@ static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params) } share->xfer_mode = params->xfer_mode; share->xrun_mode = params->xrun_mode; - pthread_mutex_lock(&slave->mutex); + share->avail_min = params->avail_min; + Pthread_mutex_lock(&slave->mutex); if (slave->setup_count > 1 || (slave->setup_count == 1 && !pcm->valid_setup)) { snd_pcm_setup_t *s = &slave->pcm->setup; if (params->format.sfmt != s->format.sfmt) { - printf("%d %d\n", params->format.sfmt, s->format.sfmt); ERR("slave is already running with different format"); params->fail_mask |= SND_PCM_PARAMS_SFMT; } @@ -434,17 +596,34 @@ static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params) } } else { snd_pcm_params_t sp = *params; + snd_pcm_setup_t *ss; + if (slave->sformat >= 0 && + params->format.sfmt != slave->sformat) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + ERR("format requested (%d) differs from configuration (%d)", params->format.sfmt, slave->sformat); + err = -EINVAL; + goto _end; + } + if (slave->srate >= 0) + sp.format.rate = slave->srate; sp.xfer_mode = SND_PCM_XFER_UNSPECIFIED; sp.xrun_mode = SND_PCM_XRUN_NONE; sp.format.channels = slave->channels_count; - err = snd_pcm_params(slave->pcm, &sp); + err = snd_pcm_params_mmap(slave->pcm, &sp); if (err < 0) goto _end; + ss = &slave->pcm->setup; + /* >= 30 ms */ + slave->safety_threshold = ss->format.rate * 30 / 1000; + slave->safety_threshold += ss->frag_size - 1; + slave->safety_threshold -= slave->safety_threshold % ss->frag_size; + slave->silence_frames = slave->safety_threshold; } share->state = SND_PCM_STATE_SETUP; slave->setup_count++; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -458,6 +637,9 @@ static int snd_pcm_share_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) return err; setup->xrun_mode = share->xrun_mode; setup->format.channels = share->channels_count; + if (share->avail_min > setup->buffer_size) + share->avail_min = setup->buffer_size; + setup->avail_min = share->avail_min; if (share->xfer_mode == SND_PCM_XFER_UNSPECIFIED) setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; else @@ -473,7 +655,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status) snd_pcm_share_slave_t *slave = share->slave; int err = 0; ssize_t sd = 0, d = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { status->avail = snd_pcm_mmap_playback_avail(pcm); if (share->state != SND_PCM_STATE_RUNNING && @@ -494,7 +676,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status) status->state = share->state; status->trigger_time = share->trigger_time; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -534,9 +716,9 @@ static int snd_pcm_share_delay(snd_pcm_t *pcm, ssize_t *delayp) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); err = _snd_pcm_share_delay(pcm, delayp); - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -544,22 +726,21 @@ static ssize_t snd_pcm_share_avail_update(snd_pcm_t *pcm) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - ssize_t ret = 0; - pthread_mutex_lock(&slave->mutex); - ret = snd_pcm_avail_update(slave->pcm); - if (share->state == SND_PCM_STATE_RUNNING) - share->hw_ptr = *slave->pcm->hw_ptr; - if (ret >= 0) { - ret = snd_pcm_mmap_avail(pcm); - if ((size_t)ret > pcm->setup.buffer_size) { - if (share->state == SND_PCM_STATE_RUNNING && - pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) - _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN); - return -EPIPE; + ssize_t avail; + Pthread_mutex_lock(&slave->mutex); + if (share->state == SND_PCM_STATE_RUNNING) { + avail = snd_pcm_avail_update(slave->pcm); + if (avail < 0) { + Pthread_mutex_unlock(&slave->mutex); + return avail; } + share->hw_ptr = *slave->pcm->hw_ptr; } - pthread_mutex_unlock(&slave->mutex); - return ret; + Pthread_mutex_unlock(&slave->mutex); + avail = snd_pcm_mmap_avail(pcm); + if ((size_t)avail > pcm->setup.buffer_size) + return -EPIPE; + return avail; } /* Call it with mutex held */ @@ -584,9 +765,15 @@ static ssize_t _snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size) } } snd_pcm_mmap_appl_forward(pcm, size); - if (share->state == SND_PCM_STATE_RUNNING) - _snd_pcm_share_slave_forward(share->slave); - _snd_pcm_update_poll(pcm); + if (share->state == SND_PCM_STATE_RUNNING) { + ssize_t frames = _snd_pcm_share_slave_forward(slave); + if (frames > 0) { + ssize_t err; + err = snd_pcm_mmap_forward(slave->pcm, frames); + assert(err == frames); + } + _snd_pcm_share_update(pcm); + } return size; } @@ -595,9 +782,9 @@ static ssize_t snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; ssize_t ret; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); ret = _snd_pcm_share_mmap_forward(pcm, size); - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return ret; } @@ -606,7 +793,7 @@ static int snd_pcm_share_prepare(snd_pcm_t *pcm) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); if (slave->prepared_count == 0) { err = snd_pcm_prepare(slave->pcm); if (err < 0) @@ -617,7 +804,7 @@ static int snd_pcm_share_prepare(snd_pcm_t *pcm) share->appl_ptr = 0; share->state = SND_PCM_STATE_PREPARED; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -628,7 +815,7 @@ static int snd_pcm_share_start(snd_pcm_t *pcm) int err = 0; if (share->state != SND_PCM_STATE_PREPARED) return -EBADFD; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); share->state = SND_PCM_STATE_RUNNING; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { size_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm); @@ -661,7 +848,9 @@ static int snd_pcm_share_start(snd_pcm_t *pcm) pcm->setup.format.sfmt); xfer += frames; } - _snd_pcm_share_mmap_forward(pcm, hw_avail); + snd_pcm_mmap_appl_forward(pcm, hw_avail); + if (slave->running_count == 0) + snd_pcm_mmap_forward(slave->pcm, hw_avail); } if (slave->running_count == 0) { err = snd_pcm_start(slave->pcm); @@ -669,9 +858,10 @@ static int snd_pcm_share_start(snd_pcm_t *pcm) goto _end; } slave->running_count++; + _snd_pcm_share_update(pcm); gettimeofday(&share->trigger_time, 0); _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -774,7 +964,7 @@ static ssize_t _snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames) n += ret; } snd_pcm_mmap_appl_backward(pcm, n); - _snd_pcm_update_poll(pcm); + _snd_pcm_share_update(pcm); return n; } @@ -783,9 +973,9 @@ static ssize_t snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; ssize_t ret; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); ret = _snd_pcm_share_rewind(pcm, frames); - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return ret; } @@ -793,10 +983,11 @@ static int snd_pcm_share_set_avail_min(snd_pcm_t *pcm, size_t frames) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); pcm->setup.avail_min = frames; - _snd_pcm_update_poll(pcm); - pthread_mutex_unlock(&slave->mutex); + share->avail_min = frames; + _snd_pcm_share_update(pcm); + Pthread_mutex_unlock(&slave->mutex); return 0; } @@ -839,7 +1030,7 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state) err = snd_pcm_delay(slave->pcm, &delay); if (err >= 0 && delay > 0) snd_pcm_rewind(slave->pcm, delay); - _snd_pcm_share_slave_forward(slave); + share->drain_silenced = 0; } share->state = state; slave->prepared_count--; @@ -855,39 +1046,48 @@ static int snd_pcm_share_drain(snd_pcm_t *pcm) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); switch (share->state) { case SND_PCM_STATE_OPEN: err = -EBADFD; goto _end; case SND_PCM_STATE_PREPARED: share->state = SND_PCM_STATE_SETUP; - break; + goto _end; case SND_PCM_STATE_SETUP: goto _end; - case SND_PCM_STATE_DRAINING: - break; - case SND_PCM_STATE_XRUN: - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + } + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + switch (share->state) { + case SND_PCM_STATE_XRUN: share->state = SND_PCM_STATE_SETUP; goto _end; + case SND_PCM_STATE_DRAINING: + case SND_PCM_STATE_RUNNING: + share->state = SND_PCM_STATE_DRAINING; + _snd_pcm_share_update(pcm); + Pthread_mutex_unlock(&slave->mutex); + if (!(pcm->mode & SND_PCM_NONBLOCK)) + snd_pcm_wait(pcm, -1); + return 0; } - /* Fall through */ - case SND_PCM_STATE_RUNNING: - if (snd_pcm_mmap_avail(pcm) <= 0) { - share->state = SND_PCM_STATE_SETUP; - goto _end; + } else { + switch (share->state) { + case SND_PCM_STATE_RUNNING: + _snd_pcm_share_stop(pcm, SND_PCM_STATE_DRAINING); + _snd_pcm_share_update(pcm); + /* Fall through */ + case SND_PCM_STATE_XRUN: + case SND_PCM_STATE_DRAINING: + if (snd_pcm_mmap_capture_avail(pcm) <= 0) + share->state = SND_PCM_STATE_SETUP; + else + share->state = SND_PCM_STATE_DRAINING; + break; } - share->state = SND_PCM_STATE_DRAINING; - break; - } - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - _snd_pcm_update_poll(pcm); - if (!(pcm->mode & SND_PCM_NONBLOCK)) - snd_pcm_wait(pcm, -1); } _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -896,7 +1096,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err = 0; - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slave->mutex); switch (share->state) { case SND_PCM_STATE_OPEN: err = -EBADFD; @@ -911,7 +1111,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm) /* Fall through */ case SND_PCM_STATE_RUNNING: _snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP); - _snd_pcm_update_poll(pcm); + _snd_pcm_share_update(pcm); break; case SND_PCM_STATE_PREPARED: case SND_PCM_STATE_XRUN: @@ -921,7 +1121,7 @@ static int snd_pcm_share_drop(snd_pcm_t *pcm) share->appl_ptr = share->hw_ptr = 0; _end: - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); return err; } @@ -930,27 +1130,27 @@ static int snd_pcm_share_close(snd_pcm_t *pcm) snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; int err = 0; - pthread_mutex_lock(&slaves_mutex); - pthread_mutex_lock(&slave->mutex); + Pthread_mutex_lock(&slaves_mutex); + Pthread_mutex_lock(&slave->mutex); if (pcm->valid_setup) slave->setup_count--; slave->open_count--; if (slave->open_count == 0) { - err = pthread_cancel(slave->thread); - assert(err == 0); + err = snd_pcm_close(slave->pcm); + pthread_cond_signal(&slave->poll_cond); + Pthread_mutex_unlock(&slave->mutex); err = pthread_join(slave->thread, 0); assert(err == 0); - err = snd_pcm_close(slave->pcm); - pthread_mutex_unlock(&slave->mutex); pthread_mutex_destroy(&slave->mutex); + pthread_cond_destroy(&slave->poll_cond); list_del(&slave->list); free(slave); list_del(&share->list); } else { list_del(&share->list); - pthread_mutex_unlock(&slave->mutex); + Pthread_mutex_unlock(&slave->mutex); } - pthread_mutex_unlock(&slaves_mutex); + Pthread_mutex_unlock(&slaves_mutex); close(share->client_socket); close(share->slave_socket); free(share->slave_channels); @@ -1012,6 +1212,7 @@ snd_pcm_fast_ops_t snd_pcm_share_fast_ops = { }; int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, + int sformat, int srate, size_t schannels_count, size_t channels_count, int *channels_map, int stream, int mode) @@ -1060,7 +1261,14 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, return -ENOMEM; } err = socketpair(AF_LOCAL, SOCK_STREAM, 0, sd); - if (err >= 0 && stream == SND_PCM_STREAM_PLAYBACK) { + if (err < 0) { + free(pcm); + free(share->slave_channels); + free(share); + return -errno; + } + + if (stream == SND_PCM_STREAM_PLAYBACK) { int bufsize = 1; err = setsockopt(sd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); if (err >= 0) { @@ -1078,13 +1286,15 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, } if (err < 0) { err = -errno; + close(sd[0]); + close(sd[1]); free(pcm); free(share->slave_channels); free(share); return err; } - pthread_mutex_lock(&slaves_mutex); + Pthread_mutex_lock(&slaves_mutex); for (i = slaves.next; i != &slaves; i = i->next) { snd_pcm_share_slave_t *s = list_entry(i, snd_pcm_share_slave_t, list); if (s->pcm->name && strcmp(s->pcm->name, sname) == 0) { @@ -1096,7 +1306,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, snd_pcm_t *spcm; err = snd_pcm_open(&spcm, sname, stream, mode); if (err < 0) { - pthread_mutex_unlock(&slaves_mutex); + Pthread_mutex_unlock(&slaves_mutex); close(sd[0]); close(sd[1]); free(pcm); @@ -1106,7 +1316,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, } slave = calloc(1, sizeof(*slave)); if (!slave) { - pthread_mutex_unlock(&slaves_mutex); + Pthread_mutex_unlock(&slaves_mutex); snd_pcm_close(spcm); close(sd[0]); close(sd[1]); @@ -1118,16 +1328,35 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, INIT_LIST_HEAD(&slave->clients); slave->pcm = spcm; slave->channels_count = schannels_count; + slave->sformat = sformat; + slave->srate = srate; pthread_mutex_init(&slave->mutex, NULL); + pthread_cond_init(&slave->poll_cond, NULL); list_add_tail(&slave->list, &slaves); + Pthread_mutex_lock(&slave->mutex); err = pthread_create(&slave->thread, NULL, snd_pcm_share_slave_thread, slave); assert(err == 0); + Pthread_mutex_unlock(&slaves_mutex); + } else { + Pthread_mutex_lock(&slave->mutex); + Pthread_mutex_unlock(&slaves_mutex); + for (i = slave->clients.next; i != &slave->clients; i = i->next) { + snd_pcm_share_t *sh = list_entry(i, snd_pcm_share_t, list); + unsigned int k; + for (k = 0; k < sh->channels_count; ++k) { + if (slave_map[sh->slave_channels[k]]) { + ERR("Slave channel %d is already in use", sh->slave_channels[k]); + Pthread_mutex_unlock(&slave->mutex); + close(sd[0]); + close(sd[1]); + free(pcm); + free(share->slave_channels); + free(share); + return -EBUSY; + } + } + } } - pthread_mutex_lock(&slave->mutex); - pthread_mutex_unlock(&slaves_mutex); - slave->open_count++; - list_add_tail(&share->list, &slave->clients); - pthread_mutex_unlock(&slave->mutex); share->slave = slave; share->pcm = pcm; @@ -1150,6 +1379,12 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname, pcm->poll_fd = share->client_socket; pcm->hw_ptr = &share->hw_ptr; pcm->appl_ptr = &share->appl_ptr; + + slave->open_count++; + list_add_tail(&share->list, &slave->clients); + + Pthread_mutex_unlock(&slave->mutex); + *pcmp = pcm; return 0; } @@ -1166,6 +1401,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, size_t channels_count = 0; long schannels_count = -1; size_t schannel_max = 0; + int sformat = -1; + long srate = -1; + snd_config_foreach(i, conf) { snd_config_t *n = snd_config_entry(i); if (strcmp(n->id, "comment") == 0) @@ -1182,6 +1420,20 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, } continue; } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) { + ERR("Invalid type for sformat"); + return -EINVAL; + } + sformat = snd_pcm_format_value(f); + if (sformat < 0) { + ERR("Unknown format %s", f); + return -EINVAL; + } + continue; + } if (strcmp(n->id, "schannels") == 0) { err = snd_config_integer_get(n, &schannels_count); if (err < 0) { @@ -1190,6 +1442,14 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, } continue; } + if (strcmp(n->id, "srate") == 0) { + err = snd_config_integer_get(n, &srate); + if (err < 0) { + ERR("Invalid type for srate"); + return -EINVAL; + } + continue; + } if (strcmp(n->id, "binding") == 0) { if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) { ERR("Invalid type for binding"); @@ -1245,8 +1505,9 @@ int _snd_pcm_share_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, } if (schannels_count <= 0) schannels_count = schannel_max + 1; - err = snd_pcm_share_open(pcmp, name, sname, schannels_count, - channels_count, channels_map, stream, mode); + err = snd_pcm_share_open(pcmp, name, sname, sformat, srate, + schannels_count, + channels_count, channels_map, stream, mode); _free: free(channels_map); return err; -- 2.47.3